// components.jsx — Terminal Edition 共用 React 元件（2026-05-10 新增）
//
// 來源：claude.ai Design 「盤勢台 · 散戶情報終端」設計檔。
// 目的：給未來重寫 page-*.jsx 時用統一的 panel/chip/btn class + Spark/BigSpark/AiScore 等元件。
// 與 primitives.jsx 並存：primitives.jsx 是舊 Editorial 版本（Card/Sparkline/ChangeChip），保留供舊頁用。
//                        components.jsx 是新終端版本（Spark/BigSpark/StockTag 等），供新頁用。
//
// 不重複定義 fmt / sign / dirColor — 已在 primitives.jsx mount 到 window。
// 額外補 fmtInt / fmtSign / pctClass。

(function(){
  // ── Format helpers ────────────────────────────────────────────────
  function fmt(n, dec = 2) {
    if (n == null || isNaN(n)) return '—';
    return Number(n).toLocaleString('en-US', { minimumFractionDigits: dec, maximumFractionDigits: dec });
  }
  function fmtInt(n) {
    if (n == null) return '—';
    return Math.round(n).toLocaleString('en-US');
  }
  function fmtSign(n, dec = 2) {
    if (n == null) return '—';
    const s = n > 0 ? '+' : '';
    return s + fmt(n, dec);
  }
  function pctClass(n) {
    if (n > 0) return 'up';
    if (n < 0) return 'down';
    return 'flat';
  }
  // 不覆寫 primitives.jsx 既有的 window.fmt / window.sign，僅補強。
  if (!window.fmtInt)   window.fmtInt   = fmtInt;
  if (!window.fmtSign)  window.fmtSign  = fmtSign;
  if (!window.pctClass) window.pctClass = pctClass;

  // ── Sparkline（小折線，sidebar / 表格用）────────────────────────
  function Spark({ data, width = 80, height = 24, up }) {
    if (!data || !data.length) return null;
    const min = Math.min(...data), max = Math.max(...data);
    const range = max - min || 1;
    const step = width / (data.length - 1);
    const points = data.map((v, i) => `${i * step},${height - ((v - min) / range) * height}`).join(' L');
    const color = up ? 'var(--up)' : up === false ? 'var(--down)' : 'var(--fg-2)';
    return (
      <svg className="spark" width={width} height={height} viewBox={`0 0 ${width} ${height}`}>
        <path d={`M${points}`} stroke={color} fill="none" />
      </svg>
    );
  }

  // ── Big sparkline（指數卡用，含 area fill）──────────────────────
  function BigSpark({ data, width = '100%', height = 56, up }) {
    if (!data || !data.length) return null;
    const min = Math.min(...data), max = Math.max(...data);
    const range = max - min || 1;
    const w = 320, h = height;
    const step = w / (data.length - 1);
    const pts = data.map((v, i) => [i * step, h - ((v - min) / range) * (h - 6) - 3]);
    const path = 'M' + pts.map(p => p.join(',')).join(' L');
    const area = path + ` L${w},${h} L0,${h} Z`;
    const color = up ? 'var(--up)' : up === false ? 'var(--down)' : 'var(--fg-2)';
    const fill  = up ? 'var(--up-bg)' : up === false ? 'var(--down-bg)' : 'rgba(138,146,163,.1)';
    return (
      <svg width={width} height={h} viewBox={`0 0 ${w} ${h}`} preserveAspectRatio="none" style={{ display: 'block' }}>
        <path d={area} fill={fill} />
        <path d={path} stroke={color} strokeWidth="1.5" fill="none" />
      </svg>
    );
  }

  // ── Stock 代號 + 名稱 cell ──────────────────────────────────────
  function StockTag({ code, name, big }) {
    return (
      <span style={{ display: 'inline-flex', alignItems: 'baseline', gap: 8 }}>
        <span className="mono" style={{ color: 'var(--fg-0)', fontWeight: 500, fontSize: big ? 14 : 12.5 }}>{code}</span>
        {name && <span style={{ color: 'var(--fg-1)', fontSize: big ? 13 : 12 }}>{name}</span>}
      </span>
    );
  }

  // ── AI 分數 pip（左側色條 + mono 數字）─────────────────────────
  function AiScore({ score }) {
    const color = score >= 80 ? 'var(--accent-2)' : score >= 65 ? 'var(--accent)' : 'var(--fg-2)';
    return (
      <span className="mono" style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}>
        <span style={{ width: 4, height: 16, background: color, borderRadius: 1 }}></span>
        <span style={{ color, fontWeight: 600 }}>{score}</span>
      </span>
    );
  }

  // ── Bar fill（0-100 進度條）────────────────────────────────────
  function BarFill({ value, color = 'var(--accent-2)', height = 4 }) {
    return (
      <div style={{ width: '100%', height, background: 'var(--bg-3)', borderRadius: 2, overflow: 'hidden' }}>
        <div style={{ width: `${Math.min(100, Math.max(0, value))}%`, height: '100%', background: color, transition: 'width .3s' }}></div>
      </div>
    );
  }

  // ── Empty state（mono 注釋風）──────────────────────────────────
  function Empty({ title, hint }) {
    return (
      <div style={{ padding: '40px 20px', textAlign: 'center', color: 'var(--fg-3)' }}>
        <div className="mono" style={{ fontSize: 11, letterSpacing: '.08em', color: 'var(--fg-3)', marginBottom: 6 }}>// {title}</div>
        {hint && <div style={{ fontSize: 12 }}>{hint}</div>}
      </div>
    );
  }

  // ── Section header（serif 大標 + mono 副標）────────────────────
  function SectionHead({ title, sub, right }) {
    return (
      <div style={{ display: 'flex', alignItems: 'flex-end', justifyContent: 'space-between', padding: '20px 20px 12px', borderBottom: '1px solid var(--line)' }}>
        <div>
          <div className="title-serif" style={{ fontSize: 18, color: 'var(--fg-0)' }}>{title}</div>
          {sub && <div className="mono" style={{ fontSize: 11, color: 'var(--fg-3)', marginTop: 4, letterSpacing: '.05em' }}>{sub}</div>}
        </div>
        {right && <div>{right}</div>}
      </div>
    );
  }

  // ── Stat block（指數卡分欄用）──────────────────────────────────
  function Stat({ label, value, cls }) {
    return (
      <div style={{ padding: '8px 14px', borderRight: '1px solid var(--line)' }}>
        <div className="mono" style={{ fontSize: 9.5, color: 'var(--fg-3)', letterSpacing: '.06em' }}>{label}</div>
        <div className={`num ${cls || ''}`} style={{ fontSize: 13, marginTop: 2, color: cls ? '' : 'var(--fg-1)' }}>{value}</div>
      </div>
    );
  }

  // ── Three-light signal cell（多 / 中性 / 空）──────────────────
  // level: 'green' | 'yellow' | 'red'
  function SignalLightCell({ level, label, note, score, max }) {
    const map = {
      green:  { c: 'var(--signal-g)', bg: 'rgba(15,153,98,.06)',  bd: 'rgba(15,153,98,.25)' },
      yellow: { c: 'var(--signal-y)', bg: 'rgba(217,148,22,.06)', bd: 'rgba(217,148,22,.30)' },
      red:    { c: 'var(--signal-r)', bg: 'rgba(217,45,63,.06)',  bd: 'rgba(217,45,63,.25)' },
    };
    const cm = map[level] || map.yellow;
    return (
      <div style={{ background: cm.bg, border: `1px solid ${cm.bd}`, borderRadius: 4, padding: '14px 16px' }}>
        <div style={{ fontSize: 12, color: 'var(--fg-2)', marginBottom: 10 }}>{label}</div>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 8 }}>
          <span style={{ width: 10, height: 10, borderRadius: '50%', background: cm.c, boxShadow: `0 0 0 3px ${cm.bg}` }}></span>
          <span className="mono" style={{ fontSize: 18, fontWeight: 700, color: cm.c, letterSpacing: '.04em' }}>
            {level.toUpperCase()}
          </span>
        </div>
        {note && <div style={{ fontSize: 12, color: 'var(--fg-1)' }}>{note}</div>}
        {score != null && (
          <div className="mono" style={{ fontSize: 11, color: 'var(--fg-3)', marginTop: 4 }}>
            分數 {score}{max != null ? `/${max}` : ''}
          </div>
        )}
      </div>
    );
  }

  // ── 暴露至 window 給 page-*.jsx 用 ─────────────────────────────
  Object.assign(window, {
    Spark, BigSpark, StockTag, AiScore, BarFill,
    Empty, SectionHead, Stat, SignalLightCell,
  });
})();
