// ═══════════════════════════════════════════════════════════
//  build-patterns-catalog.mjs
//  從《短線交易日線圖大全》目錄 + 72 頁 MD 產生完整 catalog.json
//  用法：cd app && npm run build:patterns
// ═══════════════════════════════════════════════════════════
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import {
  findPageForEntry,
  loadAllPages,
  norm,
  resolvePatternContentDir,
} from '../lib/pattern-page-index.js';
import { supplementForCatalogName } from '../lib/pattern-supplements.js';

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const BOOK_DIR = resolvePatternContentDir();
const OUT_PATH = path.resolve(__dirname, '..', 'data', 'patterns', 'catalog.json');
const OVERRIDES_PATH = path.resolve(__dirname, '..', 'data', 'patterns', 'automation-overrides.json');

if (!BOOK_DIR) {
  console.error('找不到線型教材資料夾（短線交易日線圖大全 或 content/raw/patterns）');
  process.exit(1);
}

const overrides = JSON.parse(fs.readFileSync(OVERRIDES_PATH, 'utf8'));

const CHAPTER_NAMES = {
  1: '短線交易模式與交易系統',
  2: '構成股價走勢圖的要素',
  3: '當沖交易圖表型態',
  4: '波段交易圖表型態',
  5: '超短線交易圖表型態',
  6: '大盤觀察與選股',
  0: '附錄與心態',
};

const MODE_SLUG_SUFFIX = { '①': '_1', '②': '_2', '③': '_3', '④': '_4' };

function slugId(name, category, chapter) {
  const modeMark = String(name || '').match(/[①②③④]/)?.[0] || '';
  const modeSuffix = MODE_SLUG_SUFFIX[modeMark] || '';
  const base = norm(name).slice(0, 28) || norm(category).slice(0, 12);
  const slug = `p${chapter}_${base}${modeSuffix}`
    .replace(/[^\w\u4e00-\u9fff]/g, '_')
    .replace(/_+/g, '_')
    .replace(/_$/g, '');
  return slug.slice(0, 52);
}

function inferBias(text, name) {
  const t = `${text} ${name}`;
  if (/賣出|下跌|空方|下殺|偏空|超買|死亡|跌停|黑三兵|空方|減碼|摜破|風險|小心|勿追/.test(t)) return 'bearish';
  if (/買進|上漲|多方|起漲|偏多|超賣|黃金|漲停|紅三兵|突破|反彈|續強|買點/.test(t)) return 'bullish';
  return 'neutral';
}

function inferTimeframe(chapter, category) {
  if (chapter === 5 || /超短線|跳動點|VWAP|10檔/.test(category)) return ['分', '日'];
  if (chapter === 4 || /波段|布林|一目|斐波|酒田/.test(category)) return ['日', '週'];
  if (chapter === 3 || /當沖|交易時段|9點/.test(category)) return ['日', '分'];
  if (chapter === 1 || chapter === 2) return ['日'];
  return ['日'];
}

function parseToc() {
  const raw = fs.readFileSync(path.join(BOOK_DIR, '003.md'), 'utf8');
  const lines = raw.split('\n').map(l => l.trim()).filter(Boolean);
  const entries = [];
  let chapter = 0;
  let chapterTitle = '';
  let section = 'toc';

  const skipExact = new Set([
    '交易模式', '時間意識', '未必等於', '10檔報價', '下單方式', '逆限價單', 'OCO單', 'IFD單', 'IFDOCO單',
    '構成要素', '基本要素', 'K線', '時間軸', '交易時段', '技術指標', '移動平均線', '葛蘭碧法則',
    '複數均線', '隨機指標', '騰落指標', '相對強弱指標', 'MACD', '複數指標', '道氏理論', 'K線組成的型態',
    '天花板．地板', '橫盤整理', '三角收斂型態', '旗形型態', '箱型整理', '分價量表', '當日現金交割',
    '特定的股價波動', '反彈的時機點', '練習問題①', '練習問題②', '基本操作策略', '避免在連假期間持股',
    '布林通道', '趨勢通道', '顧比均線', '一目均衡表', '基本結構', '三役好轉', '延遲線的用法',
    '斐波那契回撤', 'SAR拋物線指標', 'ENV包絡線', '不同時間軸的均線', '歷史動率', 'HV歷史波動率',
    '動向指標', 'DMI動向指標', 'PSY心理線', 'DMA指標', '酒田五法', '島狀反轉', '菱形頂型態', '杯柄型態',
    '海龜交易法', 'K線種類', 'K線型態', 'K線組合', '帶量上漲的大陽線', '上下影線', '跳空缺口', '連續K線',
    '壓力線．支撐線', '突破交易', '心理關卡', '摜破大關', '多空趨勢', '哪一種趨勢', 'VWAP', '長期趨勢',
    '大盤指數', '美股收盤', '歷史數據', '最近兩週', '新股上市', '變更交易市場', '股票下市', '宣布下市',
    '發布財報', '買賣越活躍', '股價漲幅排行', '尋找買賣點', '當沖交易', '波段交易', '超短線交易',
    '買進模式①', '買進模式②', '買進模式③', '買進模式④', '賣出模式①', '賣出模式②', '賣出模式③', '賣出模式④',
    '道氏理論①', '道氏理論②', '道氏理論③', '9點～11點', '收盤前30分', '長短參數', '移動平均乖離率',
    '黃金交叉', '死亡交叉', '多頭排列', '紅三兵', '黑三兵', '下影陽線', '下影陰線', '上影陽線', '上影陰線',
    '覆蓋線', '切入線', '穿透線', '環抱線', '孕育線', '大陽線', '大陰線', '同時線', '漲停', '跌停',
  ]);

  for (const line of lines) {
    if (line === '圖表型態一覽表' || line === '本書的頁面構成') continue;
    const ch = line.match(/^第([1-6])章　(.+)$/);
    if (ch) {
      chapter = Number(ch[1]);
      chapterTitle = ch[2];
      continue;
    }
    if (line.startsWith('末章　')) {
      chapter = 0;
      chapterTitle = '投資心態';
      entries.push({ chapter, chapterTitle, category: '末章', name: line.replace(/^末章　/, ''), fullTitle: line });
      continue;
    }
    if (line.startsWith('專欄　')) {
      entries.push({ chapter, chapterTitle, category: '專欄', name: line.replace(/^專欄　/, ''), fullTitle: line });
      continue;
    }
    const m = line.match(/^(.+?)　(.+)$/);
    if (!m) continue;
    const category = m[1].trim();
    const title = m[2].trim();
    if (title.length < 4) continue;
    if (skipExact.has(category) && category === title) continue;
    if (category === title) continue;
    entries.push({ chapter, chapterTitle, category, name: title, fullTitle: `${category}　${title}` });
  }
  return entries;
}

const PAGE_OVERRIDES = {
  '黃金交叉': 20, '死亡交叉': 20, '多頭排列': 21,
  'RSI相對強弱指標': 22, 'MACD指標': 23, 'MACD': 23,
  '成交量': 29, '向上跳空缺口': 55, '向下跳空缺口': 55,
  '多方吞噬': 54, '空方吞噬': 54, '覆蓋線': 54,
  '下影陽線': 53, '下影陰線': 53, '上影陽線': 53, '上影陰線': 53,
  '紅三兵': 45, '黑三兵': 45, '漲停': 10, '跌停': 10,
  '買進模式①': 15, '買進模式④': 16,
  '賣出模式①': 17, '賣出模式②': 18,
  '跳動點': 50, 'VWAP': 59,
  // 目錄有條目但原始擷取未產生獨立書頁
  '10分鐘掌握當沖交易的重點！': false,
  '10分鐘掌握波段交易的重點！': false,

  '運用葛蘭碧法則的賣出模式③': false,
  '運用葛蘭碧法則的賣出模式④': false,
  '反轉為上升趨勢的關鍵點位在哪裡？①': false,
};

function resolveByNameOverride(entry) {
  if (overrides.byName?.[entry.name]) return overrides.byName[entry.name];
  const keys = Object.keys(overrides.byName || {}).sort((a, b) => b.length - a.length);
  for (const key of keys) {
    if (entry.name === key) return overrides.byName[key];
  }
  return {};
}

function applyOverride(entry, page) {
  const split = overrides.byTitle?.[entry.name]?.split;
  if (split?.length) {
    return split.map(subName => {
      const sub = { ...entry, name: subName };
      const o = overrides.byName?.[subName] || {};
      return buildPattern(sub, page, o);
    });
  }

  return [buildPattern(entry, page, resolveByNameOverride(entry))];
}

function resolvePageForEntry(entry, pages) {
  return supplementForCatalogName(entry.name) || findPageForEntry(entry, pages, PAGE_OVERRIDES);
}

function buildPattern(entry, page, o = {}) {
  const summary = page?.summary || entry.name;
  const bias = o.bias || inferBias(summary, entry.name);
  const chapter = entry.chapter || 0;
  const pattern = {
    id: o.id || slugId(entry.name, entry.category, chapter),
    name: o.name || entry.name,
    category: entry.category,
    chapter,
    chapterTitle: entry.chapterTitle || CHAPTER_NAMES[chapter] || '',
    bookPage: page?.pageNum || null,
    bias,
    timeframe: o.timeframe || inferTimeframe(chapter, entry.category),
    automatable: o.automatable === true,
    teach: {
      summary: summary.slice(0, 280),
      entryHint: o.entryHint || (page?.summary2 ? page.summary2.slice(0, 120) : '依書本圖例與當下趨勢判斷進場時機。'),
      exitHint: o.exitHint || '趨勢或型態失效時出場；搭配停損/停利紀律。',
      caution: page?.isSupplement
        ? '此節為教材補遺：原始擷取缺頁，內容依書中交叉引用整理，圖例可能為鏡像對照頁。'
        : (o.caution || (entry.category === '練習問題' ? '先自行作答，再對照書本解答。' : '訊號需搭配大盤、量能與基本面，避免單一線型決策。')),
      bookRef: page?.mdFile || null,
      bookHtml: page?.htmlFile || null,
      images: page?.imageUrls || [],
      fullTitle: entry.fullTitle,
      isSupplement: !!page?.isSupplement,
      tocHint: page?.tocHint || null,
    },
  };
  if (o.rule) {
    pattern.rule = o.rule;
    pattern.params = o.params || {};
  }
  if (o.markets) pattern.markets = o.markets;
  if (o.backtest) pattern.backtest = o.backtest;

  const extra = overrides.teachExtras?.[pattern.id];
  if (extra) {
    const { teach: extraTeach, entryHint, exitHint, caution, ...rest } = extra;
    Object.assign(pattern, rest);
    if (extraTeach) pattern.teach = { ...pattern.teach, ...extraTeach };
    if (entryHint) pattern.teach.entryHint = entryHint;
    if (exitHint) pattern.teach.exitHint = exitHint;
    if (caution) pattern.teach.caution = caution;
  }
  return pattern;
}

function dedupePatterns(list) {
  const seen = new Map();
  const out = [];
  for (const p of list) {
    const key = p.id;
    if (seen.has(key)) {
      const prev = seen.get(key);
      if ((p.automatable && !prev.automatable) || (p.bookPage && !prev.bookPage)) {
        const idx = out.indexOf(prev);
        out[idx] = p;
        seen.set(key, p);
      }
      continue;
    }
    seen.set(key, p);
    out.push(p);
  }
  return out;
}

function main() {
  const toc = parseToc();
  const pages = loadAllPages(BOOK_DIR);
  const patterns = [];

  for (const entry of toc) {
    const page = resolvePageForEntry(entry, pages);
    // 圖鑑只收錄能回到實際 MD 教材的項目，避免目錄文字變成空卡。
    if (!page) continue;
    patterns.push(...applyOverride(entry, page));
  }

  for (const extra of overrides.extraPatterns || []) {
    const page = pages.find(p => p.pageNum === extra.bookPage);
    patterns.push(buildPattern(
      { name: extra.name, category: extra.category, chapter: extra.chapter, chapterTitle: CHAPTER_NAMES[extra.chapter], fullTitle: extra.name },
      page,
      extra,
    ));
  }

  // 補充：前言與導讀頁（004 等）若未出現在 TOC
  const coveredPages = new Set(patterns.map(p => p.bookPage).filter(Boolean));
  for (const page of pages) {
    if (coveredPages.has(page.pageNum) || page.pageNum <= 4) continue;
    if (page.summary.length < 20) continue;
    const entry = {
      chapter: page.pageNum <= 13 ? 1 : page.pageNum <= 19 ? 2 : page.pageNum <= 35 ? 3 : page.pageNum <= 49 ? 4 : page.pageNum <= 63 ? 5 : 6,
      chapterTitle: '',
      category: '書頁',
      name: page.headline || page.titleLines[page.titleLines.length - 1] || `第 ${page.pageNum} 頁`,
      fullTitle: page.titleBlob.slice(0, 60),
    };
    entry.chapterTitle = CHAPTER_NAMES[entry.chapter] || '';
    patterns.push(buildPattern(entry, page, {}));
  }

  const final = dedupePatterns(patterns).sort((a, b) => {
    if (a.chapter !== b.chapter) return a.chapter - b.chapter;
    if ((a.bookPage || 999) !== (b.bookPage || 999)) return (a.bookPage || 999) - (b.bookPage || 999);
    return a.name.localeCompare(b.name, 'zh-Hant');
  });

  const catalog = {
    version: 2,
    source: '短線交易日線圖大全',
    builtAt: new Date().toISOString(),
    stats: {
      total: final.length,
      automatable: final.filter(p => p.automatable).length,
      chapters: Object.keys(CHAPTER_NAMES).map(Number).filter(n => final.some(p => p.chapter === n)),
    },
    chapterNames: CHAPTER_NAMES,
    patterns: final,
  };

  fs.writeFileSync(OUT_PATH, JSON.stringify(catalog, null, 2) + '\n', 'utf8');
  console.log(`✓ catalog.json：${final.length} 筆（可掃描 ${catalog.stats.automatable}）→ ${OUT_PATH}`);
}

main();
