/* global React, store, Panel, StatCell, RegimeGauge, ProposalCard, WatchRow, TickingPrice, Sparkline */
// Pages: Dashboard, Proposals, News

const { useState, useEffect, useMemo, useRef } = React;

// ========== DASHBOARD ==========
function DashboardPage({ app, quotes, onGoto, onInjectCritical, onFocusSymbol }) {
  const [expanded, setExpanded] = useState(null);
  const [posTab, setPosTab] = useState("open"); // "open" | "closed"

  const pending = app.proposals.filter(p => p.status === "pending" && p.risk_passed);
  const openPnl = app.positions.reduce((s, p) => s + p.pnl, 0);
  // Live day P&L from backend (sum of per-position day_pnl across all positions);
  // this is the real "realized + unrealized intraday change" number.
  const dayPnl = app.portfolio_pnl?.day ?? 0;

  // Split position_groups into open / closed views; recompute group totals from filtered legs.
  const filteredGroups = useMemo(() => {
    const wantClosed = posTab === "closed";
    return (app.position_groups || []).flatMap(g => {
      const legs = g.legs.filter(l => wantClosed ? l.status === "CLOSED" : l.status !== "CLOSED");
      if (legs.length === 0) return [];
      const total_pnl       = legs.reduce((s, l) => s + (l.total_pnl || 0), 0);
      const total_day_pnl   = legs.reduce((s, l) => s + (l.day_pnl || 0), 0);
      const total_realized  = legs.reduce((s, l) => s + (l.realized_pnl || 0), 0);
      const total_since_close = legs.reduce((s, l) => s + (l.since_close_pnl || 0), 0);
      const has_data_error  = legs.some(l => !!l.data_error);
      return [{ ...g, legs, total_pnl, total_day_pnl, total_realized, total_since_close, has_data_error }];
    });
  }, [app.position_groups, posTab]);

  const closedRealized = useMemo(
    () => (app.positions || []).filter(p => p.status === "CLOSED").reduce((s, p) => s + (p.realized_pnl || 0), 0),
    [app.positions]
  );
  const closedSinceClose = useMemo(
    () => (app.positions || []).filter(p => p.status === "CLOSED").reduce((s, p) => s + (p.since_close_pnl || 0), 0),
    [app.positions]
  );

  const watchSymbols = ["NIFTY", "BANKNIFTY", "RELIANCE", "TCS", "HDFCBANK", "INFY", "ICICIBANK", "ITC"];

  return (
    <div style={{ display: "grid", gridTemplateColumns: "1fr 340px", gap: 8, height: "100%", padding: 8, overflow: "hidden" }}>
      {/* LEFT COLUMN */}
      <div style={{ display: "grid", gridTemplateRows: "auto minmax(220px, auto) 1fr", gap: 8, minHeight: 0 }}>
        {/* TOP STATS BAR */}
        <div className="panel">
          <div style={{ display: "grid", gridTemplateColumns: "repeat(7, 1fr)", alignItems: "start" }}>
            <StatCell label="Nifty 50"
              value={quotes.NIFTY?.last ? <TickingPrice value={quotes.NIFTY.last}/> : <span className="faint">—</span>}
              sub={quotes.NIFTY?.last && quotes.NIFTY?.price
                ? <span className={quotes.NIFTY.last >= quotes.NIFTY.price ? "up" : "down"}>
                    {((quotes.NIFTY.last - quotes.NIFTY.price) / quotes.NIFTY.price * 100).toFixed(2)}% vs prev
                  </span>
                : <span className="faint">— no data</span>}/>
            <StatCell label="Bank Nifty"
              value={quotes.BANKNIFTY?.last ? <TickingPrice value={quotes.BANKNIFTY.last}/> : <span className="faint">—</span>}
              sub={quotes.BANKNIFTY?.last && quotes.BANKNIFTY?.price
                ? <span className={quotes.BANKNIFTY.last >= quotes.BANKNIFTY.price ? "up" : "down"}>
                    {((quotes.BANKNIFTY.last - quotes.BANKNIFTY.price) / quotes.BANKNIFTY.price * 100).toFixed(2)}%
                  </span>
                : <span className="faint">— no data</span>}/>
            <StatCell label="Available" value={store.inr(app.funds.available)} sub={<span className="dim">of {store.inr(app.funds.opening)} opening</span>} />
            {(() => {
              const used = app.funds?.used || 0;
              const cap  = app.risk_status?.limits?.max_total_exposure_inr || 0;
              const pct  = cap > 0 ? (used / cap) * 100 : null;
              const capDisplay = cap > 0 ? store.inr(cap) : "no cap set";
              return (
                <StatCell label="Exposure" value={store.inr(used)}
                  sub={<span className="dim">{pct != null ? `${pct.toFixed(0)}% of ${capDisplay}` : `cap ${capDisplay}`}</span>} />
              );
            })()}
            <StatCell label="Day P&L" value={store.inrSigned(dayPnl)} tone={dayPnl >= 0 ? "up" : "down"} sub={<span className="dim">realized + open</span>} />
            <StatCell label="Open Pos" value={app.positions.length} sub={<span className="dim">{store.inrSigned(openPnl)} open</span>} tone={openPnl >= 0 ? "up" : "down"} />
            {(() => {
              const o = app.orders_today || { placed: 0, filled: 0, rejected: 0, pending: 0, cap: 0 };
              const capDisp = o.cap > 0 ? o.cap : "—";
              const subParts = [];
              if (o.filled)   subParts.push(`${o.filled} fill${o.filled === 1 ? "" : "s"}`);
              if (o.rejected) subParts.push(`${o.rejected} rej`);
              if (o.pending)  subParts.push(`${o.pending} pending`);
              return (
                <StatCell label="Orders Today" value={`${o.placed}/${capDisp}`}
                  sub={<span className="dim">{subParts.length ? subParts.join(" · ") : "no orders today"}</span>} />
              );
            })()}
          </div>
          <div style={{ borderTop: "1px solid var(--border)" }}>
            <RegimeGauge
              regime={app.regime}
              score={app.volScore}
              vix={app.vix}
              vixChange={app.vix_change}
              nifty_atr_pct={app.nifty_atr_pct}
              news_severity_score={app.news_severity_score}
              news_events_1h={app.news_events_1h}
            />
          </div>
        </div>

        {/* CLAUDE QUEUE (the hero) */}
        <Panel
          title="Claude Proposal Queue"
          tag={pending.length || ""}
          actions={
            <span style={{ display: "flex", alignItems: "center", gap: 8 }}>
              <span className="dim" style={{ fontSize: 10 }}>auto-refresh 5s</span>
              <span className="dot dot-live"/>
              <button className="btn btn-xs" onClick={() => onGoto("council_proposals")}>VIEW ALL →</button>
            </span>
          }
        >
          {pending.length === 0 ? (
            <div style={{
              padding: "60px 10px", textAlign: "center", color: "var(--fg-dim)",
              fontSize: 11, lineHeight: 1.8, minHeight: 180,
              display: "flex", flexDirection: "column", justifyContent: "center", alignItems: "center", gap: 8,
            }}>
              <div style={{ fontSize: 18, color: "var(--fg-faint)", letterSpacing: "0.04em" }}>◆</div>
              <div>No proposals awaiting confirmation.</div>
              <div className="faint" style={{ fontSize: 10 }}>
                Claude will drop new ones here. Bots also feed this queue when enabled.
              </div>
            </div>
          ) : pending.slice(0, 4).map(p => (
            <ProposalCard
              key={p.request_id}
              p={p}
              expanded={expanded === p.request_id}
              onToggle={() => setExpanded(expanded === p.request_id ? null : p.request_id)}
              onConfirm={app.confirmProposal}
              onReject={app.rejectProposal}
            />
          ))}
        </Panel>

        {/* POSITIONS — full-width left column */}
        <Panel
            title={
              <span style={{ display: "inline-flex", alignItems: "center", gap: 8 }}>
                <span>Positions</span>
                <span style={{ display: "inline-flex", gap: 0 }}>
                  <button
                    className={`chip ${posTab === "open" ? "active" : ""}`}
                    style={{ padding: "1px 6px", fontSize: 9, letterSpacing: "0.06em" }}
                    onClick={() => setPosTab("open")}
                  >OPEN</button>
                  <button
                    className={`chip ${posTab === "closed" ? "active" : ""}`}
                    style={{ padding: "1px 6px", fontSize: 9, letterSpacing: "0.06em", marginLeft: -1 }}
                    onClick={() => setPosTab("closed")}
                  >CLOSED</button>
                </span>
              </span>
            }
            bodyFlush
            actions={
              posTab === "open" ? (
                <span className="dim" style={{ fontSize: 10 }}>
                  {(app.positions || []).filter(p => p.status !== "CLOSED").length} open ·
                  day <span className={(app.portfolio_pnl?.day ?? 0) >= 0 ? "up" : "down"}>{store.inrSigned(app.portfolio_pnl?.day ?? 0)}</span> ·
                  total <span className={(app.portfolio_pnl?.total ?? 0) >= 0 ? "up" : "down"}>{store.inrSigned(app.portfolio_pnl?.total ?? 0)}</span>
                </span>
              ) : (
                <span className="dim" style={{ fontSize: 10 }}>
                  {(app.positions || []).filter(p => p.status === "CLOSED").length} closed ·
                  realized <span className={closedRealized >= 0 ? "up" : "down"}>{store.inrSigned(closedRealized)}</span> ·
                  since close <span className={closedSinceClose >= 0 ? "up" : "down"}>{store.inrSigned(closedSinceClose)}</span>
                </span>
              )
            }>
            <div style={{ overflow: "auto", height: "100%" }}>
              {filteredGroups.length === 0 ? (
                <div className="dim" style={{ padding: 16, textAlign: "center", fontSize: 11 }}>
                  {posTab === "open" ? "No open positions." : "No closed positions today."}
                </div>
              ) : filteredGroups.map(g => (
                <div key={g.underlying} style={{ borderBottom: "1px solid var(--border)" }}>
                  {/* Group header: underlying + spot + day change + positions count */}
                  <div style={{
                    display: "grid",
                    gridTemplateColumns: "1fr auto auto auto",
                    gap: 8, alignItems: "center",
                    padding: "6px 10px",
                    background: "var(--bg-2)",
                    borderBottom: "1px solid var(--border)",
                  }}>
                    <div style={{ display: "flex", alignItems: "baseline", gap: 8 }}>
                      <span className="bright" style={{ fontSize: 12, fontWeight: 600, letterSpacing: "0.04em" }}>{g.underlying}</span>
                      <span className="tnum" style={{ fontSize: 12, opacity: g.underlying_stale ? 0.55 : 1 }}
                            title={g.underlying_stale ? "Stale — last-known-good (market closed or quote unavailable)" : ""}>
                        {g.underlying_last != null ? g.underlying_last.toFixed(2) : "—"}
                      </span>
                      {g.underlying_change != null && g.underlying_last != null ? (
                        <span className={`tnum ${g.underlying_change >= 0 ? "up" : "down"}`} style={{ fontSize: 10, opacity: g.underlying_stale ? 0.55 : 1 }}>
                          {g.underlying_change >= 0 ? "+" : ""}{g.underlying_change.toFixed(2)} ({g.underlying_pct >= 0 ? "+" : ""}{g.underlying_pct.toFixed(2)}%)
                        </span>
                      ) : null}
                      {g.underlying_stale ? (
                        <span className="faint" style={{ fontSize: 9, letterSpacing: "0.06em" }} title="Showing last-known-good quote — Dhan didn't return a fresh tick.">stale</span>
                      ) : null}
                      {g.has_data_error ? (
                        <span className="down" style={{ fontSize: 9, letterSpacing: "0.06em", border: "1px solid var(--down)", padding: "0 4px" }}
                              title="One or more legs in this group could not be valued — contract size unresolved. Group total excludes these legs.">data error</span>
                      ) : null}
                    </div>
                    <span className="faint" style={{ fontSize: 10 }}>
                      {g.legs.length} {posTab === "closed" ? "closed" : "pos"}
                    </span>
                    <span className="h-xxs">{posTab === "closed" ? "Realized" : "Day"}</span>
                    {posTab === "closed" ? (
                      <span className={`tnum ${g.total_realized >= 0 ? "up" : "down"}`} style={{ fontSize: 11 }}>
                        {store.inrSigned(g.total_realized)}
                      </span>
                    ) : (
                      <span className={`tnum ${g.total_day_pnl >= 0 ? "up" : "down"}`} style={{ fontSize: 11 }}>
                        {store.inrSigned(g.total_day_pnl)}
                      </span>
                    )}
                  </div>
                  {/* Legs: compact table */}
                  {posTab === "open" ? (
                    <table className="tbl tbl-compact" style={{ fontSize: 10, tableLayout: "fixed" }}>
                      <colgroup>
                        <col style={{ width: 32 }}/>       {/* B/S */}
                        <col/>                              {/* Name (flex) */}
                        <col style={{ width: 60 }}/>       {/* Product */}
                        <col style={{ width: 50 }}/>       {/* Lots */}
                        <col style={{ width: 60 }}/>       {/* Qty */}
                        <col style={{ width: 70 }}/>       {/* Avg */}
                        <col style={{ width: 70 }}/>       {/* LTP */}
                        <col style={{ width: 110 }}/>      {/* P&L */}
                        <col style={{ width: 110 }}/>      {/* Net P&L */}
                        <col style={{ width: 110 }}/>      {/* Realizable */}
                        <col style={{ width: 70 }}/>       {/* % */}
                      </colgroup>
                      <thead><tr>
                        <th>B/S</th>
                        <th>Name</th>
                        <th>Product</th>
                        <th className="num">Lots</th>
                        <th className="num">Qty</th>
                        <th className="num">Avg</th>
                        <th className="num">LTP</th>
                        <th className="num">P&L</th>
                        <th className="num" title="Gross P&L − entry charges − estimated exit charges at LTP">Net P&L</th>
                        <th className="num" title="What you'd actually net if you market-sell now: walks orderbook for full qty, then subtracts all charges">Realizable</th>
                        <th className="num">%</th>
                      </tr></thead>
                      <tbody>
                        {g.legs.map(pos => {
                          if (pos.data_error) {
                            return (
                              <tr key={pos.symbol} style={{ borderLeft: "2px solid var(--down)" }}
                                  title={`data unavailable: ${pos.data_error.code} (${pos.data_error.reason}) — segment ${pos.data_error.segment}, sec_id ${pos.data_error.security_id}`}>
                                <td><span className="pill pill-down" style={{ fontSize: 9, padding: "0 4px" }}>!</span></td>
                                <td className="bright truncate" style={{ fontSize: 10 }} title={pos.display_name || pos.symbol}>{pos.display_name || pos.symbol}</td>
                                <td className="faint" style={{ fontSize: 9 }}>{pos.product}</td>
                                <td className="num tnum faint">—</td>
                                <td className="num tnum faint">—</td>
                                <td className="num tnum faint">—</td>
                                <td className="num tnum faint">—</td>
                                <td className="num tnum down" style={{ fontSize: 9 }}>contract size unresolved</td>
                                <td className="num tnum faint">—</td>
                                <td className="num tnum faint">—</td>
                                <td className="num tnum faint">—</td>
                              </tr>
                            );
                          }
                          const totalPnl = pos.total_pnl;
                          const pnlPct   = pos.pnl_pct;
                          const netPnl   = pos.net_pnl;
                          const realPnl  = pos.realizable_pnl;
                          const realTitle = realPnl == null
                            ? (pos.realizable_partial
                                ? `partial: book filled ${pos.realizable_filled_qty}/${pos.qty} (${pos.realizable_levels_used} lvls) — depth too thin to fill full qty`
                                : "depth unavailable")
                            : `avg exit ₹${pos.realizable_avg_exit} across ${pos.realizable_levels_used} levels`;
                          return (
                            <tr key={pos.symbol}>
                              <td>
                                <span className={`pill ${pos.b_s === "B" ? "pill-up" : "pill-down"}`} style={{ fontSize: 9, padding: "0 4px" }}>{pos.b_s}</span>
                              </td>
                              <td className="bright truncate" style={{ fontSize: 10 }} title={pos.display_name || pos.symbol}>{pos.display_name || pos.symbol}</td>
                              <td className="faint" style={{ fontSize: 9 }}>{pos.product}</td>
                              <td className="num tnum" title={`lot size ${pos.lot_size}`}>{pos.lots}</td>
                              <td className="num tnum">{pos.qty}</td>
                              <td className="num tnum">{pos.avg != null ? pos.avg.toFixed(2) : "—"}</td>
                              <td className="num tnum">{pos.ltp > 0 ? <TickingPrice value={pos.ltp}/> : <span className="faint">—</span>}</td>
                              <td className={`num tnum ${(totalPnl ?? 0) >= 0 ? "up" : "down"}`}>
                                {totalPnl == null ? <span className="faint">—</span> : store.inrSigned(totalPnl)}
                              </td>
                              <td className={`num tnum ${(netPnl ?? 0) >= 0 ? "up" : "down"}`}
                                  title={pos.entry_charges != null ? `charges: entry ₹${pos.entry_charges.toFixed(0)} · est. exit ₹${pos.exit_charges_est.toFixed(0)}` : ""}>
                                {netPnl == null ? <span className="faint">—</span> : store.inrSigned(netPnl)}
                              </td>
                              <td className={`num tnum ${(realPnl ?? 0) >= 0 ? "up" : "down"}`} title={realTitle}>
                                {realPnl == null
                                  ? <span className="faint">—</span>
                                  : store.inrSigned(realPnl)}
                              </td>
                              <td className={`num tnum ${(pnlPct ?? 0) >= 0 ? "up" : "down"}`}>
                                {pnlPct == null ? <span className="faint">—</span> : `${pnlPct >= 0 ? "+" : ""}${pnlPct.toFixed(2)}%`}
                              </td>
                            </tr>
                          );
                        })}
                      </tbody>
                    </table>
                  ) : (
                    <table className="tbl tbl-compact" style={{ fontSize: 10, tableLayout: "fixed" }}>
                      <colgroup>
                        <col style={{ width: 32 }}/>       {/* B/S (original) */}
                        <col/>                              {/* Name */}
                        <col style={{ width: 50 }}/>       {/* Lots */}
                        <col style={{ width: 60 }}/>       {/* Qty */}
                        <col style={{ width: 70 }}/>       {/* Entry */}
                        <col style={{ width: 70 }}/>       {/* Exit */}
                        <col style={{ width: 70 }}/>       {/* LTP */}
                        <col style={{ width: 100 }}/>      {/* Realized */}
                        <col style={{ width: 100 }}/>      {/* Since Close */}
                      </colgroup>
                      <thead><tr>
                        <th>B/S</th>
                        <th>Name</th>
                        <th className="num">Lots</th>
                        <th className="num">Qty</th>
                        <th className="num">Entry</th>
                        <th className="num">Exit</th>
                        <th className="num">LTP</th>
                        <th className="num">Realized</th>
                        <th className="num" title="P&L since close: what the position would have made/lost from exit price to current LTP — i.e. profit missed (or loss avoided) by closing.">Since Close</th>
                      </tr></thead>
                      <tbody>
                        {g.legs.map(pos => {
                          if (pos.data_error) {
                            return (
                              <tr key={pos.symbol} style={{ borderLeft: "2px solid var(--down)" }}
                                  title={`data unavailable: ${pos.data_error.code} (${pos.data_error.reason})`}>
                                <td><span className="pill pill-down" style={{ fontSize: 9, padding: "0 4px" }}>!</span></td>
                                <td className="bright truncate" style={{ fontSize: 10 }}>{pos.display_name || pos.symbol}</td>
                                <td className="num tnum faint">—</td>
                                <td className="num tnum faint">—</td>
                                <td className="num tnum faint">—</td>
                                <td className="num tnum faint">—</td>
                                <td className="num tnum faint">—</td>
                                <td className="num tnum down" style={{ fontSize: 9 }}>contract size unresolved</td>
                                <td className="num tnum faint">—</td>
                              </tr>
                            );
                          }
                          const dir = pos.original_side || pos.b_s;
                          const since = pos.since_close_pnl;
                          const realized = pos.realized_pnl;
                          return (
                            <tr key={pos.symbol}>
                              <td>
                                <span className={`pill ${dir === "B" ? "pill-up" : "pill-down"}`} style={{ fontSize: 9, padding: "0 4px" }}>{dir}</span>
                              </td>
                              <td className="bright truncate" style={{ fontSize: 10 }} title={pos.display_name || pos.symbol}>{pos.display_name || pos.symbol}</td>
                              <td className="num tnum" title={`lot size ${pos.lot_size}`}>{pos.lots}</td>
                              <td className="num tnum">{pos.qty}</td>
                              <td className="num tnum">{pos.entry_avg != null ? pos.entry_avg.toFixed(2) : "—"}</td>
                              <td className="num tnum">{pos.exit_avg != null ? pos.exit_avg.toFixed(2) : "—"}</td>
                              <td className="num tnum">{pos.ltp > 0 ? <TickingPrice value={pos.ltp}/> : <span className="faint">—</span>}</td>
                              <td className={`num tnum ${(realized ?? 0) >= 0 ? "up" : "down"}`}>
                                {realized == null ? <span className="faint">—</span> : store.inrSigned(realized)}
                              </td>
                              <td className={`num tnum ${(since ?? 0) >= 0 ? "up" : "down"}`}
                                  title={since == null ? "" : (since >= 0 ? "Profit missed by closing — price moved in your favor after exit." : "Loss avoided by closing — price moved against you after exit.")}>
                                {since == null ? <span className="faint">—</span> : store.inrSigned(since)}
                              </td>
                            </tr>
                          );
                        })}
                      </tbody>
                    </table>
                  )}
                  {/* Group footer */}
                  <div style={{
                    display: "flex", justifyContent: "flex-end", gap: 16,
                    padding: "4px 10px",
                    background: "var(--bg-0)",
                    fontSize: 10, letterSpacing: "0.06em",
                  }}>
                    {posTab === "closed" ? (
                      <>
                        <span className="faint">TOTAL REALIZED</span>
                        <span className={`tnum ${g.total_realized >= 0 ? "up" : "down"}`} style={{ fontWeight: 500 }}>
                          {store.inrSigned(g.total_realized)}
                        </span>
                        <span className="faint">SINCE CLOSE</span>
                        <span className={`tnum ${g.total_since_close >= 0 ? "up" : "down"}`} style={{ fontWeight: 500 }}>
                          {store.inrSigned(g.total_since_close)}
                        </span>
                      </>
                    ) : (
                      <>
                        <span className="faint">TOTAL P&L</span>
                        <span className={`tnum ${g.total_pnl >= 0 ? "up" : "down"}`} style={{ fontWeight: 500 }}>
                          {store.inrSigned(g.total_pnl)}
                        </span>
                      </>
                    )}
                  </div>
                </div>
              ))}
            </div>
        </Panel>
      </div>

      {/* RIGHT COLUMN: watchlist + activity feed + news */}
      <div style={{ display: "grid", gridTemplateRows: "1.2fr minmax(140px, 200px) 1fr", gap: 8, minHeight: 0 }}>
        <Panel title="Watchlist" bodyFlush actions={<button className="btn btn-xs btn-ghost">EDIT</button>}>
          <div style={{ display: "grid", gridTemplateColumns: "70px 1fr 64px 72px", padding: "4px 10px", borderBottom: "1px solid var(--border)", background: "var(--bg-2)" }}>
            <span className="h-xxs">SYM</span>
            <span className="h-xxs" style={{ textAlign: "left" }}>LAST</span>
            <span className="h-xxs" style={{ textAlign: "center" }}>1D</span>
            <span className="h-xxs" style={{ textAlign: "right" }}>CHG%</span>
          </div>
          <div style={{ overflow: "auto" }}>
            {watchSymbols.map(s => quotes[s] ? (
              <WatchRow key={s} q={quotes[s]} onClick={() => onFocusSymbol(s)} />
            ) : null)}
          </div>
        </Panel>

        <Panel title="Activity Feed" bodyFlush actions={<button className="btn btn-xs btn-ghost">FILTER</button>}>
          <div style={{ maxHeight: "100%", overflow: "auto", fontSize: 11 }}>
            {buildActivityFeed(app).map((a, i) => (
              <div key={i} style={{ display: "flex", gap: 8, padding: "6px 10px", borderBottom: "1px solid var(--grid)" }}>
                <span style={{ color: a.color, fontSize: 10, marginTop: 1, width: 14, textAlign: "center" }}>{a.icon}</span>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div className="truncate">{a.text}</div>
                  <div className="faint" style={{ fontSize: 9 }}>{a.source} · {store.relTime(a.t)}</div>
                </div>
              </div>
            ))}
          </div>
        </Panel>

        <Panel
          title="News & Events"
          tag={app.news.filter(n => ["HIGH", "CRITICAL"].includes(n.severity)).length || ""}
          actions={<button className="btn btn-xs btn-ghost" onClick={() => onGoto("news")}>ALL →</button>}
          bodyFlush
        >
          <div style={{ maxHeight: 210, overflow: "auto" }}>
            {app.news.slice(0, 6).map(n => (
              <div key={n.id} style={{ padding: "6px 10px", borderBottom: "1px solid var(--grid)", fontSize: 11 }}>
                <div style={{ display: "flex", alignItems: "center", gap: 6, marginBottom: 2 }}>
                  <span className={`pill ${severityPill(n.severity)}`} style={{ fontSize: 9 }}>{n.severity}</span>
                  <span className="faint" style={{ fontSize: 9 }}>{n.category}</span>
                  <div style={{ flex: 1 }}/>
                  <span className="faint" style={{ fontSize: 9 }}>{store.relTime(n.received_at)}</span>
                </div>
                <div style={{ lineHeight: 1.4 }}>{n.headline}</div>
                <div className="faint" style={{ fontSize: 9, marginTop: 1 }}>{n.source}{n.symbols?.length ? " · " + n.symbols.join(", ") : ""}</div>
              </div>
            ))}
          </div>
        </Panel>

      </div>
    </div>
  );
}

function severityPill(s) {
  switch (s) {
    case "CRITICAL": return "pill-red-solid";
    case "HIGH": return "pill-down";
    case "MEDIUM": return "pill-amber";
    case "LOW": return "pill-cyan";
    default: return "pill-dim";
  }
}

function buildActivityFeed(app) {
  const out = [];
  app.proposals.slice(0, 8).forEach(p => {
    if (p.status === "filled") {
      out.push({ icon: "✓", color: "var(--up)", text: `FILL ${p.transaction_type} ${p.quantity} ${p.symbol} @ ₹${p.fill_price?.toFixed(2)}`, source: p.source, t: p.fill_time || p.created_at });
    } else if (p.status === "rejected") {
      out.push({ icon: "✗", color: "var(--down)", text: `RISK FAIL · ${p.transaction_type} ${p.symbol} ×${p.quantity}`, source: p.source, t: p.created_at });
    } else {
      out.push({ icon: "◆", color: "var(--violet)", text: `PROPOSE ${p.transaction_type} ${p.quantity} ${p.symbol}`, source: p.source, t: p.created_at });
    }
  });
  app.bots.filter(b => b.enabled).forEach(b => {
    out.push({ icon: "⚙", color: "var(--cyan)", text: `Bot scan · ${b.name} · ${b.symbols.length} symbols`, source: b.bot_id, t: b.last_run });
  });
  return out.sort((a, b) => new Date(b.t) - new Date(a.t)).slice(0, 20);
}

// ========== PROPOSALS ==========
function ProposalsPage({ app }) {
  const [filter, setFilter] = useState("all");
  const [expanded, setExpanded] = useState(null);
  const [sel, setSel] = useState(app.proposals[0]?.request_id);

  const filtered = app.proposals.filter(p => {
    if (filter === "pending") return p.status === "pending";
    if (filter === "filled") return p.status === "filled";
    if (filter === "rejected") return p.status === "rejected" || p.status === "cancelled";
    return true;
  });

  const selected = app.proposals.find(p => p.request_id === sel) || filtered[0];

  return (
    <div style={{ display: "grid", gridTemplateColumns: "1.4fr 1fr", gap: 8, height: "100%", padding: 8, overflow: "hidden" }}>
      <Panel
        title="Proposals"
        tag={app.proposals.filter(p => p.status === "pending").length || ""}
        actions={
          <div style={{ display: "flex", gap: 4 }}>
            {[["all", "ALL"], ["pending", "PENDING"], ["filled", "FILLED"], ["rejected", "REJECTED"]].map(([k, l]) => (
              <button key={k} className={`chip ${filter === k ? "active" : ""}`} onClick={() => setFilter(k)} style={{ padding: "1px 6px", fontSize: 9 }}>{l}</button>
            ))}
          </div>
        }
        bodyFlush
      >
        <div style={{ overflow: "auto" }}>
          <table className="tbl tbl-compact">
            <thead><tr>
              <th>Time</th><th>Source</th><th>Sym</th><th>Side</th><th>Qty</th><th>Type</th><th className="num">Ref ₹</th><th>Status</th>
            </tr></thead>
            <tbody>
              {filtered.map(p => (
                <tr key={p.request_id} onClick={() => setSel(p.request_id)} className={sel === p.request_id ? "selected" : ""} style={{ cursor: "pointer" }}>
                  <td className="faint" style={{ fontSize: 10 }}>{store.relTime(p.created_at)}</td>
                  <td style={{ fontSize: 10 }}>
                    {p.source === "claude" ?
                      <span className="violet">◆ claude</span> :
                      <span className="cyan">▸ {p.source.replace("bot:", "")}</span>}
                  </td>
                  <td className="bright">{p.symbol}</td>
                  <td><span className={`pill ${p.transaction_type === "BUY" ? "pill-up" : "pill-down"}`} style={{ fontSize: 9 }}>{p.transaction_type}</span></td>
                  <td>{p.quantity}</td>
                  <td style={{ fontSize: 10 }}>{p.order_type}/{p.product_type}</td>
                  <td className="num">{p.reference_price.toFixed(2)}</td>
                  <td>
                    {p.status === "pending" && p.risk_passed ? <span className="pill pill-amber">AWAITING</span> :
                     p.status === "confirmed" ? <span className="pill pill-cyan">EXECUTING</span> :
                     p.status === "filled" ? <span className="pill pill-up">FILLED</span> :
                     p.status === "rejected" ? <span className="pill pill-down">RISK FAIL</span> :
                     <span className="pill pill-dim">{p.status.toUpperCase()}</span>}
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </Panel>

      {/* DETAIL PANEL */}
      <Panel title={selected ? `Detail · ${selected.request_id}` : "Detail"} bodyFlush>
        {selected ? <ProposalDetail p={selected} app={app} /> : <div style={{ padding: 20, color: "var(--fg-dim)" }}>Select a proposal</div>}
      </Panel>
    </div>
  );
}

function ProposalDetail({ p, app }) {
  const isBuy = p.transaction_type === "BUY";
  const isClaude = p.source === "claude";
  return (
    <div style={{ padding: 12, overflow: "auto", height: "100%" }}>
      {/* header */}
      <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 12 }}>
        <span className="pill" style={{ color: isBuy ? "var(--up)" : "var(--down)", borderColor: isBuy ? "var(--up)" : "var(--down)", background: isBuy ? "rgba(34,214,111,0.1)" : "rgba(255,77,77,0.1)", fontSize: 11, padding: "3px 10px" }}>
          {p.transaction_type}
        </span>
        <span className="bright" style={{ fontSize: 20, fontWeight: 500 }}>{p.symbol}</span>
        <span className="dim">× {p.quantity}</span>
        <div style={{ flex: 1 }}/>
        <span className="faint" style={{ fontSize: 10 }}>{new Date(p.created_at).toLocaleString("en-IN", { hour12: false })}</span>
      </div>

      <dl className="kv" style={{ marginBottom: 14 }}>
        <dt>Order type</dt><dd>{p.order_type}</dd>
        <dt>Product</dt><dd>{p.product_type}</dd>
        <dt>Reference price</dt><dd>₹{p.reference_price.toFixed(2)}</dd>
        <dt>Order value</dt><dd>{store.inr(p.order_value)}</dd>
        <dt>Source</dt><dd className={isClaude ? "violet" : "cyan"}>{isClaude ? "◆ claude" : p.source}</dd>
      </dl>

      {/* rationale */}
      <div style={{ marginBottom: 14, padding: 10, background: "var(--bg-0)", border: "1px solid var(--border)", borderLeft: "2px solid var(--violet)" }}>
        <div className="h-xxs" style={{ marginBottom: 6, color: "var(--violet)" }}>◆ Claude's rationale</div>
        <div style={{ fontSize: 11, lineHeight: 1.6 }}>{p.rationale}</div>
      </div>

      {/* bias */}
      {p.bias ? (
        <div style={{ marginBottom: 14 }}>
          <div className="h-xxs" style={{ marginBottom: 6 }}>Bias snapshot at proposal time</div>
          <div style={{ display: "grid", gridTemplateColumns: "repeat(6, 1fr)", gap: 6 }}>
            <Kv k="Verdict" v={<span className={p.bias.verdict === "BULLISH" ? "up" : p.bias.verdict === "BEARISH" ? "down" : "dim"}>{p.bias.verdict}</span>} />
            <Kv k="Score" v={(p.bias.score > 0 ? "+" : "") + p.bias.score} />
            <Kv k="RSI14" v={p.bias.rsi14 || "—"} />
            <Kv k="EMA20" v={p.bias.ema20 || "—"} />
            <Kv k="MACD h" v={p.bias.macd_hist || "—"} />
            <Kv k="ATR14" v={p.bias.atr14 || "—"} />
          </div>
        </div>
      ) : null}

      {/* risk checks */}
      {p.risk_checks?.length ? (
        <div style={{ marginBottom: 14 }}>
          <div className="h-xxs" style={{ marginBottom: 6 }}>
            Pre-trade risk checks ({p.risk_checks.filter(c => c.status === "pass").length}/{p.risk_checks.length} passed)
          </div>
          <div style={{ background: "var(--bg-0)", border: "1px solid var(--border)" }}>
            {p.risk_checks.map((c, i) => (
              <div key={i} style={{ display: "flex", alignItems: "center", gap: 8, padding: "5px 10px", borderBottom: i < p.risk_checks.length - 1 ? "1px solid var(--grid)" : "none", fontSize: 11 }}>
                <span style={{ color: c.status === "pass" ? "var(--up)" : "var(--down)", width: 14 }}>{c.status === "pass" ? "✓" : "✗"}</span>
                <span className="dim" style={{ width: 160 }}>{c.name}</span>
                <span>{c.detail}</span>
              </div>
            ))}
          </div>
        </div>
      ) : null}

      {/* actions */}
      <div style={{ display: "flex", gap: 6, marginTop: 16, borderTop: "1px solid var(--border)", paddingTop: 12 }}>
        {p.status === "pending" && p.risk_passed ? (
          <>
            <button className="btn btn-up" style={{ padding: "8px 14px" }} onClick={() => app.confirmProposal(p.request_id)}>
              ▸ CONFIRM & EXECUTE
            </button>
            <button className="btn" onClick={() => app.rejectProposal(p.request_id)}>CANCEL</button>
            <div style={{ flex: 1 }}/>
            <span className="faint" style={{ fontSize: 9, alignSelf: "center" }}>
              You must confirm. Claude never self-executes.
            </span>
          </>
        ) : p.status === "filled" ? (
          <div className="up">✓ Filled @ ₹{p.fill_price?.toFixed(2)} · {store.relTime(p.fill_time)}</div>
        ) : p.status === "rejected" ? (
          <div className="down">✗ Proposal rejected by risk manager</div>
        ) : p.status === "confirmed" ? (
          <div className="cyan">◉ Releasing to broker…</div>
        ) : (
          <div className="dim">{p.status}</div>
        )}
      </div>
    </div>
  );
}
function Kv({ k, v }) {
  return (
    <div style={{ padding: 6, background: "var(--bg-0)", border: "1px solid var(--border)" }}>
      <div className="h-xxs" style={{ fontSize: 8 }}>{k}</div>
      <div className="tnum" style={{ fontSize: 12, marginTop: 2 }}>{v}</div>
    </div>
  );
}

// ========== NEWS ==========
// useNewsStream — opens /api/news/stream and surfaces feeds health, news,
// and volatility.  Replaces the old 30s `useFeedsHealth` poll.  The same
// stream pushes a news_event the moment an item lands in news_store, so
// the page sees new headlines immediately rather than waiting for the
// next 3s dashboard tick.
function useNewsStream() {
  const { snapshot, lastEvent, status, error } = useStream("/api/news/stream", { bufferSize: 100 });
  const [news, setNews]               = useState([]);
  const [feeds, setFeeds]             = useState([]);
  const [volatility, setVolatility]   = useState(null);

  useEffect(() => {
    if (!snapshot) return;
    if (Array.isArray(snapshot.news))  setNews(snapshot.news);
    if (Array.isArray(snapshot.feeds)) setFeeds(snapshot.feeds);
    if (snapshot.volatility)           setVolatility(snapshot.volatility);
  }, [snapshot]);

  useEffect(() => {
    if (!lastEvent) return;
    if (lastEvent.type === "news_event" && lastEvent.record) {
      // Append + dedupe by id; keep most recent 200.
      setNews(prev => {
        const seen = new Set(prev.map(n => n.id));
        if (seen.has(lastEvent.record.id)) return prev;
        const next = prev.concat(lastEvent.record);
        return next.length > 200 ? next.slice(next.length - 200) : next;
      });
    } else if (lastEvent.type === "feed_status_changed") {
      if (Array.isArray(lastEvent.feeds))   setFeeds(lastEvent.feeds);
      if (lastEvent.volatility)             setVolatility(lastEvent.volatility);
    }
  }, [lastEvent]);

  const summary = useMemo(() => {
    if (!feeds.length) return null;
    const live = feeds.filter(f => f.ok === true).length;
    return { live, total: feeds.length };
  }, [feeds]);

  return {
    news, feeds, volatility, summary,
    loading: status === "connecting" || status === "idle",
    error:   status === "error" || status === "reconnecting" ? error : null,
  };
}

// Backwards-compat alias — the old name only exposed feeds; new callers should
// use useNewsStream() directly to access news + volatility too.
function useFeedsHealth() {
  const s = useNewsStream();
  return { feeds: s.feeds, summary: s.summary, loading: s.loading, error: s.error };
}

function NewsPage({ app }) {
  const [minSev, setMinSev] = useState("LOW");
  const [category, setCategory] = useState("");
  const [windowMin, setWindowMin] = useState(240);
  const [sources, setSources] = useState(() => new Set()); // empty = all sources
  const [draft, setDraft] = useState({ headline: "", sev: "MEDIUM", cat: "MACRO" });
  // Single news SSE subscription powers BOTH the headline feed and the
  // sources panel.  Push events arrive within milliseconds of news landing
  // server-side, vs the 3s dashboard tick that drives `app.news`.
  const newsStream = useNewsStream();
  const feedsHealth = newsStream;
  const news = newsStream.news.length ? newsStream.news : app.news;

  const order = { INFO: 0, LOW: 1, MEDIUM: 2, HIGH: 3, CRITICAL: 4 };
  const filtered = news.filter(n =>
    order[n.severity] >= order[minSev] &&
    (!category || n.category === category) &&
    (sources.size === 0 || sources.has(n.source)) &&
    (Date.now() - new Date(n.received_at).getTime()) < windowMin * 60 * 1000
  );

  const sevCount = { INFO: 0, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 0 };
  news.forEach(n => { if (sevCount[n.severity] != null) sevCount[n.severity]++; });

  // Per-source counts for the chip labels — restricted to the active window
  // so a chip's count matches what the user would see if they clicked it.
  const inWindow = news.filter(n =>
    (Date.now() - new Date(n.received_at).getTime()) < windowMin * 60 * 1000
  );
  const countBySource = {};
  inWindow.forEach(n => { countBySource[n.source] = (countBySource[n.source] || 0) + 1; });

  const toggleSource = (id) => setSources(prev => {
    const next = new Set(prev);
    if (next.has(id)) next.delete(id); else next.add(id);
    return next;
  });

  return (
    <div style={{ display: "grid", gridTemplateColumns: "320px 1fr", gap: 8, height: "100%", padding: 8, overflow: "hidden" }}>
      {/* left: regime + filters + inject */}
      <div style={{ display: "flex", flexDirection: "column", gap: 8, minHeight: 0 }}>
        <Panel title="Volatility Regime">
          <RegimeGauge regime={app.regime} score={app.volScore} />
          <div style={{ marginTop: 12, padding: 8, background: "var(--bg-0)", border: "1px solid var(--border)", fontSize: 11, lineHeight: 1.5 }}>
            <div className="h-xxs" style={{ marginBottom: 4 }}>Recommendation</div>
            {app.regimeInfo.rec}
          </div>
          <div style={{ display: "grid", gridTemplateColumns: "repeat(5, 1fr)", gap: 3, marginTop: 10 }}>
            {Object.entries(sevCount).reverse().map(([s, c]) => (
              <div key={s} style={{ padding: 4, background: "var(--bg-0)", border: "1px solid var(--border)", textAlign: "center" }}>
                <div style={{ fontSize: 9, color: "var(--fg-dim)", letterSpacing: "0.08em" }}>{s}</div>
                <div className={`tnum ${severityColor(s)}`} style={{ fontSize: 14, marginTop: 2 }}>{c}</div>
              </div>
            ))}
          </div>
        </Panel>

        <Panel title="Filter">
          <div className="label">Min severity</div>
          <select className="select" value={minSev} onChange={e => setMinSev(e.target.value)}>
            <option>INFO</option><option>LOW</option><option>MEDIUM</option><option>HIGH</option><option>CRITICAL</option>
          </select>
          <div className="label" style={{ marginTop: 8 }}>Category</div>
          <select className="select" value={category} onChange={e => setCategory(e.target.value)}>
            <option value="">Any</option>
            <option>WAR_GEOPOLITICS</option><option>MACRO</option><option>CORPORATE</option>
            <option>NATURAL_DISASTER</option><option>POLITICAL</option><option>COMMODITY</option>
          </select>
          <div className="label" style={{ marginTop: 8 }}>Window (minutes)</div>
          <input className="input" type="number" value={windowMin} onChange={e => setWindowMin(Number(e.target.value))} />
        </Panel>

        <Panel title="Sources"
          tag={feedsHealth.summary ? `${feedsHealth.summary.live}/${feedsHealth.summary.total} live` : (feedsHealth.loading ? "loading…" : (feedsHealth.error ? "error" : ""))}
          actions={sources.size > 0 ? <button className="btn btn-xs" onClick={() => setSources(new Set())}>clear</button> : null}>
          {feedsHealth.error ? (
            <div className="down" style={{ fontSize: 11 }}>{feedsHealth.error}</div>
          ) : !feedsHealth.feeds.length ? (
            <div className="faint" style={{ fontSize: 11 }}>{feedsHealth.loading ? "loading…" : "no sources configured"}</div>
          ) : (
            <div style={{ display: "flex", flexDirection: "column", gap: 3 }}>
              {feedsHealth.feeds.map(f => {
                const active = sources.has(f.source_id);
                const status = f.ok === true ? "LIVE" : f.ok === false ? "OFFLINE" : "PENDING";
                const statusCls = f.ok === true ? "up" : f.ok === false ? "down" : "dim";
                const count = countBySource[f.source_id] || 0;
                return (
                  <button key={f.url}
                    className={`chip ${active ? "active" : ""}`}
                    onClick={() => toggleSource(f.source_id)}
                    title={`${f.url}${f.last_error ? `\nlast error: ${f.last_error}` : ""}${f.last_attempt_at ? `\nlast attempt: ${f.last_attempt_at}` : ""}`}
                    style={{ display: "flex", alignItems: "center", justifyContent: "space-between",
                             gap: 6, fontSize: 10, padding: "3px 8px", whiteSpace: "nowrap" }}>
                    <span style={{ fontWeight: 600 }}>{f.label}</span>
                    <span style={{ display: "flex", alignItems: "center", gap: 6 }}>
                      <span className={statusCls} style={{ fontSize: 9, letterSpacing: "0.06em" }}>{status}</span>
                      <span className="faint" style={{ fontSize: 9, minWidth: 18, textAlign: "right" }}>{count}</span>
                    </span>
                  </button>
                );
              })}
            </div>
          )}
        </Panel>

        <Panel title="Log manual event">
          <input className="input" placeholder="Headline…" value={draft.headline} onChange={e => setDraft({ ...draft, headline: e.target.value })} />
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 4, marginTop: 6 }}>
            <select className="select" value={draft.sev} onChange={e => setDraft({ ...draft, sev: e.target.value })}>
              <option>LOW</option><option>MEDIUM</option><option>HIGH</option><option>CRITICAL</option>
            </select>
            <select className="select" value={draft.cat} onChange={e => setDraft({ ...draft, cat: e.target.value })}>
              <option>MACRO</option><option>CORPORATE</option><option>WAR_GEOPOLITICS</option>
              <option>NATURAL_DISASTER</option><option>POLITICAL</option><option>COMMODITY</option>
            </select>
          </div>
          <button className="btn btn-primary" style={{ width: "100%", marginTop: 6 }}
            disabled={!draft.headline} onClick={() => {
              app.pushNews({ headline: draft.headline, severity: draft.sev, category: draft.cat, source: "Manual" });
              setDraft({ headline: "", sev: "MEDIUM", cat: "MACRO" });
            }}
          >LOG EVENT</button>
        </Panel>
      </div>

      {/* right: feed */}
      <Panel title={`News Feed · ${filtered.length} events`} bodyFlush actions={<button className="btn btn-xs">PULL RSS</button>}>
        <div style={{ overflow: "auto", height: "100%" }}>
          {filtered.map(n => (
            <div key={n.id} style={{ padding: 10, borderBottom: "1px solid var(--grid)" }}>
              <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 4, fontSize: 11 }}>
                <span className={`pill ${severityPill(n.severity)}`}>{n.severity}</span>
                <span className="faint" style={{ fontSize: 10 }}>{n.category}</span>
                <span className="faint">·</span>
                <span className="faint" style={{ fontSize: 10 }}>{n.source}</span>
                <div style={{ flex: 1 }}/>
                <span className="faint" style={{ fontSize: 10 }}>{store.relTime(n.received_at)}</span>
              </div>
              <div style={{ fontSize: 13, color: "var(--fg-bright)", lineHeight: 1.4, marginBottom: 4 }}>{n.headline}</div>
              {n.body ? <div style={{ fontSize: 11, color: "var(--fg-dim)", lineHeight: 1.5, marginBottom: 4 }}>{n.body}</div> : null}
              {n.symbols?.length ? (
                <div style={{ display: "flex", gap: 4, flexWrap: "wrap" }}>
                  {n.symbols.map(s => <span key={s} className="pill pill-dim" style={{ fontSize: 9 }}>{s}</span>)}
                </div>
              ) : null}
            </div>
          ))}
        </div>
      </Panel>
    </div>
  );
}
function severityColor(s) {
  return s === "CRITICAL" ? "down" : s === "HIGH" ? "down" : s === "MEDIUM" ? "amber" : s === "LOW" ? "cyan" : "dim";
}

Object.assign(window, { DashboardPage, ProposalsPage, NewsPage, severityPill });
