#!/usr/bin/env node
// Copyright (c) 2026 defconxt. All rights reserved.
// Licensed under AGPL-3.0 — see LICENSE file for details.

/**
 * NERF Agent Runtime
 *
 * Shared runtime for all 1,436 technique agent scripts.
 * Reads SKILL.md + api-reference.md at execution time, extracts structured data,
 * and dispatches to domain-specific handlers.
 *
 * Usage from agent.js:
 *   import { run } from '../../../cli/lib/agent-runtime/index.js';
 *   run(import.meta.url);
 */

import { readFileSync, existsSync } from 'node:fs';
import { dirname, join, resolve, basename, sep } from 'node:path';
import { fileURLToPath } from 'node:url';
import { parseSkillMd } from './parser.js';
import { getHandler } from './handlers.js';

// ── Domain → mode mapping ──────────────────────────────────────────────
const DOMAIN_MODE = {
  'red-team':          'red',
  'adversary-simulation': 'red',
  'exploit-development': 'red',
  'binary-exploitation': 'red',
  'c2-frameworks':     'red',
  'password-cracking': 'red',
  'bug-bounty':        'red',
  'social-engineering': 'red',

  'blue-team':         'blue',
  'soc-operations':    'blue',
  'detection-engineering': 'blue',
  'incident-response': 'blue',
  'incident-management': 'blue',
  'endpoint-security': 'blue',
  'ransomware-defense': 'blue',
  'phishing-defense':  'blue',

  'purple-team':       'purple',

  'digital-forensics':      'incident',
  'investigation-attribution': 'incident',
  'log-analysis':            'incident',
  'malware-analysis':        'incident',

  'privacy-engineering':        'privacy',
  'data-security':              'privacy',
  'compliance-audit':           'privacy',
  'governance-risk-compliance': 'privacy',

  'osint-recon':          'recon',
  'threat-intelligence':  'recon',

  'ai-llm-security':     'researcher',
  'reverse-engineering':  'researcher',
};

// Everything else → architect
const DEFAULT_MODE = 'architect';

/**
 * Resolve technique paths from the agent.js location.
 * Expected layout:
 *   skills/<domain>/techniques/<technique>/scripts/agent.js
 */
function resolvePaths(agentUrl) {
  const agentPath = fileURLToPath(agentUrl);
  const scriptsDir = dirname(agentPath);
  const techniqueDir = dirname(scriptsDir);
  const techniquesDir = dirname(techniqueDir);
  const domainDir = dirname(techniquesDir);

  const technique = basename(techniqueDir);
  const domain = basename(domainDir);

  const skillPath = join(techniqueDir, 'SKILL.md');
  const apiRefPath = join(techniqueDir, 'references', 'api-reference.md');

  return { agentPath, techniqueDir, technique, domain, skillPath, apiRefPath };
}

/**
 * Main entry point for all agent scripts.
 */
export async function run(importMetaUrl) {
  const { technique, domain, skillPath, apiRefPath } = resolvePaths(importMetaUrl);
  const args = process.argv.slice(2);
  const command = args[0];

  // Resolve mode
  const mode = DOMAIN_MODE[domain] || DEFAULT_MODE;

  // Parse SKILL.md
  let skill = null;
  if (existsSync(skillPath)) {
    const raw = readFileSync(skillPath, 'utf8');
    skill = parseSkillMd(raw);
  } else {
    console.error(`[WARN] SKILL.md not found: ${skillPath}`);
  }

  // Parse api-reference.md
  let apiRef = null;
  if (existsSync(apiRefPath)) {
    const raw = readFileSync(apiRefPath, 'utf8');
    apiRef = parseSkillMd(raw);
  }

  // Build context
  const ctx = {
    technique,
    domain,
    mode,
    skill,
    apiRef,
    args: args.slice(1),
    flags: parseFlags(args.slice(1)),
  };

  // No command → show help
  if (!command) {
    showHelp(ctx);
    process.exit(0);
  }

  // Get domain handler and dispatch
  const handler = getHandler(mode);
  const actions = handler.actions();

  if (command === 'help') {
    showHelp(ctx);
    process.exit(0);
  }

  if (!actions.includes(command)) {
    console.error(`Unknown command: ${command}`);
    console.error(`Available: ${actions.join(', ')}`);
    process.exit(1);
  }

  try {
    const result = await handler.execute(command, ctx);
    console.log(JSON.stringify(result, null, 2));
  } catch (err) {
    console.error(JSON.stringify({
      error: err.message,
      action: command,
      technique,
      domain,
      mode,
    }, null, 2));
    process.exit(1);
  }
}

/**
 * Parse --flag value pairs from argv.
 */
function parseFlags(args) {
  const flags = {};
  for (let i = 0; i < args.length; i++) {
    if (args[i].startsWith('--')) {
      const key = args[i].slice(2);
      const val = (i + 1 < args.length && !args[i + 1].startsWith('--'))
        ? args[++i]
        : true;
      flags[key] = val;
    }
  }
  return flags;
}

/**
 * Show help for this technique's available actions.
 */
function showHelp(ctx) {
  const handler = getHandler(ctx.mode);
  const actions = handler.actions();
  const title = ctx.skill?.frontmatter?.name || ctx.technique;
  const desc = ctx.skill?.frontmatter?.description || '';

  console.log(`\n  ${title}`);
  if (desc) console.log(`  ${desc.slice(0, 120)}`);
  console.log(`\n  Mode: ${ctx.mode.toUpperCase()} | Domain: ${ctx.domain}`);
  console.log(`\n  Commands:`);
  for (const action of actions) {
    const info = handler.describe(action);
    console.log(`    ${action.padEnd(16)} ${info}`);
  }
  console.log(`\n  Usage: node agent.js <command> [--flag value ...]`);
  console.log();
}
