/**
 * acr install-harness — 把「用戶 CC harness」裝進當前專案。
 *
 * 對象：在「用 arcrun 開發」的專案裡工作的 CC + 使用者。讓使用者的 CC 自動載入 arcrun 防護：
 *   - CLAUDE.md 區塊（事前提醒：別自寫 Python）
 *   - .claude/skills/arcrun-mindset/（世界觀 + 資源去哪取）
 *   - .claude/commands/arcrun.md（/arcrun slash command）
 *   - .claude/hooks/arcrun-guard.sh + settings.json（做錯被糾正）
 *
 * 冪等：重裝不重複、不破壞使用者既有 CLAUDE.md / settings。
 * SDD：.agents/specs/user-cc-harness/design.md §2
 */
import { fileURLToPath } from 'node:url';
import {
  readFileSync, writeFileSync, existsSync, mkdirSync, copyFileSync, chmodSync, readdirSync,
} from 'node:fs';
import { join, dirname } from 'node:path';
import chalk from 'chalk';

/** harness 素材根目錄（內嵌 npm 套件，SSOT=cli/harness/）。
 *  build 後此檔在 dist/commands/，harness/ 在套件根 → ../../harness。 */
function harnessRoot(): string {
  const here = dirname(fileURLToPath(import.meta.url));     // .../dist/commands
  return join(here, '..', '..', 'harness');                // .../harness
}

const START = '<!-- arcrun-harness:start -->';
const END = '<!-- arcrun-harness:end -->';

export async function cmdInstallHarness(): Promise<void> {
  const cwd = process.cwd();
  const src = harnessRoot();

  if (!existsSync(src)) {
    console.error(chalk.red(`找不到 harness 素材（${src}）。套件安裝可能不完整，請重裝 arcrun。`));
    process.exit(1);
  }

  console.log(chalk.bold('\n  安裝 arcrun harness 到當前專案\n'));

  // 1. CLAUDE.md：append/取代 arcrun 區塊（標記包夾，冪等）
  installClaudeBlock(cwd, src);
  // 2. mindset Skill
  copyTree(join(src, 'skills'), join(cwd, '.claude', 'skills'));
  console.log(chalk.green('  ✓ .claude/skills/arcrun-mindset/'));
  // 3. /arcrun command
  copyTree(join(src, 'commands'), join(cwd, '.claude', 'commands'));
  console.log(chalk.green('  ✓ .claude/commands/arcrun.md'));
  // 4. hook
  const hookDst = join(cwd, '.claude', 'hooks', 'arcrun-guard.sh');
  mkdirSync(dirname(hookDst), { recursive: true });
  copyFileSync(join(src, 'hooks', 'arcrun-guard.sh'), hookDst);
  chmodSync(hookDst, 0o755);
  console.log(chalk.green('  ✓ .claude/hooks/arcrun-guard.sh'));
  // 5. settings.json：合併 hook 註冊（不覆蓋使用者既有設定）
  mergeSettings(cwd, src);
  console.log(chalk.green('  ✓ .claude/settings.json（已合併 arcrun guard hook）'));

  console.log(chalk.gray('\n  提示：'));
  console.log(chalk.gray('  • 首次在此專案開 Claude Code 會要求「信任工作區」，按信任 hook 才生效。'));
  console.log(chalk.gray('  • 之後跟 CC 說需求即可（或打 /arcrun <你的需求>）。'));
  console.log(chalk.gray('  • CC 偏好 MCP？可另跑 acr update 連 arcrun MCP（MCP 對齊中，optional）。\n'));
}

/** CLAUDE.md：無→建；有 arcrun 區塊→取代；有但無區塊→append。標記包夾，冪等。 */
function installClaudeBlock(cwd: string, src: string): void {
  const block = readFileSync(join(src, 'CLAUDE.block.md'), 'utf8').trim();
  const path = join(cwd, 'CLAUDE.md');

  if (!existsSync(path)) {
    writeFileSync(path, block + '\n', 'utf8');
    console.log(chalk.green('  ✓ CLAUDE.md（已建立，含 arcrun 區塊）'));
    return;
  }
  const cur = readFileSync(path, 'utf8');
  if (cur.includes(START) && cur.includes(END)) {
    // 取代既有區塊
    const re = new RegExp(escapeRe(START) + '[\\s\\S]*?' + escapeRe(END));
    writeFileSync(path, cur.replace(re, block), 'utf8');
    console.log(chalk.green('  ✓ CLAUDE.md（已更新 arcrun 區塊）'));
  } else {
    writeFileSync(path, cur.replace(/\s*$/, '') + '\n\n' + block + '\n', 'utf8');
    console.log(chalk.green('  ✓ CLAUDE.md（已附加 arcrun 區塊，未動既有內容）'));
  }
}

/** 把 settings.fragment.json 的 hook 合併進專案 settings.json（不覆蓋使用者既有 hooks/設定）。 */
function mergeSettings(cwd: string, src: string): void {
  const fragment = JSON.parse(readFileSync(join(src, 'settings.fragment.json'), 'utf8'));
  const path = join(cwd, '.claude', 'settings.json');
  mkdirSync(dirname(path), { recursive: true });

  let settings: Record<string, unknown> = {};
  if (existsSync(path)) {
    try { settings = JSON.parse(readFileSync(path, 'utf8')); } catch { settings = {}; }
  }

  const hooks = (settings.hooks ?? {}) as Record<string, unknown[]>;
  const fragHooks = fragment.hooks as Record<string, unknown[]>;
  for (const [event, entries] of Object.entries(fragHooks)) {
    const existing = Array.isArray(hooks[event]) ? hooks[event] : [];
    // 去重：避免重裝重複加 arcrun-guard
    const serialized = new Set(existing.map(e => JSON.stringify(e)));
    for (const e of entries) {
      if (!serialized.has(JSON.stringify(e))) existing.push(e);
    }
    hooks[event] = existing;
  }
  settings.hooks = hooks;
  writeFileSync(path, JSON.stringify(settings, null, 2) + '\n', 'utf8');
}

/** 建置期產物的來源片段（`SKILL.md.head` / `.tail`），只給 build-harness-skill.mjs 用，
 *  不該被鋪進使用者專案（使用者拿到的是拼接好的 `SKILL.md`）。 */
function isBuildSource(name: string): boolean {
  return name.endsWith('.head') || name.endsWith('.tail');
}

/** 遞迴複製目錄樹（覆蓋同名檔；跳過建置期來源片段）。 */
function copyTree(srcDir: string, dstDir: string): void {
  if (!existsSync(srcDir)) return;
  mkdirSync(dstDir, { recursive: true });
  for (const name of readdirSync(srcDir, { withFileTypes: true })) {
    if (isBuildSource(name.name)) continue;
    const s = join(srcDir, name.name);
    const d = join(dstDir, name.name);
    if (name.isDirectory()) copyTree(s, d);
    else copyFileSync(s, d);
  }
}

function escapeRe(s: string): string {
  return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
