// Replay — historical playback. Big timeline scrubber as centerpiece.
const { Card, Pill, fmtTs, fmtNum, fmtPct } = window.UI;
const RC2 = window.Recharts;

// adapt a backend apiEvent to the page-shape `{id, at, kind, payload}`.
// payload is delivered as a JSON RawMessage; parse it once so the row
// renderer can read `payload.account`, `.coin`, `.score`, etc.
function adaptReplayEvent(e) {
  let payload = {};
  try {
    if (typeof e.payload === 'string') payload = JSON.parse(e.payload);
    else if (e.payload && typeof e.payload === 'object') payload = e.payload;
  } catch (_) { payload = {}; }
  return {
    id: e.id,
    at: e.at_ms,
    kind: e.kind,
    payload,
  };
}

function PageReplay({ app }) {
  const accounts = app.accounts || [];
  const [accountId, setAccountId] = React.useState(app.account);
  React.useEffect(() => { setAccountId(app.account); }, [app.account]);
  const acct = accounts.find(a => a.id === accountId) || null;
  const accountID = acct && acct.accountID;

  const { data: rawEvents, error, loading, refresh } = window.API.useAPI(
    () => accountID
      ? window.API.get('/api/v1/replay/events', { account_id: accountID, limit: 2000 })
      : Promise.resolve([]),
    [accountID],
  );

  // No mock fallback: when the event log is empty (fresh DB / no
  // strategy run yet) the page renders an empty-state card.
  const events = (rawEvents && Array.isArray(rawEvents))
    ? rawEvents.map(adaptReplayEvent)
    : [];
  const haveEvents = events.length >= 2;

  if (!haveEvents) {
    return (
      <div className="p-4 space-y-4">
        <div>
          <h1 className="text-[16px] font-medium text-ink-0">replay</h1>
          <p className="mono text-[11px] text-ink-3 mt-0.5">
            deterministic playback of recorded event log · {' '}
            {loading && <span className="text-ink-3">loading…</span>}
            {error && <span className="text-neg-0">api error</span>}
            {!loading && !error && <span className="text-ink-3">no events recorded yet</span>}
            <button onClick={refresh} className="ml-2 text-inf-0 hover:text-ink-0 underline-offset-2 hover:underline">refresh</button>
          </p>
        </div>
        <Card padded>
          <div className="px-3 py-12 text-center mono text-[11.5px] text-ink-2">
            <div>no events yet for {acct ? acct.id : 'this account'}.</div>
            <div className="mt-2 text-ink-3 text-[10.5px] leading-relaxed max-w-[60ch] mx-auto">
              the event log fills as the strategy + dispatcher loop emits decisions, order placements, and risk verdicts (M11.2). once that ships, every decision is replayable here at adjustable speed.
            </div>
          </div>
        </Card>
      </div>
    );
  }

  const tMin = events[0].at;
  const tMax = events[events.length-1].at;
  const span = Math.max(1, tMax - tMin);

  const [cursor, setCursor] = React.useState(tMin + span * 0.65);
  const [playing, setPlaying] = React.useState(false);
  const [speed, setSpeed] = React.useState(4);
  const [kindFilter, setKindFilter] = React.useState('all');

  React.useEffect(() => {
    if (!playing) return;
    const id = setInterval(() => {
      setCursor(c => {
        const next = c + speed * 1000 * 0.5;
        if (next > tMax) { setPlaying(false); return tMax; }
        return next;
      });
    }, 80);
    return () => clearInterval(id);
  }, [playing, speed, tMax]);

  const buckets = React.useMemo(() => {
    const N = 80;
    const bs = new Array(N).fill(0);
    events.forEach(e => {
      const idx = Math.min(N-1, Math.floor(((e.at - tMin) / span) * N));
      bs[idx]++;
    });
    const max = Math.max(1, ...bs);
    return bs.map(v => v / max);
  }, [events, tMin, span]);

  const allKinds = React.useMemo(() => Array.from(new Set(events.map(e => e.kind))), [events]);
  const streamed = events.filter(e => e.at <= cursor && (kindFilter === 'all' || e.kind === kindFilter)).slice(-20).reverse();

  // Simulated state derived from events (M11.2 will reconstruct from
  // event_log replay). For now we use the last `trade_completed` event
  // before the cursor; equity sim shows a constant line until those
  // events are emitted.
  const lastTradeEv = [...events].reverse().find(e => e.at <= cursor && (e.kind === 'trade_completed' || e.kind === 'jump_confirmed'));
  const simCoin = (lastTradeEv && lastTradeEv.payload && lastTradeEv.payload.to_coin) || (acct && acct.current_coin) || '—';
  const simEquity = []; // empty until event_log carries equity ticks
  const simEquityNow = 0;
  const simEquityStart = 0;

  return (
    <div className="p-4 space-y-4">
      <div className="flex items-end justify-between">
        <div>
          <h1 className="text-[16px] font-medium text-ink-0">replay</h1>
          <p className="mono text-[11px] text-ink-3 mt-0.5">
            deterministic playback of recorded event log · simulated state shown · {' '}
            {loading && <span className="text-ink-3">loading…</span>}
            {error && <span className="text-neg-0">api error</span>}
            {!loading && !error && <span className="text-pos-0">live</span>}
            <button onClick={refresh} className="ml-2 text-inf-0 hover:text-ink-0 underline-offset-2 hover:underline">refresh</button>
          </p>
        </div>
        <div className="flex items-center gap-3">
          <select value={accountId} onChange={e => setAccountId(e.target.value)}
            className="bg-bg-2 border border-line-1 mono text-[11.5px] h-7 px-2 text-ink-1 outline-none">
            {accounts.map(a => <option key={a.id}>{a.id}</option>)}
          </select>
          <span className="mono text-[11px] text-ink-3">range</span>
          <span className="mono text-[11.5px] text-ink-1">{fmtTs(tMin)}</span>
          <Icon.ArrowRight size={11} className="text-ink-3"/>
          <span className="mono text-[11.5px] text-ink-1">{fmtTs(tMax)}</span>
        </div>
      </div>

      {/* Centerpiece scrubber */}
      <Card padded={false}>
        <div className="p-4 space-y-3">
          {/* heatmap row */}
          <div className="relative h-[88px]">
            <div className="absolute inset-0 flex items-end gap-px">
              {buckets.map((d, i) => {
                const cIdx = Math.floor(((cursor - tMin) / span) * buckets.length);
                const passed = i <= cIdx;
                const alpha = 0.15 + d * 0.7;
                const color = passed ? `rgba(95,207,149,${alpha})` : `rgba(122,174,216,${alpha * 0.55})`;
                return <div key={i} className="flex-1 transition-colors" style={{ height: `${20 + d * 80}%`, backgroundColor: color }}></div>;
              })}
            </div>
            {/* cursor line */}
            <div className="absolute top-0 bottom-0 pointer-events-none"
                 style={{ left: `${((cursor - tMin) / span) * 100}%` }}>
              <div className="w-px h-full bg-amb-0"></div>
              <div className="absolute -top-1 -left-1 w-2 h-2 bg-amb-0"></div>
            </div>
          </div>

          {/* range axis */}
          <div className="flex justify-between mono text-[10px] text-ink-3">
            {[0, 0.25, 0.5, 0.75, 1].map(t => (
              <span key={t}>{fmtTs(tMin + span * t, { short: true })}</span>
            ))}
          </div>

          {/* scrubber */}
          <input type="range" min={tMin} max={tMax} step={1000} value={cursor}
            onChange={e => setCursor(+e.target.value)}
            className="w-full accent-amb-0 h-1"/>

          {/* controls */}
          <div className="flex items-center justify-between pt-1">
            <div className="flex items-center gap-1">
              <CtrlBtn onClick={() => setCursor(tMin)} title="rewind"><Icon.StepBack size={14}/></CtrlBtn>
              <CtrlBtn onClick={() => setCursor(c => Math.max(tMin, c - 60000))}>−1m</CtrlBtn>
              <CtrlBtn onClick={() => setPlaying(p => !p)} primary>
                {playing ? <Icon.Pause size={14}/> : <Icon.Play size={14}/>}
              </CtrlBtn>
              <CtrlBtn onClick={() => setCursor(c => Math.min(tMax, c + 60000))}>+1m</CtrlBtn>
              <CtrlBtn onClick={() => setCursor(tMax)} title="end"><Icon.Step size={14}/></CtrlBtn>
              <div className="ml-3 flex items-center gap-1">
                <span className="text-[10px] uppercase tracking-[0.14em] text-ink-3 mono">speed</span>
                {[1,4,16,64].map(s => (
                  <button key={s} onClick={() => setSpeed(s)}
                    className={`h-7 px-2 mono text-[11px] border ${speed === s ? 'border-amb-1 bg-amb-1/15 text-amb-0' : 'border-line-0 text-ink-3 hover:text-ink-1'}`}>
                    {s}×
                  </button>
                ))}
              </div>
            </div>
            <div className="mono text-[12px] text-ink-1">
              <span className="text-ink-3 text-[10px] uppercase tracking-[0.14em] mr-2">cursor</span>
              {fmtTs(cursor)}
            </div>
          </div>
        </div>
      </Card>

      {/* Simulated dashboard widgets at cursor */}
      <div className="grid grid-cols-12 gap-4">
        <div className="col-span-3 grid grid-rows-3 gap-px bg-line-0">
          <div className="bg-bg-1 p-3">
            <div className="text-[10px] uppercase tracking-[0.14em] text-ink-3 mono">simulated coin</div>
            <div className="mono text-[20px] num text-ink-0 mt-1">{simCoin}</div>
            <div className="mono text-[10.5px] text-ink-3 mt-0.5">at cursor · {acct.bridge} bridge</div>
          </div>
          <div className="bg-bg-1 p-3">
            <div className="text-[10px] uppercase tracking-[0.14em] text-ink-3 mono">simulated equity</div>
            <div className="mono text-[20px] num text-ink-0 mt-1">{fmtNum(simEquityNow, 0)}</div>
            <div className={`mono text-[10.5px] mt-0.5 ${simEquityNow >= simEquityStart ? 'text-pos-0' : 'text-neg-0'}`}>
              {fmtPct((simEquityNow - simEquityStart) / simEquityStart, 2)} window
            </div>
          </div>
          <div className="bg-bg-1 p-3">
            <div className="text-[10px] uppercase tracking-[0.14em] text-ink-3 mono">last trade</div>
            <div className="flex items-center gap-1.5 mt-1">
              <span className="mono text-[14px] text-ink-0">{lastTrade?.from}</span>
              <Icon.ArrowRight size={12} className="text-ink-3"/>
              <span className="mono text-[14px] text-ink-0">{lastTrade?.to}</span>
            </div>
            <div className="mono text-[10.5px] text-ink-3 mt-0.5">{lastTrade && fmtTs(lastTrade.ts)}</div>
          </div>
        </div>

        <Card title="simulated equity" sub={`${simEquity.length} ticks`} className="col-span-9">
          <div style={{ height: 200 }}>
            <RC2.ResponsiveContainer>
              <RC2.AreaChart data={simEquity} margin={{ top: 6, right: 12, left: 0, bottom: 0 }}>
                <defs>
                  <linearGradient id="rep" x1="0" y1="0" x2="0" y2="1">
                    <stop offset="0%" stopColor="#e6b85c" stopOpacity={0.18}/>
                    <stop offset="100%" stopColor="#e6b85c" stopOpacity={0}/>
                  </linearGradient>
                </defs>
                <RC2.CartesianGrid stroke="#1f2630" vertical={false}/>
                <RC2.XAxis dataKey="t" tickFormatter={t => fmtTs(t,{short:true})} stroke="#3a424d" tick={{fontSize:10, fontFamily:'JetBrains Mono', fill:'#5b6573'}} axisLine={{stroke:'#1f2630'}} tickLine={false} minTickGap={50}/>
                <RC2.YAxis tick={{fontSize:10, fontFamily:'JetBrains Mono', fill:'#5b6573'}} axisLine={false} tickLine={false} width={56} tickFormatter={v=>fmtNum(v,0)} domain={['auto','auto']}/>
                <RC2.Area type="monotone" dataKey="v" stroke="#e6b85c" strokeWidth={1.25} fill="url(#rep)" isAnimationActive={false}/>
              </RC2.AreaChart>
            </RC2.ResponsiveContainer>
          </div>
        </Card>
      </div>

      {/* Event log stream */}
      <Card title="event stream" sub={`up to cursor · ${streamed.length} of ${events.filter(e => e.at <= cursor).length}`} padded={false}
        action={
          <select value={kindFilter} onChange={e => setKindFilter(e.target.value)}
            className="bg-bg-2 border border-line-0 mono text-[10.5px] h-6 px-2 text-ink-1 outline-none">
            <option value="all">all kinds</option>
            {allKinds.map(k => <option key={k}>{k}</option>)}
          </select>
        }>
        <div className="divide-y divide-line-0">
          {streamed.map((e, i) => (
            <div key={e.id} className={`px-3 py-1.5 grid grid-cols-12 gap-3 items-center ${i === 0 ? 'bg-amb-1/10' : ''}`}>
              <span className="col-span-2 mono text-[10.5px] text-ink-3">{fmtTs(e.at, { short: true })}</span>
              <span className="col-span-2 mono text-[10.5px] text-inf-0">{e.kind}</span>
              <span className="col-span-1 mono text-[10.5px] text-ink-2">{e.payload.account}</span>
              <span className="col-span-1 mono text-[10.5px] text-ink-1">{e.payload.coin}</span>
              <span className={`col-span-2 mono text-[10.5px] num ${e.payload.score >= 0 ? 'text-pos-0' : 'text-neg-0'}`}>
                {(e.payload.score * 100).toFixed(3)}%
              </span>
              <span className="col-span-4 mono text-[10.5px] text-ink-3 truncate">id={e.id}</span>
            </div>
          ))}
        </div>
      </Card>
    </div>
  );
}

function CtrlBtn({ children, onClick, primary, title }) {
  return (
    <button onClick={onClick} title={title}
      className={`h-7 min-w-[28px] px-2 grid place-items-center mono text-[11px] border ${primary ? 'border-line-2 bg-bg-3 text-ink-0 hover:bg-bg-4' : 'border-line-0 text-ink-2 hover:text-ink-0 hover:bg-bg-2'}`}>
      {children}
    </button>
  );
}

window.PageReplay = PageReplay;
