/* Page 2 — Stock Detail / Chart Workspace */
const { Icon, fmt, fmtVol, sign } = window.UI;

/* ---------- Tool rail spec — TradingView 風左側工具列 + flyout ----------
 * impl: true  → 接到 chart toolMode / action，能實際運作
 * impl: false → UI 占位，顯示「即將推出」、disabled
 */
const TOOL_GROUPS = [
  { id: "cursor", icon: "crosshair", label: "十字線", tool: "crosshair", standalone: true, impl: true },

  { id: "lines", icon: "draw", label: "趨勢線工具",
    items: [
      { id: "tline",    icon: "draw",  label: "趨勢線",      tool: "tline", impl: true, hint: "Alt+T" },
      { id: "ray",      icon: "draw",  label: "射線" },
      { id: "extend",   icon: "draw",  label: "延伸線" },
      { id: "hline",    icon: "ruler", label: "水平價位線",  tool: "hline", impl: true, hint: "H" },
      { id: "vline",    icon: "ruler", label: "垂直時間線" },
      { id: "parallel", icon: "draw",  label: "平行通道" },
    ],
  },

  { id: "fib", icon: "fib", label: "江恩和斐波那契工具",
    items: [
      { id: "fib-retrace",  icon: "fib", label: "斐波那契回撤",         section: "斐波那契", hint: "Alt+F" },
      { id: "fib-extend",   icon: "fib", label: "斐波那契趨勢擴展",     section: "斐波那契" },
      { id: "fib-channel",  icon: "fib", label: "斐波那契通道",         section: "斐波那契" },
      { id: "fib-timezone", icon: "fib", label: "斐波那契時區",         section: "斐波那契" },
      { id: "fib-fan",      icon: "fib", label: "斐波那契速度阻力扇",   section: "斐波那契" },
      { id: "fib-wedge",    icon: "fib", label: "斐波那契楔形",         section: "斐波那契" },
      { id: "gann-box",     icon: "shapes", label: "江恩箱",            section: "江恩" },
      { id: "gann-square",  icon: "shapes", label: "江恩正方",          section: "江恩" },
      { id: "gann-fan",     icon: "draw",   label: "江恩扇",            section: "江恩" },
    ],
  },

  { id: "patterns", icon: "trending-up", label: "型態工具",
    items: [
      { id: "abcd",            icon: "trending-up", label: "ABCD" },
      { id: "elliott",         icon: "trending-up", label: "Elliott Wave" },
      { id: "head-shoulders",  icon: "trending-up", label: "頭肩型態" },
      { id: "triangle-pattern",icon: "trending-up", label: "三角型態" },
    ],
  },

  { id: "positions", icon: "list", label: "進出場區",
    items: [
      { id: "long-pos",    icon: "trending-up",   label: "做多區" },
      { id: "short-pos",   icon: "trending-down", label: "做空區" },
      { id: "price-range", icon: "ruler",         label: "價格範圍" },
    ],
  },

  { id: "brush", icon: "brush", label: "筆刷與註解",
    items: [
      { id: "brush",      icon: "brush",        label: "筆刷",     section: "筆刷" },
      { id: "highlight",  icon: "brush",        label: "螢光筆",   section: "筆刷" },
      { id: "arrow-mark", icon: "trending-up",  label: "箭頭標記", section: "箭頭" },
      { id: "arrow-up",   icon: "arrow-up",     label: "上箭頭",   section: "箭頭" },
      { id: "arrow-down", icon: "arrow-down",   label: "下箭頭",   section: "箭頭" },
      { id: "rect",       icon: "shapes",       label: "矩形",     section: "形狀", hint: "Alt+Shift+R" },
      { id: "circle",     icon: "dot",          label: "圓",       section: "形狀" },
      { id: "triangle",   icon: "trending-up",  label: "三角形",   section: "形狀" },
    ],
  },

  { id: "text", icon: "text-tool", label: "文字工具",
    items: [
      { id: "text",  icon: "text-tool", label: "文字" },
      { id: "label", icon: "list",      label: "標籤" },
      { id: "note",  icon: "news",      label: "註解" },
    ],
  },

  { id: "emoji", icon: "smile", label: "表情符號",
    items: [
      { id: "emoji", icon: "smile", label: "貼圖" },
      { id: "flag",  icon: "alert", label: "旗幟" },
    ],
  },

  { id: "ruler-group", icon: "percent", label: "測量工具",
    items: [
      { id: "ruler",       icon: "percent", label: "測漲跌 %", tool: "ruler", impl: true },
      { id: "date-range",  icon: "ruler",   label: "日期範圍" },
      { id: "data-window", icon: "list",    label: "資料視窗" },
    ],
  },

  { id: "zoom-group", icon: "zoom-in", label: "縮放與檢視",
    items: [
      { id: "reset",      icon: "refresh",  label: "重設縮放" },
      { id: "screenshot", icon: "camera",   label: "截圖" },
      { id: "zoom-in",    icon: "zoom-in",  label: "放大" },
      { id: "zoom-out",   icon: "zoom-out", label: "縮小" },
    ],
  },

  { sep: true },

  { id: "magnet",    icon: "magnet", label: "磁吸（自動對齊高低點）", toggle: true, standalone: true },
  { id: "lock-draw", icon: "lock",   label: "鎖定所有繪圖",           toggle: true, standalone: true },
  { id: "hide-draw", icon: "eye",    label: "隱藏所有繪圖",           toggle: true, standalone: true },
  { id: "fx",        icon: "fx",     label: "函數視圖",               toggle: true, standalone: true },

  { sep: true },

  { id: "trash", icon: "trash", label: "刪除所有繪圖", action: "clear-drawings", standalone: true, impl: true },
];

const ToolFlyout = ({ group, toolMode, onItemClick }) => {
  /* 依 section 分群（沒指定 section 的歸 group.label） */
  const sectioned = React.useMemo(() => {
    const out = [];
    group.items.forEach(it => {
      const name = it.section || group.label;
      let bucket = out.find(b => b.name === name);
      if (!bucket) { bucket = { name, items: [] }; out.push(bucket); }
      bucket.items.push(it);
    });
    return out;
  }, [group]);

  return (
    <div className="tool-flyout surface" role="menu" aria-label={group.label}>
      {sectioned.map((sec, si) => (
        <div key={si} className="tool-flyout-section">
          <div className="tool-flyout-section-title dim">{sec.name}</div>
          {sec.items.map(it => {
            const targetTool = it.tool || it.id;
            const isActive = targetTool === toolMode;
            return (
              <button key={it.id}
                className={"tool-flyout-item" + (isActive ? " active" : "")}
                title={it.label}
                onClick={() => onItemClick(it)}>
                <Icon name={it.icon || "dot"} size={16}/>
                <span className="tool-flyout-label">{it.label}</span>
                {it.hint && <span className="tool-flyout-hint mono">{it.hint}</span>}
              </button>
            );
          })}
        </div>
      ))}
    </div>
  );
};

const ChartToolRail = ({ toolMode, setToolMode, drawings, clearDrawings, toggles, setToggle, chartActions }) => {
  const [openId, setOpenId] = React.useState(null);
  const wrapRef = React.useRef(null);

  React.useEffect(() => {
    if (!openId) return;
    const onDown = (e) => {
      if (wrapRef.current && !wrapRef.current.contains(e.target)) setOpenId(null);
    };
    document.addEventListener("mousedown", onDown);
    return () => document.removeEventListener("mousedown", onDown);
  }, [openId]);

  const isGroupActive = (g) => {
    if (g.standalone && g.tool) return toolMode === g.tool;
    if (g.standalone && g.toggle) return !!toggles[g.id];
    if (g.items) return g.items.some(it => (it.tool || it.id) === toolMode);
    return false;
  };

  const runAction = (actionId) => {
    if (!chartActions) return;
    const map = { reset: "reset", screenshot: "screenshot", "zoom-in": "zoomIn", "zoom-out": "zoomOut" };
    const fn = chartActions.current && chartActions.current[map[actionId] || actionId];
    if (typeof fn === "function") fn();
  };

  const onMainClick = (g) => {
    if (g.standalone && g.tool) {
      setToolMode(toolMode === g.tool ? "crosshair" : g.tool);
      return;
    }
    if (g.standalone && g.toggle) {
      setToggle(g.id, !toggles[g.id]);
      return;
    }
    if (g.standalone && g.action === "clear-drawings") {
      clearDrawings();
      return;
    }
    if (g.items) {
      setOpenId(openId === g.id ? null : g.id);
    }
  };

  const onItemClick = (it) => {
    /* action items（reset / screenshot / zoom-in / zoom-out） */
    if (["reset", "screenshot", "zoom-in", "zoom-out"].includes(it.id)) {
      runAction(it.id);
      setOpenId(null);
      return;
    }
    if (it.id === "data-window") {
      setToggle("data-window", !toggles["data-window"]);
      setOpenId(null);
      return;
    }
    /* shape 工具：用 id 當 tool fallback */
    const tool = it.tool || it.id;
    setToolMode(toolMode === tool ? "crosshair" : tool);
    setOpenId(null);
  };

  const hasDrawings = (drawings.hlines?.length > 0 || drawings.tlines?.length > 0);

  return (
    <div className="chart-tool-rail" ref={wrapRef} role="toolbar" aria-label="圖表工具">
      {TOOL_GROUPS.map((g, i) => {
        if (g.sep) return <div key={"sep-" + i} className="tool-rail-sep"/>;
        if (g.id === "trash" && !hasDrawings) return null;
        const active = isGroupActive(g);
        const hasMenu = !!g.items;
        return (
          <div key={g.id} className="tool-rail-slot">
            <button
              className={"tool-rail-btn" + (active ? " active" : "") + (g.id === "trash" ? " danger" : "")}
              title={g.label}
              onClick={() => onMainClick(g)}>
              <Icon name={g.icon} size={16}/>
              {hasMenu && <span className="tool-rail-chevron" aria-hidden="true"/>}
            </button>
            {hasMenu && openId === g.id && (
              <ToolFlyout group={g} toolMode={toolMode} onItemClick={onItemClick}/>
            )}
          </div>
        );
      })}
    </div>
  );
};

const ChartPage = ({ symbol = "2330.TW", goChart }) => {
  const navList = React.useMemo(() => {
    try { return JSON.parse(sessionStorage.getItem("chart.navList") || "[]"); } catch { return []; }
  }, [symbol]);
  const navIdx = navList.indexOf(symbol);
  const prevSym = navIdx > 0 ? navList[navIdx - 1] : null;
  const nextSym = navIdx >= 0 && navIdx < navList.length - 1 ? navList[navIdx + 1] : null;
  const mockMeta = window.MOCK.WATCHLIST.find((s) => s.sym === symbol);
  const [resolvedName, setResolvedName] = React.useState(mockMeta ? mockMeta.name : null);

  /* mock 找不到時去 FinMind universe 查名稱 */
  React.useEffect(() => {
    if (mockMeta) { setResolvedName(mockMeta.name); return; }
    setResolvedName(null);
    if (!window.FinMind || !window.FinMind.getTaiwanUniverse) return;
    let cancelled = false;
    window.FinMind.getTaiwanUniverse()
      .then((list) => {
        if (cancelled) return;
        const found = list.find((s) => s.sym === symbol);
        if (found) setResolvedName(found.name);
      })
      .catch(() => {});
    return () => { cancelled = true; };
  }, [symbol, mockMeta]);

  const stock = mockMeta || {
    sym: symbol,
    name: resolvedName || symbol.replace(/\.TW$/, ""),
    market: symbol.endsWith(".TW") ? "TW" : "US",
    price: 0, chg: 0, pct: 0, vol: 0, volMa: 1, rsi: 50, ma: "Sideways", ts: "", spark: [],
  };
  const market = stock.market;
  const [tf, setTf] = React.useState("1D");
  const [range, setRange] = React.useState("3M");
  const [showMA, setShowMA] = React.useState({ 5: true, 10: true, 20: true, 60: true, 120: false, 240: false });
  const [showKC, setShowKC] = React.useState(true);
  const [kc, setKc] = React.useState({
    swing: true, dashed: true, connect: true, neckline: true, trend: true,
    waitSignal: true, longEntry: true, longExit: true, shortEntry: false, shortExit: false, maLabel: true,
  });
  const [indicators, setIndicators] = React.useState({ MA: true, EMA: false, MACD: false, RSI: true, KD: false, BB: false, VolMA: true });
  const [toolMode, setToolMode] = React.useState("crosshair");
  const [drawings, _setDrawings] = window.usePersistedState("chart.draw." + symbol, { hlines: [], tlines: [], shapes: [] });
  /* drawings 變動 history — 用 ref 不觸發 render，Cmd/Ctrl+Z 取出上一版 */
  const historyRef = React.useRef([]);
  const setDrawings = (next) => {
    historyRef.current.push(drawings);
    if (historyRef.current.length > 100) historyRef.current.shift();
    _setDrawings(next);
  };
  const clearDrawings = () => setDrawings({ hlines: [], tlines: [], shapes: [] });

  /* 切 symbol 時清掉 history（不同股票的繪圖不該互相 undo） */
  React.useEffect(() => { historyRef.current = []; }, [symbol]);

  /* Keyboard: Cmd/Ctrl+Z 還原 / Delete / Backspace 刪最後繪圖 */
  React.useEffect(() => {
    const onKey = (e) => {
      const tgt = e.target;
      if (tgt && (tgt.tagName === "INPUT" || tgt.tagName === "TEXTAREA" || tgt.isContentEditable)) return;
      if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "z" && !e.shiftKey) {
        e.preventDefault();
        if (historyRef.current.length === 0) return;
        const prev = historyRef.current.pop();
        _setDrawings(prev);
        return;
      }
      if (e.key === "Delete" || e.key === "Backspace") {
        const fn = chartActionRef.current && chartActionRef.current.deleteLast;
        if (typeof fn !== "function") return;
        const before = drawings;
        if (fn()) {
          /* deleteLast 走 onDrawingsChange → setDrawings → push history，已自動入 history */
          e.preventDefault();
        }
      }
    };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [drawings]);

  /* Rail toggles — 磁吸/鎖定/隱藏/fx/data-window */
  const [toggles, setToggles] = window.usePersistedState("chart.toggles." + symbol, {
    magnet: true, "lock-draw": false, "hide-draw": false, fx: false, "data-window": false,
  });
  const setToggle = (k, v) => setToggles((t) => ({ ...t, [k]: v }));
  const chartActionRef = React.useRef({});

  /* 觀察清單（與 page-watchlists 共用 localStorage key） */
  const [lists, setLists] = window.usePersistedState("watchlists", window.DEFAULT_WATCHLISTS || []);
  const [addOpen, setAddOpen] = React.useState(false);
  const addWrapRef = React.useRef(null);
  const inListCount = lists.filter((l) => l.syms.includes(symbol)).length;
  const toggleInList = (listId) => {
    setLists((ls) => ls.map((l) => {
      if (l.id !== listId) return l;
      const has = l.syms.includes(symbol);
      return { ...l, syms: has ? l.syms.filter((s) => s !== symbol) : [...l.syms, symbol] };
    }));
  };
  React.useEffect(() => {
    if (!addOpen) return;
    const onDown = (e) => {
      if (addWrapRef.current && !addWrapRef.current.contains(e.target)) setAddOpen(false);
    };
    document.addEventListener("mousedown", onDown);
    return () => document.removeEventListener("mousedown", onDown);
  }, [addOpen]);

  /* 真實資料：FinMind 抓日 K，失敗時 fallback 到 mock 並標示 */
  const [dataSource, setDataSource] = React.useState({ candles: null, source: "loading", error: null });

  React.useEffect(() => {
    let cancelled = false;
    setDataSource({ candles: null, source: "loading", error: null });
    window.FinMind.getCandles(symbol)
      .then((candles) => {
        if (cancelled) return;
        if (!candles.length) {
          setDataSource({
            candles: window.genCandles(120, symbol.charCodeAt(0), stock.price - 60),
            source: "mock",
            error: "FinMind 回傳空資料",
          });
          return;
        }
        setDataSource({ candles, source: "finmind", error: null });
      })
      .catch((err) => {
        if (cancelled) return;
        console.warn("[chart] FinMind 抓取失敗，fallback 到 mock：", err.message);
        setDataSource({
          candles: window.genCandles(120, symbol.charCodeAt(0), stock.price - 60),
          source: "mock",
          error: err.message,
        });
      });
    return () => { cancelled = true; };
  }, [symbol, stock.price]);

  const baseData = dataSource.candles;

  /* 即時報價（CF Worker proxy）— 盤中合併到最後一根 K */
  const [intraday, setIntraday] = React.useState(null);
  const [intradayErr, setIntradayErr] = React.useState(null);

  React.useEffect(() => {
    setIntraday(null);
    setIntradayErr(null);
    if (!window.Intraday || !window.Intraday.isEnabled()) return;
    const unsub = window.Intraday.subscribe(symbol, (q, err) => {
      if (err) { setIntradayErr(err.message); return; }
      setIntraday(q);
      setIntradayErr(null);
    });
    return unsub;
  }, [symbol]);

  /* 合併資料：今天的 quote 覆蓋當日那根 K；若資料最後一根不是今天則 append 一根 forming */
  const data = React.useMemo(() => {
    if (!baseData) return null;
    if (!intraday || intraday.price == null) return baseData;
    const ts = new Date(intraday.ts);
    const tsStr = ts.toISOString().slice(0, 10);
    const last = baseData[baseData.length - 1];
    const lastStr = last ? last.date.toISOString().slice(0, 10) : null;
    const live = {
      date: ts,
      o: intraday.open ?? (last ? last.c : intraday.price),
      h: intraday.high ?? intraday.price,
      l: intraday.low ?? intraday.price,
      c: intraday.price,
      v: intraday.vol ?? 0,
    };
    if (lastStr === tsStr) return [...baseData.slice(0, -1), live];
    return [...baseData, live];
  }, [baseData, intraday]);

  /* range / tf 套用：依 range 切時間區段、再依 tf 聚合（日/週/月 K） */
  const RANGE_DAYS = { "1M": 30, "3M": 90, "6M": 180, "1Y": 365, "3Y": 365 * 3, "5Y": 365 * 5 };

  const displayData = React.useMemo(() => {
    if (!data) return null;
    const rangeDays = RANGE_DAYS[range] || 180;
    const cutoff = new Date(Date.now() - rangeDays * 24 * 60 * 60 * 1000);
    const sliced = data.filter(c => c.date >= cutoff);
    const base = sliced.length ? sliced : data;
    if (tf === "1D") return base;
    const weekKey = (d) => {
      const x = new Date(d);
      const day = x.getDay();
      const diff = day === 0 ? -6 : 1 - day;
      x.setDate(x.getDate() + diff);
      return x.toISOString().slice(0, 10);
    };
    const monthKey = (d) => new Date(d).toISOString().slice(0, 7);
    const keyFn = tf === "1W" ? weekKey : monthKey;
    const groups = [];
    let cur = null;
    for (const c of base) {
      const k = keyFn(c.date);
      if (!cur || cur._k !== k) {
        cur = { _k: k, date: c.date, o: c.o, h: c.h, l: c.l, c: c.c, v: c.v };
        groups.push(cur);
      } else {
        cur.h = Math.max(cur.h, c.h);
        cur.l = Math.min(cur.l, c.l);
        cur.c = c.c;
        cur.v += c.v;
        cur.date = c.date;
      }
    }
    return groups.map(({ _k, ...r }) => r);
  }, [data, range, tf]);

  const isFormingLast = intraday != null;
  const formingIdx = (isFormingLast && displayData) ? displayData.length - 1 : null;

  const last = data && data.length ? data[data.length - 1] : null;
  const prev = data && data.length > 1 ? data[data.length - 2] : null;

  /* KC v17 純函式運算（real data 時用，mock 時讓 chart.jsx 用 KC_MARKERS）
   * 只在 tf=日 K 跑；週/月 K 的 KC 規格與日 K 不同，先不算 */
  const kcEvents = React.useMemo(() => {
    if (!displayData || displayData.length < 25) return [];
    if (dataSource.source !== "finmind") return [];
    if (tf !== "1D") return [];
    return window.KCStructure ? window.KCStructure.compute(displayData) : [];
  }, [displayData, dataSource.source, tf]);

  /* 各 MA 最新值（取 displayData 最後 N 根 close 的平均） */
  const maLast = React.useMemo(() => {
    if (!displayData || !displayData.length) return {};
    const out = {};
    for (const p of [5, 10, 20, 60, 120, 240]) {
      if (displayData.length >= p) {
        const slice = displayData.slice(-p);
        out[p] = slice.reduce((s, c) => s + c.c, 0) / p;
      } else {
        out[p] = null;
      }
    }
    return out;
  }, [displayData]);

  const MA_COLORS = { 5: "#F6C85F", 10: "#F87171", 20: "#4DA3FF", 60: "#A78BFA", 120: "#22C55E", 240: "#94A3B8" };

  /* KC 訊號摘要 — 從 kcEvents 動態萃取 */
  const kcSummary = React.useMemo(() => {
    if (!kcEvents || !kcEvents.length || !displayData || !displayData.length) return null;
    const fmtMD = (d) => {
      if (!d) return "—";
      const dt = d instanceof Date ? d : new Date(d);
      return `${String(dt.getMonth()+1).padStart(2,"0")}-${String(dt.getDate()).padStart(2,"0")}`;
    };
    const ev = kcEvents.map(e => ({ ...e, _date: displayData[e.idx]?.date }));
    const reversed = [...ev].reverse();

    const lastTrend = reversed.find(e => e.type === "trend");
    const lastLongEntry  = reversed.find(e => e.type === "long-entry");
    const lastLongExit   = reversed.find(e => e.type === "long-exit");
    const lastShortEntry = reversed.find(e => e.type === "short-entry");
    const lastShortExit  = reversed.find(e => e.type === "short-exit");
    const lastWait       = reversed.find(e => e.type === "wait-long" || e.type === "wait-short");

    let position = "無部位", positionCls = "dim";
    if (lastLongEntry && (!lastLongExit || lastLongExit.idx < lastLongEntry.idx)) {
      position = "多單持有"; positionCls = "up";
    } else if (lastShortEntry && (!lastShortExit || lastShortExit.idx < lastShortEntry.idx)) {
      position = "空單持有"; positionCls = "down";
    }

    let waitDisplay = "—", waitCls = "dim";
    if (lastWait) {
      const sameDirEntryAfter = ev.find(e =>
        e.idx > lastWait.idx &&
        ((lastWait.type === "wait-long" && e.type === "long-entry") ||
         (lastWait.type === "wait-short" && e.type === "short-entry"))
      );
      if (!sameDirEntryAfter) {
        waitDisplay = lastWait.label + (lastWait.reason ? " · " + lastWait.reason : "");
        waitCls = "warn";
      }
    }

    const lastEntry = [lastLongEntry, lastShortEntry].filter(Boolean).sort((a,b) => b.idx - a.idx)[0];
    const lastEntryDate = lastEntry ? fmtMD(lastEntry._date) : "—";
    const lastEntryReason = lastEntry ? (lastEntry.reason || lastEntry.label) : "";
    const confirmDate = lastEntry && lastEntry.confirmIdx != null
      ? fmtMD(displayData[lastEntry.confirmIdx]?.date) : null;

    const trendLabel = lastTrend ? lastTrend.label : "—";
    const trendStructure =
      trendLabel === "多頭" ? "頭頭高 底底高" :
      trendLabel === "空頭" ? "頭頭低 底底低" :
      trendLabel === "盤整" ? "結構未明" : "—";
    const trendCls = trendLabel === "多頭" ? "danger" : trendLabel === "空頭" ? "success" : trendLabel === "盤整" ? "warn" : "";

    const tlTypes = new Set(["trend", "long-entry", "long-exit", "short-entry", "short-exit", "wait-long", "wait-short", "neckline-up", "neckline-dn"]);
    const timeline = ev.filter(e => tlTypes.has(e.type)).slice(-8).reverse();

    return {
      trendLabel, trendStructure, trendCls,
      position, positionCls,
      waitDisplay, waitCls,
      lastEntryDate, lastEntryReason,
      confirmDate,
      timeline,
      fmtMD,
    };
  }, [kcEvents, displayData]);

  /* stock-bar 顯示優先序：intraday quote > FinMind 最後一根 K > mock stock */
  const intradayDisplay = React.useMemo(() => {
    if (!intraday || intraday.price == null) return null;
    const chg = intraday.prevClose != null ? intraday.price - intraday.prevClose : 0;
    const pct = intraday.prevClose ? (chg / intraday.prevClose) * 100 : 0;
    const d = new Date(intraday.ts);
    return {
      price: intraday.price, chg, pct,
      open: intraday.open, high: intraday.high, low: intraday.low,
      vol: intraday.vol,
      ts: `${d.toISOString().slice(0, 10)} ${d.toLocaleTimeString("zh-TW", { hour12: false })}`,
      source: intraday.source,
    };
  }, [intraday]);

  const finmindLive = React.useMemo(() => {
    if (dataSource.source !== "finmind" || !baseData || baseData.length < 2) return null;
    const c = baseData[baseData.length - 1];
    const p = baseData[baseData.length - 2];
    const chg = c.c - p.c;
    const pct = p.c > 0 ? (chg / p.c) * 100 : 0;
    return {
      price: c.c, chg, pct,
      open: c.o, high: c.h, low: c.l, vol: c.v,
      ts: c.date.toISOString().slice(0, 10),
      source: "FinMind",
    };
  }, [baseData, dataSource.source]);

  const display = intradayDisplay || finmindLive || {
    price: stock.price, chg: stock.chg, pct: stock.pct,
    open: stock.price - stock.chg,
    high: stock.price + 4,
    low: stock.price - stock.chg - 6,
    vol: stock.vol,
    ts: stock.ts,
    source: "Mock",
  };

  return (
    <div className="page chart-page" data-color-mode={market === "US" ? "us" : "tw"}>
      {/* Stock info bar */}
      <div className="stock-bar surface">
        <div className="stock-bar-l">
          <div className="stock-id">
            <div style={{display:"flex", gap:2, marginRight:6}}>
              <button className="btn ghost icon-btn"
                title={prevSym ? `上一檔：${prevSym}` : "已是第一檔"}
                disabled={!prevSym}
                onClick={() => prevSym && goChart && goChart(prevSym)}
                style={!prevSym ? { opacity: 0.35, cursor: "not-allowed" } : undefined}>
                <Icon name="chevron-left" size={14}/>
              </button>
              <button className="btn ghost icon-btn"
                title={nextSym ? `下一檔：${nextSym}` : "已是最後一檔"}
                disabled={!nextSym}
                onClick={() => nextSym && goChart && goChart(nextSym)}
                style={!nextSym ? { opacity: 0.35, cursor: "not-allowed" } : undefined}>
                <Icon name="chevron-right" size={14}/>
              </button>
            </div>
            <span className="stock-name">{stock.name}</span>
            <span className="stock-sym mono dim">{stock.sym}</span>
            <span className="pill">{stock.market === "TW" ? "TWSE" : "NASDAQ"}</span>
            {intradayDisplay
              ? <span className="pill success dot">{intradayDisplay.source} 即時</span>
              : dataSource.source === "finmind"
                ? <span className="pill success dot">FinMind 日 K</span>
                : <span className="pill warn"><Icon name="alert" size={10}/> 模擬資料</span>}
            {intradayErr && <span className="pill danger" title={intradayErr}>即時失敗</span>}
            <div ref={addWrapRef} style={{ position: "relative", marginLeft: 4 }}>
              <button className="btn"
                onClick={() => setAddOpen((o) => !o)}
                style={{ padding: "3px 9px", fontSize: "var(--fs-xs)" }}
                title="加入 / 移除觀察清單">
                <Icon name={inListCount > 0 ? "check" : "plus"} size={11}/>
                {inListCount > 0 ? `已加入 ${inListCount}` : "加入觀察清單"}
              </button>
              {addOpen && (
                <div className="surface" style={{
                  position: "absolute", top: "calc(100% + 6px)", left: 0,
                  minWidth: 260, zIndex: 30, padding: 4,
                  border: "1px solid var(--border)", borderRadius: "var(--radius)",
                  background: "var(--bg-elevated)", boxShadow: "0 10px 28px rgba(0,0,0,0.45)",
                }}>
                    <div style={{ padding: "8px 10px 6px", fontSize: "var(--fs-xs)",
                      color: "var(--text-3)", borderBottom: "1px solid var(--border)", marginBottom: 4 }}>
                      點選清單來加入 / 移除 <strong className="mono" style={{ color: "var(--text-1)" }}>{symbol}</strong>
                    </div>
                    {lists.length === 0 ? (
                      <div style={{ padding: "12px 10px", color: "var(--text-3)", fontSize: "var(--fs-sm)" }}>
                        尚未建立任何清單，請到「觀察清單」頁建立。
                      </div>
                    ) : lists.map((l) => {
                      const inList = l.syms.includes(symbol);
                      return (
                        <button key={l.id}
                          onClick={() => toggleInList(l.id)}
                          style={{
                            display: "flex", alignItems: "center", gap: 8, width: "100%",
                            padding: "8px 10px", border: 0, borderRadius: 4,
                            background: inList ? "rgba(34,197,94,0.08)" : "transparent",
                            color: "var(--text-1)", cursor: "pointer", textAlign: "left",
                            fontFamily: "inherit", fontSize: "var(--fs-sm)",
                          }}
                          onMouseEnter={(e) => { if (!inList) e.currentTarget.style.background = "var(--bg-app)"; }}
                          onMouseLeave={(e) => { e.currentTarget.style.background = inList ? "rgba(34,197,94,0.08)" : "transparent"; }}>
                          <Icon name={l.icon || "list"} size={13}/>
                          <span style={{ flex: 1 }}>{l.name}</span>
                          <span className="dim mono" style={{ fontSize: "var(--fs-xs)" }}>{l.syms.length}</span>
                          {inList
                            ? <Icon name="check" size={14}/>
                            : <Icon name="plus" size={14}/>}
                        </button>
                      );
                    })}
                </div>
              )}
            </div>
          </div>
          <div className="stock-price">
            <span className={"stock-price-num num " + (display.pct >= 0 ? "up" : "down")}>{fmt(display.price, 2)}</span>
            <span className={"num " + (display.pct >= 0 ? "up" : "down")}>{display.chg >= 0 ? "+" : ""}{fmt(display.chg, 2)}</span>
            <span className={"pill " + (display.pct >= 0 ? "up" : "down")}>{display.pct >= 0 ? "+" : ""}{fmt(display.pct, 2)}%</span>
          </div>
        </div>
        <div className="stock-bar-r">
          <div className="stat"><span className="dim">開盤</span><span className="mono">{fmt(display.open, 2)}</span></div>
          <div className="stat"><span className="dim">最高</span><span className="mono up">{fmt(display.high, 2)}</span></div>
          <div className="stat"><span className="dim">最低</span><span className="mono down">{fmt(display.low, 2)}</span></div>
          <div className="stat"><span className="dim">成交量</span><span className="mono">{fmtVol(display.vol)}</span></div>
          <div className="stat"><span className="dim">成交金額</span><span className="mono">{fmtVol(display.vol * display.price)}</span></div>
          <div className="stat"><span className="dim">資料源</span><span className="mono">{display.source}</span></div>
          <div className="stat"><span className="dim">時間</span><span className="mono">{display.ts}</span></div>
        </div>
      </div>

      {/* Chart workspace */}
      <div className="chart-workspace">
        <section className="surface chart-area">
          {/* Toolbar — timeframe + range，互動工具搬到左側 chart-tool-rail */}
          <div className="chart-toolbar">
            <div className="seg">
              {["1D","1W","1M"].map(t => <button key={t} className={tf === t ? "active" : ""} onClick={() => setTf(t)}>{t === "1D" ? "日 K" : t === "1W" ? "週 K" : "月 K"}</button>)}
            </div>
            <div className="seg">
              {["1M","3M","6M","1Y","3Y","5Y"].map(r => <button key={r} className={range === r ? "active" : ""} onClick={() => setRange(r)}>{r}</button>)}
            </div>
            <div className="toolbar-spacer"/>
            <span className="pill">{tf} · {range}</span>
          </div>

          {/* Indicator toggles */}
          <div className="indicator-row">
            {Object.keys(indicators).map(k => (
              <label key={k} className={"chk " + (indicators[k] ? "on" : "")}>
                <input type="checkbox" checked={indicators[k]} onChange={() => setIndicators(s => ({...s, [k]: !s[k]}))}/>
                <span>{k}</span>
              </label>
            ))}
            <div className="ma-toggles">
              {[5,10,20,60,120,240].map(p => (
                <button key={p} className={"ma-toggle " + (showMA[p] ? "on" : "")}
                  onClick={() => setShowMA(s => ({...s, [p]: !s[p]}))}
                  data-period={p}>
                  MA{p}
                </button>
              ))}
            </div>
            <div className="toolbar-spacer"/>
            <label className={"chk " + (showKC ? "on" : "")}>
              <input type="checkbox" checked={showKC} onChange={() => setShowKC(!showKC)}/>
              <span style={{fontWeight:600}}>KC MA5 結構 v17</span>
            </label>
            {indicators.MA && (
              <div style={{marginLeft:"auto", display:"flex", gap:14, alignItems:"center", fontSize:"var(--fs-xs)", flexWrap:"wrap"}}>
                {[5,10,20,60,120,240].filter(p => showMA[p]).map(p => (
                  <span key={p} className="mono" style={{display:"inline-flex", alignItems:"center", gap:4}}>
                    <span style={{display:"inline-block", width:8, height:2, background:MA_COLORS[p], borderRadius:1}}/>
                    <span style={{color:MA_COLORS[p], fontWeight:600}}>MA{p}</span>
                    <span className="mono">{maLast[p] != null ? fmt(maLast[p], 2) : "—"}</span>
                  </span>
                ))}
                {[5,10,20,60,120,240].filter(p => showMA[p]).length === 0 && (
                  <span className="dim" style={{fontSize:"var(--fs-xs)"}}>未啟用任何 MA</span>
                )}
              </div>
            )}
          </div>

          {/* Chart stage — 左側 vertical tool rail（TradingView 風 + flyout） + 右側 canvas */}
          <div className="chart-stage">
            <ChartToolRail
              toolMode={toolMode}
              setToolMode={setToolMode}
              drawings={drawings}
              clearDrawings={clearDrawings}
              toggles={toggles}
              setToggle={setToggle}
              chartActions={chartActionRef}/>
            <div className="chart-canvas-host">
              {dataSource.source === "loading" ? (
                <div style={{height:520, display:"flex", alignItems:"center", justifyContent:"center", color:"var(--text-3)"}}>
                  <div className="spinner" style={{marginRight:12}}/>
                  <span>載入 {symbol} 日 K 中…</span>
                </div>
              ) : !displayData || displayData.length === 0 ? (
                <div style={{height:520, display:"flex", flexDirection:"column", alignItems:"center", justifyContent:"center", color:"var(--text-3)", gap:8}}>
                  <Icon name="alert" size={24}/>
                  <span>此區間（{range}）沒有 K 線資料</span>
                  <span style={{fontSize:"var(--fs-xs)"}} className="dim">試試切到較長區間（3M / 6M / 1Y）</span>
                </div>
              ) : (
                <window.CandlestickChart data={displayData} height={520} showMA={showMA} showKC={showKC} kcOptions={kc} market={market} kcEvents={kcEvents} formingIdx={formingIdx}
                  showIndicators={indicators} toolMode={toolMode}
                  drawings={drawings} onDrawingsChange={setDrawings}
                  magnet={toggles.magnet}
                  lockDrawings={toggles["lock-draw"]}
                  hideDrawings={toggles["hide-draw"]}
                  showDataWindow={toggles["data-window"]}
                  showFxWatch={toggles.fx}
                  actionRef={chartActionRef}/>
              )}
            </div>
          </div>
          {dataSource.source === "mock" && (
            <div style={{padding:"6px 12px", fontSize:"var(--fs-xs)", color:"var(--warning)", borderTop:"1px solid var(--border)"}}>
              ⚠ FinMind 抓取失敗，目前顯示模擬資料{dataSource.error ? `（${dataSource.error}）` : ""}。可在「設定」頁設定 token 後重整。
            </div>
          )}

          {/* KC sub-options */}
          {showKC && (
            <div className="kc-row">
              <span className="dim mono" style={{fontSize:"var(--fs-xs)"}}>KC 訊號覆層</span>
              {[
                ["swing","高低點"],["dashed","短虛線"],["connect","轉折連線"],["neckline","上下頸線"],
                ["trend","趨勢標籤"],["waitSignal","等待訊號"],
                ["longEntry","多單進場"],["longExit","多單出場"],
                ["shortEntry","空單進場"],["shortExit","空單出場"],
                ["maLabel","均線標籤"]
              ].map(([k,l]) => (
                <label key={k} className={"chk small " + (kc[k] ? "on" : "")}>
                  <input type="checkbox" checked={kc[k]} onChange={() => setKc(s => ({...s, [k]: !s[k]}))}/>
                  <span>{l}</span>
                </label>
              ))}
            </div>
          )}
        </section>

        {/* Right detail panel */}
        <aside className="chart-aside">
          <section className="surface aside-card">
            <header className="section-head"><h3 className="section-title">技術指標</h3></header>
            <div className="kv-grid">
              <div><span className="dim">RSI(14)</span><span className="num">{fmt(stock.rsi, 1)}</span></div>
              <div><span className="dim">MACD</span><span className="num up">+0.84</span></div>
              <div><span className="dim">KD</span><span className="num">72 / 65</span></div>
              <div><span className="dim">BB %B</span><span className="num">0.78</span></div>
              <div><span className="dim">量/MA20</span><span className="num up">{fmt(stock.volMa,2)}×</span></div>
              <div><span className="dim">52 週區間</span><span className="num">688 — 998</span></div>
            </div>
          </section>

          <section className="surface aside-card">
            <header className="section-head">
              <h3 className="section-title">KC 訊號摘要</h3>
              {kcSummary ? (
                <span className={"pill dot " + kcSummary.trendCls}>{kcSummary.trendLabel}</span>
              ) : (
                <span className="pill dim">資料不足</span>
              )}
            </header>
            {kcSummary ? (
              <>
                <div className="kc-summary">
                  <div className="kv-row"><span className="dim">趨勢</span>
                    <span style={{color: kcSummary.trendLabel === "多頭" ? "var(--danger)" : kcSummary.trendLabel === "空頭" ? "var(--success)" : "var(--warning)"}}>
                      {kcSummary.trendLabel} · {kcSummary.trendStructure}
                    </span>
                  </div>
                  <div className="kv-row"><span className="dim">部位</span><span className={kcSummary.positionCls}>{kcSummary.position}</span></div>
                  <div className="kv-row"><span className="dim">等待</span><span className={kcSummary.waitCls}>{kcSummary.waitDisplay}</span></div>
                  <div className="kv-row"><span className="dim">最近進場</span><span className="mono">{kcSummary.lastEntryDate}{kcSummary.lastEntryReason ? " · " + kcSummary.lastEntryReason : ""}</span></div>
                  <div className="kv-row"><span className="dim">確認時間</span><span className="mono dim">{kcSummary.confirmDate || "—"}</span></div>
                </div>

                <div className="timeline">
                  <div className="timeline-title dim">近期 KC 時間軸</div>
                  {kcSummary.timeline.map((t, i) => (
                    <div key={i} className={"tl-row tl-" + t.type}>
                      <span className="tl-date mono">{kcSummary.fmtMD(t._date)}</span>
                      <span className="tl-dot"/>
                      <span className="tl-label">{t.label}</span>
                      {t.reason && <span className="tl-reason dim">{t.reason}</span>}
                    </div>
                  ))}
                  {formingIdx != null && displayData[formingIdx] && (
                    <div className="tl-row tl-preview">
                      <span className="tl-date mono">{kcSummary.fmtMD(displayData[formingIdx].date)}</span>
                      <span className="tl-dot"/>
                      <span className="tl-label">Preview</span>
                      <span className="tl-reason dim">Forming candle</span>
                    </div>
                  )}
                </div>
              </>
            ) : (
              <div className="kc-summary">
                <div className="dim" style={{padding:"12px 4px", fontSize:"var(--fs-sm)"}}>
                  此區間資料不足以計算 KC 結構（需 ≥ 25 根日 K，且使用 FinMind 真實資料）。請切到較長區間（3M / 6M 以上）。
                </div>
              </div>
            )}
          </section>

          <section className="surface aside-card">
            <header className="section-head"><h3 className="section-title">籌碼</h3></header>
            <div className="kv-grid two">
              <div><span className="dim">外資</span><span className="num up">+12,840 張</span></div>
              <div><span className="dim">投信</span><span className="num up">+1,205 張</span></div>
              <div><span className="dim">自營</span><span className="num down">-340 張</span></div>
              <div><span className="dim">融資餘額</span><span className="num">128.4K</span></div>
            </div>
          </section>

          <section className="surface aside-card">
            <header className="section-head"><h3 className="section-title">最新新聞</h3></header>
            <ul className="news-list compact">
              {window.MOCK.NEWS.slice(0,3).map((n, i) => (
                <li key={i} className="news-item">
                  <div className="news-meta">
                    <span className="mono dim" style={{fontSize:"var(--fs-xs)"}}>{n.time}</span>
                    <span className={"weight-dot w-" + n.weight}/>
                  </div>
                  <div className="news-title">{n.title}</div>
                </li>
              ))}
            </ul>
          </section>

          <section className="surface aside-card">
            <header className="section-head"><h3 className="section-title">個人備註</h3></header>
            <textarea className="notes-area" placeholder="個人備註 — 進出場理由、觀察重點..."
              defaultValue={"05-06 盤整突破，量增超過 MA20 1.4×。\n關注 1000 整數關卡。"}/>
            <div style={{display:"flex", gap:8, marginTop:8}}>
              <button className="btn primary" style={{flex:1}}><Icon name="bell" size={13}/> 建立通知</button>
              <button className="btn"><Icon name="beaker" size={13}/></button>
            </div>
          </section>
        </aside>
      </div>
    </div>
  );
};

window.ChartPage = ChartPage;
