// Copyright (c) 2026 defconxt. All rights reserved.
// Licensed under AGPL-3.0 — see LICENSE file for details.
// NERF is a trademark of defconxt.

/**
 * prompt.js — System prompt assembly for NERF gateway.
 *
 * Ports Python gateway/prompt.py:
 * - loadBasePrompt: reads CLAUDE.md and strips the Trigger Keywords section
 * - loadSkill: reads SKILL.md for a given mode
 * - assembleSystemPrompt: composes full system prompt with mode-specific skill injection
 *
 * @module gateway/prompt
 */

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

// ---------------------------------------------------------------------------
// Repo root resolution
// ---------------------------------------------------------------------------

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

/**
 * Walk up from startDir looking for cli/ directory or package.json.
 * Returns the directory or null.
 *
 * @param {string} startDir
 * @returns {string|null}
 */
function findRepoRoot(startDir) {
  let current = resolve(startDir);
  for (let i = 0; i < 20; i++) {
    if (
      existsSync(join(current, 'cli')) ||
      existsSync(join(current, 'package.json'))
    ) {
      // Check for knowledge/ dir to distinguish from cli/package.json
      if (existsSync(join(current, 'knowledge')) || existsSync(join(current, 'skills')) || existsSync(join(current, 'src', 'resources', 'skills'))) {
        return current;
      }
      // If it has cli/, it is the repo root
      if (existsSync(join(current, 'cli'))) {
        return current;
      }
    }
    const parent = dirname(current);
    if (parent === current) break;
    current = parent;
  }
  return null;
}

/**
 * Find the NERF data root (knowledge/, skills/, CLAUDE.md).
 * @returns {string}
 */
function findDataRoot() {
  // 1. Walk up from this module
  const root = findRepoRoot(__dirname);
  if (root) return root;

  // 2. Fallback: two levels up from lib/gateway/
  return resolve(__dirname, '..', '..', '..');
}

/** Cached data root. */
const REPO_ROOT = findDataRoot();

// ---------------------------------------------------------------------------
// Keyword table stripping
// ---------------------------------------------------------------------------

// Matches from "#### Trigger Keywords" through the next horizontal rule or end of text.
// Python: re.DOTALL → JS `s` flag, re.IGNORECASE → `i` flag.
const TRIGGER_KW_RE = /####\s*Trigger Keywords.*?(?=\n---|$)/si;

/**
 * Read CLAUDE.md and strip the Trigger Keywords section.
 *
 * The keyword table is stripped so the LLM cannot override gateway mode
 * routing by seeing the trigger mapping.
 *
 * @param {string} [repoRoot] - Explicit repo root path
 * @returns {string}
 */
export function loadBasePrompt(repoRoot) {
  const root = repoRoot || REPO_ROOT;
  const claudeMd = join(root, 'CLAUDE.md');

  if (!existsSync(claudeMd)) {
    return '';
  }

  const text = readFileSync(claudeMd, 'utf-8');
  let stripped = text.replace(TRIGGER_KW_RE, '');
  // Collapse any triple+ blank lines left by the removal.
  stripped = stripped.replace(/\n{3,}/g, '\n\n').trim();
  return stripped;
}

/**
 * Load raw CLAUDE.md content (not stripped) for keyword table parsing.
 *
 * @param {string} [repoRoot] - Explicit repo root path
 * @returns {string}
 */
export function loadRawClaudeMd(repoRoot) {
  const root = repoRoot || REPO_ROOT;
  const claudeMd = join(root, 'CLAUDE.md');
  if (!existsSync(claudeMd)) return '';
  return readFileSync(claudeMd, 'utf-8');
}

// ---------------------------------------------------------------------------
// Skill map and loading
// ---------------------------------------------------------------------------

/** Maps NERF mode names to SKILL.md paths relative to skills/. */
export const MODE_SKILL_MAP = {
  RED: 'red-team/SKILL.md',
  BLUE: 'blue-team/SKILL.md',
  PURPLE: 'purple-team/SKILL.md',
  PRIVACY: 'privacy-engineering/SKILL.md',
  RECON: 'osint-recon/SKILL.md',
  INCIDENT: 'incident-response/SKILL.md',
  ARCHITECT: 'security-architecture/SKILL.md',
};

/**
 * Background layers injected per mode.
 * RED always gets PURPLE as background; ALL modes get PRIVACY.
 */
const BACKGROUND_LAYERS = {
  RED: ['PURPLE', 'PRIVACY'],
};
const DEFAULT_BACKGROUND = ['PRIVACY'];

/**
 * Load the SKILL.md file for a given mode.
 *
 * @param {string} mode - NERF mode name (e.g. "RED", "BLUE")
 * @param {string} [repoRoot] - Explicit repo root
 * @returns {string} SKILL.md contents, or empty string
 */
export function loadSkill(mode, repoRoot) {
  const root = repoRoot || REPO_ROOT;
  const relPath = MODE_SKILL_MAP[mode.toUpperCase()];
  if (!relPath) return '';

  const skillFile = join(root, 'skills', relPath);
  if (existsSync(skillFile)) return readFileSync(skillFile, 'utf-8');

  const altSkillFile = join(root, 'src', 'resources', 'skills', relPath);
  if (existsSync(altSkillFile)) return readFileSync(altSkillFile, 'utf-8');

  return '';
}

/**
 * Assemble the full NERF system prompt for a mode.
 *
 * Composition:
 * 1. Base prompt (CLAUDE.md with keyword table stripped)
 * 2. Primary skill for the requested mode
 * 3. Background layers (PURPLE for RED; PRIVACY for all modes)
 * 4. Plugin skills for the requested mode (sorted by priority)
 *
 * @param {string} mode - NERF operating mode
 * @param {string} [repoRoot] - Explicit repo root
 * @param {import('./plugins.js').PluginManager} [pluginManager] - Optional plugin manager
 * @returns {string}
 */
/** Maps NERF modes to relevant knowledge doc filenames. */
const MODE_KNOWLEDGE_MAP = {
  RED:       ['pentest-cheatsheet-ultimate.md', 'attack-chains-synthesis.md', 'linux-exploitation-deep.md', 'mitre-attack-deep.md'],
  BLUE:      ['evasion-detection-catalog.md', 'mitre-attack-deep.md', 'siem-soc-deep.md', 'sigma-detection-deep.md'],
  PURPLE:    ['sigma-detection-deep.md', 'mitre-attack-deep.md', 'attack-chains-synthesis.md'],
  PRIVACY:   ['privacy-engineering-deep.md', 'data-protection-deep.md'],
  RECON:     ['recon-osint-deep.md', 'osint-tradecraft-deep.md', 'threat-intel-deep.md'],
  INCIDENT:  ['incident-playbooks-deep.md', 'forensics-artifacts-deep.md', 'network-forensics-deep.md'],
  ARCHITECT: ['security-architecture-deep.md', 'cloud-infra-deep.md', 'devsecops-sdlc-deep.md'],
};

/**
 * Load relevant knowledge documents for a mode.
 * Returns concatenated content, capped at maxChars.
 *
 * @param {string} mode - NERF mode name
 * @param {string} [repoRoot] - Explicit repo root
 * @param {number} [maxChars=12000] - Max total characters to include
 * @returns {string}
 */
export function loadKnowledgeForMode(mode, repoRoot, maxChars = 12000) {
  const root = repoRoot || REPO_ROOT;
  const knowledgeDir = join(root, 'knowledge');
  if (!existsSync(knowledgeDir)) return '';

  const files = MODE_KNOWLEDGE_MAP[mode.toUpperCase()] || MODE_KNOWLEDGE_MAP.ARCHITECT;
  const chunks = [];
  let totalLen = 0;

  for (const filename of files) {
    const filepath = join(knowledgeDir, filename);
    if (!existsSync(filepath)) continue;
    try {
      const content = readFileSync(filepath, 'utf-8');
      const budget = Math.min(maxChars - totalLen, 4000);
      if (budget <= 200) break;
      chunks.push(`## Knowledge: ${filename}\n${content.slice(0, budget)}`);
      totalLen += budget;
    } catch { /* skip */ }
  }
  return chunks.join('\n\n');
}

export function assembleSystemPrompt(mode, repoRoot, pluginManager) {
  const root = repoRoot || REPO_ROOT;
  const upperMode = mode.toUpperCase();

  /** @type {string[]} */
  const sections = [];

  // 1. Base prompt
  const base = loadBasePrompt(root);
  if (base) sections.push(base);

  // 2. Primary skill
  const primary = loadSkill(upperMode, root);
  if (primary) sections.push(primary);

  // 3. Background layers
  const backgrounds = BACKGROUND_LAYERS[upperMode] || DEFAULT_BACKGROUND;
  for (const bgMode of backgrounds) {
    if (bgMode === upperMode) continue; // Never double-inject the primary skill
    const bgSkill = loadSkill(bgMode, root);
    if (bgSkill) sections.push(bgSkill);
  }

  // 4. Plugin skills for this mode
  if (pluginManager) {
    try {
      const plugins = pluginManager.getSkillsForMode(upperMode);
      for (const plugin of plugins) {
        if (plugin.skill_content) {
          sections.push(plugin.skill_content);
        }
      }
    } catch {
      // Plugin loading is best-effort
    }
  }

  // 5. Mode-relevant knowledge documents
  const knowledge = loadKnowledgeForMode(upperMode, root);
  if (knowledge) sections.push(knowledge);

  // 6. Capability boundaries — prevent tool hallucination in CLI chat mode
  sections.push(
    '## Session Constraints\n\n' +
    'You are running in NERF CLI chat mode (text only). You do NOT have:\n' +
    '- File system access (cannot read, write, or view files or images)\n' +
    '- Tool calling (no tool_call blocks, no function calls)\n' +
    '- Code execution (cannot run commands or scripts)\n' +
    '- Image/screenshot viewing\n\n' +
    'If the user references a file path, ask them to paste the relevant content. ' +
    'Do NOT fabricate or hallucinate file contents, image descriptions, or tool outputs. ' +
    'If you cannot do something, say so plainly.'
  );

  return sections.join('\n\n---\n\n');
}

export { REPO_ROOT };
