/* global React, Icon, Button, PageHead, fmtDate */

// ============================================================
// Configurações › Financeiro › Classificação pendente › Vendas sem tom   (rota `estoque/sem-tom`)
// + o aviso do produto em Produtos › Estoque
// ============================================================
// Venda de produto com tonalidades (hoje só o TMP10) lançada no Marketplace como "Tom não informado": a venda
// entrou, e o estoque NÃO baixou — nem tonalidade (qual?), nem produto (o número dele é a soma das tonalidades).
// A fila leva cada uma ao tom dela; definir o tom baixa a tonalidade, e o produto acompanha.
//
// O que a fila NÃO faz, de propósito:
//  · não decide QUEM está na fila — quem decide é a visão `estoque_tom_pendente` (SUPABASE_estoque-pai-derivado.sql):
//    linha sem tom, lançamento que mexe em estoque, não cancelado, e nenhuma contagem aplicada depois dela. As
//    cargas de 13/09 e 15/09 ficam fora (entraram sem mexer em estoque de produto nenhum). Uma regra, um lugar;
//  · não sugere o tom — palpite gravado vira fato sem ninguém perceber (a regra da fila de conta);
//  · não mexe em venda, resultado nem financeiro: só no estoque.
// A gravação é a `tm_definir_tom`, UMA venda por chamada, numa transação: ou a linha ganha o tom e a tonalidade
// baixa, ou nada muda. Refazer é seguro: a venda que já tem tom é recusada (TM017) e não baixa de novo.

// ─── Funções puras (a PROVA extrai estas) ────────────────────────────────────

// Por produto: quantas vendas esperam o tom e quantas unidades.
function stPorProduto(rows) {
  const m = {};
  (rows || []).forEach(r => {
    if (!r || !r.produto_id) return;
    const g = m[r.produto_id] || (m[r.produto_id] = { produtoId: r.produto_id, sku: r.sku || '', nome: r.produto_nome || '', linhas: 0, un: 0 });
    g.linhas += 1; g.un += Number(r.quantidade) || 0;
  });
  return m;
}

// O EFEITO de dar o tom `tom` às vendas `alvos`: o antes e o depois da tonalidade, do produto e da fila — a
// mesma conta que a função faz (a tonalidade baixa a soma das quantidades; o produto, que é a soma, acompanha).
function stEfeito(alvos, tom, produto, filaDoProduto) {
  const un = (alvos || []).reduce((a, r) => a + (Number(r.quantidade) || 0), 0);
  const tomAntes = Number(tom && tom.estoque) || 0, prodAntes = Number(produto && produto.estoque) || 0;
  const filaAntes = (filaDoProduto || []).reduce((a, r) => a + (Number(r.quantidade) || 0), 0);
  return { n: (alvos || []).length, un, tomAntes, tomDepois: tomAntes - un, prodAntes, prodDepois: prodAntes - un,
    filaAntes, filaDepois: filaAntes - un, jaComLinha: (alvos || []).filter(r => r.tem_linha_no_produto).length };
}

function stPeriodo(r) {
  const a = r.periodo_inicio ? fmtDate(r.periodo_inicio) : null, b = r.periodo_fim ? fmtDate(r.periodo_fim) : null;
  if (a && b) return a.slice(0, 5) + '–' + b;
  return r.data_venda ? fmtDate(r.data_venda) : '—';
}

// A leitura é da PRÓPRIA fila (não de hooks de lista, que engolem o erro): `null` = lendo; erro = digo que não li.
async function stLer() {
  const r = await window.tmDb.lerTomPendente();
  if (r.error) return { erro: String(r.error.message || r.error) };
  if (r.naoInstalado) return { naoInstalado: true };
  const rows = r.rows || [];
  const ids = [...new Set(rows.map(x => x.produto_id).filter(Boolean))];
  if (!ids.length) return { dados: { rows, tons: [], produtos: [], lojas: [] } };
  const sb = window.tmSupabase;
  const [tv, pr, lj] = await Promise.all([
    sb.from('produto_variacoes').select('id,produto_id,nome,sku,ativo,estoque,ordem').in('produto_id', ids).order('ordem'),
    sb.from('produtos').select('id,sku,nome,estoque').in('id', ids),
    sb.from('marketplace_lojas').select('id,nome'),
  ]);
  const falhou = [tv, pr, lj].map(x => x && x.error).find(Boolean);
  if (falhou) return { erro: String(falhou.message || falhou) };
  return { dados: { rows, tons: tv.data || [], produtos: pr.data || [], lojas: lj.data || [] } };
}

// ─── O aviso em Produtos › Estoque ───────────────────────────────────────────
// "TMP10 · 4.758 pelas tonalidades − 2 sem tom". O número da prateleira é o do razão (a soma das tonalidades);
// o "− N" diz que o de verdade é menor, e por quê. Some quando a fila zera.
function TomPendenteAviso({ produtos, onNav }) {
  const [st, setSt] = React.useState(null);   // null = lendo · { rows } · { naoInstalado } · { error }
  React.useEffect(() => {
    let vivo = true;
    if (!window.tmDb || !window.tmDb.lerTomPendente) return undefined;
    window.tmDb.lerTomPendente().then(r => { if (vivo) setSt(r); }).catch(e => { if (vivo) setSt({ error: e }); });
    return () => { vivo = false; };
  }, []);
  if (!st || st.naoInstalado) return null;
  if (st.error) {
    return (
      <div className="bal-aviso danger" style={{ marginBottom: 16 }}>
        <Icon name="alert-circle" size={15} />
        <div>Não consegui ler as vendas sem tom ({String(st.error.message || st.error)}). O estoque dos produtos com tonalidades pode estar acima do real sem este aviso dizer.</div>
      </div>);
  }
  const grupos = Object.values(stPorProduto(st.rows));
  if (!grupos.length) return null;
  const estDe = (id) => { const p = (produtos || []).find(x => x.id === id); return p ? (Number(p.est) || 0) : null; };
  return (
    <div className="bal-aviso warning" style={{ marginBottom: 16 }}>
      <Icon name="palette" size={15} />
      <div style={{ flex: 1 }}>
        {grupos.map(g => (
          <div key={g.produtoId}>
            <b>{g.sku}</b>{g.nome ? ' · ' + g.nome : ''} — {estDe(g.produtoId) != null ? <><b>{estDe(g.produtoId)}</b> pelas tonalidades </> : null}
            <b>− {g.un} sem tom</b>: {g.un} un. vendida{g.un !== 1 ? 's' : ''} em {g.linhas} lançamento{g.linhas !== 1 ? 's' : ''} sem tom — o estoque de verdade é menor.{' '}
            {onNav && <a style={{ cursor: 'pointer', color: 'var(--tm-brand-champagne)' }} onClick={() => onNav('estoque/sem-tom')}>Definir o tom →</a>}
          </div>
        ))}
      </div>
    </div>);
}

// ─── A fila ───────────────────────────────────────────────────────────────────
function FilaSemTom({ onNav }) {
  const [dados, setDados] = React.useState(null);
  const [estado, setEstado] = React.useState('lendo');   // lendo · ok · erro · naoInstalado
  const [erroLeitura, setErroLeitura] = React.useState('');
  const [recarga, setRecarga] = React.useState(0);
  React.useEffect(() => {
    let vivo = true;
    stLer().then(r => {
      if (!vivo) return;
      if (r.erro) { setErroLeitura(r.erro); setEstado('erro'); setDados(null); }
      else if (r.naoInstalado) { setEstado('naoInstalado'); setDados(null); }
      else { setErroLeitura(''); setEstado('ok'); setDados(r.dados); }
    }).catch(e => { if (vivo) { setErroLeitura(String((e && e.message) || e)); setEstado('erro'); } });
    return () => { vivo = false; };
  }, [recarga]);
  const [sel, setSel] = React.useState(() => new Set());
  const [tomId, setTomId] = React.useState('');
  const [confirmando, setConfirmando] = React.useState(false);
  const [gravando, setGravando] = React.useState(false);
  const [relato, setRelato] = React.useState(null);

  const rows = dados ? dados.rows : [];
  const lojaNome = (id) => { const l = ((dados && dados.lojas) || []).find(x => x.id === id); return l ? l.nome : '—'; };
  const lista = rows.slice().sort((a, b) => String(a.data_venda || '').localeCompare(String(b.data_venda || '')) || String(a.numero).localeCompare(String(b.numero)));
  const marcados = lista.filter(r => sel.has(r.item_id));
  const produtosMarcados = [...new Set(marcados.map(r => r.produto_id))];
  const umProduto = produtosMarcados.length === 1 ? produtosMarcados[0] : null;
  const tons = ((dados && dados.tons) || []).filter(v => v.produto_id === umProduto);
  const tom = tons.find(v => v.id === tomId) || null;
  const produto = ((dados && dados.produtos) || []).find(p => p.id === umProduto) || null;
  const efeito = tom && umProduto ? stEfeito(marcados, tom, produto, lista.filter(r => r.produto_id === umProduto)) : null;
  const somaSel = marcados.reduce((a, r) => a + (Number(r.quantidade) || 0), 0);
  const negado = window.tmNegadoAqui ? window.tmNegadoAqui(['criar', 'editar']) : null;

  const toggle = (id) => { setConfirmando(false); setSel(prev => { const n = new Set(prev); n.has(id) ? n.delete(id) : n.add(id); return n; }); };
  const todos = lista.length > 0 && lista.every(r => sel.has(r.item_id));
  const toggleTodos = () => { setConfirmando(false); setSel(todos ? new Set() : new Set(lista.map(r => r.item_id))); };

  const aplicar = async () => {
    // escrita que chega aqui sem passar pelo botão também pergunta (a regra do Enter)
    if (negado || !tom || !marcados.length || gravando) return;
    setGravando(true); setRelato(null);
    const feitos = [], falhas = [];
    for (const r of marcados) {   // uma venda por chamada: cada uma é uma transação, e a que falha não leva as outras
      const res = await window.tmDb.definirTom(r.item_id, tom.id);
      if (res && res.error) falhas.push({ numero: r.numero, erro: String(res.error.message || res.error) });
      else feitos.push(r.numero);
    }
    setRelato({ tom: tom.nome, total: marcados.length, feitos, falhas });
    setSel(new Set()); setConfirmando(false); setGravando(false);
    setRecarga(k => k + 1);
  };

  return (
    <div className="canvas">
      <PageHead title="Vendas sem tom"
        sub="vendas de marketplace lançadas com “Tom não informado” — escolha o tom; a tonalidade baixa e a venda sai da fila" />

      <div className="bal-aviso info" style={{ marginBottom: 16 }}>
        <Icon name="info" size={15} />
        <div>
          <b>O que muda e o que não muda.</b> Definir o tom <b>baixa o estoque da tonalidade</b> escolhida — e o do produto,
          que é a soma delas. <b>Não muda</b> a venda, o resultado nem o financeiro. A fila <b>não sugere</b> tom. Ficam fora
          dela as vendas das cargas de 13/09 e 15/09 (entraram sem mexer em estoque de produto nenhum) e as que uma contagem
          aplicada depois já viu sair.
        </div>
      </div>

      {sel.size > 0 && (
        <div className="bal-aviso" style={{ marginBottom: 10, display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap' }}>
          <Icon name="check-square" size={15} />
          <span style={{ fontSize: 12.5 }}><b>{marcados.length} venda{marcados.length !== 1 ? 's' : ''}</b> · {somaSel} un.</span>
          <span style={{ flex: 1 }} />
          {umProduto
            ? <select className="input" style={{ maxWidth: 280 }} value={tomId} onChange={e => { setTomId(e.target.value); setConfirmando(false); }}>
                <option value="">Escolha o tom…</option>
                {tons.map(v => <option key={v.id} value={v.id}>{v.nome}{v.sku ? ' · ' + v.sku : ''}{v.ativo === false ? ' (inativa)' : ''} — {Number(v.estoque) || 0} un.</option>)}
              </select>
            : <span className="muted" style={{ fontSize: 12 }}>Marque vendas de um produto só — o tom é de um produto.</span>}
          <Button variant="primary" size="sm" icon="palette" pede={['criar', 'editar']} disabled={!tom || gravando}
            onClick={() => setConfirmando(true)}>Ver o efeito</Button>
        </div>
      )}

      {confirmando && efeito && (
        <div className="bal-aviso warning" style={{ marginBottom: 12 }}>
          <Icon name="alert-triangle" size={15} />
          <div style={{ flex: 1 }}>
            <b>Definir {tom.nome} em {efeito.n} venda{efeito.n !== 1 ? 's' : ''} ({efeito.un} un. de {produto ? produto.sku : 'produto'}):</b>
            <div style={{ marginTop: 4 }}>· o estoque de {tom.nome} vai de {efeito.tomAntes} para <b>{efeito.tomDepois}</b>;</div>
            <div style={{ marginTop: 2 }}>· o de {produto ? produto.sku : 'produto'} (a soma das tonalidades) vai de {efeito.prodAntes} para <b>{efeito.prodDepois}</b>;</div>
            <div style={{ marginTop: 2 }}>· a fila de {produto ? produto.sku : 'produto'} cai de {efeito.filaAntes} para {efeito.filaDepois} un. sem tom;</div>
            {efeito.jaComLinha > 0 && (
              <div style={{ marginTop: 2 }}>· {efeito.jaComLinha} dela{efeito.jaComLinha !== 1 ? 's' : ''} o app antigo já tinha baixado no produto: a linha do razão que existe ganha o tom — nenhuma linha nova, a unidade não conta duas vezes;</div>)}
            <div style={{ marginTop: 2 }}>· a venda, o resultado e o financeiro <b>não mudam</b>.</div>
            <div style={{ display: 'flex', gap: 8, marginTop: 10 }}>
              <Button variant="primary" size="sm" icon="check" pede={['criar', 'editar']} disabled={gravando} onClick={aplicar}>
                {gravando ? 'Definindo…' : 'Confirmar'}</Button>
              <Button variant="secondary" size="sm" disabled={gravando} onClick={() => setConfirmando(false)}>Voltar</Button>
            </div>
          </div>
        </div>
      )}

      {relato && (
        <div className={'bal-aviso ' + (relato.falhas.length ? 'danger' : 'success')} style={{ marginBottom: 12 }}>
          <Icon name={relato.falhas.length ? 'alert-circle' : 'check-circle'} size={15} />
          <div>
            <b>Definido {relato.tom} em {relato.feitos.length} de {relato.total} venda{relato.total !== 1 ? 's' : ''}.</b>
            {relato.falhas.map(f => <div key={f.numero} style={{ marginTop: 2 }}>✗ {f.numero}: {f.erro}</div>)}
            {relato.falhas.length > 0 && <div style={{ marginTop: 2 }}>As que falharam continuam na fila, sem nada baixado. Tentar de novo é seguro.</div>}
          </div>
        </div>
      )}

      {estado === 'erro' ? (
        <div className="bal-aviso danger"><Icon name="alert-circle" size={15} />
          <div><b>Não consegui ler a fila</b> ({erroLeitura}). A fila não diz que está vazia sem ter lido — recarregue a página;
            se continuar, o erro acima é o que o banco respondeu.</div></div>
      ) : estado === 'naoInstalado' ? (
        <div className="bal-aviso info"><Icon name="info" size={15} />
          <div>A fila de vendas sem tom ainda não existe no banco: falta rodar o <b>SUPABASE_estoque-pai-derivado.sql</b>.
            As vendas lançadas com “Tom não informado” até lá entram nela quando ele rodar.</div></div>
      ) : estado === 'lendo' ? (
        <div className="muted" style={{ padding: '32px 0', textAlign: 'center', fontSize: 13 }}>Lendo a fila…</div>
      ) : lista.length === 0 ? (
        <div className="muted" style={{ padding: '40px 0', textAlign: 'center', fontSize: 13 }}>Nenhuma venda esperando tom — o estoque dos produtos com tonalidades é o da prateleira.</div>
      ) : (
        <table className="tbl" style={{ fontSize: 12.5 }}>
          <thead><tr>
            <th style={{ width: 28 }}><input type="checkbox" checked={todos} onChange={toggleTodos} title="Marcar todas" /></th>
            <th>Venda</th><th>Loja</th><th>Período</th><th>Produto</th><th className="num right">Un.</th>
          </tr></thead>
          <tbody>
            {lista.map(r => (
              <tr key={r.item_id} className="clickable" onClick={() => toggle(r.item_id)}>
                <td onClick={e => e.stopPropagation()}><input type="checkbox" checked={sel.has(r.item_id)} onChange={() => toggle(r.item_id)} /></td>
                <td><span className="ref-mono">{r.numero}</span>
                  {r.tem_linha_no_produto && <div className="muted" style={{ fontSize: 10.5 }}>o app antigo já baixou no produto — a linha do razão ganha o tom</div>}</td>
                <td>{lojaNome(r.loja_id)}</td>
                <td className="muted">{stPeriodo(r)}</td>
                <td><span className="ref-mono">{r.sku}</span></td>
                <td className="num right">{r.quantidade}</td>
              </tr>
            ))}
          </tbody>
        </table>
      )}
      {estado === 'ok' && (
        <div className="muted" style={{ fontSize: 11.5, marginTop: 10 }}>
          Na fila: {lista.length} venda{lista.length !== 1 ? 's' : ''} · {lista.reduce((a, r) => a + (Number(r.quantidade) || 0), 0)} un.
          {onNav && <> · <a onClick={() => onNav('produtos/estoque')} style={{ cursor: 'pointer' }}>ver em Produtos › Estoque</a></>}
        </div>
      )}
    </div>
  );
}

Object.assign(window, { FilaSemTom, TomPendenteAviso, stPorProduto, stEfeito, stLer });
