/* charts.jsx — Charts tab.
   Hand-rolled SVG candlestick (reliable across browsers/iframes).
   Real OHLC via Alpha Vantage (with deterministic mock fallback when no API key).
   Per-analyst-colored signal markers, hover tooltip on candles AND signals, click → flyout.
*/

function niceStep(range, n) {
  const rough = range / n;
  const pow = Math.pow(10, Math.floor(Math.log10(rough)));
  const norm = rough / pow;
  let step;
  if (norm < 1.5) step = 1;
  else if (norm < 3) step = 2;
  else if (norm < 7) step = 5;
  else step = 10;
  return step * pow;
}

function actionColor(a) {
  switch (a) {
    case "open":  return "var(--c-open)";
    case "add":   return "var(--c-add)";
    case "trim":  return "var(--c-trim)";
    case "cut":   return "var(--c-cut)";
    case "close": return "var(--c-close)";
    case "hold":  return "var(--c-hold)";
    case "watch": return "var(--c-watch)";
    default:      return "var(--fg-2)";
  }
}

// High-contrast marker colors that read against red/green candles. White first
// (used when there's a single contract/play), then distinct colors per contract.
const MARKER_PALETTE = ["#FFFFFF", "#FFD54A", "#4FC3F7", "#FF80AB", "#B388FF", "#FFAB40", "#80D8FF"];
const contractKey = (e) => (isOption(e) ? (contractText(e) || "option") : "shares");

function CandleChart({ ticker, candles, events, analysts, source, error, loading, onClickEvent }) {
  const wrapRef = useRef(null);
  const [width, setWidth] = useState(900);
  const [hover, setHover] = useState(null); // { kind: candle|event, payload, x, y }

  useEffect(() => {
    if (!wrapRef.current) return;
    const ro = new ResizeObserver(entries => {
      for (const e of entries) setWidth(e.contentRect.width);
    });
    ro.observe(wrapRef.current);
    return () => ro.disconnect();
  }, []);

  // Layout
  const H = 460;
  const padL = 8, padR = 64, padT = 24, padB = 36;
  const W = Math.max(560, width);
  const innerW = W - padL - padR;
  const innerH = H - padT - padB;

  // Scale setup — handles empty candles to keep hook order stable
  const haveCandles = !!(candles && candles.length);
  const xMin = haveCandles ? candles[0].t : 0;
  const xMax = haveCandles ? candles[candles.length - 1].t : 1;
  const xRange = xMax - xMin || 1;
  let yMin = haveCandles ? Math.min(...candles.map(c => c.l)) : 0;
  let yMax = haveCandles ? Math.max(...candles.map(c => c.h)) : 1;
  const pad = (yMax - yMin) * 0.04;
  yMin -= pad; yMax += pad;
  const xScale = (t) => padL + ((t - xMin) / xRange) * innerW;
  const yScale = (p) => padT + (1 - (p - yMin) / (yMax - yMin || 1)) * innerH;

  // One color per distinct contract/play (white when there's only one).
  const contractColors = useMemo(() => {
    const m = new Map();
    for (const e of events) {
      const k = contractKey(e);
      if (!m.has(k)) m.set(k, MARKER_PALETTE[m.size % MARKER_PALETTE.length]);
    }
    return m;
  }, [events]);

  // Hooks must run on every render — do them BEFORE any conditional return
  const snappedEvents = useMemo(() => {
    if (!haveCandles) return [];
    return events.map(e => {
      let nearest = candles[0], minDt = Infinity;
      for (const c of candles) {
        const dt = Math.abs(c.t - e.ts);
        if (dt < minDt) { minDt = dt; nearest = c; }
      }
      const isUp = e.action === "open" || e.action === "add";
      const isDown = e.action === "trim" || e.action === "close" || e.action === "cut";
      const yPx = isUp ? yScale(nearest.l) + 14 : isDown ? yScale(nearest.h) - 14 : yScale((nearest.h + nearest.l) / 2);
      const color = contractColors.get(contractKey(e)) || "#FFFFFF";
      return { e, candle: nearest, x: xScale(nearest.t), y: yPx, color, dir: isUp ? "up" : isDown ? "down" : "side" };
    });
  }, [events, candles, contractColors, width, haveCandles]);

  const eventsByX = useMemo(() => {
    const m = new Map();
    for (const s of snappedEvents) {
      const k = Math.round(s.x);
      if (!m.has(k)) m.set(k, []);
      m.get(k).push(s.e);
    }
    return m;
  }, [snappedEvents]);

  // Conditional return AFTER all hooks
  if (!haveCandles) {
    return (
      <div className="chart-wrap" ref={wrapRef}>
        <div className="chart-hdr">
          <div><span className="ticker-big">${ticker}</span><span className="sub">loading…</span></div>
        </div>
        <div style={{ height: H, display: "grid", placeItems: "center", color: "var(--fg-2)", fontFamily: "var(--f-mono)", fontSize: 12 }}>
          {loading ? `fetching $${ticker} OHLC…` : "no data"}
        </div>
      </div>
    );
  }

  const candleW = Math.max(2, Math.min(14, innerW / candles.length - 2));
  const lastClose = candles[candles.length - 1].c;
  const firstClose = candles[0].c;
  const change = (lastClose - firstClose) / firstClose;

  // Distinct option strikes among the visible signals that fall within the chart's
  // price range — drawn as dashed reference lines so option entries read against the
  // underlying. (Strikes far out of range are listed below the chart instead.)
  const optionEvents = events.filter(e => isOption(e) && e.strike != null);
  const strikeLines = (() => {
    const seen = new Map();
    for (const e of optionEvents) {
      if (e.strike < yMin || e.strike > yMax) continue;
      if (!seen.has(e.strike)) seen.set(e.strike, e.optionType);
    }
    return [...seen.entries()].map(([strike, type]) => ({ strike, type }));
  })();
  const offChartStrikes = (() => {
    const set = new Set();
    for (const e of optionEvents) {
      if (e.strike < yMin || e.strike > yMax) set.add(`${e.strike}${e.optionType === "put" ? "P" : "C"}`);
    }
    return [...set];
  })();

  // Y axis ticks
  const yTicks = (() => {
    const ticks = [];
    const step = niceStep(yMax - yMin, 6);
    let start = Math.ceil(yMin / step) * step;
    for (let y = start; y <= yMax; y += step) ticks.push(y);
    return ticks;
  })();

  // X axis ticks (month boundaries)
  const xTicks = (() => {
    const ticks = [];
    let last = -1;
    for (const c of candles) {
      const d = new Date(c.t);
      const key = d.getUTCFullYear() * 100 + d.getUTCMonth();
      if (key !== last) { ticks.push(c.t); last = key; }
    }
    return ticks;
  })();

  function handleMouseMove(evt) {
    const rect = wrapRef.current.getBoundingClientRect();
    const px = evt.clientX - rect.left;
    if (px < padL || px > W - padR) { setHover(null); return; }
    let nearest = candles[0], minDt = Infinity;
    for (const c of candles) {
      const dt = Math.abs(xScale(c.t) - px);
      if (dt < minDt) { minDt = dt; nearest = c; }
    }
    const k = Math.round(xScale(nearest.t));
    const overlay = eventsByX.get(k);
    setHover({ kind: "candle", payload: nearest, overlay, x: xScale(nearest.t), y: evt.clientY - rect.top });
  }

  return (
    <div className="chart-wrap" ref={wrapRef} onMouseMove={handleMouseMove} onMouseLeave={() => setHover(null)} style={{ position: "relative" }}>
      <div className="chart-hdr">
        <div>
          <span className="ticker-big">${ticker}</span>
          <span style={{ marginLeft: 14, fontFamily: "var(--f-mono)", fontSize: 18, color: "var(--fg-0)" }}>{lastClose.toFixed(2)}</span>
          <span style={{ marginLeft: 8, fontFamily: "var(--f-mono)", fontSize: 12, color: change >= 0 ? "var(--c-up)" : "var(--c-down)" }}>
            {(change >= 0 ? "+" : "")}{(change * 100).toFixed(2)}%
          </span>
          <span className="sub">{events.length} signal{events.length === 1 ? "" : "s"} · {candles.length} bars</span>
          {offChartStrikes.length > 0 && (
            <span className="sub" style={{ color: "var(--c-trim)" }}>off-chart strikes: {offChartStrikes.join(", ")}</span>
          )}
        </div>
        <span className="source-attr">
          {source === "alphavantage" && <><span className="dot" style={{ background: "var(--c-up)", color: "var(--c-up)", width: 6, height: 6 }} />Alpha Vantage · live</>}
          {source === "cache"        && <><span className="dot" style={{ background: "var(--c-watch)", color: "var(--c-watch)", width: 6, height: 6 }} />Alpha Vantage · cached</>}
          {source === "mock"         && <><span className="dot" style={{ background: "var(--c-trim)", color: "var(--c-trim)", width: 6, height: 6 }} />Synthesized · add API key</>}
          {!source                   && <>loading…</>}
        </span>
      </div>
      {contractColors.size > 0 && (
        <div className="chart-legend">
          {[...contractColors.entries()].map(([k, col]) => (
            <span key={k} className="leg-item">
              <span className="leg-dot" style={{ background: col }} />{k === "shares" ? "shares" : k}
            </span>
          ))}
        </div>
      )}
      <svg className="chart-svg" width={W} height={H} viewBox={`0 0 ${W} ${H}`}>
        {/* Grid */}
        {yTicks.map((y, i) => (
          <line key={"hy" + i} x1={padL} x2={W - padR} y1={yScale(y)} y2={yScale(y)} stroke="var(--grid-line)" />
        ))}
        {xTicks.map((t, i) => (
          <line key={"hx" + i} x1={xScale(t)} x2={xScale(t)} y1={padT} y2={H - padB} stroke="var(--grid-line)" />
        ))}

        {/* Y axis labels (right) */}
        {yTicks.map((y, i) => (
          <text key={"yl" + i} x={W - padR + 8} y={yScale(y) + 4}
            fontSize="10" fontFamily="var(--f-mono)" fill="var(--fg-2)" textAnchor="start">{y.toFixed(2)}</text>
        ))}
        {/* X axis labels */}
        {xTicks.map((t, i) => {
          const d = new Date(t);
          return <text key={"xl" + i} x={xScale(t)} y={H - padB + 18}
            fontSize="10" fontFamily="var(--f-mono)" fill="var(--fg-2)" textAnchor="middle">
            {d.toLocaleString("en-US", { month: "short" })}
          </text>;
        })}

        {/* Last close marker */}
        <line x1={padL} x2={W - padR} y1={yScale(lastClose)} y2={yScale(lastClose)}
          stroke="var(--accent)" strokeOpacity="0.5" strokeDasharray="3 3" strokeWidth="1" />
        <rect x={W - padR - 2} y={yScale(lastClose) - 9} width={56} height={18}
          fill="var(--accent)" rx="2" />
        <text x={W - padR + 26} y={yScale(lastClose) + 4} fontSize="10" fontFamily="var(--f-mono)"
          fontWeight="600" fill="var(--bg-0)" textAnchor="middle">{lastClose.toFixed(2)}</text>

        {/* Candles */}
        {candles.map((c, i) => {
          const x = xScale(c.t);
          const up = c.c >= c.o;
          const color = up ? "var(--c-up)" : "var(--c-down)";
          return (
            <g key={i}>
              <line x1={x} x2={x} y1={yScale(c.h)} y2={yScale(c.l)} stroke={color} strokeWidth="1" />
              <rect x={x - candleW / 2} y={yScale(Math.max(c.o, c.c))}
                width={candleW} height={Math.max(1, Math.abs(yScale(c.o) - yScale(c.c)))}
                fill={color} />
            </g>
          );
        })}

        {/* Option strike reference lines */}
        {strikeLines.map((s, i) => {
          const col = s.type === "put" ? "var(--c-down)" : "var(--c-open)";
          const y = yScale(s.strike);
          return (
            <g key={"strike" + i}>
              <line x1={padL} x2={W - padR} y1={y} y2={y} stroke={col} strokeOpacity="0.45" strokeDasharray="2 4" strokeWidth="1" />
              <text x={padL + 4} y={y - 3} fontSize="9.5" fontFamily="var(--f-mono)" fill={col} fillOpacity="0.95">
                {s.strike}{s.type === "put" ? "P" : "C"} strike
              </text>
            </g>
          );
        })}

        {/* Crosshair */}
        {hover?.kind === "candle" && (
          <line x1={hover.x} x2={hover.x} y1={padT} y2={H - padB} stroke="var(--fg-2)" strokeOpacity="0.45" strokeDasharray="2 3" />
        )}

        {/* Signal markers — colored by contract/play, dark outline for contrast */}
        {snappedEvents.map((s) => {
          const pts = s.dir === "up"   ? `${s.x},${s.y - 9} ${s.x - 6},${s.y + 3} ${s.x + 6},${s.y + 3}`
                    : s.dir === "down" ? `${s.x},${s.y + 9} ${s.x - 6},${s.y - 3} ${s.x + 6},${s.y - 3}`
                    :                    `${s.x},${s.y - 7} ${s.x + 7},${s.y} ${s.x},${s.y + 7} ${s.x - 7},${s.y}`;
          return (
            <g key={s.e.id} style={{ cursor: "pointer" }}
              onMouseEnter={(ev) => { ev.stopPropagation(); setHover({ kind: "event", payload: s.e, x: s.x, y: s.y }); }}
              onClick={(ev) => { ev.stopPropagation(); onClickEvent(s.e); }}>
              <polygon points={pts} fill={s.color} stroke="rgba(0,0,0,0.85)" strokeWidth="1.5" strokeLinejoin="round" />
            </g>
          );
        })}
      </svg>

      {/* Tooltips */}
      {hover?.kind === "candle" && hover.payload && (
        <div className="chart-tooltip" style={{
          left: Math.min(W - 260, Math.max(8, hover.x + 14)),
          top: 56,
        }}>
          <div className="t-hdr">
            <span>{fmtDay(hover.payload.t)} {new Date(hover.payload.t).getUTCFullYear()}</span>
          </div>
          <div className="t-meta">
            O <span style={{ color: "var(--fg-0)" }}>{hover.payload.o.toFixed(2)}</span> ·{" "}
            H <span style={{ color: "var(--c-up)" }}>{hover.payload.h.toFixed(2)}</span> ·{" "}
            L <span style={{ color: "var(--c-down)" }}>{hover.payload.l.toFixed(2)}</span> ·{" "}
            C <span style={{ color: "var(--fg-0)" }}>{hover.payload.c.toFixed(2)}</span>
          </div>
          {hover.overlay && hover.overlay.length > 0 && (
            <div style={{ marginTop: 10, paddingTop: 10, borderTop: "1px dashed var(--border-2)" }}>
              {hover.overlay.slice(0, 4).map((e, i) => (
                <div key={i} style={{ display: "flex", gap: 8, alignItems: "center", marginBottom: 6, fontSize: 11 }}>
                  <span className="dot" style={{
                    background: analysts.find(a => a.handle === e.analyst)?.color,
                    color: analysts.find(a => a.handle === e.analyst)?.color,
                    width: 7, height: 7,
                  }} />
                  <span style={{ color: "var(--fg-0)" }}>{e.analyst}</span>
                  <span style={{ color: "var(--fg-2)" }}>{e.action.toUpperCase()}</span>
                  <span style={{ color: "var(--fg-1)" }}>{e.contract || "shares"}</span>
                </div>
              ))}
              {hover.overlay.length > 4 && (
                <div style={{ color: "var(--fg-3)", fontSize: 10 }}>+ {hover.overlay.length - 4} more</div>
              )}
            </div>
          )}
        </div>
      )}
      {hover?.kind === "event" && (
        <div className="chart-tooltip" style={{
          left: Math.min(W - 300, Math.max(8, hover.x + 14)),
          top: Math.max(40, hover.y - 80),
          maxWidth: 300,
        }}>
          <div className="t-hdr">
            <span className="dot" style={{ background: analysts.find(a => a.handle === hover.payload.analyst)?.color, color: analysts.find(a => a.handle === hover.payload.analyst)?.color, width: 8, height: 8 }} />
            <span>{hover.payload.analyst}</span>
            <ActionPill kind={hover.payload.action} />
          </div>
          <div className="t-note">{hover.payload.raw}</div>
          <div className="t-meta">{hover.payload.contract || "shares"} · {fmtTime(hover.payload.ts, { short: true })} · click for details</div>
        </div>
      )}

      {source === "mock" && error && (
        <div style={{
          position: "absolute", bottom: 12, left: 18, right: 18,
          background: "var(--bg-2)", border: "1px solid var(--border-2)",
          padding: "8px 12px", borderRadius: 6, fontSize: 11,
          fontFamily: "var(--f-mono)", color: "var(--fg-2)",
          display: "flex", justifyContent: "space-between", alignItems: "center",
        }}>
          <span>Synthesized chart — {error.length > 110 ? error.slice(0, 110) + "…" : error}</span>
          <span style={{ color: "var(--accent)" }}>Settings → Integrations → API key</span>
        </div>
      )}
    </div>
  );
}

function ChartsTab({ events, analysts, enabled, onToggle, currentTicker, setCurrentTicker, onClickEvent }) {
  const { loading, candles, source, error } = useOHLC(currentTicker);

  const filteredEvents = events.filter(e => enabled.has(e.analyst));

  const tickerStats = useMemo(() => {
    const t = new Map();
    for (const e of filteredEvents) {
      let s = t.get(e.ticker);
      if (!s) { s = { ticker: e.ticker, events: 0, sources: new Set() }; t.set(e.ticker, s); }
      s.events++;
      s.sources.add(e.analyst);
    }
    return [...t.values()].sort((a, b) => b.events - a.events);
  }, [filteredEvents]);

  const tickerEvents = useMemo(() =>
    events.filter(e => e.ticker === currentTicker && enabled.has(e.analyst))
      .sort((a, b) => b.ts - a.ts),
    [events, currentTicker, enabled]);

  const allPositions = useMemo(() =>
    aggregatePositions(events.filter(e => e.ticker === currentTicker && enabled.has(e.analyst))),
    [events, currentTicker, enabled]);
  const openPositions = allPositions.filter(p => p.status !== "closed" && p.status !== "expired");

  return (
    <div className="split cols-charts">
      <div className="panel">
        <div className="panel-hdr">
          <div className="panel-title">Sources <span className="panel-meta" style={{ marginLeft: 10 }}>{enabled.size} active</span></div>
        </div>
        <div className="scroll-y" style={{ maxHeight: 280 }}>
          {analysts.map(a => {
            const on = enabled.has(a.handle);
            const ct = events.filter(e => e.analyst === a.handle).length;
            return (
              <div key={a.handle} className="source-card" style={{ padding: "10px 16px" }} onClick={() => onToggle(a.handle, !on)}>
                <div className={cx("check", on && "on")} />
                <div>
                  <div className="analyst" style={{ marginBottom: 0 }}>
                    <span className="swatch" style={{ "--col": a.color }} />
                    <span>{a.handle}</span>
                  </div>
                </div>
                <div className="count">{ct} ev</div>
              </div>
            );
          })}
        </div>
        <div className="panel-hdr" style={{ borderTop: "1px solid var(--border-1)" }}>
          <div className="panel-title" style={{ fontSize: 14 }}>Tickers</div>
          <div className="panel-meta">{tickerStats.length} found</div>
        </div>
        <div className="scroll-y" style={{ maxHeight: "calc(100vh - 720px)", minHeight: 180 }}>
          {tickerStats.map(t => (
            <div key={t.ticker} className={cx("ticker-row", t.ticker === currentTicker && "active")} onClick={() => setCurrentTicker(t.ticker)}>
              <div>
                <div className="name">${t.ticker}</div>
              </div>
              <div className="meta">{t.events} evt · {t.sources.size} src</div>
            </div>
          ))}
        </div>
      </div>

      <div style={{ display: "grid", gap: 14 }}>
        <CandleChart
          key={currentTicker}
          ticker={currentTicker}
          candles={candles}
          events={tickerEvents}
          analysts={analysts}
          source={source}
          error={error}
          loading={loading}
          onClickEvent={onClickEvent}
        />
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 14 }}>
          <div className="panel">
            <div className="panel-hdr">
              <div className="panel-title">Current Positions</div>
              <div className="panel-meta">{openPositions.length} active</div>
            </div>
            <div className="scroll-y" style={{ maxHeight: 260 }}>
              <table className="tbl">
                <thead><tr><th>Source</th><th>Contract</th><th className="num">Entry</th><th className="num">Last</th><th>Status</th></tr></thead>
                <tbody>
                  {openPositions.map(p => (
                    <tr key={p.key}>
                      <td><AnalystChip analyst={p.analyst} analysts={analysts} /></td>
                      <td><span className="contract">{p.contract || "shares"}</span></td>
                      <td className="num">{p.entryPrice ? fmtNum(p.entryPrice) : "—"}</td>
                      <td className="num">{fmtNum(p.lastPrice)}</td>
                      <td><span className="pill" style={{ color: p.status === "partial" ? "var(--c-trim)" : "var(--accent)" }}>{p.status}</span></td>
                    </tr>
                  ))}
                  {openPositions.length === 0 && <tr><td colSpan="5"><Empty msg="No open positions on this ticker." /></td></tr>}
                </tbody>
              </table>
            </div>
          </div>
          <div className="panel">
            <div className="panel-hdr">
              <div className="panel-title">Chart Events</div>
              <div className="panel-meta">{tickerEvents.length} shown</div>
            </div>
            <div className="scroll-y" style={{ maxHeight: 260 }}>
              <table className="tbl">
                <thead><tr><th>Time</th><th>Source</th><th>Action</th><th>Contract</th></tr></thead>
                <tbody>
                  {tickerEvents.slice(0, 80).map(e => (
                    <tr key={e.id} onClick={() => onClickEvent(e)} style={{ cursor: "pointer" }}>
                      <td className="muted mono">{fmtTime(e.ts, { short: true })}</td>
                      <td><AnalystChip analyst={e.analyst} analysts={analysts} /></td>
                      <td><ActionPill kind={e.action} /></td>
                      <td><span className="contract">{e.contract || "shares"}</span></td>
                    </tr>
                  ))}
                  {tickerEvents.length === 0 && <tr><td colSpan="4"><Empty msg="No events for this ticker." /></td></tr>}
                </tbody>
              </table>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { CandleChart, ChartsTab });
