// SGL · Cliente OpenAI (frontend)
// Chama a Edge Function `openai-proxy` via supa.functions.invoke, que anexa
// automaticamente o token da sessão do usuário logado. A chave da OpenAI NUNCA
// trafega pelo navegador — fica só nos Secrets do Supabase (servidor).

// Mesma chave usada pelo M13 (m_config.jsx). Apenas tunings NÃO-sensíveis ficam
// no localStorage; a OPENAI_API_KEY nunca é salva aqui.
const OPENAI_CFG_STORAGE_KEY = 'sgl_config_v1';

/**
 * Lê as configurações não-sensíveis da OpenAI salvas no M13.
 * @returns {{ modelo: string, temperatura?: number, maxTokens?: number, org?: string }}
 */
function loadOpenAICfg() {
  try {
    const all = JSON.parse(localStorage.getItem(OPENAI_CFG_STORAGE_KEY) || '{}');
    const o = (all.integracoes && all.integracoes.openai) || {};
    return {
      modelo: o.modelo || 'gpt-4o',
      temperatura: o.temperatura,
      maxTokens: o.maxTokens,
      org: o.org || undefined,
    };
  } catch {
    return { modelo: 'gpt-4o' };
  }
}

/** Mensagem amigável para erros de transporte do functions.invoke. */
function friendlyInvokeError(error) {
  const name = error && error.name;
  if (name === 'FunctionsFetchError') {
    return 'Não foi possível contatar o servidor. A Edge Function "openai-proxy" já foi publicada no Supabase?';
  }
  return (error && error.message) || 'Falha ao chamar a função openai-proxy.';
}

/**
 * Chama a Edge Function openai-proxy.
 * @param {string} action - ex.: 'test'
 * @param {object} [payload] - dados adicionais para a ação
 * @returns {Promise<{ ok: boolean, error?: string, latencia?: number, modelos?: number }>}
 */
async function callOpenAI(action, payload = {}) {
  if (!window.supa) {
    return { ok: false, error: 'Supabase não inicializado.' };
  }

  // Exige usuário logado — a função do servidor também valida, isto é só UX.
  const { data: { session } } = await window.supa.auth.getSession();
  if (!session) {
    return { ok: false, error: 'Você precisa estar logado para usar a IA.' };
  }

  const cfg = loadOpenAICfg();

  try {
    const { data, error } = await window.supa.functions.invoke('openai-proxy', {
      body: { action, ...cfg, ...payload },
    });

    if (error) {
      // FunctionsHttpError carrega a Response em .context — extraímos a mensagem PT do corpo.
      let serverMsg = null;
      try {
        if (error.context && typeof error.context.json === 'function') {
          const body = await error.context.json();
          serverMsg = body && body.error;
        }
      } catch {
        /* corpo não-JSON: ignora e usa fallback */
      }
      return { ok: false, error: serverMsg || friendlyInvokeError(error) };
    }

    return data || { ok: false, error: 'Resposta vazia do servidor.' };
  } catch (e) {
    return { ok: false, error: (e && e.message) || 'Erro inesperado ao chamar a OpenAI.' };
  }
}

/**
 * Testa a conexão com a OpenAI (custo zero — lista modelos no servidor).
 * @returns {Promise<{ ok: boolean, error?: string, latencia?: number, modelos?: number }>}
 */
async function testOpenAI() {
  return callOpenAI('test');
}

// ─── Análise de edital (M02) ──────────────────────────────────

const PDFJS_WORKER_SRC = 'https://unpkg.com/pdfjs-dist@3.11.174/build/pdf.worker.min.js';
const ANALISE_MAX_CHARS = 24000; // limita contexto/custo enviado à OpenAI

/**
 * Extrai a camada de texto de um PDF no próprio navegador (pdf.js).
 * Retorna '' se o PDF não tiver texto (ex.: escaneado/imagem).
 * @param {File|Blob} file
 * @returns {Promise<string>}
 */
async function extrairTextoPdf(file) {
  const lib = window.pdfjsLib;
  if (!lib) throw new Error('Leitor de PDF (pdf.js) não carregado.');
  lib.GlobalWorkerOptions.workerSrc = PDFJS_WORKER_SRC;

  const buf = await file.arrayBuffer();
  const pdf = await lib.getDocument({ data: buf }).promise;
  let texto = '';
  for (let i = 1; i <= pdf.numPages; i++) {
    const page = await pdf.getPage(i);
    const content = await page.getTextContent();
    texto += content.items.map(it => it.str).join(' ') + '\n';
  }
  return texto.trim();
}

/** Monta as mensagens do prompt de extração estruturada do edital. */
function buildEditalMessages(texto) {
  const sys = [
    'Você é um analista de licitações públicas brasileiro.',
    'Extraia do EDITAL as informações estruturadas e responda SOMENTE em JSON válido,',
    'sem nenhum texto fora do JSON. NÃO invente: se um campo não constar no edital, use null',
    '(ou lista vazia). Use exatamente este schema:',
    '{',
    '  "objeto": string|null,',
    '  "orgao": string|null, "uf": string|null, "cidade": string|null, "plataforma": string|null,',
    '  "valor_estimado": number|null,',
    '  "data_abertura": string|null,',
    '  "prazo_entrega": string|null, "prazo_pagamento": string|null,',
    '  "validade_proposta": string|null, "vigencia_arp": string|null,',
    '  "intervalo_lances": string|null, "local_entrega": string|null,',
    '  "habilitacao": [{"documento": string, "obrigatorio": boolean}],',
    '  "alertas": [{"tom": "amber"|"red"|"cyan", "titulo": string, "descricao": string}],',
    '  "confianca": number',
    '}',
    'Regras: valor_estimado como número em reais (sem "R$" nem separador de milhar).',
    'alertas = prazos curtos, exigência de amostras, condições incomuns ou riscos relevantes',
    '(tom: red=crítico, amber=atenção, cyan=informativo). confianca = 0 a 1.',
  ].join('\n');
  const user = 'EDITAL:\n"""\n' + texto.slice(0, ANALISE_MAX_CHARS) + '\n"""';
  return [
    { role: 'system', content: sys },
    { role: 'user', content: user },
  ];
}

/**
 * Analisa o texto de um edital via OpenAI e retorna campos estruturados.
 * @param {string} texto
 * @returns {Promise<{ ok: boolean, error?: string, analise?: object, usage?: object }>}
 */
async function analisarEdital(texto) {
  const limpo = (texto || '').trim();
  if (limpo.length < 50) {
    return { ok: false, error: 'Texto do edital muito curto para analisar (mínimo ~50 caracteres).' };
  }

  const cfg = loadOpenAICfg();
  const r = await callOpenAI('chat', {
    messages: buildEditalMessages(limpo),
    model: cfg.modelo,
    temperature: typeof cfg.temperatura === 'number' ? cfg.temperatura : 0.2,
    max_tokens: cfg.maxTokens || 2000,
    response_format: { type: 'json_object' },
  });
  if (!r.ok) return r;

  let analise;
  try {
    analise = JSON.parse(r.content);
  } catch {
    return { ok: false, error: 'A IA retornou um formato inesperado. Tente novamente.' };
  }

  analise.modelo = r.model || cfg.modelo || 'gpt-4o';
  analise.analisado_em = new Date().toISOString();
  return { ok: true, analise, usage: r.usage };
}

window.callOpenAI = callOpenAI;
window.testOpenAI = testOpenAI;
window.extrairTextoPdf = extrairTextoPdf;
window.analisarEdital = analisarEdital;
