/* watchlist.jsx — Watchlist & analysis posts: tickers floated as ideas, NOT
   positions. A column table (like the events feed), grouped by day. Click a
   row to read the full raw note. */

// Grouped by MARKET day (see the market-time block in shared.jsx), so an
// after-hours post files under the trading day it was made on rather than the
// viewer's calendar day. The anchor is local midnight OF that market date.
function dayStartMs(ts) {
  const d = marketDayDate(marketDayKey(ts));
  return d ? d.getTime() : 0;
}

// "Today · Fri, Jun 6 2026" / "Yesterday · …" / "Wed, Jun 4 2026" — market days.
function fmtDayLabel(ms) {
  const d = new Date(ms);
  return marketDayLabel(`${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`);
}

// Mobile Watchlist & analysis — compact accordion grouped by day. Each row:
// category · tickers · analyst · time; tap to read the full note + actions.
function MobileWatchlistList({ days, analysts, onShowParser, onDelete, canDeleteRow, selectable, selected, onToggle }) {
  const canDelRow = (e) => !canDeleteRow || canDeleteRow(e);
  const [open, setOpen] = useState(null);
  if (!days.length) return <Empty msg="No watchlist or analysis posts yet. These appear when an analyst floats tickers as ideas (not a position)." />;
  return (
    <div className="mpos-list">
      {days.map(([d, list]) => {
        const sources = new Set(list.map(e => e.analyst)).size;
        const tcount = new Set(list.flatMap(e => e.tickers || [])).size;
        return (
          <Fragment key={d}>
            <div className="mpos-day">
              <span className="mpos-day-t">{fmtDayLabel(d)}</span>
              <span className="mpos-day-m mono">{list.length} post{list.length === 1 ? "" : "s"} · {sources} source{sources === 1 ? "" : "s"} · {tcount} ticker{tcount === 1 ? "" : "s"}</span>
            </div>
            {list.map(e => {
              const isOpen = open === e.id;
              return (
                <div key={e.id} className={cx("mpos", isOpen && "open", selectable && selected.has(e.id) && "msel")}>
                  <div className="mpos-head" onClick={() => setOpen(isOpen ? null : e.id)}>
                    <div className="mpos-r1">
                      <span className="mpos-tk">
                        {selectable && <input type="checkbox" className="mpos-cb" checked={selected.has(e.id)} onClick={ev => ev.stopPropagation()} onChange={() => onToggle(e.id)} />}
                        <span className={cx("wl-kind", e.kind)}>{e.kind}</span>
                      </span>
                      <span className="mpos-chev">{I(isOpen ? "chevDown" : "chevRight", { size: 16 })}</span>
                    </div>
                    <div className="mpos-tickrow">
                      {e.tickers && e.tickers.length > 0
                        ? e.tickers.map(t => <span key={t} className="wl-tick">${t}</span>)
                        : <span className="muted">—</span>}
                    </div>
                    <div className="mpos-r2">
                      <AnalystChip analyst={e.analyst} analysts={analysts} />
                      <span className="mpos-meta mono">{fmtTime(e.ts, { short: true })}</span>
                    </div>
                  </div>
                  {isOpen && (
                    <div className="mpos-body">
                      <dl className="mpos-grid">
                        <div><dt>Category</dt><dd><span className={cx("wl-kind", e.kind)}>{e.kind}</span></dd></div>
                        <div><dt>Channel</dt><dd>{e.channel}</dd></div>
                      </dl>
                      {(e.title || e.raw) && (
                        <div className="mpos-sec">
                          <span className="mpos-lbl">Note</span>
                          {e.title && <p className="mpos-note mpos-note-title">{e.title}</p>}
                          {e.raw && <p className="mpos-note">{e.raw}</p>}
                        </div>
                      )}
                      {(onShowParser || (onDelete && canDelRow(e))) && (
                        <div className="mpos-btns">
                          {onShowParser && <button className="btn" onClick={() => onShowParser(e)}>{I("brain", { size: 15 })} Parser</button>}
                          {onDelete && canDelRow(e) && <button className="btn danger" onClick={async () => { if (await confirmDialog({ title: "Delete post", message: "Remove this watchlist / analysis post?", confirmLabel: "Delete", danger: true })) onDelete(e.id); }}>{I("trash", { size: 14 })} Delete</button>}
                        </div>
                      )}
                    </div>
                  )}
                </div>
              );
            })}
          </Fragment>
        );
      })}
    </div>
  );
}

function Watchlist({ entries, analysts, onDelete, onDeleteMany, canDeleteRow, onShowParser, serverIds, onRestore }) {
  const canDelRow = (e) => !canDeleteRow || canDeleteRow(e);
  const isMobile = useIsMobile();
  const [filter, setFilter] = useState("");
  const [kind, setKind] = useState("all");
  const [range, setRange] = useState("week");  // timeframe window — default: the current trading week (other ranges still available)
  const [anFilter, setAnFilter] = useState(() => new Set());
  const [open, setOpen] = useState(null); // entry id whose raw note is expanded
  const [selected, setSelected] = useState(() => new Set());
  const [showDeleted, setShowDeleted] = useState(false); // recycle-bin view
  const selectable = !!onDeleteMany;

  const availableAnalysts = useMemo(() => {
    const set = new Set();
    for (const e of (entries || [])) if (e.analyst) set.add(e.analyst);
    return [...set].sort();
  }, [entries]);

  const filtered = useMemo(() => {
    const fl = filter.toLowerCase().trim();
    const cutoff = Date.now() - rangeDays(range) * 86400000;
    return (entries || []).filter(e => {
      if (e.ts < cutoff) return false;
      if (kind !== "all" && e.kind !== kind) return false;
      if (!analystMatch(e.analyst, anFilter)) return false;
      if (!fl) return true;
      return (e.tickers || []).some(t => String(t).toLowerCase().includes(fl));
    });
  }, [entries, filter, kind, range, anFilter]);

  // Group into days (newest first); entries within a day newest-first too.
  const days = useMemo(() => {
    const m = new Map();
    for (const e of filtered) {
      const d = dayStartMs(e.ts);
      if (!m.has(d)) m.set(d, []);
      m.get(d).push(e);
    }
    for (const list of m.values()) list.sort((a, b) => b.ts - a.ts);
    return [...m.entries()].sort((a, b) => b[0] - a[0]);
  }, [filtered]);

  // ── Multi-select ──
  const filteredIds = filtered.map(e => e.id);
  const allSelected = selectable && filteredIds.length > 0 && filteredIds.every(id => selected.has(id));
  function toggle(id) {
    setSelected(prev => { const n = new Set(prev); n.has(id) ? n.delete(id) : n.add(id); return n; });
  }
  function toggleAll() {
    setSelected(prev => {
      const n = new Set(prev);
      if (allSelected) filteredIds.forEach(id => n.delete(id)); else filteredIds.forEach(id => n.add(id));
      return n;
    });
  }
  async function bulkDelete() {
    const ids = [...selected];
    if (!ids.length) return;
    if (await confirmDialog({ title: "Delete posts", message: `Delete ${ids.length} selected watchlist / analysis post${ids.length === 1 ? "" : "s"}?`, confirmLabel: `Delete ${ids.length}`, danger: true })) {
      onDeleteMany(ids);
      setSelected(new Set());
    }
  }
  const colCount = 7 + (selectable ? 1 : 0);

  // Live | Deleted toggle — only for users who can delete (they own a bin).
  const showBinToggle = !!onDelete;
  const binToggle = showBinToggle && (
    <div className="seg" role="group" aria-label="Recycle bin">
      <button className={cx("seg-btn", !showDeleted && "on")} onClick={() => setShowDeleted(false)}>Live</button>
      <button className={cx("seg-btn", showDeleted && "on")} onClick={() => setShowDeleted(true)}>Deleted</button>
    </div>
  );

  // A restored post older than the active time window would stay invisible back in
  // Live — widen to All so the user actually sees what they just restored.
  const handleRestored = (rows) => {
    if (onRestore) onRestore(rows);
    const cutoff = Date.now() - rangeDays(range) * 86400000;
    if ((rows || []).some(r => r && r.ts < cutoff)) setRange("all");
  };

  if (showDeleted && showBinToggle) {
    return (
      <div>
        <div className="filterbar" style={{ gridTemplateColumns: "1fr auto" }}>
          <div className="muted" style={{ alignSelf: "center", fontSize: "0.8125rem" }}>Recycle bin — restore a deleted post or remove it for good</div>
          {binToggle}
        </div>
        <div className="panel">
          <div className="panel-hdr"><div className="panel-title">Recycle bin</div></div>
          <div className="scroll-y feed-scroll" style={{ maxHeight: "calc(100vh - 250px)", minHeight: 400 }}>
            <TrashList source="tape_watchlist" serverIds={serverIds} onRestored={handleRestored} noun="post" />
          </div>
        </div>
      </div>
    );
  }

  return (
    <div>
      <div className="filterbar" style={{ gridTemplateColumns: "1fr auto auto auto" + (showBinToggle ? " auto" : "") }}>
        <input className="input search" placeholder="Filter by ticker…" value={filter} onChange={e => setFilter(e.target.value)} />
        <AnalystFilter available={availableAnalysts} selected={anFilter} onChange={setAnFilter} analysts={analysts} />
        <div className="seg" role="group" aria-label="Time window">
          {[["week", "This week"], ["2w", "2W"], ["month", "1M"], ["all", "All"]].map(([v, l]) => (
            <button key={v} className={cx("seg-btn", range === v && "on")} onClick={() => setRange(v)}>{l}</button>
          ))}
        </div>
        <div className="seg">
          {[["all", "All"], ["watchlist", "Watchlists"], ["analysis", "Analysis"]].map(([v, l]) => (
            <button key={v} className={cx("seg-btn", kind === v && "on")} onClick={() => setKind(v)}>{l}</button>
          ))}
        </div>
        {binToggle}
      </div>

      {selectable && selected.size > 0 && (
        <div className="bulk-bar">
          <span><strong>{selected.size}</strong> selected</span>
          <span style={{ flex: 1 }} />
          <button className="btn" onClick={() => setSelected(new Set())}>Clear</button>
          <button className="btn danger" onClick={bulkDelete}>{I("trash", { size: 14 })} Delete {selected.size}</button>
        </div>
      )}

      <div className="panel">
        <div className="panel-hdr">
          <div className="panel-title">Watchlist &amp; analysis</div>
          <div className="panel-meta">{filtered.length} post{filtered.length === 1 ? "" : "s"} · live</div>
        </div>
        {selectable && (
          <label className="cards-selectall">
            <input type="checkbox" checked={allSelected} onChange={toggleAll} />
            <span>Select all</span>
            {selected.size > 0 && <span className="cards-selectall-n mono">{selected.size} selected</span>}
          </label>
        )}
        <div className="scroll-y feed-scroll" style={{ maxHeight: "calc(100vh - 250px)", minHeight: 400 }}>
          {isMobile ? (
            <MobileWatchlistList days={days} analysts={analysts} onShowParser={onShowParser} onDelete={onDelete} canDeleteRow={canDeleteRow} selectable={selectable} selected={selected} onToggle={toggle} />
          ) : (
          <table className="tbl tbl-fixed tbl-cards">
            <colgroup>
              {selectable && <col style={{ width: "3%" }} />}
              <col style={{ width: "11%" }} />
              <col style={{ width: "13%" }} />
              <col style={{ width: "9%" }} />
              <col style={{ width: "16%" }} />
              <col style={{ width: "13%" }} />
              <col style={{ width: "27%" }} />
              <col style={{ width: "8%" }} />
            </colgroup>
            <thead>
              <tr>
                {selectable && <th style={{ textAlign: "center" }}><input type="checkbox" checked={allSelected} onChange={toggleAll} title="Select all" /></th>}
                <th>Time</th>
                <th>Analyst</th>
                <th>Category</th>
                <th>Tickers</th>
                <th>Channel</th>
                <th>Raw note</th>
                <th></th>
              </tr>
            </thead>
            <tbody>
              {days.length === 0 && (
                <tr className="is-empty"><td colSpan={colCount}><Empty msg="No watchlist or analysis posts yet. These appear when an analyst floats tickers as ideas (not a position)." /></td></tr>
              )}
              {days.map(([d, list]) => {
                const sources = new Set(list.map(e => e.analyst)).size;
                const tcount = new Set(list.flatMap(e => e.tickers || [])).size;
                return (
                  <Fragment key={d}>
                    <tr className="day-sep">
                      <td colSpan={colCount}>
                        <span className="day-sep-title">{fmtDayLabel(d)}</span>
                        <span className="day-sep-meta mono">{list.length} post{list.length === 1 ? "" : "s"} · {sources} source{sources === 1 ? "" : "s"} · {tcount} ticker{tcount === 1 ? "" : "s"}</span>
                      </td>
                    </tr>
                    {list.map(e => {
                      const isOpen = open === e.id;
                      return (
                        <Fragment key={e.id}>
                          <tr className={cx("wl-row", selected.has(e.id) && "row-selected", isOpen && "expanded")} onClick={() => setOpen(isOpen ? null : e.id)} style={{ cursor: "pointer" }}>
                            {selectable && (
                              <td className="cell-check" style={{ textAlign: "center" }} onClick={(ev) => ev.stopPropagation()}>
                                <input type="checkbox" checked={selected.has(e.id)} onChange={() => toggle(e.id)} />
                              </td>
                            )}
                            <td data-label="Time" className="muted mono">{fmtTime(e.ts, { short: true })}</td>
                            <td data-label="Analyst"><AnalystChip analyst={e.analyst} analysts={analysts} /></td>
                            <td data-label="Category"><span className={cx("wl-kind", e.kind)}>{e.kind}</span></td>
                            <td data-label="Tickers" className="cell-chips">
                              {e.tickers && e.tickers.length > 0
                                ? <div className="wl-tickcell">{e.tickers.map(t => <span key={t} className="wl-tick">${t}</span>)}</div>
                                : <span className="muted">—</span>}
                            </td>
                            <td data-label="Channel" className="muted mono" style={{ fontSize: "0.6875rem", whiteSpace: "nowrap" }}>{e.channel}</td>
                            <td data-label="Note" className="muted cell-note" style={{ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}><span className="cell-text">{e.title || e.raw}</span></td>
                            <td className="cell-actions" style={{ textAlign: "right", whiteSpace: "nowrap" }}>
                              {onShowParser && (
                                <button
                                  className="icon-btn"
                                  title="Parser reasoning"
                                  onClick={(ev) => { ev.stopPropagation(); onShowParser(e); }}
                                >{I("brain", { size: 15 })}<span className="btn-label">Parser</span></button>
                              )}
                              {onDelete && canDelRow(e) && (
                                <button
                                  className="icon-btn danger"
                                  title="Delete post"
                                  onClick={async (ev) => { ev.stopPropagation(); if (await confirmDialog({ title: "Delete post", message: "Remove this watchlist / analysis post?", confirmLabel: "Delete", danger: true })) onDelete(e.id); }}
                                >{I("trash", { size: 14 })}<span className="btn-label">Delete</span></button>
                              )}
                            </td>
                          </tr>
                          {isOpen && (e.raw || e.title) && (
                            <tr className="wl-raw-row">
                              <td colSpan={colCount}>
                                {e.title && <div className="wl-raw-title">{e.title}</div>}
                                {e.raw && <div className="wl-raw">{e.raw}</div>}
                              </td>
                            </tr>
                          )}
                        </Fragment>
                      );
                    })}
                  </Fragment>
                );
              })}
            </tbody>
          </table>
          )}
        </div>
      </div>
    </div>
  );
}

// ─── Flyout: parser internals for a single watchlist / analysis post (owner-only)
function WatchlistFlyout({ entry, onClose, onDelete }) {
  if (!entry) return null;
  useEffect(() => {
    const handler = (e) => { if (e.key === "Escape") onClose(); };
    window.addEventListener("keydown", handler);
    return () => window.removeEventListener("keydown", handler);
  }, []);
  const tickers = entry.tickers || [];
  const reasoning = entry.reasoning && entry.reasoning.summary ? entry.reasoning.summary : "";
  return [
    <div key="back" className="flyout-back" onClick={onClose} />,
    <div key="flyout" className="flyout" role="dialog" aria-modal="true">
      <div className="flyout-hdr">
        <div>
          <div style={{ display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" }}>
            <span className={cx("wl-kind", entry.kind)}>{entry.kind}</span>
            {tickers.slice(0, 6).map(t => <span key={t} className="wl-tick">${t}</span>)}
          </div>
          <div className="mono" style={{ fontSize: "0.6875rem", color: "var(--fg-2)", marginTop: 6 }}>
            {entry.analyst} · {fmtTime(entry.ts)}
          </div>
        </div>
        <div style={{ display: "flex", gap: 6 }}>
          {onDelete && (
            <button
              className="icon-btn danger"
              title="Delete post"
              onClick={async () => { if (await confirmDialog({ title: "Delete post", message: "Remove this watchlist / analysis post?", confirmLabel: "Delete", danger: true })) onDelete(entry.id); }}
            >{I("trash", { size: 16 })}</button>
          )}
          <button className="btn" onClick={onClose} style={{ padding: 6 }}>{I("close", { size: 16 })}</button>
        </div>
      </div>
      <div className="flyout-body">
        {entry.title && (
          <div className="field">
            <h5>Title</h5>
            <div style={{ fontSize: "0.8125rem", fontWeight: 600, color: "var(--fg-0)" }}>{entry.title}</div>
          </div>
        )}
        <div className="field">
          <h5>Raw message</h5>
          <div className="raw-msg" style={{ background: "var(--bg-2)", border: "1px solid var(--border-1)", borderRadius: 6, padding: "12px 14px", whiteSpace: "pre-wrap" }}>{entry.raw}</div>
        </div>
        <div className="field">
          <h5>Source</h5>
          <div className="mono" style={{ fontSize: "0.75rem" }}>
            {entry.channel}
            <span style={{ color: "var(--fg-3)", margin: "0 8px" }}>·</span>
            <span style={{ color: "var(--fg-2)" }}>kind</span> <strong style={{ color: "var(--fg-0)" }}>{entry.kind}</strong>
            <span style={{ color: "var(--fg-3)", margin: "0 8px" }}>·</span>
            <span style={{ color: "var(--fg-2)" }}>id</span> <span style={{ color: "var(--fg-1)" }}>{entry.id}</span>
          </div>
        </div>
        <div className="field">
          <h5>Extracted watchlist</h5>
          <pre style={{ background: "var(--bg-0)", border: "1px solid var(--border-1)", borderRadius: 6, padding: "12px 14px", fontFamily: "var(--f-mono)", fontSize: "0.75rem", color: "var(--fg-mono)", margin: 0, lineHeight: 1.6, whiteSpace: "pre-wrap" }}>
{`{
  "kind":     "${entry.kind}",
  "title":    "${(entry.title || "").replace(/"/g, '\\"')}",
  "tickers":  [${tickers.map(t => `"${t}"`).join(", ")}]
}`}
          </pre>
        </div>
        {reasoning && (
          <div className="field">
            <h5>Parser reasoning</h5>
            <div style={{ fontSize: "0.75rem", color: "var(--fg-1)", lineHeight: 1.6 }}>
              {reasoning.split(/("[^"]+")/).map((part, i) =>
                part.startsWith('"') ? <span key={i} style={{ background: "var(--accent-glow)", color: "var(--accent)", padding: "1px 4px", borderRadius: 2 }}>{part}</span> : <span key={i}>{part}</span>
              )}
            </div>
          </div>
        )}
        <div className="field">
          <h5>Pipeline</h5>
          <div style={{ display: "grid", gridTemplateColumns: "1fr", gap: 6, fontFamily: "var(--f-mono)", fontSize: "0.6875rem" }}>
            <div style={{ display: "grid", gridTemplateColumns: "120px 1fr", gap: 8 }}>
              <span style={{ color: "var(--fg-2)" }}>scraper</span>
              <span style={{ color: "var(--fg-0)" }}>discord.py-self · {entry.channel}</span>
            </div>
            <div style={{ display: "grid", gridTemplateColumns: "120px 1fr", gap: 8 }}>
              <span style={{ color: "var(--fg-2)" }}>classifier</span>
              <span style={{ color: "var(--fg-0)" }}>claude-haiku-4-5 · {entry.kind}</span>
            </div>
            <div style={{ display: "grid", gridTemplateColumns: "120px 1fr", gap: 8 }}>
              <span style={{ color: "var(--fg-2)" }}>storage</span>
              <span style={{ color: "var(--fg-0)" }}>tape_watchlist · row {entry.id}</span>
            </div>
            <div style={{ display: "grid", gridTemplateColumns: "120px 1fr", gap: 8 }}>
              <span style={{ color: "var(--fg-2)" }}>tickers</span>
              <span style={{ color: "var(--fg-0)" }}>{tickers.length} floated</span>
            </div>
          </div>
        </div>
      </div>
    </div>
  ];
}

Object.assign(window, { Watchlist, WatchlistFlyout });
