/* ================================================================
   Tracker FMN — Leads
   A lista de e-mail e WhatsApp inteira, filtrável por tag.

   Por que esta tela existe: até 25/10/2026 a base de e-mail morava
   na Hotmart, que tinha 5.080 contatos enquanto o Tracker tinha
   12.050. Ninguém conseguia responder "quem são os inscritos na
   Black" sem exportar CSV e cruzar na mão. As tags vêm do script
   scripts/leads-tags.py e são sempre derivadas, nunca escritas aqui.
   ================================================================ */
const { useState, useEffect, useMemo, useCallback } = React;
const { LucideIcon, Btn, Badge, CardKPI, SectionCard, PageBody, PageHeader, buscarTudo } = window;

const estiloData = { height: 32, padding: '0 8px', borderRadius: 8, background: 'var(--app-bg)', border: '1px solid var(--app-border)', color: 'var(--text-1)', fontFamily: 'Roboto,sans-serif', fontSize: 12, outline: 'none', colorScheme: 'dark' };
const nfLeads = n => (n == null ? '—' : Number(n).toLocaleString('pt-BR'));

/* As tags que o script gera. A ordem aqui é a ordem da tela: primeiro o que
   decide disparo, depois origem, depois estado do e-mail. Tag que o script
   criar e não estiver nesta lista ainda funciona no filtro pela busca, só não
   ganha botão próprio. */
const TAGS_LEADS = [
  { id: 'enviavel',                 label: 'Enviável',            grupo: 'Disparo',  tone: 'ok' },
  { id: 'black-2026',               label: 'Black 2026',          grupo: 'Disparo',  tone: 'gold' },
  { id: 'comprador',                label: 'Comprador',           grupo: 'Disparo',  tone: 'teal' },
  { id: 'comprador-blindagem',      label: 'Comprador Blindagem', grupo: 'Disparo',  tone: 'teal' },

  { id: 'quiz-fotografo-protegido', label: 'Quiz Fotógrafo Protegido', grupo: 'Origem', tone: 'info' },
  { id: 'quiz-blindagem',           label: 'Quiz Blindagem',      grupo: 'Origem',   tone: 'info' },
  { id: 'completou-quiz',           label: 'Completou o quiz',    grupo: 'Origem',   tone: 'info' },
  { id: 'hotmart-send',             label: 'Veio da Hotmart',     grupo: 'Origem',   tone: 'neutral' },
  { id: 'so-na-hotmart',            label: 'Só existia na Hotmart', grupo: 'Origem', tone: 'neutral' },
  { id: 'abandono-carrinho',        label: 'Abandonou carrinho',  grupo: 'Origem',   tone: 'warn' },
  { id: 'aguardando-pagamento',     label: 'Aguardando pagamento', grupo: 'Origem',  tone: 'warn' },
  { id: 'compra-aprovada',          label: 'Compra aprovada',     grupo: 'Origem',   tone: 'ok' },
  { id: 'area-membros',             label: 'Área de membros',     grupo: 'Origem',   tone: 'neutral' },
  { id: 'area-membros-inativo',     label: 'Membro inativo',      grupo: 'Origem',   tone: 'neutral' },
  { id: 'natal-2023',               label: 'Natal 2023',          grupo: 'Origem',   tone: 'neutral' },
  { id: 'quiz-inlead',              label: 'Quiz InLead',         grupo: 'Origem',   tone: 'neutral' },
  { id: 'captura-comunidade',       label: 'Captura comunidade',  grupo: 'Origem',   tone: 'neutral' },

  { id: 'sem-email',                label: 'Sem e-mail',          grupo: 'Estado',   tone: 'neutral' },
  { id: 'descadastrado',            label: 'Descadastrado',       grupo: 'Estado',   tone: 'danger' },
  { id: 'bounce-hard',              label: 'E-mail não existe',   grupo: 'Estado',   tone: 'danger' },
  { id: 'bounce-soft',              label: 'Caixa cheia',         grupo: 'Estado',   tone: 'warning' },
];

const GRUPOS_TAG = ['Disparo', 'Origem', 'Estado'];
const CAMPOS = 'id,email,nome,whatsapp,tags,funnel_slug,email_status,email_bounce,' +
               'email_engagement,email_subscribed_at,created_at';

/* Captação e CPL por dia na tela de Leads (28/10/2026), o mesmo gráfico do Funis
   (window.GraficoCaptacaoCPL), com os números da função custo-por-lead. */
const FUNIS_GRAF = [['black-2026', 'Black 2026'], ['fotografo-protegido', 'MCV'], ['blindagem', 'Blindagem'], ['todos', 'Todos']];
function CardCaptacaoLeads() {
  const [funil, setFunil] = useState('black-2026');
  const [dias, setDias]   = useState(30);
  const [d, setD]         = useState(null);
  const [erro, setErro]   = useState(null);
  useEffect(() => {
    let vivo = true; setD(null); setErro(null);
    (async () => {
      try {
        const ini = new Date(); ini.setDate(ini.getDate() - (dias - 1));
        const { data: s } = await window.db.auth.getSession();
        const r = await fetch(`${window.db.supabaseUrl}/functions/v1/custo-por-lead`, {
          method: 'POST',
          headers: { 'Content-Type': 'application/json', apikey: window.db.supabaseKey,
            Authorization: `Bearer ${s?.session?.access_token || window.db.supabaseKey}` },
          body: JSON.stringify({ since: window.FMNFinancas.dataBRT(ini), until: window.FMNFinancas.dataBRT(new Date()) }),
        });
        const j = await r.json();
        if (!r.ok || j.error) throw new Error(j.error || `erro ${r.status}`);
        if (vivo) setD(j);
      } catch (e) { if (vivo) setErro(e.message || String(e)); }
    })();
    return () => { vivo = false; };
  }, [dias]);
  const serie = (d?.dias || []).map(x => {
    const fs = funil === 'todos' ? Object.values(x.funis) : [x.funis[funil] || { gasto: 0, leads: 0 }];
    const g = fs.reduce((a, f) => a + (f.gasto || 0), 0), l = fs.reduce((a, f) => a + (f.leads || 0), 0);
    return { dia: x.dia, gasto: g, leads: l, cpl: l ? g / l : null };
  });
  const tg = serie.reduce((a, x) => a + x.gasto, 0), tl = serie.reduce((a, x) => a + x.leads, 0);
  return (
    <SectionCard title="Captação e CPL por dia" icon="chart-line"
      headerRight={<div style={{ display: 'flex', gap: 10, flexWrap: 'wrap', justifyContent: 'flex-end' }}>
        <window.Pilulas valor={funil} onChange={setFunil} opcoes={FUNIS_GRAF.map(([id, label]) => ({ id, label }))}/>
        <window.Pilulas valor={dias} onChange={setDias} opcoes={[{ id: 7, label: '7 dias' }, { id: 14, label: '14 dias' }, { id: 30, label: '30 dias' }]}/>
      </div>}>
      {erro && <div style={{ fontSize: 12.5, color: 'var(--clr-neg)' }}>{erro}</div>}
      {!erro && !d && <div style={{ fontSize: 12.5, color: 'var(--text-3)' }}>Buscando o gasto no Meta...</div>}
      {d && (<>
        <div style={{ display: 'flex', gap: 18, fontSize: 12.5, color: 'var(--text-2)', marginBottom: 10, flexWrap: 'wrap' }}>
          <span><b style={{ color: '#79e06f' }}>■</b> leads no período: <b style={{ color: 'var(--text-1)' }}>{nfLeads(tl)}</b></span>
          <span><b style={{ color: 'var(--app-primary)' }}>●</b> CPL médio: <b style={{ color: 'var(--text-1)' }}>{tl ? window.fmtBRL(tg / tl) : '—'}</b></span>
          <span>gasto no Meta: <b style={{ color: 'var(--text-1)' }}>{window.fmtBRL(tg)}</b></span>
        </div>
        {window.GraficoCaptacaoCPL ? <window.GraficoCaptacaoCPL dias={serie}/> : null}
      </>)}
    </SectionCard>
  );
}

function LeadsScreen() {
  const [contagens, setContagens] = useState({});
  const [ativas, setAtivas]       = useState([]);     // tags que precisam estar todas presentes
  const [busca, setBusca]         = useState('');
  // Data de inscrição (28/10/2026): filtra por created_at, o dia em que a pessoa
  // entrou na base, no horário de Brasília. Vale para lista, contagem e CSV.
  const hojeBR = () => window.FMNFinancas.dataBRT(new Date());
  const [periodo, setPeriodo]     = useState('tudo');
  const [deData, setDeData]       = useState(() => { const d = new Date(); d.setDate(d.getDate() - 7); return window.FMNFinancas.dataBRT(d); });
  const [ateData, setAteData]     = useState(hojeBR);
  const [linhas, setLinhas]       = useState([]);
  const [total, setTotal]         = useState(null);
  const [carregando, setCarregando] = useState(true);
  const [baixando, setBaixando]   = useState(false);
  const [recado, setRecado]       = useState(null);

  /* O filtro é montado num lugar só, porque a lista, a contagem e a exportação
     precisam ser exatamente o mesmo recorte. Se divergirem, o CSV sai diferente
     do que a tela mostrou, e isso é o tipo de erro que só aparece depois do
     disparo. */
  const aplicarFiltro = useCallback(q => {
    if (ativas.length) q = q.contains('tags', ativas);
    const t = busca.trim();
    if (t) q = q.or(`email.ilike.%${t}%,nome.ilike.%${t}%,whatsapp.ilike.%${t}%`);
    if (periodo !== 'tudo') {
      let de = deData, ate = ateData;
      if (periodo !== 'custom') {
        const d = new Date(); d.setDate(d.getDate() - ({ hoje: 0, '7d': 6, '30d': 29 })[periodo]);
        de = window.FMNFinancas.dataBRT(d); ate = hojeBR();
      }
      if (de)  q = q.gte('created_at', de + 'T00:00:00-03:00');
      if (ate) q = q.lte('created_at', ate + 'T23:59:59.999-03:00');
    }
    return q;
  }, [ativas, busca, periodo, deData, ateData]);

  /* Contagem por tag. Uma consulta por tag, com head: true, então o banco conta
     sem devolver linha nenhuma. Sai mais barato que carregar 30 mil registros
     para contar no navegador. */
  useEffect(() => {
    let vivo = true;
    (async () => {
      const pares = await Promise.all(TAGS_LEADS.map(async t => {
        const { count } = await window.db.from('quiz_leads')
          .select('id', { count: 'exact', head: true }).contains('tags', [t.id]);
        return [t.id, count || 0];
      }));
      if (vivo) setContagens(Object.fromEntries(pares));
    })();
    return () => { vivo = false; };
  }, []);

  /* Lista. Traz só as 300 primeiras: a tela serve para conferir e exportar, não
     para rolar 30 mil linhas. O total vem do count, então o número no topo é o
     real mesmo com a lista cortada. */
  useEffect(() => {
    let vivo = true;
    setCarregando(true);
    const id = setTimeout(async () => {
      const q = aplicarFiltro(window.db.from('quiz_leads')
        .select(CAMPOS, { count: 'exact' })).order('created_at', { ascending: false, nullsFirst: false }).limit(300);
      const { data, count, error } = await q;
      if (!vivo) return;
      if (error) { setRecado({ tipo: 'erro', texto: error.message }); setCarregando(false); return; }
      setLinhas(data || []); setTotal(count ?? 0); setCarregando(false);
    }, 300);  // espera a digitação parar antes de consultar
    return () => { vivo = false; clearTimeout(id); };
  }, [aplicarFiltro]);

  const alternar = id => setAtivas(p => p.includes(id) ? p.filter(x => x !== id) : [...p, id]);

  /* Exportação. Só aqui a base inteira é paginada, e só do recorte ativo.
     Deduplica por e-mail porque a mesma pessoa aparece várias vezes na tabela
     (cada preenchimento do quiz é uma linha) e disparo duplicado queima a
     reputação de envio. */
  const exportar = async () => {
    setBaixando(true); setRecado(null);
    try {
      const tudo = await buscarTudo(() =>
        aplicarFiltro(window.db.from('quiz_leads').select('email,nome,whatsapp,tags'))
          .order('id', { ascending: true }));

      const vistos = new Set(); const saida = [];
      for (const l of tudo) {
        const e = (l.email || '').trim().toLowerCase();
        if (!e || vistos.has(e)) continue;
        vistos.add(e); saida.push(l);
      }
      if (!saida.length) { setRecado({ tipo: 'aviso', texto: 'Esse filtro não tem ninguém com e-mail.' }); return; }

      const esc = v => `"${String(v ?? '').replace(/"/g, '""')}"`;
      const csv = ['email,nome,whatsapp,tags',
        ...saida.map(l => [l.email, l.nome, l.whatsapp, (l.tags || []).join(' ')].map(esc).join(','))
      ].join('\n');

      const nome = `leads-${ativas.join('-') || 'todos'}-${new Date().toISOString().slice(0, 10)}.csv`;
      const url = URL.createObjectURL(new Blob(['﻿' + csv], { type: 'text/csv;charset=utf-8' }));
      const a = document.createElement('a');
      a.href = url; a.download = nome; a.click();
      URL.revokeObjectURL(url);
      setRecado({ tipo: 'ok', texto: `${nfLeads(saida.length)} pessoas no arquivo, uma linha por e-mail.` });
    } catch (e) {
      setRecado({ tipo: 'erro', texto: e.message || 'Não deu para gerar o arquivo.' });
    } finally { setBaixando(false); }
  };

  /* Os quatro números seguem o filtro (Felipe, 26/10/2026): com "Black 2026" marcado, "Pode
     receber e-mail" é quantos inscritos na Black podem receber, e assim por diante. Sem filtro,
     são os totais da base. Cada número é uma contagem no banco, sem trazer linha nenhuma. */
  const [kpis, setKpis] = useState({});
  useEffect(() => {
    let vivo = true;
    const filtrado = ativas.length > 0 || busca.trim() !== '' || periodo !== 'tudo';
    if (!filtrado) {
      setKpis({
        enviavel: contagens['enviavel'], black: contagens['black-2026'],
        barrados: (contagens['descadastrado'] || 0) + (contagens['bounce-hard'] || 0),
      });
      return;
    }
    const contar = async tag => {
      const { count } = await aplicarFiltro(window.db.from('quiz_leads')
        .select('id', { count: 'exact', head: true })).contains('tags', [tag]);
      return count || 0;
    };
    const id = setTimeout(async () => {
      const [enviavel, black, descad, bounce] = await Promise.all(
        ['enviavel', 'black-2026', 'descadastrado', 'bounce-hard'].map(contar));
      if (vivo) setKpis({ enviavel, black, barrados: descad + bounce });
    }, 300);
    return () => { vivo = false; clearTimeout(id); };
  }, [aplicarFiltro, contagens, ativas, busca]);
  const noFiltro = ativas.length > 0 || busca.trim() !== '' || periodo !== 'tudo';

  return (
    <div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
      <PageBody>
        <PageHeader
          title="Leads"
          description="Todo mundo que já deixou e-mail ou WhatsApp, com a origem marcada em cada um."
          actions={
            <Btn icon="download" onClick={exportar} disabled={baixando || !total}>
              {baixando ? 'Montando o arquivo...' : 'Exportar CSV do filtro'}
            </Btn>
          }
        />

        <div style={{ display: 'flex', gap: 16, flexWrap: 'wrap' }}>
          <CardKPI label={noFiltro ? 'Pode receber e-mail, no filtro' : 'Pode receber e-mail'} value={nfLeads(kpis.enviavel)} icon="mail-check"
            title="Tem e-mail válido, não pediu descadastro e não deu erro permanente" accent/>
          <CardKPI label={noFiltro ? 'Inscritos na Black, no filtro' : 'Inscritos na Black'} value={nfLeads(kpis.black)} icon="flame"/>
          <CardKPI label={noFiltro ? 'Não podem receber, no filtro' : 'Não podem receber'} value={nfLeads(kpis.barrados)} icon="mail-x"
            title="Descadastrados e endereços que não existem" alerta={kpis.barrados > 0}/>
        </div>

        <CardCaptacaoLeads/>

        <SectionCard title="Filtrar por tag" icon="tags"
          headerRight={ativas.length
            ? <Btn size="sm" variant="ghost" icon="x" onClick={() => setAtivas([])}>Limpar {ativas.length}</Btn>
            : null}>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
            {GRUPOS_TAG.map(grupo => (
              <div key={grupo}>
                <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: .4, textTransform: 'uppercase',
                  color: 'var(--text-3)', marginBottom: 8 }}>{grupo}</div>
                <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
                  {TAGS_LEADS.filter(t => t.grupo === grupo).map(t => {
                    const on = ativas.includes(t.id);
                    const n = contagens[t.id];
                    return (
                      <button key={t.id} onClick={() => alternar(t.id)}
                        style={{
                          display: 'flex', alignItems: 'center', gap: 6, cursor: 'pointer',
                          padding: '6px 12px', borderRadius: 999, fontSize: 12.5,
                          fontFamily: 'Roboto, sans-serif', transition: 'all .15s',
                          background: on ? 'var(--app-primary)' : 'var(--app-surface)',
                          color: on ? 'var(--app-primary-fg)' : 'var(--text-2)',
                          border: `1px solid ${on ? 'var(--app-primary)' : 'var(--app-border)'}`,
                        }}>
                        <span>{t.label}</span>
                        <span style={{ opacity: .65, fontVariantNumeric: 'tabular-nums' }}>
                          {n == null ? '·' : nfLeads(n)}
                        </span>
                      </button>
                    );
                  })}
                </div>
              </div>
            ))}
          </div>
          {ativas.length > 1 && (
            <div style={{ marginTop: 14, fontSize: 12, color: 'var(--text-3)' }}>
              Mostrando quem tem <strong>todas</strong> as {ativas.length} tags ao mesmo tempo.
            </div>
          )}
        </SectionCard>

        <SectionCard
          title={total == null ? 'Carregando...' : `${nfLeads(total)} registros`}
          icon="users"
          headerRight={
            <div style={{ display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap', justifyContent: 'flex-end' }}>
            <span style={{ fontSize: 12, color: 'var(--text-3)' }}>Inscrição</span>
            <window.Pilulas valor={periodo} onChange={setPeriodo} opcoes={[
              { id: 'tudo', label: 'Tudo' }, { id: 'hoje', label: 'Hoje' }, { id: '7d', label: '7 dias' },
              { id: '30d', label: '30 dias' }, { id: 'custom', label: 'Personalizado' }]}/>
            {periodo === 'custom' && (<>
              <input type="date" value={deData} onChange={e => setDeData(e.target.value)} style={estiloData}/>
              <span style={{ color: 'var(--text-3)', fontSize: 12 }}>até</span>
              <input type="date" value={ateData} onChange={e => setAteData(e.target.value)} style={estiloData}/>
            </>)}
            <input value={busca} onChange={e => setBusca(e.target.value)}
              placeholder="Buscar por e-mail, nome ou WhatsApp"
              style={{
                width: 280, height: 32, padding: '0 12px', borderRadius: 8, fontSize: 13,
                fontFamily: 'Roboto, sans-serif', background: 'var(--app-bg)',
                color: 'var(--text-1)', border: '1px solid var(--app-border)', outline: 'none',
              }}/>
            </div>
          }>
          {recado && (
            <div style={{
              marginBottom: 12, padding: '10px 14px', borderRadius: 8, fontSize: 13,
              background: recado.tipo === 'erro' ? 'rgba(254,98,108,.12)'
                : recado.tipo === 'aviso' ? 'rgba(255,152,69,.12)' : 'rgba(121,224,111,.12)',
              color: recado.tipo === 'erro' ? 'var(--clr-neg)'
                : recado.tipo === 'aviso' ? 'var(--clr-warn)' : 'var(--clr-pos)',
            }}>{recado.texto}</div>
          )}

          {carregando ? (
            <div style={{ padding: 40, textAlign: 'center', color: 'var(--text-3)', fontSize: 13 }}>
              Carregando...
            </div>
          ) : !linhas.length ? (
            <div style={{ padding: 40, textAlign: 'center', color: 'var(--text-3)', fontSize: 13 }}>
              Nenhum lead com esse filtro.
            </div>
          ) : (
            <div style={{ overflowX: 'auto' }}>
              <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 13 }}>
                <thead>
                  <tr style={{ textAlign: 'left', color: 'var(--text-3)', fontSize: 11,
                    textTransform: 'uppercase', letterSpacing: .4 }}>
                    <th style={{ padding: '8px 10px' }}>E-mail</th>
                    <th style={{ padding: '8px 10px' }}>Nome</th>
                    <th style={{ padding: '8px 10px' }}>WhatsApp</th>
                    <th style={{ padding: '8px 10px' }}>Inscrição</th>
                    <th style={{ padding: '8px 10px' }}>Tags</th>
                  </tr>
                </thead>
                <tbody>
                  {linhas.map(l => (
                    <tr key={l.id} style={{ borderTop: '1px solid var(--app-border)' }}>
                      <td style={{ padding: '10px', color: 'var(--text-1)', whiteSpace: 'nowrap' }}>
                        {l.email || <span style={{ color: 'var(--text-3)' }}>sem e-mail</span>}
                      </td>
                      <td title={l.nome || ''} style={{ padding: '10px', color: 'var(--text-2)', whiteSpace: 'nowrap',
                        maxWidth: 240, overflow: 'hidden', textOverflow: 'ellipsis' }}>
                        {l.nome || '—'}
                      </td>
                      <td style={{ padding: '10px', color: 'var(--text-2)', whiteSpace: 'nowrap',
                        fontVariantNumeric: 'tabular-nums' }}>{l.whatsapp || '—'}</td>
                      <td style={{ padding: '10px', color: 'var(--text-2)', whiteSpace: 'nowrap',
                        fontVariantNumeric: 'tabular-nums' }}>
                        {l.created_at ? new Date(l.created_at).toLocaleDateString('pt-BR', { timeZone: 'America/Sao_Paulo' }) : '—'}
                      </td>
                      <td style={{ padding: '10px' }}>
                        <div style={{ display: 'flex', gap: 4, flexWrap: 'wrap' }}>
                          {(l.tags || []).map(t => {
                            const def = TAGS_LEADS.find(x => x.id === t);
                            return <Badge key={t} tone={def?.tone || 'default'}>{def?.label || t}</Badge>;
                          })}
                        </div>
                      </td>
                    </tr>
                  ))}
                </tbody>
              </table>
              {total > linhas.length && (
                <div style={{ padding: '14px 10px 2px', fontSize: 12, color: 'var(--text-3)' }}>
                  Mostrando os {nfLeads(linhas.length)} mais recentes de {nfLeads(total)}.
                  O CSV leva todos.
                </div>
              )}
            </div>
          )}
        </SectionCard>

        <div style={{ fontSize: 12, color: 'var(--text-3)', lineHeight: 1.6 }}>
          As tags são recalculadas por <code>scripts/leads-tags.py</code>, nunca escritas à mão.
          Rodar o script de novo depois de uma venda ou de uma importação já corrige todo mundo.
          O CSV sai com uma linha por e-mail, mesmo quando a pessoa preencheu o quiz várias vezes.
        </div>
      </PageBody>
    </div>
  );
}

window.LeadsScreen = LeadsScreen;
