#!/usr/bin/env node
/**
 * TFDS B-End CLI —— 让 AI / 工程把「读规范」变成确定性命令调用。
 *
 * 用法（在已安装 @tfdesign/b-end 的项目里）：
 *   npx tfds spec <id>                 打印某组件/模板的完整契约（props/rules/examples/starterSnippet）
 *   npx tfds spec --pattern <关键词>    模糊查找页面模板并打印其契约
 *   npx tfds spec --block <id>         打印某「自定义区块」兜底原型（tokenRoles/交互态/骨架）
 *   npx tfds blocks                    列出全部第二层自定义区块（组件未覆盖时的兜底）
 *   npx tfds list [component|page-pattern|page-template]   列出可用条目（轻量）
 *   npx tfds archetypes                列出全部页面范式（第 0 步选型用）
 *   npx tfds archetype <id>            打印单个范式的完整决策数据
 *   npx tfds validate-plan [file]      校验 tfdsPlan（YAML/JSON；缺省读 stdin）是否满足范式 mustUse/mustNotUse
 *
 * 设计目标：替代「靠模型自律 grep 640KB 索引」，提供可被 Agent / CI 调用的稳定接口。
 */
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const SKILL_NAME = '产品设计-体服平台B端-界面生成';
const skillDir = path.resolve(__dirname, '..', 'skills', SKILL_NAME);

function fail(msg) {
  console.error(`[tfds] ${msg}`);
  process.exit(1);
}

function readJson(p) {
  if (!fs.existsSync(p)) fail(`找不到文件：${path.relative(process.cwd(), p)}（请确认 @tfdesign/b-end 已正确安装/构建）`);
  return JSON.parse(fs.readFileSync(p, 'utf8'));
}

export function loadSummary() {
  return readJson(path.join(skillDir, 'components.summary.json'));
}
export function loadArchetypes() {
  return readJson(path.join(skillDir, 'archetypes.json'));
}
export function loadCustomBlocks() {
  return readJson(path.join(skillDir, 'custom-blocks.json'));
}

/** 把任意 token（id / 导出名 / 中文名）归一化为组件 id，便于比较 */
function buildCanonicalizer(summary) {
  const tokenToId = new Map();
  for (const e of summary.entries || []) {
    const id = e.id;
    tokenToId.set(id.toLowerCase(), id);
    if (e.import?.default) tokenToId.set(String(e.import.default).toLowerCase(), id);
    if (e.name) tokenToId.set(String(e.name).toLowerCase(), id);
    for (const k of e.import?.named || []) tokenToId.set(String(k).toLowerCase(), id);
  }
  return (token) => {
    if (token == null) return null;
    const key = String(token).trim().toLowerCase();
    return tokenToId.get(key) || key; // 找不到映射时返回小写原值，仍可弱比较
  };
}

// ---------------- spec ----------------
function cmdSpec(args) {
  const blockFlagIdx = args.findIndex((a) => a === '--block' || a === '-b');
  if (blockFlagIdx >= 0) return cmdSpecBlock(args[blockFlagIdx + 1]);

  const patternFlagIdx = args.findIndex((a) => a === '--pattern' || a === '-p');
  const isPattern = patternFlagIdx >= 0;
  const query = (isPattern ? args[patternFlagIdx + 1] : args[0]) || '';
  if (!query) fail('用法：tfds spec <id>  ·  tfds spec --pattern <关键词>  ·  tfds spec --block <id>');

  const summary = loadSummary();
  const entries = summary.entries || [];

  let entry = null;
  if (isPattern) {
    const pool = entries.filter((e) => e.kind === 'page-pattern' || e.kind === 'page-template');
    entry = matchEntry(pool, query) || matchEntry(entries, query);
  } else {
    entry = entries.find((e) => e.id === query) || matchEntry(entries, query);
  }
  if (!entry) {
    const sugg = entries
      .map((e) => e.id)
      .filter((id) => id.includes(query.toLowerCase()))
      .slice(0, 8);
    fail(`未命中条目「${query}」。${sugg.length ? `相近 id：${sugg.join(', ')}` : '用 `tfds list` 查看全部。'}`);
  }

  const detailPath = path.join(skillDir, 'detail', `${entry.id}.json`);
  const detail = readJson(detailPath);
  printDetail(detail);
}

function matchEntry(pool, query) {
  const q = query.toLowerCase();
  return (
    pool.find((e) => e.id === q) ||
    pool.find((e) => String(e.import?.default || '').toLowerCase() === q) ||
    pool.find((e) => String(e.name || '').toLowerCase() === q) ||
    pool.find((e) => e.id.includes(q) || String(e.name || '').toLowerCase().includes(q)) ||
    pool.find((e) => (e.keywords || []).some((k) => String(k).toLowerCase().includes(q))) ||
    null
  );
}

function printDetail(d) {
  const lines = [];
  lines.push(`# ${d.name}  (${d.id} · ${d.kind})`);
  if (d.description) lines.push(d.description);
  if (d.import) {
    const named = d.import.named ? `, { ${d.import.named.join(', ')} }` : '';
    lines.push(`\nimport ${d.import.default}${named} from '${d.import.from}';`);
  }
  if (d.archetype) {
    lines.push(`\n范式 archetype: ${d.archetype}   recipe: ${d.recipe}`);
    if (d.mustUse?.length) lines.push(`必须使用: ${d.mustUse.join(', ')}`);
    if (d.mustNotUse?.length) lines.push(`禁止使用: ${d.mustNotUse.join(', ')}`);
  }
  if (d.props?.length) {
    lines.push('\n## props');
    for (const p of d.props) {
      const opt = p.options ? ` = ${p.options.join(' | ')}` : '';
      const def = p.default !== undefined && p.default !== null ? `  (default: ${JSON.stringify(p.default)})` : '';
      lines.push(`- ${p.name}: ${p.type}${opt}${def}`);
    }
  }
  if (d.rules?.length) {
    lines.push('\n## rules');
    d.rules.forEach((r, i) => lines.push(`${i + 1}. ${r}`));
  }
  if (d.examples?.length) {
    lines.push('\n## examples');
    for (const ex of d.examples) lines.push(`- ${ex.label}: ${ex.code}`);
  }
  if (d.starterSnippet) {
    lines.push('\n## starterSnippet');
    lines.push(d.starterSnippet);
  }
  console.log(lines.join('\n'));
}

// ---------------- custom blocks（第二层兜底）----------------
function cmdSpecBlock(query) {
  const { blocks, interactionStates } = loadCustomBlocks();
  if (!query) {
    fail(`用法：tfds spec --block <id>。可用区块：${(blocks || []).map((b) => b.id).join(', ')}`);
  }
  const q = String(query).toLowerCase();
  const block =
    blocks.find((b) => b.id === q) ||
    blocks.find((b) => b.id.includes(q) || String(b.name || '').toLowerCase().includes(q));
  if (!block) {
    fail(`未命中区块「${query}」。可用：${blocks.map((b) => b.id).join(', ')}`);
  }
  printBlock(block, interactionStates);
}

function cmdBlocks() {
  const { blocks } = loadCustomBlocks();
  console.log('# 第二层自定义区块（组件/模板都不覆盖时的兜底骨架；先确认确无对应组件再用）\n');
  for (const b of blocks) {
    console.log(`## ${b.id}  —  ${b.name}${b.interactive ? '  [可交互]' : ''}`);
    console.log(`   适用：${b.when}`);
    if (b.requiredStates?.length) console.log(`   必备交互态：${b.requiredStates.join(', ')}`);
    console.log('');
  }
  console.log('用 `tfds spec --block <id>` 取单个区块的 tokenRoles / starterSnippet / 自检清单。');
}

function printBlock(b, interactionStates) {
  const lines = [];
  lines.push(`# 自定义区块 · ${b.name}  (${b.id})${b.interactive ? '  [可交互]' : ''}`);
  lines.push(`\n适用：${b.when}`);
  if (b.principleRefs?.length) {
    lines.push('\n## principleRefs（气质取舍 · 骨架只给"形"，这里给"为什么"，必读）');
    b.principleRefs.forEach((p) => lines.push(`- ${p}`));
  }
  if (b.tokenRoles && Object.keys(b.tokenRoles).length) {
    lines.push('\n## tokenRoles（角色 → token，禁止硬编码）');
    for (const [role, token] of Object.entries(b.tokenRoles)) lines.push(`- ${role}: ${token}`);
  }
  if (b.requiredStates?.length) {
    lines.push('\n## requiredStates（必须实现的交互态）');
    const byState = new Map((interactionStates || []).map((s) => [s.state, s]));
    for (const s of b.requiredStates) {
      const def = byState.get(s);
      lines.push(`- ${s}${def ? `: ${def.token}（${def.note}）` : ''}`);
    }
  }
  if (b.forbidden?.length) {
    lines.push('\n## forbidden（禁止）');
    b.forbidden.forEach((f) => lines.push(`- ${f}`));
  }
  if (b.starterSnippet) {
    lines.push('\n## starterSnippet（合规骨架，按需替换内容）');
    lines.push(b.starterSnippet);
  }
  if (b.checklist?.length) {
    lines.push('\n## checklist（生成后自检）');
    b.checklist.forEach((c, i) => lines.push(`${i + 1}. ${c}`));
  }
  console.log(lines.join('\n'));
}

// ---------------- list ----------------
function cmdList(args) {
  const kind = args[0];
  const summary = loadSummary();
  const entries = (summary.entries || []).filter((e) => !kind || e.kind === kind);
  const byKind = {};
  for (const e of entries) (byKind[e.kind] ||= []).push(e);
  for (const k of Object.keys(byKind)) {
    console.log(`\n# ${k} (${byKind[k].length})`);
    for (const e of byKind[k]) console.log(`  ${e.id}  —  ${e.name}`);
  }
}

// ---------------- archetypes ----------------
function cmdArchetypes() {
  const { archetypes } = loadArchetypes();
  console.log('# 页面范式（第 0 步选型；先判 archetype 再选组件）\n');
  for (const a of archetypes) {
    console.log(`## ${a.id}  →  ${a.pattern}  (recipe: ${a.recipe})`);
    console.log(`   ${a.name} —— ${a.firstAction}`);
    console.log(`   触发词: ${(a.triggers || []).slice(0, 8).join('、')}`);
    if (a.mustUse?.length) console.log(`   必须使用: ${a.mustUse.join(', ')}`);
    if (a.mustNotUse?.length) console.log(`   禁止使用: ${a.mustNotUse.join(', ')}`);
    console.log('');
  }
}

function cmdArchetype(args) {
  const id = args[0];
  if (!id) fail('用法：tfds archetype <id>（用 `tfds archetypes` 看全部 id）');
  const { archetypes } = loadArchetypes();
  const a = archetypes.find((x) => x.id === id || x.pattern === id);
  if (!a) fail(`未找到范式「${id}」。可用：${archetypes.map((x) => x.id).join(', ')}`);
  console.log(JSON.stringify(a, null, 2));
}

// ---------------- validate-plan ----------------
function readStdin() {
  try {
    return fs.readFileSync(0, 'utf8');
  } catch {
    return '';
  }
}

/** 极简 YAML 子集解析：支持 key: value、内联数组 [a, b]、块数组(- item)、可选 tfdsPlan: 包裹、```fenced``` */
export function parsePlan(raw) {
  let text = String(raw).trim();
  if (!text) fail('plan 为空。请传入文件或通过 stdin 提供 tfdsPlan（YAML/JSON）。');

  // 去掉 markdown 代码围栏
  const fence = text.match(/```[a-zA-Z]*\n([\s\S]*?)```/);
  if (fence) text = fence[1].trim();

  // 尝试 JSON
  if (text.startsWith('{')) {
    try {
      const obj = JSON.parse(text);
      return obj.tfdsPlan || obj;
    } catch {
      /* fallthrough to yaml */
    }
  }

  const lines = text.split('\n');
  const obj = {};
  let curKey = null;
  const splitInline = (v) =>
    v
      .replace(/^\[|\]$/g, '')
      .split(',')
      .map((s) => s.trim().replace(/^["']|["']$/g, ''))
      .filter(Boolean);

  for (let line of lines) {
    if (!line.trim() || line.trim().startsWith('#')) continue;
    const blockItem = line.match(/^\s*-\s+(.*)$/);
    if (blockItem && curKey) {
      (obj[curKey] ||= []);
      if (Array.isArray(obj[curKey])) obj[curKey].push(blockItem[1].trim().replace(/^["']|["']$/g, ''));
      continue;
    }
    const kv = line.match(/^\s*([A-Za-z0-9_]+)\s*:\s*(.*)$/);
    if (!kv) continue;
    const key = kv[1];
    let val = kv[2].trim();
    if (key === 'tfdsPlan') {
      continue; // 包裹键，忽略
    }
    if (val === '') {
      curKey = key;
      obj[key] = [];
    } else if (val.startsWith('[')) {
      obj[key] = splitInline(val);
      curKey = null;
    } else {
      obj[key] = val.replace(/^["']|["']$/g, '');
      curKey = null;
    }
  }
  return obj;
}

/**
 * 纯校验核心：对一份 plan 校验是否满足范式 mustUse/mustNotUse。
 * 返回 { errors, warns, oks, archetype }，不打印、不退出，供 CLI 与 eval 复用。
 */
export function evaluatePlan(plan, archetypes, summary) {
  const canon = buildCanonicalizer(summary);
  const errors = [];
  const warns = [];
  const oks = [];

  const archId = plan.archetype || plan.id;
  if (!archId) errors.push('plan 缺少 `archetype` 字段。');
  const arc =
    archetypes.find((a) => a.id === archId) ||
    archetypes.find((a) => a.pattern === plan.pattern);
  if (!arc) {
    errors.push(
      `archetype「${archId || plan.pattern || '?'}」无效。可用：${archetypes.map((a) => a.id).join(', ')}`,
    );
    return { errors, warns, oks, archetype: null };
  }
  oks.push(`命中范式：${arc.id} (${arc.name})`);

  if (plan.pattern && plan.pattern !== arc.pattern) {
    errors.push(`pattern 不匹配：plan=${plan.pattern}，范式要求=${arc.pattern}`);
  } else {
    oks.push(`pattern=${arc.pattern} ✓`);
  }
  if (plan.recipe && plan.recipe !== arc.recipe) {
    warns.push(`recipe 建议为 ${arc.recipe}（plan 写的是 ${plan.recipe}）`);
  }

  const planComps = new Set((plan.components || []).map((c) => canon(c)));
  const planForbidden = new Set((plan.forbidden || []).map((c) => canon(c)));

  for (const m of arc.mustUse || []) {
    if (planComps.has(canon(m))) oks.push(`必含 ${m} ✓`);
    else errors.push(`缺少必需组件：${m}（范式 ${arc.id} 要求 mustUse）`);
  }
  for (const m of arc.mustNotUse || []) {
    if (planComps.has(canon(m))) errors.push(`出现禁用组件：${m}（范式 ${arc.id} mustNotUse）`);
    else oks.push(`未用禁用项 ${m} ✓`);
  }
  for (const m of arc.avoid || []) {
    const cid = canon(m);
    if (planComps.has(cid)) warns.push(`建议规避：${m}`);
  }
  for (const m of arc.mustUse || []) {
    if (planForbidden.has(canon(m))) errors.push(`plan.forbidden 包含了范式必需组件：${m}`);
  }

  return { errors, warns, oks, archetype: arc };
}

function cmdValidatePlan(args) {
  const file = args.find((a) => !a.startsWith('-'));
  const raw = file ? fs.readFileSync(path.resolve(process.cwd(), file), 'utf8') : readStdin();
  const plan = parsePlan(raw);
  const { archetypes } = loadArchetypes();
  const summary = loadSummary();
  const { errors, warns, oks } = evaluatePlan(plan, archetypes, summary);
  return report(errors, warns, oks);
}

function report(errors, warns, oks) {
  for (const o of oks) console.log(`  ✓ ${o}`);
  for (const w of warns) console.log(`  ⚠ ${w}`);
  for (const e of errors) console.log(`  ✗ ${e}`);
  if (errors.length) {
    console.log(`\n[tfds] validate-plan 未通过：${errors.length} 个错误，${warns.length} 个告警。`);
    process.exit(1);
  }
  console.log(`\n[tfds] validate-plan 通过${warns.length ? `（${warns.length} 个告警）` : ''}。`);
}

// ---------------- help ----------------
function help() {
  console.log(
    [
      'TFDS B-End CLI',
      '',
      '  tfds spec <id>                组件/模板完整契约（props/rules/examples/starter）',
      '  tfds spec --pattern <kw>      模糊查找页面模板并打印契约',
      '  tfds spec --block <id>        自定义区块兜底原型（tokenRoles/交互态/骨架）',
      '  tfds blocks                   列出全部第二层自定义区块',
      '  tfds list [kind]              列出条目（component | page-pattern | page-template）',
      '  tfds archetypes               列出全部页面范式（第 0 步选型）',
      '  tfds archetype <id>           单个范式完整决策数据（JSON）',
      '  tfds validate-plan [file]     校验 tfdsPlan（YAML/JSON；缺省读 stdin）',
    ].join('\n'),
  );
}

function main() {
  const [cmd, ...args] = process.argv.slice(2);
  switch (cmd) {
    case 'spec':
      return cmdSpec(args);
    case 'blocks':
      return cmdBlocks();
    case 'list':
      return cmdList(args);
    case 'archetypes':
      return cmdArchetypes();
    case 'archetype':
      return cmdArchetype(args);
    case 'validate-plan':
      return cmdValidatePlan(args);
    case undefined:
    case '-h':
    case '--help':
    case 'help':
      return help();
    default:
      console.error(`[tfds] 未知命令：${cmd}\n`);
      return help();
  }
}

// 仅在被直接执行时运行 CLI；作为模块 import 时只暴露纯函数（供 eval 复用）。
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
  main();
}
