// js/components/widgets/RankingVendedoresCard.jsx
// [v224.218 PRIVACIDADE-RANKING 20260728] Ranking de vendedores com máscara NO SERVIDOR.
//
// Fonte única: RPC znx_ranking_vendedores_v1(p_from, p_to).
//   - admin        → total_vendido e num_pedidos preenchidos em TODAS as linhas
//   - demais papéis→ nome + posição de todos, valores SÓ na própria linha;
//                    os do colega vêm NULL DO BANCO (não escondidos aqui).
//
// Por isso este componente não tem nenhum filtro de privacidade: não há o que
// esconder no cliente — o que não pode ser visto simplesmente não chega. Se algum
// dia aparecer valor de colega pra não-admin, o defeito é na RPC, não aqui.
//
// ⚠️ LEIA ANTES DE "CONSERTAR" O R$ 0,00 (v224.219):
//   R$ 0,00 na PRÓPRIA linha  → CERTO. É o número real de quem não vendeu no período.
//                               Quem não vendeu SEMPRE vê a própria linha, zerada.
//   R$ 0,00 na linha de COLEGA → BUG. Colega sem permissão tem que vir NULL do servidor.
//   '—' (traço) na linha de colega → CERTO. É a máscara da RPC.
//
// Ou seja: null → '—' · 0 → 'R$ 0,00'. NÃO trocar 0 por '—' "pra ficar bonito":
// isso apagaria o zero real da pessoa e ela ia achar que a tela quebrou.
//
// Deps runtime: sb, fmt (globals)
(function(){
  'use strict';
  const {useState, useEffect} = React;

  // Mês corrente em YYYY-MM-DD, hora local (a coluna sales.date é `date`, não timestamp —
  // usar toISOString() daria o dia errado perto da virada por causa do UTC).
  function mesCorrente(){
    const d = new Date(), p = n => String(n).padStart(2,'0');
    const ini = new Date(d.getFullYear(), d.getMonth(), 1);
    const fim = new Date(d.getFullYear(), d.getMonth()+1, 0);
    const s = x => x.getFullYear()+'-'+p(x.getMonth()+1)+'-'+p(x.getDate());
    return { from: s(ini), to: s(fim) };
  }

  function RankingVendedoresCard({ from, to, titulo }){
    const periodo = (from && to) ? { from, to } : mesCorrente();
    from = periodo.from; to = periodo.to;

    const [linhas, setLinhas] = useState([]);
    const [erro, setErro]     = useState(null);
    const [carregando, setCarregando] = useState(true);
    const fmt = typeof window.fmt === 'function' ? window.fmt : (v)=>String(v);

    const [aguardandoSessao, setAguardandoSessao] = useState(false);

    useEffect(() => {
      let alive = true, generation = 0, busy = false, pending = false;
      let waiting = true, identity, timer, subscription;
      // Only a stale-response key: authorization and financial masks remain in the RPC.
      const sessionKey = session => {
        const user = session && session.user;
        return user && user.id ? JSON.stringify([user.id,
          user.app_metadata && user.app_metadata.tenant_id,
          user.app_metadata && user.app_metadata.role]) : null;
      };
      const waitForSession = () => {
        waiting = true;
        setLinhas([]); setErro(null); setCarregando(false); setAguardandoSessao(true);
      };
      const schedule = () => {
        if (!alive || timer != null) return;
        timer = setTimeout(() => { timer = null; carregar(); }, 0);
      };
      async function carregar() {
        if (!alive) return;
        if (busy) { pending = true; return; }
        busy = true;
        const mine = generation;
        const current = () => alive && mine === generation;
        setCarregando(true); setErro(null);
        try {
          if (typeof sb === 'undefined' || !sb.rpc || !sb.auth || !sb.auth.getSession) {
            throw new Error('sem conexão');
          }
          const api = window.ZNX && window.ZNX.api;
          if (!api || typeof api.ensureFreshAuth !== 'function' || !(await api.ensureFreshAuth())) {
            if (current()) waitForSession();
            return;
          }
          const before = await sb.auth.getSession();
          if (!current()) return;
          if (before.error) throw before.error;
          const key = sessionKey(before.data && before.data.session);
          if (!key) { waitForSession(); return; }
          identity = key;
          waiting = false; setAguardandoSessao(false);
          // A rejection is also checked against the live session before being reported.
          let result, failure;
          try { result = await sb.rpc('znx_ranking_vendedores_v1', { p_from: from, p_to: to }); }
          catch (e) { failure = e; }
          if (!current()) return;
          const after = await sb.auth.getSession();
          if (!current()) return;
          if (after.error) throw after.error;
          const latest = sessionKey(after.data && after.data.session);
          if (latest !== key) {
            generation++; identity = latest; waitForSession();
            if (latest) pending = true;
            return;
          }
          if (failure) throw failure;
          if (result.error) throw result.error;
          setLinhas(Array.isArray(result.data) ? result.data : []);
          setErro(null);
        } catch (e) {
          if (!current()) return;
          waiting = true;
          setLinhas([]); setAguardandoSessao(false);
          console.error('[RankingVendedoresCard]', e);
          setErro(e && e.message ? e.message : String(e));
          if (typeof Sentry !== 'undefined') Sentry.captureException(e, { extra:{ context:'RankingVendedoresCard', from, to } });
        } finally {
          if (current()) setCarregando(false);
          busy = false;
          if (alive && pending) { pending = false; schedule(); }
        }
      }
      const onAuth = (event, session) => {
        if (!alive) return;
        const key = sessionKey(session);
        const changed = identity !== undefined && key !== identity;
        if (event === 'SIGNED_OUT' || changed) {
          generation++; identity = key; waitForSession();
          // No SDK calls inside its synchronous callback (the SDK holds its auth lock).
          if (key) { if (busy) pending = true; else schedule(); }
          return;
        }
        identity = key;
        if (key && waiting && !busy) schedule();
      };
      const onResume = () => { if (waiting && !busy) schedule(); };
      if (typeof sb !== 'undefined' && sb.auth && sb.auth.onAuthStateChange) {
        subscription = sb.auth.onAuthStateChange(onAuth).data.subscription;
      }
      window.addEventListener('focus', onResume);
      window.addEventListener('online', onResume);
      carregar();
      return () => {
        alive = false; generation++;
        clearTimeout(timer);
        if (subscription) subscription.unsubscribe();
        window.removeEventListener('focus', onResume);
        window.removeEventListener('online', onResume);
      };
    }, [from, to]);

    // [v224.219] medalha pela POSIÇÃO que o servidor mandou, não pelo índice do array.
    // Pelo índice, quem estivesse em 16º mas fosse a 3ª linha da lista ganhava 🥉.
    const medalha = pos => ['🥇','🥈','🥉'][Number(pos)-1] || null;

    return (
      <div className="card" style={{padding:14}}>
        <div style={{fontSize:11,color:'#9CA3AF',fontWeight:700,textTransform:'uppercase',letterSpacing:1.2,marginBottom:2}}>
          🏆 {titulo || 'Ranking de vendedores'}
        </div>
        {/* Rótulo obrigatório: este total NÃO desconta crédito do cliente, então diverge
            do dashboard de propósito. Ver v224.218 §1. */}
        <div style={{fontSize:10,color:'#9CA3AF',marginBottom:10}}>
          Ranking por valor vendido (não desconta crédito do cliente)
        </div>

        {carregando && <div style={{fontSize:12,color:'#9CA3AF',fontStyle:'italic',padding:'8px 0'}}>Carregando...</div>}
        {!carregando && erro && <div style={{fontSize:12,color:'#DC2626',padding:'8px 0'}}>Não consegui carregar o ranking.</div>}
        {!carregando && aguardandoSessao && <div style={{fontSize:12,color:'#9CA3AF',padding:'8px 0'}}>Aguardando sessão para carregar o ranking.</div>}
        {!carregando && !erro && !aguardandoSessao && linhas.length===0 && (
          <div style={{fontSize:12,color:'#9CA3AF',fontStyle:'italic',padding:'8px 0'}}>Nenhuma venda no período.</div>
        )}

        {!carregando && !erro && !aguardandoSessao && linhas.length>0 && (
          <div style={{display:'flex',flexDirection:'column',gap:5}}>
            {linhas.map(r=>(
              <div key={r.seller_id || r.posicao}
                style={{
                  display:'flex',justifyContent:'space-between',alignItems:'center',
                  padding:'6px 10px',borderRadius:6,
                  background: r.is_me ? '#DBEAFE' : (Number(r.posicao)===1 ? '#FEF3C7' : '#F9FAFB'),
                  border: r.is_me ? '1px solid #2563EB55' : '1px solid transparent'
                }}>
                <div style={{display:'flex',alignItems:'center',gap:6,fontSize:12,minWidth:0}}>
                  <span style={{color:'#9CA3AF',fontWeight:700,minWidth:20}}>{medalha(r.posicao) || ('#'+r.posicao)}</span>
                  <strong style={{color:'#1B2A4A',overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap'}}>{r.vendedor}</strong>
                  {r.is_me && <span style={{fontSize:9,background:'#2563EB',color:'#fff',padding:'1px 6px',borderRadius:10,fontWeight:700}}>VOCÊ</span>}
                </div>
                <div style={{textAlign:'right',whiteSpace:'nowrap'}}>
                  {/* `==null` pega null E undefined, mas NÃO pega 0 — é essa distinção que
                      separa "não posso ver" (traço) de "vendi zero" (R$ 0,00). Ver topo. */}
                  <div style={{fontSize:13,fontWeight:700,
                    color: r.total_vendido==null ? '#D1D5DB' : (Number(r.total_vendido)===0 ? '#9CA3AF' : '#16A34A')}}>
                    {r.total_vendido==null ? '—' : fmt(r.total_vendido)}
                  </div>
                  <div style={{fontSize:10,color:'#9CA3AF'}}>
                    {r.num_pedidos==null ? '—' : r.num_pedidos+' vendas'}
                  </div>
                </div>
              </div>
            ))}
          </div>
        )}
      </div>
    );
  }

  window.ZNX = window.ZNX || {};
  window.ZNX.components = window.ZNX.components || {};
  window.ZNX.components.RankingVendedoresCard = RankingVendedoresCard;
  window.RankingVendedoresCard = RankingVendedoresCard;
})();
