/**
 * NERF Command Router
 *
 * Central routing for all /nerf:* slash commands.
 * Parses input, loads command templates, substitutes arguments,
 * and maps commands to either mode-based prompt injection or handler dispatch.
 */

import { readFileSync, readdirSync, existsSync } from 'node:fs';
import { join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = resolve(__filename, '..');
// Resolution order:
//   1. src/resources/extensions/nerf/prompts  — dev / repo mode
//   2. dist/resources/extensions/nerf/prompts — global npm install (src/ not in tarball)
//   3. commands/                               — legacy fallback
const COMMANDS_DIR_PRIMARY   = resolve(__dirname, '..', '..', 'src', 'resources', 'extensions', 'nerf', 'prompts');
const COMMANDS_DIR_SECONDARY = resolve(__dirname, '..', '..', 'dist', 'resources', 'extensions', 'nerf', 'prompts');
const COMMANDS_DIR_FALLBACK  = resolve(__dirname, '..', '..', 'commands');
const COMMANDS_DIR = existsSync(COMMANDS_DIR_PRIMARY)
  ? COMMANDS_DIR_PRIMARY
  : existsSync(COMMANDS_DIR_SECONDARY)
    ? COMMANDS_DIR_SECONDARY
    : COMMANDS_DIR_FALLBACK;

// ---------------------------------------------------------------------------
// MODE_COMMANDS: canonical map of mode command names to their NERF mode.
// Sourced from nerf-tools.js MODE_COMMANDS — kept in sync here as the
// command-router is the new single source of truth for routing.
// ---------------------------------------------------------------------------

const MODE_COMMANDS = new Map([
  ['redteam',     { mode: 'RED',       desc: 'Red team operations' }],
  ['hunt',        { mode: 'BLUE',      desc: 'Threat hunting' }],
  ['sigma',       { mode: 'BLUE',      desc: 'Sigma rule creation' }],
  ['hardening',   { mode: 'BLUE',      desc: 'System hardening guidance' }],
  ['forensics',   { mode: 'INCIDENT',  desc: 'Digital forensics' }],
  ['purple',      { mode: 'PURPLE',    desc: 'Purple team / adversary emulation' }],
  ['recon',       { mode: 'RECON',     desc: 'OSINT and reconnaissance' }],
  ['privacy',     { mode: 'PRIVACY',   desc: 'Privacy engineering' }],
  ['threatmodel', { mode: 'ARCHITECT', desc: 'Threat modeling' }],
  ['cloud',       { mode: 'ARCHITECT', desc: 'Cloud security' }],
  ['crypto',      { mode: 'ARCHITECT', desc: 'Cryptographic security' }],
  ['devsecops',   { mode: 'BUILD',     desc: 'DevSecOps pipeline security' }],
  ['ir',          { mode: 'INCIDENT',  desc: 'Incident response' }],
  ['cve',         { mode: 'RED',       desc: 'CVE analysis' }],
  ['threatintel', { mode: 'BLUE',      desc: 'Threat intelligence' }],
  ['web',         { mode: 'RED',       desc: 'Web application security' }],
  ['phishing',    { mode: 'BLUE',      desc: 'Phishing defense' }],
  ['malware',     { mode: 'INCIDENT',  desc: 'Malware analysis' }],
  ['mobile',      { mode: 'RED',       desc: 'Mobile security' }],
  ['audit',       { mode: 'ARCHITECT', desc: 'Compliance audit' }],
  ['insider',     { mode: 'BLUE',      desc: 'Insider threat detection' }],
  ['aisec',       { mode: 'ARCHITECT', desc: 'AI/ML security' }],
  ['ics',         { mode: 'ARCHITECT', desc: 'ICS/OT security' }],
]);

// Prompt-template commands: these have .md files in commands/ and work by
// loading the template, substituting $ARGUMENTS, and injecting as a prompt.
// Same dispatch path as mode commands, but without a specific NERF mode.
const PROMPT_COMMANDS = new Map([
  ['engage',          { desc: 'Start a new engagement' }],
  ['resume',          { desc: 'Resume engagement' }],
  ['brief',           { desc: 'Full target briefing' }],
  ['report',          { desc: 'Generate engagement report' }],
  ['cc',              { desc: 'Security query via Claude Code knowledge base' }],
  ['claudeapi',       { desc: 'Security query via Claude API' }],
  ['ollama',          { desc: 'Security query via local Ollama' }],
  ['smart',           { desc: 'Security query with auto-routing' }],
]);

// Handler commands: these call dedicated handler functions directly
// rather than injecting a prompt template. handler is wired at import time.
const HANDLER_COMMANDS = new Map([
  ['status',          { desc: 'Progress dashboard' }],
  ['quick',           { desc: 'Quick task with NERF guarantees' }],
  ['export',          { desc: 'Export engagement report' }],
  ['auto',            { desc: 'Autonomous mode' }],
  ['stop',            { desc: 'Stop autonomous mode' }],
  ['discuss',         { desc: 'Discuss architecture/tactics' }],
  ['steer',           { desc: 'Hard-steer plan documents' }],
  ['forensics-debug', { desc: 'Auto-mode failure investigation' }],
  ['queue',           { desc: 'Queue future campaigns' }],
  ['keys',            { desc: 'API key manager' }],
  ['logs',            { desc: 'Browse activity logs' }],
  ['setup-hooks',     { desc: 'Install NERF hooks' }],
  ['update',          { desc: 'Check for updates and upgrade' }],
  ['doctor',          { desc: 'Diagnostics and health checks' }],
]);

// ---------------------------------------------------------------------------
// COMMAND_REGISTRY: unified map of command name to routing metadata.
// type: 'mode' (mode+template), 'prompt' (template only), 'handler' (direct)
// ---------------------------------------------------------------------------

/** @type {Map<string, {type: 'mode'|'prompt'|'handler', mode?: string, desc: string, handler?: Function}>} */
export const COMMAND_REGISTRY = new Map();

for (const [name, { mode, desc }] of MODE_COMMANDS) {
  COMMAND_REGISTRY.set(name, { type: 'mode', mode, desc });
}

for (const [name, { desc }] of PROMPT_COMMANDS) {
  COMMAND_REGISTRY.set(name, { type: 'prompt', desc });
}

for (const [name, { desc }] of HANDLER_COMMANDS) {
  COMMAND_REGISTRY.set(name, { type: 'handler', desc, handler: null });
}

// ---------------------------------------------------------------------------
// wireHandlers(handlers) — connect handler functions to registry entries
// ---------------------------------------------------------------------------

/**
 * Wire handler functions into COMMAND_REGISTRY entries of type 'handler'.
 * @param {Record<string, Function>} handlers - map of command name to handler fn
 */
export function wireHandlers(handlers) {
  for (const [name, fn] of Object.entries(handlers)) {
    const entry = COMMAND_REGISTRY.get(name);
    if (entry && entry.type === 'handler') {
      entry.handler = fn;
    }
  }
}

// ---------------------------------------------------------------------------
// parseSlashCommand(input)
// ---------------------------------------------------------------------------

/**
 * Parse a slash command string into { command, args } or null.
 *
 * Accepts:
 *   /nerf              -> { command: 'nerf', args: '' }
 *   /nerf:redteam foo  -> { command: 'redteam', args: 'foo' }
 *   /nerf:web SQLi     -> { command: 'web', args: 'SQLi' }
 *
 * Returns null if input is not a /nerf command.
 *
 * @param {string} input
 * @returns {{ command: string, args: string } | null}
 */
export function parseSlashCommand(input) {
  if (typeof input !== 'string') return null;
  const trimmed = input.trim();
  if (!trimmed.startsWith('/nerf')) return null;

  // Match /nerf or /nerf:subcommand
  const match = trimmed.match(/^\/nerf(?::([a-zA-Z][\w-]*))?(?:\s+(.*))?$/);
  if (!match) return null;

  const subcommand = match[1] || null;
  const args = (match[2] || '').trim();

  // Bare /nerf
  if (!subcommand) {
    return { command: 'nerf', args };
  }

  return { command: subcommand, args };
}

// ---------------------------------------------------------------------------
// loadCommandTemplate(commandName)
// ---------------------------------------------------------------------------

/**
 * Strip YAML frontmatter (--- ... ---) and HTML comments from markdown content.
 * Returns the remaining template body trimmed.
 */
function stripFrontmatterAndComments(content) {
  // Strip HTML comments
  let text = content.replace(/<!--[\s\S]*?-->/g, '');
  // Strip leading whitespace left after comment removal
  text = text.trimStart();
  // Strip YAML frontmatter
  text = text.replace(/^---\n[\s\S]*?\n---\n?/, '');
  return text.trim();
}

/**
 * Parse YAML frontmatter from markdown content.
 * Returns an object with name and description, or null if no frontmatter.
 */
function parseFrontmatter(content) {
  const match = content.match(/^(?:<!--[\s\S]*?-->\s*)*---\n([\s\S]*?)\n---/);
  if (!match) return null;

  const fm = {};
  for (const line of match[1].split('\n')) {
    const kv = line.match(/^(\w+):\s*(.+)$/);
    if (kv) {
      fm[kv[1]] = kv[2].trim();
    }
  }
  return fm;
}

/**
 * Load a command template by name from commands/{name}.md.
 * Strips YAML frontmatter and HTML comments, returns the template body.
 *
 * @param {string} commandName - Command name without prefix (e.g. 'redteam')
 * @returns {string} Template body
 * @throws {Error} If the template file does not exist
 */
export function loadCommandTemplate(commandName) {
  let filePath = join(COMMANDS_DIR, `${commandName}.md`);
  if (!existsSync(filePath)) {
    filePath = join(COMMANDS_DIR, `nerf--${commandName}.md`);
  }
  if (!existsSync(filePath)) {
    // Legacy colon-separated filenames (pre-1.0.9)
    filePath = join(COMMANDS_DIR, `nerf:${commandName}.md`);
  }
  const content = readFileSync(filePath, 'utf-8');
  return stripFrontmatterAndComments(content);
}

// ---------------------------------------------------------------------------
// substituteArgs(template, args)
// ---------------------------------------------------------------------------

/**
 * Replace $ARGUMENTS placeholder in a template with the user's query text.
 * If the template has no $ARGUMENTS placeholder, appends the args at the end.
 *
 * @param {string} template
 * @param {string} args
 * @returns {string}
 */
export function substituteArgs(template, args) {
  if (template.includes('$ARGUMENTS')) {
    return template.replace(/\$ARGUMENTS/g, args);
  }
  // If no placeholder, append args as a query section
  if (args) {
    return `${template}\n\nQuery: ${args}`;
  }
  return template;
}

// ---------------------------------------------------------------------------
// listCommands()
// ---------------------------------------------------------------------------

/**
 * List all available commands.
 * Merges commands from the commands/ directory (with YAML frontmatter)
 * and the COMMAND_REGISTRY (for handler commands without .md files).
 * Returns array of { name, description, type }.
 *
 * @returns {Array<{ name: string, description: string, type: string }>}
 */
export function listCommands() {
  const seen = new Set();
  const commands = [];

  // 1. Commands from .md files
  let files = [];
  try {
    files = readdirSync(COMMANDS_DIR).filter(f => f.endsWith('.md')).sort();
  } catch { /* no commands dir */ }

  for (const file of files) {
    try {
      const content = readFileSync(join(COMMANDS_DIR, file), 'utf-8');
      const fm = parseFrontmatter(content);
      if (fm && fm.name) {
        const entry = COMMAND_REGISTRY.get(fm.name);
        commands.push({
          name: fm.name,
          description: fm.description || '',
          type: entry ? entry.type : 'prompt',
        });
        seen.add(fm.name);
      }
    } catch {
      // Skip unreadable files
    }
  }

  // 2. Registry-only commands (handler commands without .md files)
  for (const [name, entry] of COMMAND_REGISTRY) {
    if (!seen.has(name)) {
      commands.push({
        name,
        description: entry.desc || '',
        type: entry.type,
      });
      seen.add(name);
    }
  }

  return commands;
}
