// Skill bundle install/uninstall logic — auto-detect agent tools and deploy.
const fs = require('fs');
const path = require('path');
const os = require('os');

const RULES_SOURCE = 'routing_zh.md';
const RULES_FILENAME = 'agent-pojia-reverse-skill.md';
const APPEND_BEGIN = '<!-- AGENT-POJIA-BEGIN -->';
const APPEND_END = '<!-- AGENT-POJIA-END -->';

// Bundled skills live in resources/skills (extraResources in build)
function bundledSkillsDir() {
  if (process.env.AGENT_POJIA_DEV_SKILLS) return process.env.AGENT_POJIA_DEV_SKILLS;
  if (process.env.AGENT_POJIA_RESOURCES) return path.join(process.env.AGENT_POJIA_RESOURCES, 'skills');
  // packaged: resources/skills under process.resourcesPath
  try { return path.join(process.resourcesPath, 'skills'); } catch { return null; }
}

function dirHasEntry(dir, prefix) {
  try { return fs.readdirSync(dir).some(n => n.startsWith(prefix)); } catch { return false; }
}

// 已知的 Agent 工具及其部署方式：
// - skills-dir：部署技能目录（Claude / Codex / Kimi / Reasonix）
// - rules-file：部署单文件路由规则（Cursor / Cline / Kiro steering）
// - rules-file-append：以标记块追加到全局规则文件（Windsurf global_rules.md）
function knownTargets() {
  const home = os.homedir();
  const isMac = process.platform === 'darwin';
  const programs = isMac ? '/Applications' : path.join(home, 'AppData', 'Local', 'Programs');
  const reasonixBase = isMac
    ? path.join(home, 'Library', 'Application Support', 'reasonix')
    : path.join(home, 'AppData', 'Roaming', 'reasonix');
  return [
    {
      id: 'claude', name: 'Claude Code', kind: 'skills-dir',
      dest: path.join(home, '.claude', 'skills'),
      detected: () => fs.existsSync(path.join(home, '.claude'))
    },
    {
      id: 'codex', name: 'Codex (ChatGPT)', kind: 'skills-dir',
      dest: path.join(home, '.codex', 'skills'),
      detected: () => fs.existsSync(path.join(home, '.codex'))
    },
    {
      id: 'kimi', name: 'Kimi Code', kind: 'skills-dir',
      dest: path.join(home, '.agents', 'skills'),
      detected: () => fs.existsSync(path.join(home, '.kimi-code')) || fs.existsSync(path.join(home, '.agents'))
    },
    {
      id: 'reasonix', name: 'Reasonix', kind: 'skills-dir',
      dest: path.join(reasonixBase, 'global-workspace', '.reasonix', 'skills'),
      detected: () => fs.existsSync(reasonixBase)
    },
    {
      id: 'cursor', name: 'Cursor', kind: 'rules-file',
      dest: path.join(home, '.cursor', 'rules', RULES_FILENAME),
      detected: () => fs.existsSync(path.join(home, '.cursor')) || fs.existsSync(path.join(programs, isMac ? 'Cursor.app' : 'Cursor'))
    },
    {
      id: 'cline', name: 'Cline (VS Code)', kind: 'rules-file',
      dest: path.join(home, 'Documents', 'Cline', 'Rules', RULES_FILENAME),
      detected: () =>
        dirHasEntry(path.join(home, '.vscode', 'extensions'), 'saoudrizwan.claude-dev') ||
        dirHasEntry(path.join(home, '.cursor', 'extensions'), 'saoudrizwan.claude-dev') ||
        fs.existsSync(path.join(home, 'Documents', 'Cline'))
    },
    {
      id: 'windsurf', name: 'Windsurf', kind: 'rules-file-append',
      dest: path.join(home, '.codeium', 'windsurf', 'memories', 'global_rules.md'),
      detected: () => fs.existsSync(path.join(home, '.codeium', 'windsurf')) || fs.existsSync(path.join(programs, isMac ? 'Windsurf.app' : 'Windsurf'))
    },
    {
      id: 'kiro', name: 'Kiro', kind: 'rules-file',
      dest: path.join(home, '.kiro', 'steering', 'agent-pojia-reverse-routing.md'),
      detected: () => fs.existsSync(path.join(home, '.kiro')) || fs.existsSync(path.join(programs, isMac ? 'Kiro.app' : 'Kiro'))
    }
  ];
}

// 返回全部已知工具及本机检测结果
function detectTargets() {
  return knownTargets().map(t => ({
    id: t.id, name: t.name, kind: t.kind, path: t.dest, detected: t.detected()
  }));
}

function listBundled() {
  const dir = bundledSkillsDir();
  if (!dir || !fs.existsSync(dir)) return [];
  return fs.readdirSync(dir).filter(n => fs.statSync(path.join(dir, n)).isDirectory());
}

// A skill name is only ever a plain directory name: no separators, no traversal.
const NAME_RE = /^[A-Za-z0-9._-]+$/;
function isValidName(name) {
  return typeof name === 'string' && NAME_RE.test(name) && name !== '.' && name !== '..';
}

// Defense in depth: resolved path must stay inside base.
function resolveInside(base, name) {
  const p = path.resolve(base, name);
  const root = path.resolve(base);
  if (p !== root && !p.startsWith(root + path.sep)) return null;
  return p;
}

// Names must additionally exist in the bundled whitelist.
function sanitizeNames(skillNames) {
  if (!Array.isArray(skillNames)) return [];
  const whitelist = new Set(listBundled());
  const out = [];
  for (const n of skillNames) {
    if (isValidName(n) && whitelist.has(n)) out.push(n);
  }
  return [...new Set(out)];
}

function copyDir(src, dst) {
  fs.mkdirSync(dst, { recursive: true });
  for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
    const s = path.join(src, entry.name);
    const d = path.join(dst, entry.name);
    if (entry.isSymbolicLink()) continue; // never follow links out of the bundle
    if (entry.isDirectory()) { copyDir(s, d); continue; }
    // Do not write through a pre-existing symlink/junction at the destination.
    try { if (fs.lstatSync(d).isSymbolicLink()) fs.unlinkSync(d); } catch {}
    fs.copyFileSync(s, d);
  }
}

function rulesContent() {
  const src = bundledSkillsDir();
  if (!src) throw new Error('no-bundle');
  const body = fs.readFileSync(path.join(src, RULES_SOURCE), 'utf8');
  return '# AGENT 破甲 · 逆向技能路由（全局规则）\n\n' +
    '> 本文件由 AGENT 破甲自动部署，卸载时会被移除。\n\n' + body;
}

function installRulesFile(dest) {
  fs.mkdirSync(path.dirname(dest), { recursive: true });
  fs.writeFileSync(dest, rulesContent());
  return dest;
}

function uninstallRulesFile(dest) {
  if (fs.existsSync(dest)) { fs.rmSync(dest, { force: true }); return true; }
  return false;
}

function stripAppendBlock(text) {
  const re = new RegExp('\\n?' + APPEND_BEGIN.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') +
    '[\\s\\S]*?' + APPEND_END.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + '\\n?', 'g');
  return text.replace(re, '\n');
}

function installRulesAppend(dest) {
  fs.mkdirSync(path.dirname(dest), { recursive: true });
  let existing = '';
  try { existing = fs.readFileSync(dest, 'utf8'); } catch {}
  const block = APPEND_BEGIN + '\n\n' + rulesContent() + '\n\n' + APPEND_END;
  const stripped = stripAppendBlock(existing).trimEnd();
  fs.writeFileSync(dest, (stripped ? stripped + '\n\n' : '') + block + '\n');
  return dest;
}

function uninstallRulesAppend(dest) {
  if (!fs.existsSync(dest)) return false;
  const existing = fs.readFileSync(dest, 'utf8');
  const stripped = stripAppendBlock(existing).trim();
  if (stripped === existing.trim()) return false; // 没有我们的块
  if (stripped) fs.writeFileSync(dest, stripped + '\n');
  else fs.rmSync(dest, { force: true }); // 文件是我们创建的，内容清空后删除
  return true;
}

function install(targetId, skillNames) {
  const target = knownTargets().find(t => t.id === targetId);
  if (!target) return { ok: false, reason: 'unknown-target' };
  if (target.kind === 'rules-file') {
    try { installRulesFile(target.dest); return { ok: true, installed: [RULES_FILENAME], failed: [], target: target.dest }; }
    catch (e) { return { ok: false, reason: 'write-failed: ' + e.message }; }
  }
  if (target.kind === 'rules-file-append') {
    try { installRulesAppend(target.dest); return { ok: true, installed: ['global_rules.md(追加块)'], failed: [], target: target.dest }; }
    catch (e) { return { ok: false, reason: 'write-failed: ' + e.message }; }
  }
  // skills-dir
  const src = bundledSkillsDir();
  if (!src) return { ok: false, reason: 'no-bundle' };
  const names = sanitizeNames(skillNames);
  if (!names.length) return { ok: false, reason: 'no-valid-names' };
  fs.mkdirSync(target.dest, { recursive: true });
  const installed = [];
  const failed = [];
  for (const name of names) {
    try {
      const s = resolveInside(src, name);
      const d = s && resolveInside(target.dest, name);
      if (!s || !d || !fs.existsSync(s) || !fs.statSync(s).isDirectory()) { failed.push(name); continue; }
      copyDir(s, d);
      installed.push(name);
    } catch { failed.push(name); }
  }
  return { ok: failed.length === 0, installed, failed, target: target.dest };
}

function uninstall(targetId, skillNames) {
  const target = knownTargets().find(t => t.id === targetId);
  if (!target) return { ok: false, reason: 'unknown-target' };
  if (target.kind === 'rules-file') {
    const removed = uninstallRulesFile(target.dest);
    return { ok: true, removed: removed ? [RULES_FILENAME] : [] };
  }
  if (target.kind === 'rules-file-append') {
    const removed = uninstallRulesAppend(target.dest);
    return { ok: true, removed: removed ? ['global_rules.md(追加块)'] : [] };
  }
  const names = sanitizeNames(skillNames);
  if (!names.length) return { ok: false, reason: 'no-valid-names' };
  const removed = [];
  const failed = [];
  for (const name of names) {
    try {
      const p = resolveInside(target.dest, name);
      if (!p) { failed.push(name); continue; }
      if (fs.existsSync(p)) { fs.rmSync(p, { recursive: true, force: true }); removed.push(name); }
    } catch { failed.push(name); }
  }
  return { ok: failed.length === 0, removed, failed };
}

module.exports = { listBundled, detectTargets, install, uninstall };
