// OH Lead — Modo TV v2 · board (React)
const { useState: uS, useEffect: uE, useRef: uR, useMemo: uM } = React;

// ── helpers ───────────────────────────────
const brl = (cents) => (cents / 100).toLocaleString('pt-BR', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
const brl0 = (cents) => Math.round(cents / 100).toLocaleString('pt-BR');
const pct = (x) => Math.round(x * 100) + '%';
const delta = (cur, prev) => (prev ? Math.round(((cur - prev) / prev) * 100) : 0);

// conta-progressivo suave. Semeia o valor real na 1ª carga (captura estática
// correta) e anima apenas os incrementos ao vivo seguintes.
function useCountUp(target, dur = 750) {
  const tgt = Number.isFinite(target) ? target : 0;
  const [v, setV] = uS(tgt);
  const from = uR(tgt); const t0 = uR(0); const raf = uR(0); const seeded = uR(false);
  uE(() => {
    if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) { setV(tgt); return; }
    if (!seeded.current) {
      if (tgt !== 0) { seeded.current = true; from.current = tgt; setV(tgt); }
      return;
    }
    from.current = Number.isFinite(v) ? v : tgt;
    t0.current = performance.now();
    cancelAnimationFrame(raf.current);
    // trava dentro do intervalo real [from, target] — impede runaway caso um
    // frame venha com now < t0 (k negativo faria e = 1-(1-k)³ explodir e o valor
    // dispararia; o `from` seguinte herdaria o valor corrompido, sem volta).
    const lo = Math.min(from.current, tgt), hi = Math.max(from.current, tgt);
    const step = (now) => {
      const k = Math.min(1, Math.max(0, (now - t0.current) / dur));
      const e = 1 - Math.pow(1 - k, 3);
      let nv = from.current + (tgt - from.current) * e;
      nv = Math.min(hi, Math.max(lo, nv));
      setV(k >= 1 ? tgt : nv);
      if (k < 1) raf.current = requestAnimationFrame(step);
    };
    raf.current = requestAnimationFrame(step);
    return () => cancelAnimationFrame(raf.current);
  }, [target]);
  return v;
}

function Sparkline({ data, color, w = 70, h = 26 }) {
  const max = Math.max(1, ...data);
  const step = w / (data.length - 1);
  const pts = data.map((d, i) => `${i * step},${h - (d / max) * (h - 3) - 1}`).join(' ');
  return (
    <svg width={w} height={h} className="kpi-spark" viewBox={`0 0 ${w} ${h}`}>
      <polyline points={pts} fill="none" stroke={color} strokeWidth="2" strokeLinejoin="round" strokeLinecap="round" opacity="0.85" />
    </svg>
  );
}

// ── ring de meta ──────────────────────────
function MetaRing({ cur, goal, accent }) {
  const ratio = Math.min(1, goal ? cur / goal : 0);
  const shown = useCountUp(ratio, 800);
  const R = 66, C = 2 * Math.PI * R;
  const done = ratio >= 1;
  const left = Math.max(0, goal - cur);
  return (
    <section className="card c-meta">
      <div className="card-label">Meta do dia</div>
      <div className="meta-wrap">
        <div className="ring">
          <svg width="150" height="150">
            <circle cx="75" cy="75" r={R} fill="none" stroke="rgba(255,255,255,0.08)" strokeWidth="13" />
            <circle cx="75" cy="75" r={R} fill="none" stroke={done ? '#4ade80' : accent} strokeWidth="13"
              strokeLinecap="round" strokeDasharray={C} strokeDashoffset={C * (1 - shown)} />
          </svg>
          <div className="ring-c">
            <div className="ring-pct" style={done ? { color: '#4ade80' } : null}>{Math.round(shown * 100)}%</div>
            <div className="ring-cap">da meta</div>
          </div>
        </div>
        <div className="meta-info">
          <div className="meta-cur">R$ {brl0(cur)}</div>
          <div className="meta-goal">de R$ {brl0(goal)}</div>
          <div className={'meta-left' + (done ? ' done' : '')}>
            {done ? '✓ Meta batida!' : `faltam R$ ${brl0(left)}`}
          </div>
        </div>
      </div>
    </section>
  );
}

// ── KPI tile ──────────────────────────────
function Kpi({ label, value, accent, deltaPct, spark, accentColor }) {
  const dCls = deltaPct == null ? 'flat' : deltaPct > 0 ? 'up' : deltaPct < 0 ? 'down' : 'flat';
  return (
    <div className="kpi">
      <div className="kpi-l">{label}</div>
      <div className={'kpi-v' + (accent ? ' accent' : '')}>{value}</div>
      {deltaPct != null && (
        <div className={'kpi-d ' + dCls}>
          {deltaPct > 0 ? '▲' : deltaPct < 0 ? '▼' : '—'} {Math.abs(deltaPct)}%
        </div>
      )}
      {spark && <Sparkline data={spark} color={accentColor} />}
    </div>
  );
}

// ── gráfico por hora ──────────────────────
function HourChart({ hours, accent }) {
  const W = 1000, H = 300, PT = 16, PB = 30;
  const IH = H - PT - PB;
  const max = Math.max(1, ...hours.map((h) => h.checkins + h.signups));
  const n = hours.length;
  const bw = W / n;
  const barW = Math.min(94, Math.max(8, bw * 0.5));
  const nowI = n - 1;
  return (
    <svg viewBox={`0 0 ${W} ${H}`} preserveAspectRatio="none">
      {[0.25, 0.5, 0.75, 1].map((f) => (
        <line key={f} x1="0" x2={W} y1={PT + IH * (1 - f)} y2={PT + IH * (1 - f)} stroke="rgba(255,255,255,0.06)" strokeWidth="1" />
      ))}
      {hours.map((s, i) => {
        const x = i * bw + (bw - barW) / 2;
        const hC = (s.checkins / max) * IH;
        const hS = (s.signups / max) * IH;
        const cur = i === nowI;
        return (
          <g key={s.hour} opacity={cur ? 1 : 0.92}>
            {cur && <rect x={i * bw + 1} y={PT} width={bw - 2} height={IH} fill="rgba(255,255,255,0.04)" rx="6" />}
            <rect x={x} y={PT + IH - hC} width={barW} height={hC} rx="4" fill="rgba(255,255,255,0.2)" />
            <rect x={x} y={PT + IH - hC - hS} width={barW} height={hS} rx="4" fill={accent} opacity={cur ? 1 : 0.88} />
            {cur && hS > 0 && <circle cx={x + barW / 2} cy={PT + IH - hC - hS - 4} r="4" fill={accent}><animate attributeName="opacity" values="1;0.3;1" dur="1.6s" repeatCount="indefinite" /></circle>}
          </g>
        );
      })}
      {hours.map((s, i) => (
        (n <= 8 || i % 2 === 0) ? (
          <text key={'l' + s.hour} x={(i + 0.5) * bw} y={H - 8} textAnchor="middle" fill="rgba(255,255,255,0.4)" fontSize="15" fontFamily="Inter Tight, sans-serif" fontWeight={i === nowI ? 800 : 500}>
            {s.hour}h
          </text>
        ) : null
      ))}
    </svg>
  );
}

// ── feed ao vivo ──────────────────────────
function agoLabel(m) {
  if (m <= 0) return 'agora';
  if (m < 60) return `há ${m} min`;
  return `há ${Math.floor(m / 60)}h`;
}
function FeedItem({ it, fresh }) {
  return (
    <div className={'feed-item' + (fresh ? ' enter' : '')}>
      <div className={'feed-av' + (it.isReturn ? ' ret' : '')}>{it.initials}</div>
      <div className="feed-mid">
        <div className="feed-name">{it.name.split(' ')[0]}</div>
        <div className="feed-meta">
          {it.isReturn ? 'voltou' : 'novo'} · totem <span className="tt">{it.totem}</span> · {it.city.label}
        </div>
      </div>
      <div className="feed-right">
        {it.valueCents > 0
          ? <div className="feed-val">R$ {brl0(it.valueCents)}</div>
          : <div className="feed-val" style={{ color: 'var(--t3)' }}>—</div>}
        <div className={'feed-ago' + (fresh ? '' : '')}>{fresh ? <span className="feed-tagnew">● AGORA</span> : agoLabel(it.minsAgo)}</div>
      </div>
    </div>
  );
}

// ── mapa (Leaflet, bolinhas por cidade) ───
const CITY_COORD = {
  'Novo Hamburgo': [-29.6783, -51.1309], 'São Leopoldo': [-29.7604, -51.1474],
  'Canoas': [-29.9178, -51.1836], 'Estância Velha': [-29.6489, -51.1739],
  'Sapucaia do Sul': [-29.8275, -51.1450], 'Portão': [-29.7019, -51.2419],
};
function TvMap({ cidades, accent, ping }) {
  const elRef = uR(null), mapRef = uR(null), layerRef = uR(null), pingRef = uR(null), boundsRef = uR(null);
  uE(() => {
    const map = L.map(elRef.current, {
      zoomControl: false, attributionControl: false, dragging: false, scrollWheelZoom: false,
      doubleClickZoom: false, boxZoom: false, keyboard: false, touchZoom: false, tap: false,
    }).setView([-29.75, -51.15], 10);
    L.tileLayer('https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png', { subdomains: 'abcd', maxZoom: 19 }).addTo(map);
    layerRef.current = L.layerGroup().addTo(map);
    pingRef.current = L.layerGroup().addTo(map);
    mapRef.current = map;
    const t = setTimeout(() => map.invalidateSize(), 140);
    // recalcula o tamanho quando o container muda (troca de layout, escala, resize)
    let ro = null;
    if (typeof ResizeObserver !== 'undefined') {
      ro = new ResizeObserver(() => {
        map.invalidateSize();
        if (boundsRef.current) map.fitBounds(boundsRef.current, { maxZoom: 11, animate: false });
      });
      ro.observe(elRef.current);
    }
    return () => { clearTimeout(t); if (ro) ro.disconnect(); map.remove(); mapRef.current = null; };
  }, []);
  // marcadores
  uE(() => {
    const map = mapRef.current, layer = layerRef.current; if (!map || !layer) return;
    const items = cidades.map((c) => ({ ...c, coord: CITY_COORD[c.label] })).filter((c) => c.coord);
    const max = Math.max(1, ...items.map((i) => i.count));
    layer.clearLayers();
    items.forEach((it) => {
      L.circleMarker(it.coord, {
        radius: 7 + Math.sqrt(it.count) * 5, color: accent, weight: 1.5,
        fillColor: accent, fillOpacity: 0.32 + 0.4 * (it.count / max),
      }).addTo(layer);
    });
    if (items.length) {
      boundsRef.current = L.latLngBounds(items.map((i) => i.coord)).pad(0.45);
      map.fitBounds(boundsRef.current, { maxZoom: 11, animate: false });
    }
  }, [cidades, accent]);
  // ping ao vivo a cada novo cadastro
  uE(() => {
    if (!ping || !ping.city) return;
    const map = mapRef.current, layer = pingRef.current, coord = CITY_COORD[ping.city];
    if (!map || !layer || !coord) return;
    if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
    let r = 8, op = 0.8;
    const mk = L.circleMarker(coord, { radius: r, color: accent, weight: 2.5, fillColor: accent, fillOpacity: 0.25 }).addTo(layer);
    const id = setInterval(() => {
      r += 2.6; op -= 0.05;
      if (op <= 0) { clearInterval(id); layer.removeLayer(mk); return; }
      mk.setRadius(r); mk.setStyle({ opacity: op, fillOpacity: op * 0.25 });
    }, 40);
    return () => { clearInterval(id); layer.removeLayer(mk); };
  }, [ping]);
  return <div ref={elRef} className="tv-map"></div>;
}

// ── board ─────────────────────────────────
function TvBoard({ t }) {
  const accent = t.accent || '#ff6a00';
  const showGoal = t.meta !== false;
  const layout = t.layout || 'compacto';
  const devicesN = parseInt(t.totens, 10) || 3;
  const engineRef = uR(null);
  const [snap, setSnap] = uS(null);
  const [burst, setBurst] = uS(0);
  const [toast, setToast] = uS(null);
  const [ping, setPing] = uS(null);
  const [now, setNow] = uS(() => new Date());

  // acento como variável CSS
  uE(() => { document.getElementById('tv-board').style.setProperty('--accent', accent); }, [accent]);

  // engine
  uE(() => {
    const eng = window.TVSim.createEngine({ pace: t.pace || 'busy', devices: devicesN, hourNow: t.dia === 'cheio' ? 20 : undefined });
    engineRef.current = eng;
    setSnap(eng.snapshot());
    const unsub = eng.subscribe((s, evt) => {
      setSnap(s);
      if (evt && evt.type === 'signup') {
        setPing({ city: evt.item.city.label, n: evt.item.id });
        if (t.celeb !== false) {
          setBurst((b) => b + 1);
          setToast({ id: evt.item.id, name: evt.item.name, totem: evt.item.totem });
        }
      }
    });
    eng.start();
    return () => { unsub(); eng.stop(); };
  }, []);

  // troca de ritmo sem remontar
  uE(() => { if (engineRef.current) engineRef.current.setPace(t.pace || 'busy'); }, [t.pace]);

  // troca da qtd de totens (demo de responsividade)
  uE(() => { if (engineRef.current) engineRef.current.configure({ devices: devicesN }); }, [devicesN]);

  // simulação de dia cheio (12 barras no gráfico)
  uE(() => { if (engineRef.current) engineRef.current.configure({ hourNow: t.dia === 'cheio' ? 20 : null }); }, [t.dia]);

  // relógio
  uE(() => { const id = setInterval(() => setNow(new Date()), 1000); return () => clearInterval(id); }, []);

  // limpa toast
  uE(() => { if (!toast) return; const id = setTimeout(() => setToast(null), 3000); return () => clearTimeout(id); }, [toast]);

  // count-ups dos números-herói
  const rev = useCountUp(snap ? snap.revenueCents : 0, 700);
  const cad = useCountUp(snap ? snap.signups : 0, 600);
  const vis = useCountUp(snap ? snap.visits : 0, 600);

  if (!snap) return null;

  const goal = showGoal ? snap.goalCents : 0;
  const revDelta = delta(snap.revenueCents, snap.yRevenueCents);
  const cadDelta = delta(snap.signups, snap.ySignups);
  const visDelta = delta(snap.visits, snap.yVisits);
  const feedItems = snap.feed.slice(-7).reverse();
  const maxCity = Math.max(1, ...snap.cidades.map((c) => c.count));
  const intColors = [accent, '#3b82f6', '#a78bfa', '#f472b6'];

  const time = now.toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit' });
  const date = now.toLocaleDateString('pt-BR', { weekday: 'long', day: 'numeric', month: 'short' });
  const rankN = layout === 'mapa' ? 6 : 4;

  // ── cards reutilizáveis (posicionados diferente por layout) ──
  const metaCard = showGoal
    ? <MetaRing cur={Math.round(rev)} goal={goal} accent={accent} />
    : <section className="card c-meta"><div className="card-label">Faturamento de hoje</div><div className="meta-wrap"><div className="hero-val" style={{ fontSize: '64px' }}><span className="cur">R$</span>{brl0(rev)}</div></div></section>;

  const kpiVisitas = <Kpi label="Visitas hoje" value={Math.round(vis)} deltaPct={visDelta} spark={snap.devices[0] && snap.devices[0].spark} accentColor={accent} />;
  const kpiNovos = <Kpi label="Novos cadastros" value={Math.round(cad)} accent deltaPct={cadDelta} spark={snap.devices[1] && snap.devices[1].spark} accentColor={accent} />;
  const kpiRetorno = <Kpi label="Taxa de retorno" value={pct(snap.returnRate)} accentColor={accent} />;
  const kpiOptin = <Kpi label="Opt-in WhatsApp" value={pct(snap.optInRate)} accentColor={accent} />;

  // feed "Atividade ao vivo" (volta à coluna direita no layout "mapa")
  const liveItems = snap.feed.slice(0, 6);
  const feedCard = (
    <section className="card c-live">
      <div className="card-label">Atividade ao vivo</div>
      <div className="feed-list">
        {liveItems.map((it, i) => <FeedItem key={it.id} it={it} fresh={i === 0 && !!it.fresh} />)}
      </div>
    </section>
  );

  // card grande de cadastros (usado no topo da 3ª coluna no layout "compacto")
  const cadBigCard = (
    <section className="card c-cad">
      <div className="card-label">Cadastros hoje</div>
      <div key={'cv' + burst} className="cad-v cad-pop">{Math.round(cad)}</div>
      <div className="cad-foot">
        <span className="cad-delta" style={cadDelta < 0 ? { color: 'var(--bad)' } : null}>{cadDelta >= 0 ? '▲' : '▼'} {Math.abs(cadDelta)}%</span>
        <span className="cad-cap">vs ontem · meta {snap.pace === 'quiet' ? 8 : 45}</span>
      </div>
      <div className={'cad-chip' + (toast ? ' go' : '')}>novo cadastro</div>
      <div key={'cb' + burst} className={'cad-burst' + (burst ? ' go' : '')}></div>
    </section>
  );

  const heroCard = (
    <section className="card c-hero">
      <div className="card-label">Faturamento de hoje</div>
      <div className="hero-row">
        <div className="hero-main">
          <div className="hero-val"><span className="cur">R$</span>{brl(rev)}</div>
          <div className="hero-sub">
            ticket médio <b>R$ {brl(snap.avgTicketCents)}</b> · {snap.withValue} de {snap.signups} com valor
          </div>
        </div>
        <div className="hero-delta">
          <div className="pill" style={revDelta < 0 ? { color: 'var(--bad)' } : null}>{revDelta >= 0 ? '▲' : '▼'} {Math.abs(revDelta)}%</div>
          <div className="cap">vs ontem<br />no mesmo horário</div>
        </div>
      </div>
    </section>
  );

  const chartCard = (
    <section className="card c-chart">
      <div className="chart-head">
        <div className="card-label">Movimento por hora</div>
        <div className="chart-legend">
          <span><i style={{ background: accent }}></i>Cadastros</span>
          <span><i style={{ background: 'rgba(255,255,255,0.3)' }}></i>Check-ins</span>
        </div>
      </div>
      <div className="chart-body"><HourChart hours={snap.hours} accent={accent} /></div>
    </section>
  );

  const mapCard = (
    <section className="card c-feed">
      <div className="card-label">De onde vêm — cadastros no mapa</div>
      <TvMap cidades={snap.cidades} accent={accent} ping={ping} />
      <div className="feed-rec">
        <span>Recorde do mês: <b>{snap.records.bestDaySignups}</b> em {snap.records.bestDayLabel}</span>
        <span>Sequência: <b>{snap.records.streakDays} dias</b> batendo meta</span>
      </div>
    </section>
  );

  const totensCard = (
    <section className="mini">
      <div className="card-label">Totens <span className="mini-count">{snap.devices.length}</span></div>
      <div className={'tot-list' + (snap.devices.length > 5 ? ' cols2' : '')}>
        {snap.devices.map((d) => (
          <div className="tot-row" key={d.name}>
            <span className={'tot-dot ' + (d.online ? 'on' : 'off')}></span>
            <span className="tot-name">{d.name}</span>
            <span className={'tot-r' + (d.online ? '' : ' off')}>
              {d.online ? <React.Fragment><b>{d.signupsToday}</b> hoje</React.Fragment> : 'offline'}
            </span>
          </div>
        ))}
      </div>
    </section>
  );
  const interessesCard = (
    <section className="mini">
      <div className="card-label">Interesses da base</div>
      <div className="int-list">
        {snap.interesses.map((it, i) => (
          <div className="int-row" key={it.label}>
            <span className="int-name">{it.label}</span>
            <span className="int-bar"><i style={{ width: pct(it.pct), background: intColors[i % intColors.length] }}></i></span>
            <span className="int-pct">{pct(it.pct)}</span>
          </div>
        ))}
      </div>
    </section>
  );
  const bottomRow = (
    <div className="c-bottom">{totensCard}{interessesCard}</div>
  );

  const rankingCard = (
    <section className="card c-geo">
      <div className="card-label">Ranking de cidades</div>
      <div className="geo-list">
        {snap.cidades.slice(0, rankN).map((c) => (
          <div className="geo-row" key={c.label}>
            <span className="geo-name">{c.label} <span>/ {c.uf}</span></span>
            <span className="geo-n">{c.count}</span>
            <span className="geo-bar"><i style={{ width: pct(c.count / maxCity), background: accent }}></i></span>
          </div>
        ))}
      </div>
    </section>
  );

  const header = (
    <header className="tvh">
      <div className="tvh-brand">
        <div className="tvh-logo">OH</div>
        <div>
          <div className="tvh-name">OH<b>Lead</b><span className="tvh-store">· Loja Centro</span></div>
        </div>
      </div>
      <div className="tvh-live">
        <span className="dot"></span> AO VIVO <span className="ago">· atualizado agora</span>
      </div>
      <div className="tvh-right">
        <div className="tvh-clock">
          <div className="t">{time}</div>
          <div className="d">{date}</div>
        </div>
        <button type="button" className="tvh-exit">Sair</button>
      </div>
    </header>
  );

  const toastEl = toast ? (
    <div key={toast.id} className="tv-toast go">
      <span className="ti">+1</span>
      Novo cadastro · {toast.name.split(' ')[0]} no totem {toast.totem}
    </div>
  ) : null;

  // ── layout "mapa": mapa no centro; cadastros sobe p/ coluna 1; chart + ranking 50/50 à direita ──
  if (layout === 'mapa') {
    return (
      <React.Fragment>
        {header}
        <div className="tv-grid lay-mapa">
          {cadBigCard}
          <div className="c-kpis kpis-3">{kpiVisitas}{kpiRetorno}{kpiOptin}</div>
          {heroCard}
          {mapCard}
          <div className="c-bottom">{totensCard}{chartCard}</div>
          <div className="c-right">{feedCard}{rankingCard}</div>
        </div>
        {toastEl}
      </React.Fragment>
    );
  }

  // ── layout "compacto" (original): chart no centro; mapa à direita ──
  return (
    <React.Fragment>
      {header}
      <div className="tv-grid lay-compacto">
        {metaCard}
        <div className="c-kpis">{kpiVisitas}{kpiNovos}{kpiRetorno}{kpiOptin}</div>
        {heroCard}
        {chartCard}
        {cadBigCard}
        {mapCard}
        {bottomRow}
        {rankingCard}
      </div>
      {toastEl}
    </React.Fragment>
  );
}

window.TvBoard = TvBoard;
