/* Shared SVG icon set + small visual primitives */
const Icon = ({ name, size = 16, stroke = 1.6, ...rest }) => {
  const s = size;
  const sw = stroke;
  const props = { width: s, height: s, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: sw, strokeLinecap: "round", strokeLinejoin: "round", ...rest };
  switch (name) {
    case "dashboard":   return <svg {...props}><path d="M3 12l9-9 9 9"/><path d="M5 10v10h14V10"/></svg>;
    case "list":        return <svg {...props}><path d="M8 6h12M8 12h12M8 18h12"/><circle cx="4" cy="6" r="1"/><circle cx="4" cy="12" r="1"/><circle cx="4" cy="18" r="1"/></svg>;
    case "chart":       return <svg {...props}><path d="M3 3v18h18"/><path d="M7 14l3-4 3 3 5-7"/></svg>;
    case "grid":        return <svg {...props}><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/></svg>;
    case "bell":        return <svg {...props}><path d="M6 8a6 6 0 1112 0c0 7 3 7 3 9H3c0-2 3-2 3-9z"/><path d="M10 20a2 2 0 004 0"/></svg>;
    case "beaker":      return <svg {...props}><path d="M9 3v6L4 19a2 2 0 002 3h12a2 2 0 002-3l-5-10V3"/><path d="M9 3h6"/></svg>;
    case "news":        return <svg {...props}><rect x="3" y="4" width="18" height="16" rx="2"/><path d="M7 8h10M7 12h10M7 16h6"/></svg>;
    case "settings":    return <svg {...props}><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.7 1.7 0 00.3 1.8l.1.1a2 2 0 11-2.8 2.8l-.1-.1a1.7 1.7 0 00-1.8-.3 1.7 1.7 0 00-1 1.5V21a2 2 0 11-4 0v-.1a1.7 1.7 0 00-1-1.6 1.7 1.7 0 00-1.9.3l-.1.1a2 2 0 11-2.8-2.8l.1-.1a1.7 1.7 0 00.3-1.8 1.7 1.7 0 00-1.5-1H3a2 2 0 110-4h.1a1.7 1.7 0 001.6-1 1.7 1.7 0 00-.3-1.9l-.1-.1A2 2 0 117.1 4.2l.1.1a1.7 1.7 0 001.8.3H9a1.7 1.7 0 001-1.5V3a2 2 0 114 0v.1a1.7 1.7 0 001 1.5 1.7 1.7 0 001.9-.3l.1-.1a2 2 0 112.8 2.8l-.1.1a1.7 1.7 0 00-.3 1.8V9c.4.6 1 1 1.6 1H21a2 2 0 110 4h-.1a1.7 1.7 0 00-1.5 1z"/></svg>;
    case "search":      return <svg {...props}><circle cx="11" cy="11" r="7"/><path d="M21 21l-4.35-4.35"/></svg>;
    case "sun":         return <svg {...props}><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41"/></svg>;
    case "moon":        return <svg {...props}><path d="M21 12.8A9 9 0 1111.2 3a7 7 0 009.8 9.8z"/></svg>;
    case "send":        return <svg {...props}><path d="M22 2L11 13"/><path d="M22 2l-7 20-4-9-9-4 20-7z"/></svg>;
    case "plus":        return <svg {...props}><path d="M12 5v14M5 12h14"/></svg>;
    case "x":           return <svg {...props}><path d="M18 6L6 18M6 6l12 12"/></svg>;
    case "play":        return <svg {...props}><path d="M5 3l14 9-14 9V3z"/></svg>;
    case "refresh":     return <svg {...props}><path d="M21 12a9 9 0 11-3-6.7L21 8"/><path d="M21 3v5h-5"/></svg>;
    case "filter":      return <svg {...props}><path d="M3 4h18l-7 9v6l-4 2v-8L3 4z"/></svg>;
    case "crosshair":   return <svg {...props}><circle cx="12" cy="12" r="9"/><path d="M12 3v6M12 15v6M3 12h6M15 12h6"/></svg>;
    case "draw":        return <svg {...props}><path d="M3 21l5-1 12-12-4-4L4 16l-1 5z"/></svg>;
    case "ruler":       return <svg {...props}><path d="M3 17L17 3l4 4L7 21l-4-4z"/><path d="M7 13l2 2M11 9l2 2M15 5l2 2"/></svg>;
    case "camera":      return <svg {...props}><path d="M23 19a2 2 0 01-2 2H3a2 2 0 01-2-2V8a2 2 0 012-2h4l2-3h6l2 3h4a2 2 0 012 2z"/><circle cx="12" cy="13" r="4"/></svg>;
    case "external":    return <svg {...props}><path d="M14 3h7v7"/><path d="M10 14L21 3"/><path d="M21 14v7H3V3h7"/></svg>;
    case "check":       return <svg {...props}><path d="M5 12l5 5L20 7"/></svg>;
    case "alert":       return <svg {...props}><path d="M12 2L2 21h20L12 2z"/><path d="M12 9v5M12 18v.01"/></svg>;
    case "trending-up": return <svg {...props}><path d="M3 17l6-6 4 4 8-8"/><path d="M14 7h7v7"/></svg>;
    case "trending-down": return <svg {...props}><path d="M3 7l6 6 4-4 8 8"/><path d="M14 17h7v-7"/></svg>;
    case "tw":          return <svg {...props}><circle cx="12" cy="12" r="9"/><path d="M3 12h18M12 3v18"/></svg>;
    case "us":          return <svg {...props}><rect x="3" y="5" width="18" height="14" rx="1"/><path d="M3 9h18M3 13h18M3 17h18"/></svg>;
    case "drag":        return <svg {...props}><circle cx="9" cy="6" r="1"/><circle cx="15" cy="6" r="1"/><circle cx="9" cy="12" r="1"/><circle cx="15" cy="12" r="1"/><circle cx="9" cy="18" r="1"/><circle cx="15" cy="18" r="1"/></svg>;
    case "dot":         return <svg {...props}><circle cx="12" cy="12" r="4" fill="currentColor" stroke="none"/></svg>;
    case "menu":        return <svg {...props}><path d="M3 6h18M3 12h18M3 18h18"/></svg>;
    case "minus":       return <svg {...props}><path d="M5 12h14"/></svg>;
    case "chevron-left":  return <svg {...props}><path d="M15 6l-6 6 6 6"/></svg>;
    case "chevron-right": return <svg {...props}><path d="M9 6l6 6-6 6"/></svg>;
    case "percent":     return <svg {...props}><line x1="19" y1="5" x2="5" y2="19"/><circle cx="6.5" cy="6.5" r="2.5"/><circle cx="17.5" cy="17.5" r="2.5"/></svg>;
    case "fib":         return <svg {...props}><path d="M3 5h18M3 9h18M3 13h12M3 17h6"/></svg>;
    case "shapes":      return <svg {...props}><rect x="3" y="3" width="11" height="11" rx="1"/><circle cx="16.5" cy="16.5" r="4.5"/></svg>;
    case "brush":       return <svg {...props}><path d="M9 13l8-8 3 3-8 8z"/><path d="M9 13c0 3-1 5-4 6 1-3 1-5 4-6z"/></svg>;
    case "text-tool":   return <svg {...props}><path d="M5 5h14M12 5v14"/></svg>;
    case "smile":       return <svg {...props}><circle cx="12" cy="12" r="9"/><path d="M9 14s1 2 3 2 3-2 3-2"/><path d="M9 9h.01M15 9h.01"/></svg>;
    case "zoom-in":     return <svg {...props}><circle cx="11" cy="11" r="7"/><path d="M21 21l-4.35-4.35M11 8v6M8 11h6"/></svg>;
    case "zoom-out":    return <svg {...props}><circle cx="11" cy="11" r="7"/><path d="M21 21l-4.35-4.35M8 11h6"/></svg>;
    case "magnet":      return <svg {...props}><path d="M5 4h4v6a3 3 0 006 0V4h4v6a7 7 0 11-14 0V4z"/><path d="M5 4v3M19 4v3"/></svg>;
    case "lock":        return <svg {...props}><rect x="4" y="11" width="16" height="10" rx="2"/><path d="M8 11V8a4 4 0 018 0v3"/></svg>;
    case "eye":         return <svg {...props}><path d="M2 12s4-7 10-7 10 7 10 7-4 7-10 7S2 12 2 12z"/><circle cx="12" cy="12" r="3"/></svg>;
    case "fx":          return <svg {...props}><path d="M5 9h5M7.5 5v14"/><path d="M13 16l6-8M13 8l6 8"/></svg>;
    case "trash":       return <svg {...props}><path d="M3 6h18"/><path d="M8 6V4a1 1 0 011-1h6a1 1 0 011 1v2"/><path d="M19 6l-1 14a2 2 0 01-2 2H8a2 2 0 01-2-2L5 6"/><path d="M10 11v6M14 11v6"/></svg>;
    case "calendar":    return <svg {...props}><rect x="3" y="4" width="18" height="17" rx="2"/><path d="M3 9h18M8 2v4M16 2v4"/></svg>;
    case "arrow-up":    return <svg {...props}><path d="M12 19V5M5 12l7-7 7 7"/></svg>;
    case "arrow-down":  return <svg {...props}><path d="M12 5v14M5 12l7 7 7-7"/></svg>;
    default: return null;
  }
};

/* Sparkline */
const Sparkline = ({ data, width = 80, height = 22, color }) => {
  if (!data || data.length < 2) return null;
  const min = Math.min(...data);
  const max = Math.max(...data);
  const range = max - min || 1;
  const step = width / (data.length - 1);
  const pts = data.map((v, i) => `${(i*step).toFixed(1)},${(height - ((v-min)/range) * height).toFixed(1)}`).join(" ");
  const isUp = data[data.length-1] >= data[0];
  const c = color || (isUp ? "var(--c-up)" : "var(--c-down)");
  return (
    <svg width={width} height={height} style={{display:"block"}}>
      <polyline points={pts} fill="none" stroke={c} strokeWidth="1.4" strokeLinecap="round" strokeLinejoin="round"/>
    </svg>
  );
};

/* Number formatting */
const fmt = (n, d = 2) => Number(n).toLocaleString("en-US", { minimumFractionDigits: d, maximumFractionDigits: d });
const fmtVol = (n) => {
  if (n >= 1e9) return (n/1e9).toFixed(2) + "B";
  if (n >= 1e6) return (n/1e6).toFixed(2) + "M";
  if (n >= 1e3) return (n/1e3).toFixed(1) + "K";
  return String(n);
};
const sign = (n) => (n > 0 ? "+" : "") + fmt(n);

/* Price change badge — scoped color via data-market */
const PctBadge = ({ pct, market }) => {
  const cls = pct >= 0 ? "up" : "down";
  return (
    <span className={"pill " + cls} data-color-mode={market === "US" ? "us" : "tw"}>
      {pct >= 0 ? "+" : ""}{fmt(pct, 2)}%
    </span>
  );
};

/* Trend pill */
const TrendBadge = ({ trend }) => {
  const map = {
    Bullish: { cls: "up", icon: "trending-up", label: "均線多頭" },
    Bearish: { cls: "down", icon: "trending-down", label: "均線空頭" },
    Sideways: { cls: "warn", icon: "dot", label: "盤整" },
  };
  const m = map[trend] || map.Sideways;
  return <span className={"pill " + m.cls}><Icon name={m.icon} size={11}/> {m.label}</span>;
};

const QuoteLoadingView = ({ count, loading = false, autoRefresh = true, remainingSeconds = 0, intervalSeconds = 10 }) => {
  const progress = autoRefresh && intervalSeconds > 0
    ? Math.max(0, Math.min(100, (remainingSeconds / intervalSeconds) * 100))
    : 0;
  const countdownText = autoRefresh
    ? `${Math.max(0, Math.ceil(remainingSeconds))}s`
    : "手動";
  return (
    <div
      className={"quote-loading-view" + (loading ? " loading" : "")}
      role="status"
      aria-live={loading ? "polite" : "off"}
      style={{ "--quote-refresh-progress": `${progress}%` }}>
      <span className={"quote-loading-spinner" + (loading ? "" : " idle")} aria-hidden="true"></span>
      <div className="quote-loading-copy">
        <div className="quote-loading-title">
          {loading ? "正在撈取即時股價" : "即時股價自動刷新中"}
        </div>
        <div className="quote-loading-sub">
          {loading
            ? (count > 0 ? `更新 ${count} 檔股票報價，完成後會自動刷新畫面` : "完成後會自動刷新畫面")
            : (autoRefresh ? `下次自動刷新 ${countdownText}` : "非交易時段，按右上角 refresh 手動更新")}
        </div>
      </div>
      <span className="quote-loading-countdown mono">{loading ? "更新中" : countdownText}</span>
      <span className="quote-refresh-progress" aria-hidden="true"></span>
    </div>
  );
};

const MANUAL_REFRESH_EVENT = "twstock:manual-refresh";

const useManualRefreshKey = () => {
  const [key, setKey] = React.useState(0);
  React.useEffect(() => {
    const refresh = () => setKey((v) => v + 1);
    window.addEventListener(MANUAL_REFRESH_EVENT, refresh);
    return () => window.removeEventListener(MANUAL_REFRESH_EVENT, refresh);
  }, []);
  return key;
};

const getQuotePollSeconds = () => window.Intraday?.getPollSeconds?.() || 10;

const useQuoteRefreshCountdown = ({ active, loading, autoRefresh }) => {
  const [intervalSeconds, setIntervalSeconds] = React.useState(getQuotePollSeconds);
  const [remainingSeconds, setRemainingSeconds] = React.useState(getQuotePollSeconds);

  React.useEffect(() => {
    if (!active) return;
    const seconds = getQuotePollSeconds();
    setIntervalSeconds(seconds);
    setRemainingSeconds(seconds);
  }, [active, loading, autoRefresh]);

  React.useEffect(() => {
    if (!active || loading || !autoRefresh) return;
    const timer = setInterval(() => {
      setRemainingSeconds((seconds) => Math.max(0, seconds - 1));
    }, 1000);
    return () => clearInterval(timer);
  }, [active, loading, autoRefresh]);

  return { intervalSeconds, remainingSeconds };
};

window.UI = {
  Icon, Sparkline, fmt, fmtVol, sign, PctBadge, TrendBadge,
  QuoteLoadingView, MANUAL_REFRESH_EVENT, useManualRefreshKey, useQuoteRefreshCountdown,
};
