/* global supa */
// SGL · Camada de acesso a dados (Supabase)
// Cada função retorna uma Promise. Erros sobem para o caller.

let _orgIdCache = null;

async function getCurrentOrgId() {
  if (_orgIdCache) return _orgIdCache;
  const { data, error } = await window.supa
    .from('memberships')
    .select('organization_id')
    .limit(1)
    .single();
  if (error) throw error;
  _orgIdCache = data?.organization_id;
  return _orgIdCache;
}

function _normalizeEdital(row) {
  if (!row) return row;
  return { ...row, valor: parseFloat(row.valor) || 0 };
}

async function listEditais() {
  const { data, error } = await window.supa
    .from('editais')
    .select('*')
    .order('abertura', { ascending: true });
  if (error) throw error;
  return (data || []).map(_normalizeEdital);
}

async function insertEdital(fields) {
  const orgId = await getCurrentOrgId();
  const { data: userData } = await window.supa.auth.getUser();
  const payload = {
    organization_id: orgId,
    numero: fields.numero,
    orgao: fields.orgao,
    uf: fields.uf,
    cidade: fields.cidade || null,
    plataforma: fields.plataforma,
    valor: Number(fields.valor) || 0,
    abertura: fields.abertura,
    itens: parseInt(fields.itens, 10) || 0,
    score: fields.score ?? 75,
    status: fields.status || 'novo',
    match: fields.match || 'media',
    verba: fields.verba || null,
    created_by: userData?.user?.id || null,
  };
  const { data, error } = await window.supa
    .from('editais')
    .insert(payload)
    .select()
    .single();
  if (error) throw error;
  return _normalizeEdital(data);
}

async function updateEditalStatus(id, status) {
  const { data, error } = await window.supa
    .from('editais')
    .update({ status })
    .eq('id', id)
    .select()
    .single();
  if (error) throw error;
  return _normalizeEdital(data);
}

// ─── EMAILS (M08) ─────────────────────────────────────────

async function listEmails() {
  const { data, error } = await window.supa
    .from('emails')
    .select('*')
    .order('recebido_em', { ascending: false, nullsFirst: false })
    .limit(200);
  if (error) throw error;
  return data || [];
}

async function updateEmailStatus(id, status) {
  const { data, error } = await window.supa
    .from('emails')
    .update({ status })
    .eq('id', id)
    .select()
    .single();
  if (error) throw error;
  return data;
}

async function updateEditalAnalise(id, analise) {
  const { data, error } = await window.supa
    .from('editais')
    .update({ analise_ia: analise })
    .eq('id', id)
    .select()
    .single();
  if (error) throw error;
  return _normalizeEdital(data);
}

// ─── FORNECEDORES ─────────────────────────────────────────

async function listFornecedores() {
  const { data, error } = await window.supa
    .from('fornecedores')
    .select('*')
    .order('nome', { ascending: true });
  if (error) throw error;
  return data || [];
}

// ─── PRODUTOS ─────────────────────────────────────────────

function _normalizeProduto(row) {
  if (!row) return row;
  return {
    id: row.id,
    sku: row.ean || row.cod,
    cod: row.cod,
    ean: row.ean,
    desc: row.descricao,
    cat: row.categoria || '—',
    ncm: row.ncm || '—',
    st: row.estoque ?? 0,
    min: row.estoque_minimo ?? 0,
    max: row.estoque_maximo || Math.max((row.estoque_minimo ?? 0) * 4, 100),
    custo: parseFloat(row.custo) || 0,
    preco: parseFloat(row.preco_venda) || 0,
    fornecedor: row.fornecedor?.nome || '—',
    fornecedor_id: row.fornecedor_principal_id,
    inner: row.inner_minimo ?? 1,
    mov30: { entr: 0, said: 0 },
    giro: 0,
    _raw: row,
  };
}

async function listProdutos() {
  const { data, error } = await window.supa
    .from('produtos')
    .select('*, fornecedor:fornecedores!fornecedor_principal_id(id, nome)')
    .eq('ativo', true)
    .order('descricao', { ascending: true });
  if (error) throw error;
  return (data || []).map(_normalizeProduto);
}

async function listMovimentosByProduto(produtoId, limit = 30) {
  const { data, error } = await window.supa
    .from('movimentos_estoque')
    .select('*')
    .eq('produto_id', produtoId)
    .order('ocorrido_em', { ascending: false })
    .limit(limit);
  if (error) throw error;
  return data || [];
}

async function insertFornecedor(fields) {
  const orgId = await getCurrentOrgId();
  const { data: userData } = await window.supa.auth.getUser();
  const payload = {
    organization_id: orgId,
    nome: fields.nome.trim(),
    cnpj: fields.cnpj?.trim() || null,
    email: fields.email?.trim() || null,
    telefone: fields.telefone?.trim() || null,
    observacoes: fields.observacoes?.trim() || null,
    ativo: fields.ativo !== false,
    created_by: userData?.user?.id || null,
  };
  const { data, error } = await window.supa
    .from('fornecedores')
    .insert(payload)
    .select()
    .single();
  if (error) throw error;
  return data;
}

async function updateFornecedor(id, fields) {
  const payload = {
    nome: fields.nome?.trim(),
    cnpj: fields.cnpj?.trim() || null,
    email: fields.email?.trim() || null,
    telefone: fields.telefone?.trim() || null,
    observacoes: fields.observacoes?.trim() || null,
    ativo: fields.ativo !== false,
  };
  const { data, error } = await window.supa
    .from('fornecedores')
    .update(payload)
    .eq('id', id)
    .select()
    .single();
  if (error) throw error;
  return data;
}

async function deleteFornecedor(id) {
  const { error } = await window.supa
    .from('fornecedores')
    .delete()
    .eq('id', id);
  if (error) throw error;
  return true;
}

async function insertProduto(fields) {
  const orgId = await getCurrentOrgId();
  const { data: userData } = await window.supa.auth.getUser();
  const payload = {
    organization_id: orgId,
    cod: fields.cod.trim(),
    ean: fields.ean?.trim() || null,
    descricao: fields.descricao.trim(),
    categoria: fields.categoria?.trim() || null,
    ncm: fields.ncm?.trim() || null,
    custo: Number(fields.custo) || 0,
    preco_venda: fields.preco_venda ? Number(fields.preco_venda) : null,
    inner_minimo: parseInt(fields.inner_minimo, 10) || 1,
    estoque: parseInt(fields.estoque, 10) || 0,
    estoque_minimo: parseInt(fields.estoque_minimo, 10) || 0,
    estoque_maximo: fields.estoque_maximo ? parseInt(fields.estoque_maximo, 10) : null,
    fornecedor_principal_id: fields.fornecedor_principal_id || null,
    ativo: fields.ativo !== false,
    created_by: userData?.user?.id || null,
  };
  const { data, error } = await window.supa
    .from('produtos')
    .insert(payload)
    .select('*, fornecedor:fornecedores!fornecedor_principal_id(id, nome)')
    .single();
  if (error) throw error;
  return _normalizeProduto(data);
}

async function updateProduto(id, fields) {
  const payload = {
    cod: fields.cod?.trim(),
    ean: fields.ean?.trim() || null,
    descricao: fields.descricao?.trim(),
    categoria: fields.categoria?.trim() || null,
    ncm: fields.ncm?.trim() || null,
    custo: Number(fields.custo) || 0,
    preco_venda: fields.preco_venda ? Number(fields.preco_venda) : null,
    inner_minimo: parseInt(fields.inner_minimo, 10) || 1,
    estoque_minimo: parseInt(fields.estoque_minimo, 10) || 0,
    estoque_maximo: fields.estoque_maximo ? parseInt(fields.estoque_maximo, 10) : null,
    fornecedor_principal_id: fields.fornecedor_principal_id || null,
    ativo: fields.ativo !== false,
  };
  // OBS: estoque NÃO é atualizado aqui — apenas via movimentos_estoque (trigger)
  const { data, error } = await window.supa
    .from('produtos')
    .update(payload)
    .eq('id', id)
    .select('*, fornecedor:fornecedores!fornecedor_principal_id(id, nome)')
    .single();
  if (error) throw error;
  return _normalizeProduto(data);
}

async function deleteProduto(id) {
  const { error } = await window.supa
    .from('produtos')
    .delete()
    .eq('id', id);
  if (error) throw error;
  return true;
}

async function updateProdutoPreco(id, preco) {
  const { data, error } = await window.supa
    .from('produtos')
    .update({ preco_venda: preco })
    .eq('id', id)
    .select('*, fornecedor:fornecedores!fornecedor_principal_id(id, nome)')
    .single();
  if (error) throw error;
  return _normalizeProduto(data);
}

async function insertAjusteEstoque(produtoId, quantidadeReal, observacoes) {
  const orgId = await getCurrentOrgId();
  const { data: userData } = await window.supa.auth.getUser();
  const payload = {
    organization_id: orgId,
    produto_id: produtoId,
    tipo: 'ajuste',
    quantidade: parseInt(quantidadeReal, 10),
    documento: 'Ajuste de inventário',
    observacoes: observacoes || null,
    usuario_id: userData?.user?.id || null,
  };
  const { data, error } = await window.supa
    .from('movimentos_estoque')
    .insert(payload)
    .select()
    .single();
  if (error) throw error;
  return data;
}

async function listFornecedoresByProduto(produtoId) {
  const { data, error } = await window.supa
    .from('produto_fornecedores')
    .select('*, fornecedor:fornecedores!fornecedor_id(id, nome, cnpj)')
    .eq('produto_id', produtoId);
  if (error) throw error;
  return data || [];
}

// ─── EDITAL · ITENS / DOCS / MENSAGENS / HISTÓRICO ─────────

async function listEditalItens(editalId) {
  const { data, error } = await window.supa
    .from('edital_itens')
    .select('*, produto:produtos!produto_id(id, cod, descricao, estoque, custo)')
    .eq('edital_id', editalId)
    .order('numero_item', { ascending: true });
  if (error) throw error;
  return (data || []).map(row => {
    const unit = parseFloat(row.valor_unitario_estimado) || 0;
    const custo = parseFloat(row.produto?.custo) || 0;
    const margem = unit > 0 ? +((unit - custo) / unit * 100).toFixed(1) : 0;
    return {
      id: row.id,
      numero_item: row.numero_item,
      desc: row.descricao,
      btm: row.produto?.cod || null,
      ncm: row.ncm || '—',
      qtd: row.quantidade,
      unit,
      match: row.match_score ?? 0,
      margem,
      estoque: row.produto?.estoque ?? 0,
      _raw: row,
    };
  });
}

async function listEditalDocumentos(editalId) {
  const { data, error } = await window.supa
    .from('edital_documentos')
    .select('*')
    .eq('edital_id', editalId)
    .order('created_at', { ascending: true });
  if (error) throw error;
  return (data || []).map(d => ({
    id: d.id,
    nome: d.nome,
    ext: d.tipo,
    tamanho: d.tamanho || '—',
    paginas: d.paginas,
    publicado: d.publicado_em ? new Date(d.publicado_em).toLocaleDateString('pt-BR') : '—',
    url: d.url,
    _raw: d,
  }));
}

async function listEditalMensagens(editalId) {
  const { data, error } = await window.supa
    .from('edital_mensagens')
    .select('*')
    .eq('edital_id', editalId)
    .order('ocorrido_em', { ascending: false });
  if (error) throw error;
  return (data || []).map(m => ({
    id: m.id,
    de: m.remetente,
    tipo: m.tipo, // 'oficial' | 'esclarecimento' | 'interna'
    quando: new Date(m.ocorrido_em).toLocaleString('pt-BR', { day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit' }),
    assunto: m.assunto,
    corpo: m.corpo,
    _raw: m,
  }));
}

async function listEditalHistorico(editalId) {
  const { data, error } = await window.supa
    .from('edital_historico')
    .select('*')
    .eq('edital_id', editalId)
    .order('ocorrido_em', { ascending: false });
  if (error) throw error;
  return (data || []).map(h => ({
    id: h.id,
    titulo: h.titulo,
    quando: new Date(h.ocorrido_em).toLocaleString('pt-BR', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' }),
    descricao: h.descricao,
    tone: h.tom || '',
    _raw: h,
  }));
}

async function insertEditalMensagem(editalId, fields) {
  const { data: userData } = await window.supa.auth.getUser();
  const payload = {
    edital_id: editalId,
    remetente: fields.remetente?.trim() || 'Mariana (BTM)',
    tipo: fields.tipo || 'interna',
    assunto: fields.assunto.trim(),
    corpo: fields.corpo.trim(),
    ocorrido_em: new Date().toISOString(),
    created_by: userData?.user?.id || null,
  };
  const { data, error } = await window.supa
    .from('edital_mensagens')
    .insert(payload)
    .select()
    .single();
  if (error) throw error;
  return data;
}

// ─── PRECIFICAÇÕES ────────────────────────────────────────

function _normalizePrecif(row) {
  if (!row) return row;
  return {
    id: row.id,
    edital_id: row.edital_id,
    status: row.status,
    regime: row.regime_tributario,
    markup_pct: parseFloat(row.markup_pct) || 60,
    margem_minima_pct: parseFloat(row.margem_minima_pct) || 35,
    icms_inter_pct: parseFloat(row.icms_inter_pct) || 0,
    icms_intra_pct: parseFloat(row.icms_intra_pct) || 0,
    ipi_pct: parseFloat(row.ipi_pct) || 0,
    pis_pct: parseFloat(row.pis_pct) || 0,
    cofins_pct: parseFloat(row.cofins_pct) || 0,
    proposta_gerada: !!row.proposta_gerada,
    proposta_versao: row.proposta_versao || 0,
    edital: row.edital ? { ...row.edital, valor: parseFloat(row.edital.valor) || 0 } : null,
    _raw: row,
  };
}

async function listPrecificacoes() {
  const { data, error } = await window.supa
    .from('precificacoes')
    .select('*, edital:editais(*)')
    .order('created_at', { ascending: false });
  if (error) throw error;
  return (data || []).map(_normalizePrecif);
}

async function listPrecificacaoItens(precificacaoId) {
  const { data, error } = await window.supa
    .from('precificacao_itens')
    .select('*, edital_item:edital_itens(*, produto:produtos!produto_id(id, cod, descricao, custo, inner_minimo, estoque))')
    .eq('precificacao_id', precificacaoId);
  if (error) throw error;
  return (data || [])
    .map(row => {
      const ei = row.edital_item || {};
      const p = ei.produto || {};
      return {
        id: row.id,
        precificacao_id: row.precificacao_id,
        edital_item_id: row.edital_item_id,
        idx: ei.numero_item || 0,
        desc: ei.descricao || '',
        cod: p.cod || null,
        ncm: ei.ncm || '—',
        qtd: ei.quantidade || 0,
        edital_unit: parseFloat(ei.valor_unitario_estimado) || 0,
        custo: parseFloat(p.custo) || 0,
        inner: p.inner_minimo || 1,
        estoque: p.estoque || 0,
        preco_lance: parseFloat(row.preco_lance) || 0,
        margem: parseFloat(row.margem_calculada) || 0,
        frete: parseFloat(row.frete_unitario) || 0,
        alerta_inner: !!row.alerta_inner,
        descartado: !!row.descartado,
        match: ei.match_score ?? 0,
        _raw: row,
      };
    })
    .sort((a, b) => a.idx - b.idx);
}

async function updatePrecificacao(id, fields) {
  const payload = {};
  if ('status' in fields) payload.status = fields.status;
  if ('regime' in fields) payload.regime_tributario = fields.regime;
  if ('markup_pct' in fields) payload.markup_pct = Number(fields.markup_pct);
  if ('margem_minima_pct' in fields) payload.margem_minima_pct = Number(fields.margem_minima_pct);
  if ('icms_inter_pct' in fields) payload.icms_inter_pct = Number(fields.icms_inter_pct);
  if ('icms_intra_pct' in fields) payload.icms_intra_pct = Number(fields.icms_intra_pct);
  if ('ipi_pct' in fields) payload.ipi_pct = Number(fields.ipi_pct);
  if ('pis_pct' in fields) payload.pis_pct = Number(fields.pis_pct);
  if ('cofins_pct' in fields) payload.cofins_pct = Number(fields.cofins_pct);
  if ('proposta_gerada' in fields) payload.proposta_gerada = !!fields.proposta_gerada;
  if ('proposta_versao' in fields) payload.proposta_versao = parseInt(fields.proposta_versao, 10);
  const { data, error } = await window.supa
    .from('precificacoes')
    .update(payload)
    .eq('id', id)
    .select('*, edital:editais(*)')
    .single();
  if (error) throw error;
  return _normalizePrecif(data);
}

// ─── DISPUTAS / LANCES ───────────────────────────────────

async function listDisputas() {
  const { data, error } = await window.supa
    .from('disputas')
    .select('*, edital:editais(*)')
    .order('created_at', { ascending: false });
  if (error) throw error;
  return (data || []).map(d => ({
    ...d,
    edital: d.edital ? { ...d.edital, valor: parseFloat(d.edital.valor) || 0 } : null,
  }));
}

async function insertDisputa({ edital_id, status = 'aguardando' }) {
  const orgId = await getCurrentOrgId();
  const payload = {
    organization_id: orgId,
    edital_id,
    status,
  };
  const { data, error } = await window.supa
    .from('disputas')
    .insert(payload)
    .select('*, edital:editais(*)')
    .single();
  if (error) throw error;
  return {
    ...data,
    edital: data.edital ? { ...data.edital, valor: parseFloat(data.edital.valor) || 0 } : null,
  };
}

async function listLancesByDisputa(disputaId) {
  const { data, error } = await window.supa
    .from('disputa_lances')
    .select('*')
    .eq('disputa_id', disputaId)
    .order('ocorrido_em', { ascending: false });
  if (error) throw error;
  return (data || []).map(l => ({ ...l, valor: parseFloat(l.valor) || 0 }));
}

async function insertLance({ disputa_id, edital_item_id, valor, autor, meu_lance, cnpj_autor }) {
  const { data: userData } = await window.supa.auth.getUser();
  const payload = {
    disputa_id,
    edital_item_id,
    valor: Number(valor),
    autor: autor || 'BTM',
    meu_lance: meu_lance !== false,
    cnpj_autor: cnpj_autor || null,
    ocorrido_em: new Date().toISOString(),
    created_by: userData?.user?.id || null,
  };
  const { data, error } = await window.supa
    .from('disputa_lances')
    .insert(payload)
    .select()
    .single();
  if (error) throw error;
  return { ...data, valor: parseFloat(data.valor) || 0 };
}

async function updateDisputaStatus(disputaId, status) {
  const updates = { status };
  if (status === 'live') updates.aberta_em = new Date().toISOString();
  if (status === 'encerrada') updates.encerrada_em = new Date().toISOString();
  const { data, error } = await window.supa
    .from('disputas')
    .update(updates)
    .eq('id', disputaId)
    .select()
    .single();
  if (error) throw error;
  return data;
}

// ─── EMPENHOS (M05) ───────────────────────────────────────

function _normalizeEmpenho(row) {
  if (!row) return row;
  return {
    id: row.id,
    edital_id: row.edital_id,
    numero: row.numero,
    orgao: row.orgao,
    orgaoFull: row.orgao_full,
    uf: row.uf,
    email: row.email_origem,
    arp: row.arp,
    valor: parseFloat(row.valor) || 0,
    status: row.status,
    recebido_em: row.recebido_em,
    dias: row.recebido_em ? Math.floor((Date.now() - new Date(row.recebido_em).getTime()) / 86400000) : 0,
    prazo: row.prazo_dias || 30,
    triangulacao: !!row.triangulacao,
    atraso: !!row.atraso,
    nf: row.nf_numero || '—',
    rastreio: row.rastreio,
    edital: row.edital ? row.edital.numero : null,
    _raw: row,
  };
}

async function listEmpenhos() {
  const { data, error } = await window.supa
    .from('empenhos')
    .select('*, edital:editais(id, numero)')
    .order('recebido_em', { ascending: false });
  if (error) throw error;
  return (data || []).map(_normalizeEmpenho);
}

async function insertEmpenho(fields) {
  const orgId = await getCurrentOrgId();
  const { data, error } = await window.supa
    .from('empenhos')
    .insert({
      organization_id: orgId,
      edital_id: fields.edital_id || null,
      numero: fields.numero,
      orgao: fields.orgao,
      orgao_full: fields.orgaoFull || fields.orgao,
      uf: fields.uf,
      email_origem: fields.email || null,
      arp: fields.arp || null,
      valor: Number(fields.valor) || 0,
      status: 'rec',
      triangulacao: !!fields.triangulacao,
    })
    .select('*, edital:editais(id, numero)')
    .single();
  if (error) throw error;
  return _normalizeEmpenho(data);
}

async function updateEmpenhoStatus(id, status) {
  const { data, error } = await window.supa
    .from('empenhos')
    .update({ status })
    .eq('id', id)
    .select('*, edital:editais(id, numero)')
    .single();
  if (error) throw error;
  return _normalizeEmpenho(data);
}

// ─── POSTAGENS (M06) ──────────────────────────────────────

function _normalizePostagem(row) {
  if (!row) return row;
  return {
    id: row.id,
    rastreio: row.rastreio,
    empenho_id: row.empenho_id,
    empenho: row.empenho?.numero || null,
    modal: row.modal || '—',
    dest: row.destino_cidade_uf || '—',
    destFull: row.destino_endereco || row.destino_cidade_uf || '—',
    postado: row.postado_em ? new Date(row.postado_em).toLocaleDateString('pt-BR') : '—',
    status: row.status,
    statusLabel: row.status_label || row.status,
    sCls: row.status === 'entregue' ? 'lime' : row.status === 'reverso' ? 'amber' : 'cyan',
    peso: parseFloat(row.peso_kg) || 0,
    volumes: row.volumes || 0,
    valor: parseFloat(row.valor_nota) || 0,
    frete: parseFloat(row.frete) || 0,
    prazo: row.prazo_estimado ? new Date(row.prazo_estimado).toLocaleDateString('pt-BR') : '—',
    reverso: !!row.reverso,
    motivoReverso: row.motivo_reverso,
    eventos: Array.isArray(row.eventos) ? row.eventos : [],
    _raw: row,
  };
}

async function listPostagens() {
  const { data, error } = await window.supa
    .from('postagens')
    .select('*, empenho:empenhos!empenho_id(id, numero)')
    .order('postado_em', { ascending: false, nullsFirst: false });
  if (error) throw error;
  return (data || []).map(_normalizePostagem);
}

// ─── TRIANGULAÇÕES (M06) ──────────────────────────────────

async function listTriangulacoes() {
  const { data, error } = await window.supa
    .from('triangulacoes')
    .select('*, fornecedor:fornecedores(id, nome), empenho:empenhos(id, numero, orgao, uf), itens:triangulacao_itens(*)')
    .order('emitido_em', { ascending: false });
  if (error) throw error;
  return (data || []).map(t => ({
    id: t.numero_pc || t.id,
    realId: t.id,
    forn: t.fornecedor?.nome || '—',
    forn_id: t.fornecedor_id,
    dest: t.empenho ? `${t.empenho.orgao}/${t.empenho.uf || '?'}` : '—',
    destEmpenho: t.empenho?.numero || null,
    val: parseFloat(t.valor_total) || 0,
    inner: !!t.inner_alerta,
    sobra: t.sobra_estimada || 0,
    status: t.status,
    pedidoCompra: t.numero_pc,
    emitido: t.emitido_em ? new Date(t.emitido_em).toLocaleDateString('pt-BR') : '—',
    previsao: t.previsao ? new Date(t.previsao).toLocaleDateString('pt-BR') : '—',
    itens: (t.itens || []).map(it => ({
      desc: it.descricao,
      cod: null,
      qtd: it.quantidade,
      inner: it.inner_minimo,
      qtdComprar: it.qtd_a_comprar,
      sobra: it.sobra,
      custo: parseFloat(it.custo_unitario) || 0,
    })),
    _raw: t,
  }));
}

// ─── TÍTULOS (M07 — CAR + CAP) ────────────────────────────

function _normalizeTitulo(row) {
  if (!row) return row;
  return {
    id: row.numero,
    realId: row.id,
    tipo: row.tipo,
    vcto: row.vencimento ? new Date(row.vencimento).toLocaleDateString('pt-BR') : '—',
    emissao: row.emitido_em ? new Date(row.emitido_em).toLocaleDateString('pt-BR') : '—',
    desc: row.descricao,
    cliente: row.tipo === 'car' ? row.contraparte_nome : null,
    fornecedor: row.tipo === 'cap' ? row.contraparte_nome : null,
    cnpj: row.contraparte_cnpj,
    valor: parseFloat(row.valor) || 0,
    status: row.status,
    empenho: row.empenho?.numero || null,
    nf: row.nf_numero,
    nfOrigem: row.tipo === 'cap' ? row.nf_numero : null,
    forma: row.forma_pagamento,
    banco: row.banco,
    pagoEm: row.pago_em ? new Date(row.pago_em).toLocaleDateString('pt-BR') : null,
    _raw: row,
  };
}

async function listTitulos(tipo) {
  let query = window.supa
    .from('titulos')
    .select('*, empenho:empenhos!empenho_id(id, numero)')
    .order('vencimento', { ascending: true });
  if (tipo) query = query.eq('tipo', tipo);
  const { data, error } = await query;
  if (error) throw error;
  return (data || []).map(_normalizeTitulo);
}

async function insertTitulo(fields) {
  const orgId = await getCurrentOrgId();
  const { data, error } = await window.supa
    .from('titulos')
    .insert({
      organization_id: orgId,
      numero: fields.numero,
      tipo: fields.tipo,
      descricao: fields.descricao || null,
      valor: Number(fields.valor) || 0,
      vencimento: fields.vencimento,
      emitido_em: fields.emitido_em || null,
      status: fields.status || 'aberto',
      forma_pagamento: fields.forma_pagamento || null,
      banco: fields.banco || null,
      contraparte_nome: fields.contraparte_nome,
      contraparte_cnpj: fields.contraparte_cnpj || null,
      empenho_id: fields.empenho_id || null,
      fornecedor_id: fields.fornecedor_id || null,
      nf_numero: fields.nf_numero || null,
    })
    .select('*, empenho:empenhos!empenho_id(id, numero)')
    .single();
  if (error) throw error;
  return _normalizeTitulo(data);
}

async function updateTituloStatus(id, status) {
  const updates = { status };
  if (status === 'pago') updates.pago_em = new Date().toISOString().slice(0, 10);
  const { data, error } = await window.supa
    .from('titulos')
    .update(updates)
    .eq('id', id)
    .select('*, empenho:empenhos!empenho_id(id, numero)')
    .single();
  if (error) throw error;
  return _normalizeTitulo(data);
}

// ─── NFS CONTRA BTM (M07) ─────────────────────────────────

function _normalizeNfContra(row) {
  if (!row) return row;
  return {
    id: row.id,
    nf: row.numero_nf,
    emitente: row.emitente_nome,
    cnpj: row.emitente_cnpj,
    uf: row.uf,
    emissao: row.emissao ? new Date(row.emissao).toLocaleDateString('pt-BR') : '—',
    valor: parseFloat(row.valor_total) || 0,
    venc: row.vencimento ? new Date(row.vencimento).toLocaleDateString('pt-BR') : '—',
    status: row.status,
    cfop: row.cfop || '—',
    baseICMS: parseFloat(row.base_icms) || 0,
    icms: parseFloat(row.icms) || 0,
    ipi: parseFloat(row.ipi) || 0,
    frete: parseFloat(row.frete) || 0,
    observacao: row.observacoes,
    pagoEm: row.pago_em ? new Date(row.pago_em).toLocaleDateString('pt-BR') : null,
    _raw: row,
  };
}

async function listNfsContra() {
  const { data, error } = await window.supa
    .from('nfs_contra')
    .select('*')
    .order('emissao', { ascending: false });
  if (error) throw error;
  return (data || []).map(_normalizeNfContra);
}

async function updateNfContraStatus(id, status) {
  const updates = { status };
  if (status === 'pago') updates.pago_em = new Date().toISOString().slice(0, 10);
  const { data, error } = await window.supa
    .from('nfs_contra')
    .update(updates)
    .eq('id', id)
    .select()
    .single();
  if (error) throw error;
  return _normalizeNfContra(data);
}

// ─── CERTIDÕES (Fase 6) ───────────────────────────────────

function _normalizeCertidao(row) {
  if (!row) return row;
  const validade = row.validade ? new Date(row.validade) : null;
  const today = new Date();
  today.setHours(0, 0, 0, 0);
  const dias = validade ? Math.floor((validade - today) / 86400000) : 999;
  let status;
  if (dias < 0)        status = 'vencida';
  else if (dias <= 7)  status = 'critico';
  else if (dias <= 30) status = 'alerta';
  else                 status = 'vigente';
  return {
    id: row.id,
    nome: row.nome,
    orgao: row.orgao,
    numero: row.numero,
    emissao: row.emissao,
    validade: row.validade,
    dias,
    status,
    arquivo_url: row.arquivo_url,
    observacoes: row.observacoes,
    ativo: row.ativo,
    _raw: row,
  };
}

async function listCertidoes() {
  const { data, error } = await window.supa
    .from('certidoes')
    .select('*')
    .eq('ativo', true)
    .order('validade', { ascending: true });
  if (error) throw error;
  return (data || []).map(_normalizeCertidao);
}

// ─── AUDITORIA LOG (Fase 6) ───────────────────────────────

async function listAuditoriaEvents({ tipo, modulo, limit = 100 } = {}) {
  let q = window.supa
    .from('auditoria_log')
    .select('*')
    .order('ts', { ascending: false })
    .limit(limit);
  if (tipo)   q = q.eq('tipo', tipo);
  if (modulo) q = q.eq('modulo', modulo);
  const { data, error } = await q;
  if (error) throw error;
  return data || [];
}

async function logAuditEvent({ evento, modulo, tipo, detalhes, valor_relacionado, modulo_target, autor }) {
  const orgId = await getCurrentOrgId();
  const { data: userData } = await window.supa.auth.getUser();
  const payload = {
    organization_id: orgId,
    autor: autor || userData?.user?.email?.split('@')[0] || 'Sistema',
    autor_user_id: userData?.user?.id || null,
    evento,
    modulo: modulo || null,
    tipo: tipo || 'audit',
    detalhes: detalhes || null,
    valor_relacionado: valor_relacionado != null ? Number(valor_relacionado) : null,
    modulo_target: modulo_target || null,
  };
  const { data, error } = await window.supa
    .from('auditoria_log')
    .insert(payload)
    .select()
    .single();
  if (error) throw error;
  return data;
}

// ─── CLIENTES (M11) ───────────────────────────────────────

async function listClientes() {
  const { data, error } = await window.supa
    .from('clientes')
    .select('*')
    .order('nome', { ascending: true });
  if (error) throw error;
  return data || [];
}

async function insertCliente(fields) {
  const orgId = await getCurrentOrgId();
  const { data: userData } = await window.supa.auth.getUser();
  const { data, error } = await window.supa
    .from('clientes')
    .insert({
      organization_id: orgId,
      nome: fields.nome.trim(),
      nome_fantasia: fields.nome_fantasia?.trim() || null,
      cnpj: fields.cnpj?.trim() || null,
      tipo: fields.tipo || 'publico',
      email: fields.email?.trim() || null,
      telefone: fields.telefone?.trim() || null,
      endereco: fields.endereco?.trim() || null,
      cidade: fields.cidade?.trim() || null,
      uf: fields.uf?.trim() || null,
      cep: fields.cep?.trim() || null,
      contato_responsavel: fields.contato_responsavel?.trim() || null,
      observacoes: fields.observacoes?.trim() || null,
      ativo: fields.ativo !== false,
      created_by: userData?.user?.id || null,
    })
    .select()
    .single();
  if (error) throw error;
  return data;
}

async function updateCliente(id, fields) {
  const payload = {
    nome: fields.nome?.trim(),
    nome_fantasia: fields.nome_fantasia?.trim() || null,
    cnpj: fields.cnpj?.trim() || null,
    tipo: fields.tipo || 'publico',
    email: fields.email?.trim() || null,
    telefone: fields.telefone?.trim() || null,
    endereco: fields.endereco?.trim() || null,
    cidade: fields.cidade?.trim() || null,
    uf: fields.uf?.trim() || null,
    cep: fields.cep?.trim() || null,
    contato_responsavel: fields.contato_responsavel?.trim() || null,
    observacoes: fields.observacoes?.trim() || null,
    ativo: fields.ativo !== false,
  };
  const { data, error } = await window.supa
    .from('clientes')
    .update(payload)
    .eq('id', id)
    .select()
    .single();
  if (error) throw error;
  return data;
}

async function deleteCliente(id) {
  const { error } = await window.supa.from('clientes').delete().eq('id', id);
  if (error) throw error;
  return true;
}

async function listClienteRelations(cliente) {
  const tokens = [cliente.nome_fantasia, cliente.nome].filter(Boolean);
  const orgaoFilter = tokens.map(t => `orgao.ilike.%${t}%`).join(',');

  let titulosQuery = window.supa
    .from('titulos')
    .select('*, empenho:empenhos!empenho_id(id, numero)')
    .eq('tipo', 'car');
  if (cliente.cnpj) {
    titulosQuery = titulosQuery.eq('contraparte_cnpj', cliente.cnpj);
  } else if (tokens.length) {
    titulosQuery = titulosQuery.or(tokens.map(t => `contraparte_nome.ilike.%${t}%`).join(','));
  }
  titulosQuery = titulosQuery.order('vencimento', { ascending: true }).limit(50);

  const [edRes, empRes, titRes] = await Promise.all([
    tokens.length
      ? window.supa.from('editais')
          .select('id, numero, orgao, uf, valor, status, abertura')
          .or(orgaoFilter)
          .order('abertura', { ascending: false }).limit(50)
      : Promise.resolve({ data: [] }),
    tokens.length
      ? window.supa.from('empenhos')
          .select('id, numero, orgao, uf, valor, status, recebido_em, nf_numero')
          .or(orgaoFilter)
          .order('recebido_em', { ascending: false }).limit(50)
      : Promise.resolve({ data: [] }),
    titulosQuery,
  ]);

  return {
    editais: (edRes.data || []).map(e => ({ ...e, valor: parseFloat(e.valor) || 0 })),
    empenhos: (empRes.data || []).map(e => ({ ...e, valor: parseFloat(e.valor) || 0 })),
    titulos: (titRes.data || []).map(_normalizeTitulo),
  };
}

async function getDashboardStats() {
  const [editais, produtos, fornecedores] = await Promise.all([
    listEditais(),
    listProdutos(),
    listFornecedores(),
  ]);
  return { editais, produtos, fornecedores };
}

async function getTickerStats() {
  const orgId = await getCurrentOrgId();
  const todayStart = new Date(); todayStart.setHours(0, 0, 0, 0);
  const [editaisRes, empenhosRes, titulosRes, certRes, lancesRes] = await Promise.all([
    window.supa.from('editais').select('status', { count: 'exact' }).eq('organization_id', orgId),
    window.supa.from('empenhos').select('status', { count: 'exact' }).eq('organization_id', orgId),
    window.supa.from('titulos').select('valor, status, tipo').eq('organization_id', orgId).eq('tipo', 'car').neq('status', 'pago'),
    window.supa.from('certidoes').select('vencimento').eq('organization_id', orgId).order('vencimento', { ascending: true }).limit(1),
    window.supa.from('disputa_lances').select('id', { count: 'exact', head: true }).gte('ocorrido_em', todayStart.toISOString()),
  ]);
  const editaisAtivos = (editaisRes.data || []).filter(e => !['descartado', 'ganho'].includes(e.status)).length;
  const empenhosAtivos = (empenhosRes.data || []).filter(e => e.status !== 'pago').length;
  const aReceber = (titulosRes.data || []).reduce((s, t) => s + (parseFloat(t.valor) || 0), 0);
  const cndDias = certRes.data?.[0]?.vencimento
    ? Math.max(0, Math.ceil((new Date(certRes.data[0].vencimento) - Date.now()) / 86400000))
    : null;
  const lancesHoje = lancesRes.count || 0;
  return { editaisAtivos, empenhosAtivos, aReceber, cndDias, lancesHoje };
}

window.dataApi = {
  getCurrentOrgId,
  getDashboardStats,
  getTickerStats,
  listEditais,
  insertEdital,
  updateEditalStatus,
  updateEditalAnalise,
  listEmails,
  updateEmailStatus,
  listFornecedores,
  insertFornecedor,
  updateFornecedor,
  deleteFornecedor,
  listProdutos,
  insertProduto,
  updateProduto,
  deleteProduto,
  updateProdutoPreco,
  insertAjusteEstoque,
  listMovimentosByProduto,
  listFornecedoresByProduto,
  listEditalItens,
  listEditalDocumentos,
  listEditalMensagens,
  listEditalHistorico,
  insertEditalMensagem,
  listPrecificacoes,
  listPrecificacaoItens,
  updatePrecificacao,
  listDisputas,
  insertDisputa,
  listLancesByDisputa,
  insertLance,
  updateDisputaStatus,
  listEmpenhos,
  insertEmpenho,
  updateEmpenhoStatus,
  listPostagens,
  listTriangulacoes,
  listTitulos,
  insertTitulo,
  updateTituloStatus,
  listNfsContra,
  updateNfContraStatus,
  listCertidoes,
  listAuditoriaEvents,
  logAuditEvent,
  listClientes,
  insertCliente,
  updateCliente,
  deleteCliente,
  listClienteRelations,
};
