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

/**
 * Strategy Template Loader — selects and loads vuln-class-specific
 * strategy templates for solver prompts.
 *
 * @module benchmark/strategies/loader
 */

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

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

/**
 * Map of challenge tags to strategy template filenames (without .md).
 * First match wins. More specific tags map to specialized strategies;
 * generic tags fall through to default.
 */
const TAG_TO_STRATEGY = {
  // RED mode — vuln class tags
  blind_sqli:          'blind-sqli',
  sqli:                'blind-sqli',       // Most CTF SQLi is blind
  sql_injection:       'blind-sqli',
  xss:                 'xss',
  cross_site_scripting: 'xss',
  reflected_xss:       'xss',
  stored_xss:          'xss',
  ssti:                'ssti',
  template_injection:  'ssti',
  lfi:                 'lfi-path-traversal',
  path_traversal:      'lfi-path-traversal',
  directory_traversal: 'lfi-path-traversal',
  file_inclusion:      'lfi-path-traversal',
  command_injection:   'command-injection',
  rce:                 'command-injection',
  os_command_injection: 'command-injection',
};

/**
 * Mode-specific tag mappings for non-RED modes.
 */
const MODE_TAG_TO_STRATEGY = {
  blue: {
    log_analysis:          'log-analysis',
    siem:                  'log-analysis',
    threat_hunt:           'threat-hunt',
    hunting:               'threat-hunt',
    detection:             'detection-engineering',
    detection_engineering: 'detection-engineering',
    sigma:                 'detection-engineering',
  },
  purple: {
    coverage:     'coverage-gap',
    gap_analysis: 'coverage-gap',
    emulation:    'emulation',
    atomic:       'emulation',
  },
  incident: {
    triage:     'triage',
    incident:   'triage',
    response:   'triage',
    breach:     'triage',
    ransomware: 'triage',
  },
};

/**
 * Load a strategy template from disk.
 *
 * @param {string} mode - Operating mode (red, blue, purple, etc.)
 * @param {string} templateName - Template filename without .md
 * @returns {string|null} Template content or null if not found
 */
export function loadTemplate(mode, templateName) {
  const templatePath = join(__dirname, mode, `${templateName}.md`);
  if (!existsSync(templatePath)) return null;
  try {
    return readFileSync(templatePath, 'utf8');
  } catch {
    return null;
  }
}

/**
 * List all available strategy templates for a mode.
 *
 * @param {string} mode - Operating mode (red, blue, purple, etc.)
 * @returns {string[]} Template names (without .md extension)
 */
export function listTemplates(mode) {
  const modeDir = join(__dirname, mode);
  if (!existsSync(modeDir)) return [];
  try {
    return readdirSync(modeDir)
      .filter(f => f.endsWith('.md'))
      .map(f => f.replace(/\.md$/, ''));
  } catch {
    return [];
  }
}

/**
 * Select the best strategy template based on challenge tags and docker analysis.
 *
 * Selection priority:
 * 1. Explicit override (if provided)
 * 2. First tag that maps to a specialized template
 * 3. Framework-informed heuristic (e.g., PHP + file param → lfi-path-traversal)
 * 4. Default template for the mode
 *
 * @param {object} opts
 * @param {string} opts.mode - Operating mode ('red', 'blue', etc.)
 * @param {string[]} opts.tags - Challenge/task tags
 * @param {object} [opts.envBrief] - Docker environment brief from analyzer
 * @param {string} [opts.override] - Explicit template name override
 * @returns {{ name: string, content: string, source: string }}
 */
export function selectStrategy({ mode = 'red', tags = [], envBrief = null, override = null }) {
  // 1. Explicit override
  if (override) {
    const content = loadTemplate(mode, override);
    if (content) return { name: override, content, source: 'override' };
    // Fallback: try override as a tag
  }

  // 2. Tag-based selection — first match wins
  //    Check mode-specific mappings first, then RED mappings
  const modeMap = MODE_TAG_TO_STRATEGY[mode] || {};
  for (const tag of tags) {
    const normalized = tag.toLowerCase().replace(/[- ]/g, '_');
    // Mode-specific mapping takes priority
    const modeName = modeMap[normalized];
    if (modeName) {
      const content = loadTemplate(mode, modeName);
      if (content) return { name: modeName, content, source: `tag:${tag}` };
    }
    // Fall back to RED mappings (only for red mode)
    if (mode === 'red') {
      const templateName = TAG_TO_STRATEGY[normalized];
      if (templateName) {
        const content = loadTemplate(mode, templateName);
        if (content) return { name: templateName, content, source: `tag:${tag}` };
      }
    }
  }

  // 3. Framework-informed heuristic
  if (envBrief) {
    const heuristic = frameworkHeuristic(envBrief, tags);
    if (heuristic) {
      const content = loadTemplate(mode, heuristic);
      if (content) return { name: heuristic, content, source: `heuristic:${envBrief.framework}` };
    }
  }

  // 4. Default template
  const defaultName = `default-${mode === 'red' ? 'web' : mode}`;
  const content = loadTemplate(mode, defaultName);
  if (content) return { name: defaultName, content, source: 'default' };

  // 5. Nothing found
  return null;
}

/**
 * Framework-based heuristic strategy selection.
 * Uses docker analysis to suggest likely vulnerability class.
 */
function frameworkHeuristic(envBrief, tags) {
  const fw = (envBrief.framework || '').toLowerCase();
  const lang = (envBrief.language || '').toLowerCase();
  const packages = (envBrief.packages || []).map(p => p.toLowerCase());

  // Jinja2 present → likely SSTI target
  if (packages.some(p => p.includes('jinja'))) return 'ssti';

  // Flask without explicit tags → could be SSTI
  if (fw === 'flask' && !tags.some(t => TAG_TO_STRATEGY[t.toLowerCase().replace(/[- ]/g, '_')])) {
    return 'ssti';
  }

  // PHP framework + no specific tag → default (PHP has many vuln classes)
  if (lang === 'php') return 'default-web';

  return null;
}

/**
 * Format a strategy template for prompt injection.
 * Strips YAML frontmatter if present, trims to maxChars.
 *
 * @param {string} content - Raw template content
 * @param {number} [maxChars=6000] - Maximum characters
 * @returns {string} Formatted strategy text
 */
export function formatStrategy(content, maxChars = 6000) {
  if (!content) return '';

  // Strip YAML frontmatter
  let text = content.replace(/^---[\s\S]*?---\n*/m, '');

  // Trim to budget
  if (text.length > maxChars) {
    text = text.slice(0, maxChars) + '\n\n[Strategy truncated — focus on phases above]';
  }

  return text.trim();
}
