const {
  useState: uS,
  useEffect: uE,
  useMemo: uM,
  useRef: uR,
  useCallback: uC,
} = React;

// ——————————————— Value Chain Strip
function ValueChainStrip({ center, onNode }) {
  const buckets = {};
  sectorOrder.forEach((s) => (buckets[s] = []));
  // collect supply-chain neighbors + center + theme peers
  const include = new Set([center]);
  // Direct supply chain links
  window.LINKS.forEach((l) => {
    if (l.from === center) include.add(l.to);
    if (l.to === center) include.add(l.from);
  });
  // Theme peers — stocks from the same themes as center
  (window.THEMES || []).forEach((th) => {
    if ((th.tickers || []).includes(center)) {
      (th.tickers || []).forEach((t) => {
        if (window.STOCKS[t]) include.add(t);
      });
    }
  });
  const added = new Set();
  include.forEach((t) => {
    const s = window.STOCKS[t];
    if (s && buckets[s.sector] && !added.has(t)) {
      buckets[s.sector].push(t);
      added.add(t);
    }
  });
  return (
    <div className="vcs">
      {sectorOrder.map((s, si) => (
        <div className="vcs-col" key={s}>
          <div className="vcs-col-h">
            <span className="vcs-col-i">{si + 1}</span>
            <span className="vcs-col-n">{s}</span>
          </div>
          <div className="vcs-col-body">
            {buckets[s].map((t) => {
              const st = window.STOCKS[t];
              const up = st.chg >= 0;
              return (
                <button
                  key={t}
                  className={"vcs-chip" + (t === center ? " c" : "")}
                  onClick={() => onNode(t)}
                >
                  <span className="vcs-chip-t">{t.replace(".T", "")}</span>
                  <span className="vcs-chip-n">{st.name}</span>
                  <span
                    className="vcs-chip-c"
                    style={{
                      color: up ? "oklch(78% 0.16 155)" : "oklch(68% 0.18 25)",
                    }}
                  >
                    {fmtPct(st.chg)}
                  </span>
                </button>
              );
            })}
            {buckets[s].length === 0 && (
              <div className="vcs-empty" aria-hidden="true" />
            )}
          </div>
        </div>
      ))}
    </div>
  );
}

// ——————————————— News Panel
function NewsPanel({ center, onHover, lang }) {
  const [filter, setFilter] = uS("related");
  const relevant = uM(() => {
    const themes = new Set(
      window.THEMES.filter((th) => (th.tickers || []).includes(center)).map(
        (t) => t.id,
      ),
    );
    let list = window.NEWS;
    if (filter === "related")
      list = list.filter(
        (n) =>
          (n.tickers || []).includes(center) ||
          (n.themes || []).some((t) => themes.has(t)),
      );
    if (filter === "direct")
      list = list.filter((n) => (n.tickers || []).includes(center));
    return list;
  }, [center, filter]);

  return (
    <div className="news-panel">
      <div className="panel-h">
        <div>
          <div className="panel-t">
            {lang === "jp" ? "市場ニュース" : "Market Feed"}
          </div>
          <div className="panel-s">
            {lang === "jp" ? "シグナル同期" : "Live signal propagation"}
          </div>
        </div>
        <div className="seg">
          <button
            className={filter === "related" ? "on" : ""}
            onClick={() => setFilter("related")}
          >
            {lang === "jp" ? "関連" : "Related"}
          </button>
          <button
            className={filter === "direct" ? "on" : ""}
            onClick={() => setFilter("direct")}
          >
            {lang === "jp" ? "直接" : "Direct"}
          </button>
          <button
            className={filter === "all" ? "on" : ""}
            onClick={() => setFilter("all")}
          >
            {lang === "jp" ? "全て" : "All"}
          </button>
        </div>
      </div>
      <div className="news-list">
        {relevant.map((n, i) => (
          <div
            key={n.id}
            className={"news-item imp-" + n.impact}
            style={{ animationDelay: i * 60 + "ms" }}
            onMouseEnter={() => onHover(n)}
            onMouseLeave={() => onHover(null)}
          >
            <div className="news-top">
              <span className="news-time">{(n.date || "").slice(5)}</span>
              <span className="news-src">{n.source}</span>
              <span
                className={
                  "news-sent s-" +
                  (n.sentiment > 0.3 ? "p" : n.sentiment < -0.2 ? "n" : "z")
                }
              >
                {n.sentiment > 0.3 ? "▲" : n.sentiment < -0.2 ? "▼" : "◆"}{" "}
                {n.sentiment >= 0 ? "+" : ""}
                {((n.sentiment * 100) | 0) / 100}
              </span>
            </div>
            <div className="news-h">{n.headline}</div>
            <div className="news-bot">
              {(n.tickers || []).slice(0, 4).map((t) => (
                <span
                  key={t}
                  className={"news-chip" + (t === center ? " c" : "")}
                >
                  {t.replace(".T", "")}
                </span>
              ))}
              {(n.themes || []).map((th) => {
                const theme = window.THEMES.find((x) => x.id === th);
                return (
                  theme && (
                    <span
                      key={th}
                      className="news-theme"
                      style={{ color: theme.color, borderColor: theme.color }}
                    >
                      {theme.name}
                    </span>
                  )
                );
              })}
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

// ——————————————— Correlation matrix
function CorrelationGrid({ center, onNode }) {
  const peers = uM(() => {
    const themes = window.THEMES.filter((th) =>
      (th.tickers || []).includes(center),
    );
    const set = new Set();
    themes.forEach((th) => th.tickers.forEach((t) => set.add(t)));
    peersOf(center).forEach((t) => set.add(t));
    set.delete(center);
    // Only include tickers that actually have stock data
    return [...set].filter((t) => window.STOCKS[t]).slice(0, 10);
  }, [center]);

  // synthetic corr
  function corr(a, b) {
    if (a === b) return 1;
    const aTh = new Set(
      window.THEMES.filter((th) => (th.tickers || []).includes(a)).map(
        (t) => t.id,
      ),
    );
    const bTh = new Set(
      window.THEMES.filter((th) => (th.tickers || []).includes(b)).map(
        (t) => t.id,
      ),
    );
    const overlap = [...aTh].filter((x) => bTh.has(x)).length;
    const linked = window.LINKS.some(
      (l) => (l.from === a && l.to === b) || (l.from === b && l.to === a),
    );
    const sameSec = window.STOCKS[a]?.sector === window.STOCKS[b]?.sector;
    let c = 0.15 + overlap * 0.22 + (linked ? 0.22 : 0) + (sameSec ? 0.08 : 0);
    // deterministic jitter
    let s = a.charCodeAt(0) * 13 + (b.charCodeAt(2) || 0) * 7;
    c += ((s % 100) / 100 - 0.5) * 0.15;
    return Math.max(-0.2, Math.min(0.95, c));
  }

  return (
    <div className="corr-wrap">
      <div className="panel-t">Peer correlation · 90d</div>
      <div className="panel-s">
        ρ vs. {center.replace(".T", "")} {window.STOCKS[center]?.name || center}
      </div>
      <div className="corr-list">
        {peers.map((p, i) => {
          const c = corr(center, p);
          const pct = (c * 100) | 0;
          const col =
            c > 0.5
              ? "oklch(78% 0.16 155)"
              : c > 0.2
                ? "oklch(76% 0.12 95)"
                : c > 0
                  ? "oklch(70% 0.08 230)"
                  : "oklch(68% 0.14 25)";
          return (
            <button
              key={p}
              className="corr-row"
              onClick={() => onNode(p)}
              style={{ animationDelay: i * 40 + "ms" }}
            >
              <span className="corr-tk">{p.replace(".T", "")}</span>
              <span className="corr-name">{window.STOCKS[p].name}</span>
              <span className="corr-bar">
                <span className="corr-bar-bg">
                  <span
                    className="corr-bar-fill"
                    style={{
                      width: Math.abs(pct) + "%",
                      background: col,
                      marginLeft: c < 0 ? 50 - Math.abs(pct) / 2 + "%" : "50%",
                    }}
                  />
                  <span className="corr-bar-zero" />
                </span>
              </span>
              <span className="corr-val" style={{ color: col }}>
                {c.toFixed(2)}
              </span>
            </button>
          );
        })}
      </div>
    </div>
  );
}

// ——————————————— Theme chips (cross-theme overlap)
function ThemeBelt({ center, activeThemes, setActiveThemes }) {
  const myThemes = window.THEMES.filter((th) =>
    (th.tickers || []).includes(center),
  );
  return (
    <div className="theme-belt">
      {window.THEMES.map((th) => {
        const mine = myThemes.some((m) => m.id === th.id);
        const active = activeThemes.has(th.id);
        return (
          <button
            key={th.id}
            className={
              "theme-chip" + (mine ? " mine" : "") + (active ? " active" : "")
            }
            style={{ "--tc": th.color }}
            onClick={() => {
              const s = new Set(activeThemes);
              s.has(th.id) ? s.delete(th.id) : s.add(th.id);
              setActiveThemes(s);
            }}
          >
            <span className="tc-dot" />
            <span className="tc-n">{th.name}</span>
            <span className="tc-count">{th.tickers.length}</span>
          </button>
        );
      })}
    </div>
  );
}

// ——————————————— Watchlist / Sidebar
function Sidebar({ active, setActive, query, setQuery, lang, mobileOpen }) {
  const [wlTf, setWlTf] = uS("D"); // D | W | M
  const list = uM(() => {
    const q = query.trim().toLowerCase();
    let t = Object.keys(window.STOCKS);
    if (q)
      t = t.filter(
        (tk) =>
          tk.toLowerCase().includes(q) ||
          (window.STOCKS[tk].name || "").toLowerCase().includes(q) ||
          (window.STOCKS[tk].jp || "").includes(q),
      );
    return t.sort(
      (a, b) => Math.abs(window.STOCKS[b].chg) - Math.abs(window.STOCKS[a].chg),
    );
  }, [query]);
  // derive per-timeframe change + sparkline window
  const tfConfig = {
    D: { slice: 5, jp: "日" },
    W: { slice: 7, jp: "週" },
    M: { slice: 22, jp: "月" },
  };
  const cfg = tfConfig[wlTf];
  function tfChange(tk) {
    const prices = window.PRICES[tk];
    if (!prices || prices.length < cfg.slice + 1) return window.STOCKS[tk].chg;
    const last =
      typeof prices[prices.length - 1] === "number"
        ? prices[prices.length - 1]
        : prices[prices.length - 1].c;
    const prev =
      typeof prices[prices.length - 1 - cfg.slice] === "number"
        ? prices[prices.length - 1 - cfg.slice]
        : prices[prices.length - 1 - cfg.slice].c;
    return prev ? ((last - prev) / prev) * 100 : 0;
  }
  return (
    <aside className={"side" + (mobileOpen ? " mobile-open" : "")}>
      <div className="brand">
        <div className="brand-mark">
          <svg viewBox="0 0 24 24" width="22" height="22">
            <circle
              cx="12"
              cy="12"
              r="10"
              fill="none"
              stroke="currentColor"
              strokeWidth="1.5"
            />
            <circle cx="12" cy="12" r="3.5" fill="currentColor" />
            <circle cx="20" cy="6" r="1.8" fill="currentColor" />
            <circle cx="4" cy="18" r="1.4" fill="currentColor" opacity="0.6" />
            <line
              x1="12"
              y1="12"
              x2="20"
              y2="6"
              stroke="currentColor"
              strokeWidth="1"
            />
            <line
              x1="12"
              y1="12"
              x2="4"
              y2="18"
              stroke="currentColor"
              strokeWidth="1"
              opacity="0.5"
            />
          </svg>
        </div>
        <div>
          <div className="brand-n">
            Kairos<span className="brand-dot">·</span>JP
          </div>
          <div className="brand-s">
            {lang === "jp" ? "株式調査プラットフォーム" : "equity intelligence"}
          </div>
        </div>
      </div>
      <div className="search">
        <svg
          width="14"
          height="14"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          strokeWidth="2"
        >
          <circle cx="11" cy="11" r="7" />
          <line x1="21" y1="21" x2="16.5" y2="16.5" />
        </svg>
        <input
          placeholder={
            lang === "jp" ? "銘柄 / テーマを検索" : "Search ticker, name, theme"
          }
          value={query}
          onChange={(e) => setQuery(e.target.value)}
        />
        <span className="kbd">⌘K</span>
      </div>
      <div className="side-sec">
        <div className="side-sec-h">
          <span>{lang === "jp" ? "ウォッチリスト" : "Watchlist"}</span>
          <div className="wl-tf">
            {["D", "W", "M"].map((tf) => (
              <button
                key={tf}
                className={"wl-tf-b" + (wlTf === tf ? " on" : "")}
                onClick={() => setWlTf(tf)}
              >
                {tf}
              </button>
            ))}
          </div>
        </div>
        <div className="side-list">
          {list.slice(0, 14).map((t) => {
            const s = window.STOCKS[t];
            const chg = tfChange(t);
            const up = chg >= 0;
            const sliceLen = Math.max(6, cfg.slice * 2); // show a bit more context in sparkline
            return (
              <button
                key={t}
                className={"side-row" + (t === active ? " on" : "")}
                onClick={() => setActive(t)}
              >
                <div className="side-row-l">
                  <div className="side-row-t">{t.replace(".T", "")}</div>
                  <div className="side-row-n">
                    {lang === "jp" ? s.jp : s.name}
                  </div>
                </div>
                <div className="side-row-r">
                  <Sparkline
                    data={window.pcs(window.PRICES[t])?.slice(-sliceLen)}
                    width={56}
                    height={20}
                    glow={false}
                  />
                  <div
                    className="side-row-c"
                    style={{
                      color: up ? "oklch(78% 0.16 155)" : "oklch(68% 0.18 25)",
                    }}
                  >
                    {fmtPct(chg)}
                  </div>
                </div>
              </button>
            );
          })}
        </div>
      </div>
      <div className="side-foot">
        <div className="mkt-row">
          <span>NIKKEI 225</span>
          <span className="up">41,284.20 +0.62%</span>
        </div>
        <div className="mkt-row">
          <span>TOPIX</span>
          <span className="up">2,908.55 +0.41%</span>
        </div>
        <div className="mkt-row">
          <span>USD/JPY</span>
          <span className="down">152.18 −0.18%</span>
        </div>
      </div>
    </aside>
  );
}

// ——————————————— Top bar (ticker header)
function TickerHeader({ ticker, lang }) {
  const s = window.STOCKS[ticker];
  if (!s) return null;
  const up = s.chg >= 0;
  const themes = themesForTicker(ticker);
  return (
    <div className="tk-head">
      <div className="tk-l">
        <div className="tk-sym">
          <span className="tk-code">{ticker.replace(".T", "")}</span>
          <span className="tk-exch">.T</span>
        </div>
        <div className="tk-main">
          <h1 className="tk-name">{lang === "jp" ? s.jp || s.name : s.name}</h1>
          <div className="tk-meta">
            <span>{lang === "jp" ? s.name : s.jp || s.name}</span>
            <span className="dot">·</span>
            <span>{s.sector}</span>
            <span className="dot">·</span>
            <span>
              {lang === "jp" ? "時価総額" : "Market cap"} ¥
              {((s.mcap || 0) / 1000).toFixed(2)}T
            </span>
          </div>
        </div>
      </div>
      <div className="tk-r">
        <div className="tk-price">
          <div className="tk-price-v">{fmtPrice(s.price)}</div>
          <div className={"tk-price-c " + (up ? "up" : "down")}>
            <svg width="12" height="12" viewBox="0 0 12 12">
              {up ? (
                <path d="M2 8 L6 3 L10 8 Z" fill="currentColor" />
              ) : (
                <path d="M2 4 L6 9 L10 4 Z" fill="currentColor" />
              )}
            </svg>
            {fmtPct(s.chg)}{" "}
            <span className="muted">
              {up ? "+" : ""}
              {((s.price * s.chg) / 100).toFixed(0)}
            </span>
          </div>
        </div>
        <div className="tk-theme-row">
          {themes.map((th) => (
            <span key={th.id} className="tk-theme" style={{ "--tc": th.color }}>
              <span className="tc-dot" />
              {th.name}
            </span>
          ))}
        </div>
      </div>
    </div>
  );
}

// ——————————————— Tweaks panel
function TweaksPanel({ tweaks, setTweaks, visible }) {
  if (!visible) return null;
  const opt = (k, v, label) => (
    <button
      className={tweaks[k] === v ? "on" : ""}
      onClick={() => setTweaks({ ...tweaks, [k]: v })}
    >
      {label}
    </button>
  );
  return (
    <div className="tweaks">
      <div className="tweaks-h">Tweaks</div>
      <div className="tweaks-row">
        <label>Layout</label>
        <div className="seg">
          {opt("graphLayout", "force", "Force")}
          {opt("graphLayout", "radial", "Radial")}
          {opt("graphLayout", "hierarchy", "Value chain")}
        </div>
      </div>
      <div className="tweaks-row">
        <label>Theme</label>
        <div className="seg">
          {opt("colorTheme", "midnight", "Midnight")}
          {opt("colorTheme", "graphite", "Graphite")}
          {opt("colorTheme", "ink", "Ink (light)")}
        </div>
      </div>
      <div className="tweaks-row">
        <label>Motion</label>
        <div className="seg">
          {opt("animIntensity", "calm", "Calm")}
          {opt("animIntensity", "normal", "Normal")}
          {opt("animIntensity", "energetic", "Energetic")}
        </div>
      </div>
      <div className="tweaks-row">
        <label>Density</label>
        <div className="seg">
          {opt("density", "compact", "Compact")}
          {opt("density", "comfortable", "Comfort")}
          {opt("density", "spacious", "Spacious")}
        </div>
      </div>
      <div className="tweaks-row">
        <label>Language</label>
        <div className="seg">
          {opt("language", "en", "EN")}
          {opt("language", "jp", "日本語")}
        </div>
      </div>
    </div>
  );
}

window.ValueChainStrip = ValueChainStrip;
window.NewsPanel = NewsPanel;
window.CorrelationGrid = CorrelationGrid;
window.ThemeBelt = ThemeBelt;
window.Sidebar = Sidebar;
window.TickerHeader = TickerHeader;
window.TweaksPanel = TweaksPanel;

// ——————————————— Stock Brief Panel
// Compact "what matters about this ticker" block — thesis + 4 key metrics + analyst split
// Fills the empty space below chart-stats in the left column of the hero grid.
function StockBriefPanel({ ticker, lang }) {
  const meta = window.STOCK_META && window.STOCK_META[ticker];
  const stock = window.STOCKS[ticker];
  if (!stock) return null;

  // Themes this ticker belongs to (top 3, colored chips)
  const themes = window.THEMES.filter((th) =>
    (th.tickers || []).includes(ticker),
  ).slice(0, 4);

  // Direct supply-chain relationships (degree)
  const upstream = window.LINKS.filter((l) => l.to === ticker).length;
  const downstream = window.LINKS.filter((l) => l.from === ticker).length;

  // If we don't have meta, fall back to a compact structural card
  if (!meta) {
    return (
      <div className="brief">
        <div className="brief-head">
          <div className="brief-h-l">
            <div className="brief-lbl">
              {lang === "jp" ? "スナップショット" : "Snapshot"}
            </div>
            <div className="brief-pos">
              {stock.sector} ·{" "}
              {lang === "jp" ? stock.jp || stock.name : stock.name}
            </div>
          </div>
        </div>
        <div className="brief-metrics">
          <div className="brief-m">
            <span>{lang === "jp" ? "時価総額" : "Mkt Cap"}</span>
            <b>¥{((stock.mcap || 0) / 10).toFixed(1)}T</b>
          </div>
          <div className="brief-m">
            <span>{lang === "jp" ? "セクター" : "Sector"}</span>
            <b>{stock.sector}</b>
          </div>
          <div className="brief-m">
            <span>{lang === "jp" ? "上流" : "Upstream"}</span>
            <b>{upstream}</b>
          </div>
          <div className="brief-m">
            <span>{lang === "jp" ? "下流" : "Downstream"}</span>
            <b>{downstream}</b>
          </div>
        </div>
        {themes.length > 0 && (
          <div className="brief-themes">
            {themes.map((th) => (
              <span
                key={th.id}
                className="brief-theme"
                style={{
                  color: th.color,
                  borderColor: `color-mix(in oklch, ${th.color} 40%, transparent)`,
                }}
              >
                <span className="tc-dot" style={{ background: th.color }} />
                {lang === "jp" ? th.jp : th.name}
              </span>
            ))}
          </div>
        )}
      </div>
    );
  }

  const a = meta.analyst || { buy: 0, hold: 0, sell: 0, target: 0 };
  const totalAnalysts = (a.buy || 0) + (a.hold || 0) + (a.sell || 0);
  const upside = ((a.target - stock.price) / (stock.price || 1)) * 100;

  return (
    <div className="brief">
      <div className="brief-head">
        <div className="brief-h-l">
          <div className="brief-lbl">
            {lang === "jp" ? "投資テーゼ" : "Thesis"}
          </div>
          {meta.positioning && (
            <div className="brief-pos">{meta.positioning}</div>
          )}
        </div>
        {themes.length > 0 && (
          <div className="brief-themes brief-themes-inline">
            {themes.slice(0, 3).map((th) => (
              <span
                key={th.id}
                className="brief-theme"
                style={{
                  color: th.color,
                  borderColor: `color-mix(in oklch, ${th.color} 40%, transparent)`,
                }}
              >
                <span className="tc-dot" style={{ background: th.color }} />
                {lang === "jp" ? th.jp : th.name}
              </span>
            ))}
          </div>
        )}
      </div>

      <p className="brief-thesis">{meta.thesis}</p>

      <div className="brief-metrics">
        <div className="brief-m">
          <span>{lang === "jp" ? "収益成長" : "Rev growth"}</span>
          <b className={(meta.revGrowth || 0) >= 0 ? "up" : "down"}>
            {(meta.revGrowth || 0) >= 0 ? "+" : ""}
            {(meta.revGrowth ?? 0).toFixed(1)}%
          </b>
        </div>
        <div className="brief-m">
          <span>{lang === "jp" ? "営業利益率" : "Op margin"}</span>
          <b>{(meta.opMargin ?? 0).toFixed(1)}%</b>
        </div>
        <div className="brief-m">
          <span>ROE</span>
          <b>{(meta.roe ?? 0).toFixed(1)}%</b>
        </div>
        <div className="brief-m">
          <span>P/E · PEG</span>
          <b>
            {(meta.pe ?? 0).toFixed(1)} <span className="brief-sep">·</span>{" "}
            {(meta.peg ?? 0).toFixed(1)}
          </b>
        </div>
      </div>

      {totalAnalysts > 0 && (
        <div className="brief-analysts">
          <div className="brief-a-l">
            <div className="brief-a-lbl">
              {lang === "jp" ? "アナリスト" : "Analysts"}
            </div>
            <div className="brief-a-bar">
              <span
                className="brief-a-buy"
                style={{ flex: a.buy || 0 }}
                title={`${a.buy} buy`}
              >
                {a.buy}
              </span>
              <span
                className="brief-a-hold"
                style={{ flex: a.hold || 0 }}
                title={`${a.hold} hold`}
              >
                {a.hold}
              </span>
              <span
                className="brief-a-sell"
                style={{ flex: a.sell || 0 }}
                title={`${a.sell} sell`}
              >
                {a.sell}
              </span>
            </div>
            <div className="brief-a-legend">
              <span>
                <i className="buy" />
                Buy {a.buy}
              </span>
              <span>
                <i className="hold" />
                Hold {a.hold}
              </span>
              <span>
                <i className="sell" />
                Sell {a.sell}
              </span>
            </div>
          </div>
          {a.target > 0 && (
            <div className="brief-a-tgt">
              <div className="brief-a-tgt-lbl">
                {lang === "jp" ? "目標株価" : "Target"}
              </div>
              <div className="brief-a-tgt-v">{fmtPrice(a.target)}</div>
              <div className={"brief-a-tgt-u " + (upside >= 0 ? "up" : "down")}>
                {upside >= 0 ? "↑" : "↓"} {Math.abs(upside).toFixed(1)}%
              </div>
            </div>
          )}
        </div>
      )}
    </div>
  );
}

window.StockBriefPanel = StockBriefPanel;
