// Dashboard — KPIs + active swaps strip + FSM heatmap + alerts preview.

function ViewDashboard({ data, onSelectSwap, onNav }) {
  const { swaps, aggregate, spark, alerts, health, now } = data;
  const active = swaps.filter((s) => !ZW.TERMINAL.has(s.state));
  const stuck = swaps.filter((s) => s.stuck);
  const recentTerm = swaps
    .filter((s) => ZW.TERMINAL.has(s.state))
    .sort((a, b) => b.updatedAt - a.updatedAt)
    .slice(0, 5);

  // FSM heatmap counts
  const counts = {};
  ZW.FSM_STATES.forEach((s) => (counts[s] = 0));
  swaps.forEach((s) => (counts[s.state] = (counts[s.state] || 0) + 1));
  const maxCount = Math.max(...Object.values(counts), 1);
  const avgFillMin = ZW.isFiniteNumber(aggregate.avgFillSec)
    ? (Number(aggregate.avgFillSec) / 60).toFixed(1)
    : ZW.NA;

  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 14, padding: 14, minHeight: 0 }}>
      {/* KPI row */}
      <div style={{ display: "grid", gridTemplateColumns: "repeat(5, 1fr)", gap: 10 }}>
        <KPI label="Active Swaps" value={aggregate.active} unit="in flight"
          sparkline={spark.throughput} sparkColor="var(--alice)" />
        <KPI label="Completed 24h" value={aggregate.completed24h}
          sparkline={spark.throughput} sparkColor="var(--ok)"
          hint="happy path" />
        <KPI label="Refunded 24h" value={aggregate.refunded24h}
          delta={ZW.isFiniteNumber(aggregate.refunded24h) ? (aggregate.refunded24h > 3 ? "elevated" : "normal") : null}
          deltaTone={aggregate.refunded24h > 3 ? "down" : "flat"}
          hint="refund or CSV branch" />
        <KPI label="P&L 24h" value={ZW.fmtUsd(aggregate.pnl24h)} unit={ZW.fmtUsd(aggregate.pnl24h) === ZW.NA ? undefined : "USD"}
          sparkline={spark.pnl} sparkColor="var(--ok)"
          hint="requires live P&L source" />
        <KPI label="Avg Fill Time" value={avgFillMin} unit={avgFillMin === ZW.NA ? undefined : "min"}
          sparkline={spark.throughput} sparkColor="var(--info)"
          hint="requires event timeline" />
      </div>

      {/* Main grid */}
      <div style={{
        display: "grid",
        gridTemplateColumns: "1.4fr 1fr",
        gap: 14,
        flex: 1,
        minHeight: 0,
      }}>
        {/* Left col */}
        <div style={{ display: "flex", flexDirection: "column", gap: 14, minHeight: 0 }}>
          <Panel title="FSM State Distribution"
            right={<span style={{ fontSize: 11, color: "var(--fg-3)" }}>{swaps.length} swaps total</span>}>
            <FSMHeatmap counts={counts} max={maxCount} onPick={(state) => {
              // jump to swaps view filtered (mocked: select first swap in state)
              const s = swaps.find((x) => x.state === state);
              if (s) onSelectSwap(s.id);
            }} />
          </Panel>

          <Panel title="Active Swaps" padded={false}
            right={<Button size="sm" onClick={() => onNav("swaps")}>View all →</Button>}
            style={{ flex: 1, minHeight: 220 }} scrollable>
            <ActiveSwapsTable swaps={active.slice(0, 10)} onSelect={onSelectSwap} now={now} />
          </Panel>
        </div>

        {/* Right col */}
        <div style={{ display: "flex", flexDirection: "column", gap: 14, minHeight: 0 }}>
          <Panel title="Solver Health"
            right={<Badge tone="ok"><StatusDot status="ok" size={6} blink />all systems</Badge>}>
            <HealthSummary health={health} />
          </Panel>

          <Panel title={`Stuck / At Risk (${stuck.length})`} padded={false}
            style={{ minHeight: 140 }}>
            {stuck.length === 0 ? (
              <Empty>No stalled swaps.</Empty>
            ) : (
              <div style={{ display: "flex", flexDirection: "column" }}>
                {stuck.map((s) => (
                  <div key={s.id} onClick={() => onSelectSwap(s.id)}
                    style={{
                      padding: "8px 14px",
                      display: "flex", alignItems: "center", justifyContent: "space-between",
                      borderTop: "1px solid var(--line-soft)",
                      cursor: "pointer",
                    }}
                    onMouseEnter={(e) => (e.currentTarget.style.background = "var(--bg-2)")}
                    onMouseLeave={(e) => (e.currentTarget.style.background = "transparent")}>
                    <div style={{ display: "flex", flexDirection: "column", gap: 3 }}>
                      <span className="mono" style={{ fontSize: 12 }}>{s.id}</span>
                      <span style={{ fontSize: 11, color: "var(--fg-3)" }}>
                        stalled {ZW.fmtRelTime(s.updatedAt, now)} at {ZW.STATE_LABELS[s.state]}
                      </span>
                    </div>
                    <StateChip state={s.state} />
                  </div>
                ))}
              </div>
            )}
          </Panel>

          <Panel title="Recent Terminations" padded={false} style={{ minHeight: 140 }}>
            <div style={{ display: "flex", flexDirection: "column" }}>
              {recentTerm.map((s) => (
                <div key={s.id} onClick={() => onSelectSwap(s.id)}
                  style={{
                    padding: "8px 14px",
                    display: "flex", alignItems: "center", justifyContent: "space-between",
                    borderTop: "1px solid var(--line-soft)",
                    cursor: "pointer",
                  }}
                  onMouseEnter={(e) => (e.currentTarget.style.background = "var(--bg-2)")}
                  onMouseLeave={(e) => (e.currentTarget.style.background = "transparent")}>
                  <div style={{ display: "flex", flexDirection: "column", gap: 3 }}>
                    <span className="mono" style={{ fontSize: 12 }}>{s.id}</span>
                    <span style={{ fontSize: 11, color: "var(--fg-3)" }}>
                      <DirectionChip direction={s.direction} /> · {ZW.fmtRelTime(s.updatedAt, now)} ago
                    </span>
                  </div>
                  <StateChip state={s.state} />
                </div>
              ))}
            </div>
          </Panel>
        </div>
      </div>
    </div>
  );
}

function FSMHeatmap({ counts, max, onPick }) {
  // Show happy path row + refund row + claim row as horizontal cells.
  const rows = [
    { name: "Happy Path", states: ZW.HAPPY_PATH, accent: "var(--alice)" },
    { name: "Refund", states: ZW.REFUND_BRANCH, accent: "var(--warn)" },
    { name: "Claim-CSV", states: ZW.CLAIM_BRANCH, accent: "var(--info)" },
  ];

  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
      {rows.map((row) => (
        <div key={row.name} style={{ display: "flex", alignItems: "center", gap: 10 }}>
          <span style={{ width: 92, fontSize: 11, color: "var(--fg-2)", letterSpacing: 0.3 }}>
            {row.name}
          </span>
          <div style={{ display: "flex", gap: 3, flex: 1, flexWrap: "wrap" }}>
            {row.states.map((s) => {
              const c = counts[s] || 0;
              const intensity = c === 0 ? 0 : Math.max(0.18, c / max);
              return (
                <button key={s}
                  onClick={() => c > 0 && onPick(s)}
                  title={`${ZW.STATE_LABELS[s]} — ${c}`}
                  style={{
                    flex: "1 1 60px",
                    minWidth: 60,
                    height: 36,
                    background: c === 0 ? "var(--bg-2)" : `color-mix(in oklch, ${row.accent} ${intensity * 70}%, var(--bg-2))`,
                    border: `1px solid ${c > 0 ? `color-mix(in oklch, ${row.accent} ${intensity * 80}%, transparent)` : "var(--line-soft)"}`,
                    borderRadius: 4,
                    color: c === 0 ? "var(--fg-3)" : "var(--fg-0)",
                    cursor: c === 0 ? "default" : "pointer",
                    padding: "4px 6px",
                    display: "flex",
                    flexDirection: "column",
                    alignItems: "flex-start",
                    justifyContent: "space-between",
                    gap: 2,
                    overflow: "hidden",
                  }}>
                  <span style={{ fontSize: 9.5, opacity: 0.85, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis", width: "100%", textAlign: "left" }}>
                    {ZW.STATE_LABELS[s]}
                  </span>
                  <span className="mono" style={{ fontSize: 14, fontWeight: 600, lineHeight: 1 }}>{c}</span>
                </button>
              );
            })}
          </div>
        </div>
      ))}
    </div>
  );
}

function ActiveSwapsTable({ swaps, onSelect, now }) {
  return (
    <div style={{ display: "flex", flexDirection: "column" }}>
      <div style={{
        display: "grid",
        gridTemplateColumns: "140px 80px 90px 1fr 90px 70px 60px",
        padding: "8px 14px",
        fontSize: 10.5,
        color: "var(--fg-2)",
        letterSpacing: 0.4,
        textTransform: "uppercase",
        fontWeight: 600,
        borderBottom: "1px solid var(--line-soft)",
        position: "sticky", top: 0, background: "var(--bg-1)", zIndex: 1,
      }}>
        <span>Swap ID</span>
        <span>Dir</span>
        <span>Role</span>
        <span>State / Progress</span>
        <span style={{ textAlign: "right" }}>Amount</span>
        <span style={{ textAlign: "right" }}>Age</span>
        <span style={{ textAlign: "right" }}>CSV</span>
      </div>
      {swaps.map((s) => (
        <div key={s.id} onClick={() => onSelect(s.id)}
          style={{
            display: "grid",
            gridTemplateColumns: "140px 80px 90px 1fr 90px 70px 60px",
            padding: "9px 14px",
            fontSize: 12,
            borderBottom: "1px solid var(--line-soft)",
            cursor: "pointer",
            alignItems: "center",
          }}
          onMouseEnter={(e) => (e.currentTarget.style.background = "var(--bg-2)")}
          onMouseLeave={(e) => (e.currentTarget.style.background = "transparent")}>
          <span className="mono" style={{ fontSize: 11.5 }}>{s.id}</span>
          <DirectionChip direction={s.direction} />
          <Badge tone={s.role === "alice" ? "alice" : "bob"} subtle>
            {s.role}
          </Badge>
          <div style={{ display: "flex", alignItems: "center", gap: 8, minWidth: 0 }}>
            <FSMStepperMini state={s.state} />
            <StateChip state={s.state} />
          </div>
          <span className="mono" style={{ textAlign: "right", color: "var(--fg-1)" }}>
            {s.direction === "btc-to-zec"
              ? <>{ZW.fmtSats(s.btcSats)}<span style={{ color: "var(--btc)", fontSize: 10, marginLeft: 3 }}>BTC</span></>
              : <>{ZW.fmtZats(s.zecZats)}<span style={{ color: "var(--zec)", fontSize: 10, marginLeft: 3 }}>ZEC</span></>}
          </span>
          <span className="mono" style={{ textAlign: "right", color: "var(--fg-2)", fontSize: 11 }}>
            {ZW.fmtRelTime(s.startedAt, now)}
          </span>
          <span className="mono" style={{
            textAlign: "right", fontSize: 11,
            color: ZW.isFiniteNumber(s.csvDeadline) && s.csvDeadline - now < 30 * 60_000 ? "var(--warn)" : "var(--fg-3)",
          }}>
            {ZW.isFiniteNumber(s.csvDeadline) ? ZW.fmtCountdown((s.csvDeadline - now) / 1000) : ZW.NA}
          </span>
        </div>
      ))}
    </div>
  );
}

function HealthSummary({ health }) {
  const fmtLatency = (v) => ZW.isFiniteNumber(v) ? `${Math.round(Number(v))}ms` : ZW.NA;
  const fmtTip = (v) => ZW.isFiniteNumber(v) ? `tip ${ZW.fmtInt(v)}` : `tip ${ZW.NA}`;
  const scanWarn = ZW.isFiniteNumber(health.zec.scanLag) && health.zec.scanLag > 20;
  const quoteLive = health.quotes.source && health.quotes.source !== ZW.NA;
  const items = [
    { name: "BTC RPC", status: health.btc.rpc.status, value: fmtLatency(health.btc.rpc.latencyMs), sub: fmtTip(health.btc.height) },
    { name: "ZEC RPC", status: health.zec.rpc.status, value: fmtLatency(health.zec.rpc.latencyMs), sub: fmtTip(health.zec.tipHeight) },
    { name: "ZEC node info", status: health.zec.lwd.status, value: fmtLatency(health.zec.lwd.latencyMs), sub: "via /zec-rpc getinfo" },
    { name: "Orchard scan", status: scanWarn ? "warn" : ZW.isFiniteNumber(health.zec.scanLag) ? "ok" : "idle",
      value: ZW.isFiniteNumber(health.zec.scanLag) ? `−${health.zec.scanLag} blk` : ZW.NA,
      sub: ZW.isFiniteNumber(health.zec.scanHeight) ? `scan ${ZW.fmtInt(health.zec.scanHeight)}` : `scan ${ZW.NA}` },
    { name: "Quote feed", status: health.quotes.stale ? "warn" : "ok",
      value: ZW.isFiniteNumber(health.quotes.observedAgo) ? `${health.quotes.observedAgo}s` : ZW.NA,
      sub: quoteLive ? `${health.quotes.source} · ${ZW.isFiniteNumber(health.quotes.spreadBps) ? `${health.quotes.spreadBps}bps` : ZW.NA}` : ZW.NA },
    { name: "Preflight", status: health.preflight.ok === true ? "ok" : health.preflight.ok === false ? "err" : "idle",
      value: ZW.isFiniteNumber(health.preflight.passed) && ZW.isFiniteNumber(health.preflight.total)
        ? `${health.preflight.passed}/${health.preflight.total}`
        : ZW.NA,
      sub: "mainnet gates" },
  ];
  return (
    <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 8 }}>
      {items.map((it) => (
        <div key={it.name} style={{
          display: "flex", alignItems: "center", gap: 8,
          padding: "8px 10px",
          background: "var(--bg-2)",
          border: "1px solid var(--line-soft)",
          borderRadius: 6,
        }}>
          <StatusDot status={it.status} blink={it.status === "warn"} />
          <div style={{ display: "flex", flexDirection: "column", minWidth: 0, flex: 1 }}>
            <span style={{ fontSize: 11, color: "var(--fg-2)" }}>{it.name}</span>
            <span style={{ fontSize: 11, color: "var(--fg-3)" }}>{it.sub}</span>
          </div>
          <span className="mono" style={{ fontSize: 12, color: "var(--fg-0)", fontWeight: 500 }}>{it.value}</span>
        </div>
      ))}
    </div>
  );
}

Object.assign(window, { ViewDashboard });
