/**
 * v2.5.11: discover Claude Code's real, enabled commands + plugin skills from
 * disk so the coder slash menu shows them (claude is PTY — no ACP discovery).
 *
 * Scope (v1): user-global only — `~/.claude/commands/*.md` plus the enabled
 * plugins listed in `~/.claude/settings.json:enabledPlugins` (user-scope install
 * paths from `~/.claude/plugins/.../installed_plugins.json`). Project-local
 * plugins and `<cwd>/.claude/commands` are deferred. Names are bare.
 */
import { readFileSync, readdirSync, existsSync } from 'node:fs';
import { homedir } from 'node:os';
import { join } from 'node:path';
import type { AgentCommand } from './provider-types.js';

/**
 * Frontmatter reader between `---` fences. Handles single-line `key: value`
 * AND YAML block scalars (`key: >` folded / `key: |` literal) whose value
 * spans the following more-indented lines — the shape most plugin SKILL.md
 * descriptions use (`description: >`).
 */
function frontmatterField(content: string, field: string): string | undefined {
  const block = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
  if (!block?.[1]) return undefined;
  const lines = block[1].split(/\r?\n/);
  const keyRe = new RegExp(`^(\\s*)${field}:\\s*(.*)$`);
  for (let i = 0; i < lines.length; i++) {
    const m = lines[i]?.match(keyRe);
    if (!m) continue;
    const keyIndent = (m[1] ?? '').length;
    const inline = (m[2] ?? '').trim();
    // Block scalar: `>` (folded) or `|` (literal), optional chomping `+`/`-`.
    if (/^[>|][+-]?$/.test(inline)) {
      const folded = inline[0] === '>';
      const body: string[] = [];
      for (let j = i + 1; j < lines.length; j++) {
        const line = lines[j] ?? '';
        if (line.trim() === '') {
          body.push('');
          continue;
        }
        const indent = line.length - line.trimStart().length;
        if (indent <= keyIndent) break; // dedent ends the block
        body.push(line.slice(keyIndent + 1));
      }
      const joined = folded
        ? body
            .map((l) => l.trim())
            .join(' ')
            .replace(/\s+/g, ' ')
            .trim()
        : body.join('\n').replace(/\n+$/, '');
      return joined || undefined;
    }
    return inline.replace(/^["']|["']$/g, '').trim() || undefined;
  }
  return undefined;
}

function readCommandDir(dir: string): AgentCommand[] {
  if (!existsSync(dir)) return [];
  let files: string[];
  try {
    files = readdirSync(dir);
  } catch {
    return [];
  }
  const out: AgentCommand[] = [];
  for (const f of files) {
    if (!f.endsWith('.md')) continue;
    let description: string | undefined;
    try {
      description = frontmatterField(readFileSync(join(dir, f), 'utf8'), 'description');
    } catch {
      /* unreadable — still list the command by name */
    }
    out.push({ name: f.slice(0, -3), kind: 'command', ...(description ? { description } : {}) });
  }
  return out;
}

function readSkillDir(dir: string): AgentCommand[] {
  if (!existsSync(dir)) return [];
  let entries: string[];
  try {
    entries = readdirSync(dir);
  } catch {
    return [];
  }
  const out: AgentCommand[] = [];
  for (const sub of entries) {
    const skillMd = join(dir, sub, 'SKILL.md');
    if (!existsSync(skillMd)) continue;
    let content: string;
    try {
      content = readFileSync(skillMd, 'utf8');
    } catch {
      continue;
    }
    out.push({
      name: frontmatterField(content, 'name') ?? sub,
      kind: 'skill',
      ...(() => {
        const d = frontmatterField(content, 'description');
        return d ? { description: d } : {};
      })(),
    });
  }
  return out;
}

export function discoverClaudeCommands(): AgentCommand[] {
  const root = join(homedir(), '.claude');
  const out: AgentCommand[] = [];

  // User custom commands.
  out.push(...readCommandDir(join(root, 'commands')));

  // Enabled plugins (user-scope installs).
  try {
    const settings = JSON.parse(readFileSync(join(root, 'settings.json'), 'utf8')) as {
      enabledPlugins?: Record<string, boolean>;
    };
    const installed = JSON.parse(
      readFileSync(join(root, 'plugins', 'installed_plugins.json'), 'utf8'),
    ) as { plugins?: Record<string, Array<{ scope?: string; installPath?: string }>> };

    const enabled = settings.enabledPlugins ?? {};
    const plugins = installed.plugins ?? {};
    for (const [key, on] of Object.entries(enabled)) {
      if (!on) continue;
      const installs = plugins[key] ?? [];
      const installPath = (installs.find((i) => i.scope === 'user') ?? installs[0])?.installPath;
      if (!installPath || !existsSync(installPath)) continue;
      out.push(...readSkillDir(join(installPath, 'skills')));
      out.push(...readCommandDir(join(installPath, 'commands')));
    }
  } catch {
    /* missing/unreadable plugin config → user commands only */
  }

  // Dedupe by name (first wins).
  const seen = new Set<string>();
  return out.filter((c) => (seen.has(c.name) ? false : (seen.add(c.name), true)));
}
