#!/usr/bin/env node
/**
 * 從 knowledge.json 產生 skill-drills.json 題庫覆寫
 * - 解析 EP 實例的標的、日期視窗
 * - 同群心法作為第 1 關干擾項
 */
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const DATA_DIR = path.join(__dirname, "..", "data");
const KNOWLEDGE_PATH = path.join(DATA_DIR, "knowledge.json");
const OUT_PATH = path.join(DATA_DIR, "skill-drills.json");

const TICKER_IN_TEXT =
  /\b(NVDA|AMD|AAPL|MSFT|GOOGL|GOOGL|META|AMZN|TSLA|TSM|AVGO|NOC|BA|LMT|RTX|MU|SMCI|ORCL|CRM|COST|WMT|QQQ|SPY|SMH|IWM|LYV|DKNG|2330\.TW)\b/gi;

const NAME_TO_SYMBOL = [
  [/輝達|NVIDIA/i, "NVDA"],
  [/台積電|TSMC/i, "TSM"],
  [/蘋果|Apple/i, "AAPL"],
  [/微軟|Microsoft/i, "MSFT"],
  [/谷歌|Google|Alphabet/i, "GOOGL"],
  [/亞馬遜|Amazon/i, "AMZN"],
  [/特斯拉|Tesla/i, "TSLA"],
  [/博通|Broadcom/i, "AVGO"],
  [/洛克希德|Raytheon/i, "LMT"],
  [/萊茵金屬/i, "RTX"],
  [/台股|加權|大盤/i, "SPY"],
  [/標普|S&P|美股大盤/i, "SPY"],
  [/那斯達|Nasdaq|科技板塊/i, "QQQ"],
];

const GROUP_DEFAULT_SYMBOL = {
  總經與市場水位: "SPY",
  進攻與擴張時機: "QQQ",
  護城河與商業模式: "NVDA",
  財報估值與管理層: "AAPL",
  交易紀律與資金管理: "SPY",
  地緣政治與政策博弈: "LMT",
  生活觀察與消費信號: "COST",
  市場心理與反指標: "QQQ",
};

function hashSeed(s) {
  let h = 0;
  for (let i = 0; i < s.length; i++) h = (Math.imul(31, h) + s.charCodeAt(i)) | 0;
  return Math.abs(h);
}

function pick(arr, seed, n) {
  const copy = [...arr];
  const out = [];
  let s = hashSeed(seed);
  while (copy.length && out.length < n) {
    s = (s * 1103515245 + 12345) | 0;
    const idx = Math.abs(s) % copy.length;
    out.push(copy.splice(idx, 1)[0]);
  }
  return out;
}

function parseInstances(body) {
  const instances = [];
  for (const raw of String(body || "").split("\n")) {
    const line = raw.trim();
    if (!line.startsWith("- ") && !line.startsWith("* ")) continue;
    const content = line.slice(2).trim();
    if (/^(EP\d|EP\.|會員影片|member_)/i.test(content) || /^EP/.test(content.split(/[：:]/)[0] || "")) {
      const parts = content.split(/[：:]/);
      instances.push({
        ep: parts[0]?.trim() || "",
        text: parts.slice(1).join("：").trim() || content,
      });
    }
  }
  return instances;
}

function resolveSymbol(text, groupName) {
  const tickers = [...text.matchAll(TICKER_IN_TEXT)].map((m) => m[1].toUpperCase());
  if (tickers[0]) return tickers[0];
  for (const [re, sym] of NAME_TO_SYMBOL) {
    if (re.test(text)) return sym;
  }
  return GROUP_DEFAULT_SYMBOL[groupName] || "SPY";
}

function parseScenarioWindow(text, ep) {
  const zh = text.match(/(20\d{2})\s*年\s*(\d{1,2})\s*月/);
  if (zh) return `${zh[1]}-${String(zh[2]).padStart(2, "0")}`;
  const slash = text.match(/(20\d{2})\s*[\/年]\s*(\d{1,2})\s*[\/月]\s*(\d{1,2})/);
  if (slash) return `${slash[1]}-${String(slash[2]).padStart(2, "0")}`;
  const en = text.match(/(20\d{2})-(\d{2})(?:-(\d{2}))?/);
  if (en) return `${en[1]}-${en[2]}`;
  const epNum = ep?.match(/EP\.?\s*(\d+)/i);
  if (epNum) return `EP${epNum[1]}`;
  return null;
}

function chartRangeForWindow(window) {
  if (!window || window.startsWith("EP")) return "1y";
  const y = Number(window.slice(0, 4));
  const now = new Date().getFullYear();
  if (y >= now - 1) return "1y";
  if (y >= now - 3) return "3y";
  return "5y";
}

function buildGroupMap(principleMapBody, principles) {
  const byId = new Map(principles.map((p) => [p.id, p]));
  const groupOf = new Map();
  const groups = new Map();

  if (!principleMapBody) return { groupOf, groups };

  let curName = "全部心法";
  for (const line of principleMapBody.split("\n")) {
    const hm = line.match(/^## \d+\.\s*(.+?)（/);
    if (hm) {
      curName = hm[1].trim();
      if (!groups.has(curName)) groups.set(curName, []);
      continue;
    }
    const links = [...line.matchAll(/\[\[Emmy 投資心法#([^\]]+)\]\]/g)];
    for (const m of links) {
      const p = byId.get(m[1]);
      if (!p) continue;
      groupOf.set(p.id, curName);
      groups.get(curName)?.push(p.id);
    }
  }
  return { groupOf, groups };
}

function cleanTitle(title) {
  return String(title || "")
    .replace(/^原則[^：:]+[：:]\s*/, "")
    .trim();
}

function main() {
  if (!fs.existsSync(KNOWLEDGE_PATH)) {
    console.error("找不到 knowledge.json，請先執行 npm run build:knowledge");
    process.exit(1);
  }
  const knowledge = JSON.parse(fs.readFileSync(KNOWLEDGE_PATH, "utf8"));
  const principles = knowledge.principles || [];
  const { groupOf, groups } = buildGroupMap(knowledge.principleMap?.body, principles);

  const entries = {};
  for (const p of principles) {
    const instances = parseInstances(p.body);
    const groupName = groupOf.get(p.id) || "全部心法";
    const idx = instances.length ? hashSeed(p.id) % instances.length : 0;
    const inst = instances[idx] || instances[0];
    const blob = inst ? `${inst.ep} ${inst.text} ${p.title}` : p.title;
    const scenarioSymbol = resolveSymbol(blob, groupName);
    const scenarioWindow = inst ? parseScenarioWindow(`${inst.text} ${inst.ep}`, inst.ep) : null;

    const peers = (groups.get(groupName) || []).filter((id) => id !== p.id);
    const distractorPrinciples = pick(peers, `${p.id}:dist`, Math.min(3, peers.length));

    const entry = {
      groupName,
      instanceIndex: idx,
      scenarioSymbol,
      chartRange: chartRangeForWindow(scenarioWindow),
    };
    if (scenarioWindow) entry.scenarioWindow = scenarioWindow;
    if (inst?.ep) entry.epLabel = inst.ep;
    if (distractorPrinciples.length) entry.distractorPrinciples = distractorPrinciples;

    // 傳說/史詩卡加強提示
    if (p.num != null && p.num <= 10) {
      entry.chartHint = "十大基石心法：結合總經水位與長期趨勢判斷";
    }

    entries[p.id] = entry;
  }

  const payload = {
    version: 1,
    generatedAt: new Date().toISOString(),
    principleCount: principles.length,
    entries,
  };

  fs.writeFileSync(OUT_PATH, JSON.stringify(payload, null, 2), "utf8");
  console.log(`Wrote ${OUT_PATH} (${principles.length} entries)`);
}

main();