// Página completa do afiliado — redesenho com KPIs, gráfico dual-axis e funil (spec 2026-06-30).
// Rota: #/afiliado/{platform}/{name}. Filtros locais (período/produto) não vazam pro dashboard.
// Componentes de gráfico (AffiliateTrend, TopProductsFunnel) vivem em AffiliateCharts.jsx.
const AffiliatePage = ({ platform, name, currency, setCurrency, mockRow, families, onBack, onKpiSelect }) => {
  const [period, setPeriod] = React.useState('30d');
  const [customRange, setCustomRange] = React.useState(null);
  const [product, setProduct] = React.useState('todos');
  const [funil, setFunil]     = React.useState('todos');
  const [refundBy, setRefundBy] = React.useState('pedido');

  const handleProductChange = (p) => { setProduct(p); setFunil('todos'); };
  React.useEffect(() => { setFunil('todos'); }, [product]);
  const [detail, setDetail] = React.useState(null);
  const [state, setState] = React.useState('loading'); // loading | live | demo | notfound
  const fullLoading = useFullLoading();
  const withLoad = (fn) => (...a) => { fullLoading.mark(); fn(...a); };

  React.useEffect(() => {
    const began = fullLoading.begin();
    let cancelled = false;
    if (!detail) setState('loading');          // blank só no 1º load; refetch mantém o dado
    fetchAffiliateDetailAPI({ platform, name, period, product, funnel: funil, refundBy,
        start: customRange?.start, end: customRange?.end })
      .then(d => { if (!cancelled) { setDetail(d); setState('live'); } })
      .catch(e => {
        if (cancelled) return;
        console.error('[AffiliatePage] detail fetch falhou:', e);
        if (e.notFound) { setState('notfound'); return; }
        if (mockRow) { setDetail(buildDetailMock(mockRow, period)); setState('demo'); }
        else setState('notfound');
      })
      .finally(() => { fullLoading.end(began); });
    return () => { cancelled = true; };
  }, [platform, name, period, product, funil, refundBy, customRange]);

  if (state === 'notfound') return (
    <div style={ap.empty}>
      <div style={ap.emptyTitle}>Afiliado não encontrado</div>
      <button style={ap.back} onClick={onBack}>← Voltar aos afiliados</button>
    </div>
  );
  if (!detail) return <div style={ap.empty}>Carregando…</div>;

  const a = detail.affiliate, m = detail.metrics, rf = detail.refunds;
  const plat = PLATFORM[a.platform] || PLATFORM.buygoods;
  const health = rf.available && rf.direct?.rate != null ? refundHealth(rf.direct.rate) : null;
  const avatar = mockRow?.avatar || 'var(--fenix-navy)';

  const revDelta = m.grossTotalPrev ? (m.grossTotal - m.grossTotalPrev) / m.grossTotalPrev * 100 : null;
  const ordDelta = (m.orders || 0) - (m.ordersPrev || 0);
  const granLabel = { hour: 'horário', week: 'semanal', month: 'mensal' }[detail.granularity] || 'diário';

  // "Ativo há" (spec 2026-07-09): tempo desde a 1ª venda (histórico all-time) + data.
  const activeLabel = (() => {
    const iso = detail.activity && detail.activity.firstSale;
    if (!iso) return { big: '—', sub: 'sem histórico' };
    const first = new Date(iso);
    const days = Math.max(0, Math.floor((Date.now() - first.getTime()) / 86400000));
    const years = Math.floor(days / 365.25), months = Math.floor(days / 30.44);
    const big = years >= 1 ? `há ${years} ano${years > 1 ? 's' : ''}`
      : months >= 1 ? `há ${months} ${months > 1 ? 'meses' : 'mês'}`
      : `há ${days} dia${days !== 1 ? 's' : ''}`;
    return { big, sub: 'desde ' + first.toLocaleDateString('pt-BR', { day: '2-digit', month: 'short', year: 'numeric' }) };
  })();
  const openKpi = (o) => onKpiSelect && onKpiSelect({ ...o, data: { detail, ...(o.data || {}) } });

  return (
    <div style={{position:'relative'}}>
      <button style={ap.back} onClick={onBack}>← Voltar aos afiliados</button>

      <SectionTitle n={1} first label="Visão geral"
        sub="Quem é o afiliado, filtros do período e os números que decidem tudo."/>

      <div style={ap.header}>
        <div style={ap.idRow}>
          <div style={{...ap.av, background: avatar}}>{initials(a.name)}</div>
          <div>
            <div style={ap.name}>{a.name}</div>
            <div style={ap.id}>{a.affiliateId || '—'}</div>
          </div>
        </div>
        <div style={ap.chips}>
          {mockRow?.traffic && <TrafficChip t={mockRow.traffic}/>}
          <span style={ap.platChip}><span style={{...ap.dot, background: plat.color}}/>{plat.label}</span>
          {state==='demo' && <span style={ap.demoChip}>Dados de demonstração — API offline</span>}
        </div>
      </div>

      <div style={ap.filterRow}>
        <div style={ap.segGroup}>
          <span style={ap.segLabel}>Moeda</span>
          <Segmented value={currency} onChange={setCurrency}
            options={[{key:'USD',label:'USD'},{key:'BRL',label:'BRL'}]}/>
        </div>
        <PeriodSelector period={period} setPeriod={withLoad(setPeriod)}
          customRange={customRange} setCustomRange={withLoad(setCustomRange)}/>
        {(() => {
          const familyIndex = buildFamilyIndex(families);
          const grouped = groupProductsByFamilies(detail.products, familyIndex, families);
          const localFamilies = Object.keys(grouped).filter(k => k !== '_outros').sort();
          const variants = product !== 'todos' ? (grouped[product] || []) : [];
          return (<>
            <Dropdown icon="cart" label="Produto" value={product} onChange={withLoad(handleProductChange)} options={[
              {key:'todos', label:'Todos'},
              ...localFamilies.map(f=>({key:f, label:f})),
            ]}/>
            {product !== 'todos' && (
              <Dropdown icon="layers" label="Funil" value={funil} onChange={withLoad(setFunil)} options={[
                {key:'todos', label:'Todas variações'},
                ...variants.map(v=>({
                  key: v,
                  label: v.slice(product.length).replace(/^\s*[|\-]\s*/, '').trim() || v,
                })),
              ]}/>
            )}
          </>);
        })()}
        <Dropdown icon="layers" label="Reembolso" value={refundBy} onChange={withLoad(setRefundBy)}
          options={REFUND_BY_OPTIONS}/>
      </div>
      <RefundByNote refundBy={refundBy}/>

      {/* ===== KPI ROW (6 cards clicáveis → KpiDrawer, spec 2026-07-09) ===== */}
      <div style={ap.kpiRow}>
        {/* Receita — card de destaque (navy), clicável. Bruta: Completed+Refund+CGBK (spec 2026-07-24) */}
        <div style={{...ap.heroCard, cursor:'pointer', transition:'box-shadow .15s, transform .15s'}}
          onClick={()=>openKpi({ eye:'Receita total bruta do período', val:USD(m.grossTotal), mono:true,
            sub: revDelta!=null ? (revDelta>=0?'▲ +':'▼ ')+Math.abs(revDelta).toFixed(1).replace('.',',')+'% vs. período anterior' : undefined,
            icon:'dollar', tint:'rgba(255,168,102,.18)', fg:'#FFA866', kind:'aff-revenue' })}
          onMouseEnter={e=>{e.currentTarget.style.transform='translateY(-1px)';}}
          onMouseLeave={e=>{e.currentTarget.style.transform='none';}}>
          <span style={ap.heroEye}>Receita total bruta do período</span>
          <div>
            <div style={ap.heroVal} className="fenix-num">{USD(m.grossTotal)}</div>
          </div>
        </div>
        {/* 5 cards brancos clicáveis (padrão da Visão Geral) */}
        {[
          { eye:'AOV', val:USD(m.aov), sub:'por checkout', icon:'chart',
            tint:'var(--fenix-blue-100)', fg:'var(--fenix-blue)', kind:'aff-aov' },
          { eye:'Pedidos', val:(m.orders||0).toLocaleString('pt-BR'),
            sub:(ordDelta>=0?'▲ +':'▼ ')+Math.abs(ordDelta)+' no período',
            subColor: ordDelta>=0 ? 'var(--positive)' : 'var(--negative)',
            icon:'cart', tint:'var(--fenix-blue-100)', fg:'var(--fenix-blue)', kind:'aff-orders' },
          (rf.available && rf.direct)
            ? { eye:'Taxa refund', val:PCT(rf.direct.rate), valColor:health.color, health,
                icon:'refresh', tint:health.bg, fg:health.color, kind:'aff-refund' }
            : { eye:'Taxa refund', val:'—', sub:'sem captura', icon:'refresh',
                tint:'var(--gray-100)', fg:'var(--fg-3)', kind:'aff-refund' },
          { eye:'Ativo há', val:activeLabel.big, sub:activeLabel.sub, icon:'calendar',
            tint:'var(--positive-bg)', fg:'var(--positive)', kind:'aff-active' },
        ].map((c,i)=>(
          <DotKpi key={i} eye={c.eye} color={c.fg} value={c.val} sub={c.sub} subColor={c.subColor}
            valColor={c.valColor} health={c.health} onClick={()=>openKpi(c)}/>
        ))}
      </div>

      {/* ══════════════ § SETOR 2 · SAÚDE FINANCEIRA (pizza de produtos + potes) ══════════════ */}
      <SectionTitle n={2} label="Saúde financeira"
        sub="Concentração da receita — dependência por produto e por pote."/>
      <div style={ap.donutRow}>
        <div style={ap.card}>
          <div style={ap.cardHead}>
            <div>
              <span style={ap.eyebrow}>Receita</span>
              <div style={ap.cardTitle}>Receita por produto</div>
            </div>
            <span style={ap.hint}>clique num produto para ver a estrutura de funil →</span>
          </div>
          <TopProductsFunnel products={detail.topProducts}/>
        </div>
        <div style={ap.card}>
          <div style={ap.cardHead}>
            <div><span style={ap.eyebrow}>Front-end</span><div style={ap.cardTitle}>Vendas por pote (bottles)</div></div>
          </div>
          <AffiliatePotes potes={detail.potes}/>
        </div>
      </div>

      <SectionTitle n={3} label="Receita"
        sub="De onde vem o faturamento e como ele se distribui no tempo."/>

      {/* ══════════════ SÉRIE TEMPORAL ══════════════ */}
      <div style={ap.card}>
        <div style={ap.cardHead}>
          <div>
            <span style={ap.eyebrow}>Receita · {granLabel}</span>
            <div style={ap.cardTitle}>Série temporal</div>
          </div>
        </div>
        <AffiliateTrend trend={detail.trend}/>
        <div style={ap.note}>Receita por data da compra (coorte de venda). Passe o mouse para ver pedidos, refund e chargeback do dia — quantidade e $.</div>
      </div>

      {/* ══════════════ § REFUND (título abaixo da série temporal) ══════════════ */}
      <SectionTitle n={4} label="Refund / estorno" sub="Coorte por janela, maturação do estorno e onde nasce o refund"/>

      {rf.available ? (
        <>
          <CohortWindows windows={detail.cohortWindows}/>
          <div style={{display:'flex', gap:16, alignItems:'stretch', marginTop:16}} className="cpa-aov">
            <RefundRateOverTime timeseries={detail.trend}/>
            <RefundMaturation maturation={detail.refundMaturation}/>
          </div>
          <div style={{marginTop:16}}>
            <div style={ap.card}>
              <div style={ap.cardHead}>
                <div><span style={ap.eyebrow}>Reembolsos</span><div style={ap.cardTitle}>Refund por etapa</div></div>
                <span style={ap.hint}>clique numa fatia para ver os produtos →</span>
              </div>
              <div style={{maxWidth: 640}}>
                <AffiliateRefundByStage stages={detail.refundByStage} onKpiSelect={openKpi}/>
              </div>
            </div>
          </div>
        </>
      ) : (
        <div style={ap.card}>
          <div style={{...ap.note, padding:'18px 22px'}}>
            Esta plataforma não captura refund/chargeback — sem dados de estorno ao longo do tempo, maturação, coorte ou por etapa.
          </div>
        </div>
      )}

      {detail.untrackedUpsells && detail.untrackedUpsells.count > 0 && (
        <UntrackedUpsells data={detail.untrackedUpsells}/>
      )}
      <FullLoadingOverlay show={fullLoading.show && !!detail} conn="connecting"/>
    </div>
  );
};

// Upsells sem rastreamento de funil (spec 2026-06-24): upsell/downsell sem ordem
// principal elegível no mesmo dia. Indicador de qualidade de dado — não vira venda de
// outro funil (mantém o próprio produto). Card discreto de atenção, com lista expansível.
const UntrackedUpsells = ({ data }) => {
  const [open, setOpen] = React.useState(false);
  const [tip, setTip] = React.useState(false);
  return (
    <div style={uu.card}>
      <div style={uu.head}>
        <span style={uu.icon}>⚠</span>
        <div style={{flex:1}}>
          <div style={uu.title}>
            Upsells sem rastreamento de funil
            <span style={uu.help} onMouseEnter={()=>setTip(true)} onMouseLeave={()=>setTip(false)}>
              ?
              {tip && <span style={uu.tip}>Upsells/downsells sem uma ordem principal do mesmo
                cliente no mesmo dia para vincular a um funil. Permanecem com o produto real
                — não são contados como venda de outro produto.</span>}
            </span>
          </div>
          <div style={uu.sub}>
            <b style={uu.count}>{data.count.toLocaleString('pt-BR')}</b> pedido{data.count===1?'':'s'}
            {' · '}<span className="fenix-num">{USD(data.amount)}</span>
          </div>
        </div>
        {data.items && data.items.length > 0 && (
          <button style={uu.toggle} onClick={()=>setOpen(o=>!o)}>
            {open ? 'ocultar' : 'ver detalhes'}
          </button>
        )}
      </div>
      {open && data.items && (
        <div style={uu.list}>
          {data.items.map((it, i) => (
            <div key={i} style={uu.row}>
              <span style={uu.rowProd}>{it.product}</span>
              <span style={uu.rowDate}>{it.date && it.date !== 'mock' ? String(it.date).slice(0,10) : '—'}</span>
              <span style={uu.rowAmt} className="fenix-num">{USD(it.amount)}</span>
            </div>
          ))}
        </div>
      )}
    </div>
  );
};

const ap = {
  back:{ display:'inline-flex', alignItems:'center', gap:6, border:0, background:'transparent',
    font:'600 13px/1 var(--font-sans)', color:'var(--fg-2)', cursor:'pointer', padding:'4px 0', marginBottom:14 },
  header:{ display:'flex', alignItems:'center', justifyContent:'space-between', gap:16, flexWrap:'wrap', marginBottom:16 },
  idRow:{ display:'flex', alignItems:'center', gap:13 },
  av:{ width:52, height:52, borderRadius:'50%', color:'#F6E9E0', font:'700 17px/1 var(--font-sans)',
    display:'flex', alignItems:'center', justifyContent:'center', flex:'none' },
  name:{ font:'800 22px/1.1 var(--font-mono)', color:'var(--fg-1)', letterSpacing:'-.01em' },
  id:{ font:'12px/1.3 var(--font-mono)', color:'var(--fg-3)', marginTop:5 },
  chips:{ display:'flex', gap:8, flexWrap:'wrap', alignItems:'center' },
  platChip:{ display:'inline-flex', alignItems:'center', gap:6, font:'600 11px/1 var(--font-sans)',
    padding:'4px 9px', borderRadius:'var(--r-pill)', background:'var(--gray-100)', color:'var(--fg-2)' },
  demoChip:{ font:'600 11px/1 var(--font-sans)', padding:'4px 9px', borderRadius:'var(--r-pill)',
    background:'var(--warning-bg)', color:'var(--warning)' },
  dot:{ width:9, height:9, borderRadius:'50%', display:'inline-block', marginRight:5 },
  filterRow:{ display:'flex', alignItems:'center', gap:10, flexWrap:'wrap', margin:'0 0 18px' },
  segGroup:{ display:'inline-flex', alignItems:'center', gap:8 },
  segLabel:{ font:'600 12px/1 var(--font-sans)', color:'var(--fg-3)' },

  // KPI row
  kpiRow:{ display:'grid', gridTemplateColumns:'1.5fr 1fr 1fr 1fr 1fr', gap:14, marginBottom:16 },
  heroCard:{ background:'var(--fenix-navy)', borderRadius:'var(--r-lg)', padding:'22px 22px 20px',
    display:'flex', flexDirection:'column', justifyContent:'space-between' },
  heroEye:{ font:'700 12px/1.2 var(--font-sans)', letterSpacing:'.1em', textTransform:'uppercase', color:'#FFA866' },
  heroVal:{ font:'800 38px/1 var(--font-num)', letterSpacing:'-.02em', color:'#fff', marginTop:16, fontVariantNumeric:'tabular-nums' },
  heroDelta:{ display:'inline-flex', alignItems:'center', gap:5, font:'700 11px/1 var(--font-mono)',
    padding:'6px 9px', borderRadius:'var(--r-sm)', marginTop:13 },
  // Cards brancos clicáveis das KPIs (padrão da Visão Geral) — spec 2026-07-09
  wkpi:{ background:'#fff', border:'1px solid var(--border-1)', borderRadius:'var(--r-lg)',
    padding:'18px 18px', boxShadow:'var(--shadow-sm)', minWidth:0, display:'flex', flexDirection:'column' },
  wkpiTop:{ display:'flex', alignItems:'center', justifyContent:'space-between', gap:8, marginBottom:14 },
  wkpiEye:{ font:'700 12px/1.2 var(--font-sans)', letterSpacing:'.06em', textTransform:'uppercase', color:'var(--fg-3)' },
  wkpiIc:{ width:30, height:30, borderRadius:'var(--r-sm)', display:'flex', alignItems:'center', justifyContent:'center', flex:'none' },
  wkpiVal:{ font:'800 26px/1 var(--font-mono)', color:'var(--fg-1)', letterSpacing:'-.01em' },
  wkpiSub:{ font:'600 11.5px/1.2 var(--font-mono)', color:'var(--fg-3)', marginTop:9 },
  wkpiHealthWrap:{ marginTop:9 },
  wkpiHealthChip:{ display:'inline-flex', alignItems:'center', gap:6, font:'700 11px/1 var(--font-sans)', padding:'4px 9px', borderRadius:'var(--r-pill)' },
  wkpiHealthDot:{ width:7, height:7, borderRadius:'50%', flex:'none' },

  card:{ background:'#fff', border:'1px solid var(--border-1)', borderRadius:'var(--r-lg)',
    padding:'18px 22px', marginBottom:16, boxShadow:'var(--shadow-sm)' },
  cardHead:{ display:'flex', alignItems:'flex-start', justifyContent:'space-between', gap:12, marginBottom:14, flexWrap:'wrap' },
  cardTitle:{ font:'700 18px/1.2 var(--font-sans)', color:'var(--fg-1)', marginTop:5 },
  eyebrow:{ display:'block', font:'var(--text-label)', letterSpacing:'.05em', textTransform:'uppercase',
    color:'var(--fg-3)' },
  hint:{ font:'500 12.5px/1.3 var(--font-sans)', color:'var(--fg-3)' },
  note:{ font:'var(--text-caption)', color:'var(--fg-3)', marginTop:10 },
  donutRow:{ display:'grid', gridTemplateColumns:'repeat(auto-fit, minmax(320px, 1fr))', gap:16, marginBottom:16, alignItems:'start' },
  empty:{ padding:'80px 0', textAlign:'center', color:'var(--fg-3)', font:'var(--text-body)' },
  emptyTitle:{ font:'700 18px/1 var(--font-sans)', color:'var(--fg-1)', marginBottom:16 },
};

// Reembolsos por funil em barras
const frb = {
  list:{ display:'flex', flexDirection:'column', gap:14 },
  top:{ display:'flex', alignItems:'center', justifyContent:'space-between', marginBottom:7, gap:10 },
  name:{ display:'flex', alignItems:'center', gap:8, font:'600 13px/1.3 var(--font-mono)', color:'var(--fg-1)' },
  frontDot:{ display:'inline-block', width:7, height:7, borderRadius:'50%', background:'var(--fenix-orange)', flex:'none' },
  frontName:{ font:'700 13px/1.3 var(--font-mono)', color:'var(--fg-1)' },
  meta:{ display:'flex', alignItems:'center', gap:10, whiteSpace:'nowrap' },
  pct:{ font:'500 11.5px/1 var(--font-mono)', color:'var(--fg-3)' },
  amt:{ font:'600 13px/1 var(--font-mono)', color:'var(--negative)' },
  track:{ height:9, background:'var(--gray-100)', borderRadius:5, overflow:'hidden' },
};

const uu = {
  card:{ background:'var(--warning-bg)', border:'1px solid var(--warning)', borderRadius:'var(--r-lg)',
    padding:'14px 16px', marginBottom:16 },
  head:{ display:'flex', alignItems:'flex-start', gap:10 },
  icon:{ font:'15px/1 var(--font-sans)', color:'var(--warning)', marginTop:1 },
  title:{ display:'flex', alignItems:'center', gap:7, font:'700 13px/1.2 var(--font-sans)', color:'var(--fg-1)' },
  help:{ position:'relative', display:'inline-flex', alignItems:'center', justifyContent:'center',
    width:15, height:15, borderRadius:'50%', background:'var(--warning)', color:'#fff',
    font:'700 10px/1 var(--font-sans)', cursor:'help' },
  tip:{ position:'absolute', top:20, left:-8, width:268, zIndex:5, background:'var(--fenix-navy)', color:'#F6E9E0',
    font:'500 11.5px/1.45 var(--font-sans)', padding:'9px 11px', borderRadius:'var(--r-sm)',
    textTransform:'none', letterSpacing:0, boxShadow:'var(--shadow-md)' },
  sub:{ font:'var(--text-body-sm)', color:'var(--fg-2)', marginTop:4 },
  count:{ font:'800 14px/1 var(--font-mono)', color:'var(--warning)' },
  toggle:{ border:0, background:'transparent', color:'var(--fg-2)', cursor:'pointer',
    font:'600 12px/1 var(--font-sans)', textDecoration:'underline', padding:'2px 0', whiteSpace:'nowrap' },
  list:{ marginTop:12, borderTop:'1px solid var(--warning)', paddingTop:8 },
  row:{ display:'grid', gridTemplateColumns:'1fr auto auto', gap:12, alignItems:'center', padding:'6px 0' },
  rowProd:{ font:'12px/1.3 var(--font-mono)', color:'var(--fg-1)' },
  rowDate:{ font:'var(--text-caption)', color:'var(--fg-3)', whiteSpace:'nowrap' },
  rowAmt:{ font:'600 12px/1 var(--font-mono)', color:'var(--fg-1)', whiteSpace:'nowrap', textAlign:'right', minWidth:64 },
};

Object.assign(window, { AffiliatePage, UntrackedUpsells });
