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

/**
 * PURPLE mode agent — Emulate + Detect Cycle.
 *
 * Orchestrates a two-phase purple team exercise: (1) write an emulation plan,
 * (2) write a matching Sigma detection rule. Cross-coherence validated.
 * Ported from execution engine/modes/purple.py.
 *
 * @module execution-engine/modes/purple
 */

import { ModeAgentConfig, ToolRegistry } from '../framework.js';
import { ATTACK_TECHNIQUES } from './blue.js';
import { PurpleValidator } from '../validators/purple.js';

// ---------------------------------------------------------------------------
// Tool handlers
// ---------------------------------------------------------------------------

/**
 * Look up a MITRE ATT&CK technique by ID.
 * Thin wrapper around BLUE's ATTACK_TECHNIQUES dict.
 * @param {*} context
 * @param {Object} toolInput
 * @returns {string}
 */
export async function _purpleLookupTechnique(context, toolInput) {
  const techniqueId = (toolInput.technique_id || '').toUpperCase();
  const entry = ATTACK_TECHNIQUES[techniqueId];

  if (!entry) {
    try {
      const { skillsForTechnique } = await import('../../knowledge/attack-mappings.js');
      const skills = skillsForTechnique(techniqueId);
      if (skills.length > 0) {
        return (
          `Technique ${techniqueId}: ${skills.length} skill(s) cover it: ` +
          `${skills.slice(0, 5).join(', ')}${skills.length > 5 ? '...' : ''}. ` +
          `Proceed using your knowledge of this technique for emulation planning.`
        );
      }
    } catch { /* mappings not available */ }

    return (
      `Technique ${techniqueId} is not in the local lookup table. ` +
      `Proceed using your own knowledge of this ATT&CK technique. ` +
      `Available techniques: ${Object.keys(ATTACK_TECHNIQUES).sort().join(', ')}`
    );
  }

  const dataSourcesStr = entry.data_sources.map(ds => `  - ${ds}`).join('\n');
  return (
    `ATT&CK Technique: ${techniqueId} — ${entry.name}\n` +
    `Tactic: ${entry.tactic}\n` +
    `Description: ${entry.description}\n` +
    `Data Sources:\n${dataSourcesStr}`
  );
}

/**
 * Store an emulation plan JSON in the shared context.
 * @param {*} context
 * @param {Object} toolInput
 * @returns {string}
 */
export function _purpleWriteEmulationPlan(context, toolInput) {
  const content = toolInput.content || '';

  if (typeof context !== 'object' || context === null) {
    return 'ERROR: Context must be a dict.';
  }

  let parsed;
  try {
    parsed = JSON.parse(content);
  } catch (e) {
    return `ERROR: Invalid JSON in emulation plan: ${e.message}`;
  }

  if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
    return 'ERROR: Emulation plan must be a JSON object (dict).';
  }

  context.emulation_plan = parsed;
  const technique = parsed.technique_id || 'unknown';
  return (
    `Emulation plan stored for technique ${technique}. ` +
    `Fields: ${Object.keys(parsed).sort().join(', ')}.`
  );
}

/**
 * Store a Sigma detection rule YAML string in the shared context.
 * @param {*} context
 * @param {Object} toolInput
 * @returns {string}
 */
export function _purpleWriteDetectionRule(context, toolInput) {
  const content = toolInput.content || '';
  const filename = toolInput.filename || 'detection.yml';

  if (typeof context !== 'object' || context === null) {
    return 'ERROR: Context must be a dict.';
  }

  context.detection_rule = { filename, content };
  return (
    `Detection rule stored as ${filename}. ` +
    `Content length: ${content.length} characters.`
  );
}

// ---------------------------------------------------------------------------
// Tool schemas (Anthropic format)
// ---------------------------------------------------------------------------

const _PURPLE_LOOKUP_TECHNIQUE_SCHEMA = {
  name: 'lookup_technique',
  description:
    'Look up a MITRE ATT&CK technique by its ID (e.g. T1059.001). ' +
    'Returns technique name, tactic, description, and relevant data ' +
    'sources to help design emulation plans and detection rules.',
  input_schema: {
    type: 'object',
    properties: {
      technique_id: {
        type: 'string',
        description: 'ATT&CK technique ID (e.g. T1059.001, T1190)',
      },
    },
    required: ['technique_id'],
  },
};

const _PURPLE_WRITE_EMULATION_PLAN_SCHEMA = {
  name: 'write_emulation_plan',
  description:
    'Submit a completed emulation plan as a JSON object. Describes how to ' +
    'execute the ATT&CK technique including prerequisites, steps, artifacts, cleanup.',
  input_schema: {
    type: 'object',
    properties: {
      content: {
        type: 'string',
        description:
          'Full JSON content of the emulation plan. Must include: technique_id, ' +
          'technique_name, tactic, prerequisites, emulation_steps, expected_artifacts, cleanup.',
      },
    },
    required: ['content'],
  },
};

const _PURPLE_WRITE_DETECTION_RULE_SCHEMA = {
  name: 'write_detection_rule',
  description:
    'Submit a completed Sigma detection rule as YAML. The rule should ' +
    'detect the activity described in the emulation plan.',
  input_schema: {
    type: 'object',
    properties: {
      filename: {
        type: 'string',
        description: 'Filename for the rule (e.g. detect_powershell_execution.yml)',
      },
      content: {
        type: 'string',
        description: 'Full YAML content of the Sigma detection rule',
      },
    },
    required: ['content'],
  },
};

// ---------------------------------------------------------------------------
// System prompt template
// ---------------------------------------------------------------------------

const _PURPLE_SYSTEM_PROMPT = `\
You are an expert purple team engineer who designs both adversary emulation \
plans and matching detection rules for MITRE ATT&CK techniques. Your task \
is to produce a coherent emulate→detect cycle for a specific TTP.

## Task
Design an emulation plan and a matching Sigma detection rule for TTP: \
{ttp_id} — {ttp_description}

## Phase 1: Emulation Plan
1. Use the \`lookup_technique\` tool to retrieve metadata about the target TTP.
2. Design an emulation plan with prerequisites, steps, artifacts, cleanup.
3. Submit the plan via \`write_emulation_plan\` as a JSON object with fields:
   technique_id, technique_name, tactic, prerequisites, emulation_steps,
   expected_artifacts, cleanup.

## Phase 2: Detection Rule
4. Design a Sigma detection rule that catches the emulated activity.
5. Submit the rule via \`write_detection_rule\` as valid Sigma YAML.

## Coherence Requirement
The detection rule's ATT&CK tags MUST reference the same technique ID as \
the emulation plan.
`;

// ---------------------------------------------------------------------------
// Output parser (fallback for text-based output)
// ---------------------------------------------------------------------------

/**
 * Extract emulation plan JSON and Sigma YAML from LLM text output.
 * @param {string} text
 * @returns {Object}
 */
export function _purpleOutputParser(text) {
  if (!text || !text.trim()) {
    return { emulation_plan: null, detection_rule: null };
  }

  // Extract JSON emulation plan from ```json fences, then bare fences
  let emulationPlan = null;
  const jsonMatches = [...text.matchAll(/```json\s*\n(.*?)```/gs)].map(m => m[1]);
  for (const match of jsonMatches) {
    try {
      const parsed = JSON.parse(match.trim());
      if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) {
        emulationPlan = parsed;
        break;
      }
    } catch {
      continue;
    }
  }

  if (emulationPlan === null) {
    const bareMatches = [...text.matchAll(/```\s*\n(.*?)```/gs)].map(m => m[1]);
    for (const match of bareMatches) {
      try {
        const parsed = JSON.parse(match.trim());
        if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) {
          emulationPlan = parsed;
          break;
        }
      } catch {
        continue;
      }
    }
  }

  // Extract YAML detection rule from ```yaml fences, then bare fences
  let detectionRule = null;
  const yamlMatches = [...text.matchAll(/```ya?ml\s*\n(.*?)```/gs)].map(m => m[1]);
  if (yamlMatches.length > 0) {
    detectionRule = yamlMatches.join('\n---\n');
  } else {
    const bareMatches = [...text.matchAll(/```\s*\n(.*?)```/gs)].map(m => m[1]);
    const yamlLike = bareMatches.filter(m =>
      /^\s*(title|logsource|detection):/m.test(m)
    );
    if (yamlLike.length > 0) {
      detectionRule = yamlLike.join('\n---\n');
    }
  }

  return { emulation_plan: emulationPlan, detection_rule: detectionRule };
}

// ---------------------------------------------------------------------------
// Factory function
// ---------------------------------------------------------------------------

/**
 * Build a PURPLE-mode ModeAgentConfig for emulate→detect cycle.
 * @returns {ModeAgentConfig}
 */
function _makePurpleConfig() {
  const reg = new ToolRegistry();
  reg.register('lookup_technique', _PURPLE_LOOKUP_TECHNIQUE_SCHEMA, _purpleLookupTechnique);
  reg.register('write_emulation_plan', _PURPLE_WRITE_EMULATION_PLAN_SCHEMA, _purpleWriteEmulationPlan);
  reg.register('write_detection_rule', _PURPLE_WRITE_DETECTION_RULE_SCHEMA, _purpleWriteDetectionRule);

  return new ModeAgentConfig({
    mode: 'PURPLE',
    toolRegistry: reg,
    systemPromptTemplate: _PURPLE_SYSTEM_PROMPT,
    validator: new PurpleValidator(),
    maxTurns: 15,
    requiresSandbox: false,
    completionCheck: null,
    outputParser: _purpleOutputParser,
    outputFormat: 'json',
  });
}

// ---------------------------------------------------------------------------
// Registration function — called by runner.initModes()
// ---------------------------------------------------------------------------

/**
 * Register PURPLE mode with the given registerMode function.
 * @param {Function} registerMode
 */
export function register(registerMode) {
  registerMode('PURPLE', _makePurpleConfig);
}
