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

import { BaseHandler } from './base.js';

/**
 * BLUE mode handler — defensive security.
 * Adds: detect (Sigma rules, log queries), hunt, harden
 */
export class BlueHandler extends BaseHandler {
  constructor() { super('blue'); }

  domainActions() {
    return ['detect', 'hunt', 'harden'];
  }

  describeDomain(action) {
    return {
      'detect':  'Generate detection rules (Sigma, KQL, SPL) from technique patterns',
      'hunt':    'Build threat hunting queries from technique indicators',
      'harden':  'Extract hardening recommendations and CIS controls',
    }[action];
  }

  async executeDomain(command, ctx) {
    switch (command) {
      case 'detect':  return this.detect(ctx);
      case 'hunt':    return this.hunt(ctx);
      case 'harden':  return this.harden(ctx);
      default: throw new Error(`Unknown BLUE command: ${command}`);
    }
  }

  /** Generate detection rules from SKILL.md patterns. */
  detect(ctx) {
    const { skill, technique } = ctx;
    if (!skill) throw new Error('No SKILL.md found');

    const format = ctx.flags.format || 'sigma';
    const rules = [];

    // Extract Sigma rules from code blocks
    for (const block of skill.codeBlocks) {
      if (block.lang === 'yaml' || block.lang === 'sigma') {
        if (block.code.includes('logsource') || block.code.includes('detection')) {
          rules.push({
            type: 'sigma',
            context: block.context,
            rule: block.code,
          });
        }
      }
    }

    // Extract KQL/SPL queries
    for (const block of skill.codeBlocks) {
      if (block.lang === 'kql' || block.lang === 'kusto') {
        rules.push({ type: 'kql', context: block.context, query: block.code });
      }
      if (block.lang === 'spl' || block.lang === 'splunk') {
        rules.push({ type: 'spl', context: block.context, query: block.code });
      }
    }

    // Extract detection patterns from tables
    const detectionTables = [];
    for (const table of skill.tables) {
      if (table.headers.some(h => /event.?id|log|detection|indicator|signature/i.test(h))) {
        detectionTables.push({
          headers: table.headers,
          rows: table.rows.slice(0, 10),
        });
      }
    }

    // Build detection summary from content
    const detectionSections = skill.sections
      .filter(s => /detect|sigma|rule|alert|monitor|query/i.test(s.title))
      .map(s => ({
        title: s.title,
        summary: s.content.slice(0, 300),
      }));

    const sigmaValidation = this.validateSigmaRules(rules);

    return {
      action: 'detect',
      technique,
      mode: 'BLUE',
      attackIds: skill.attackIds,
      requestedFormat: format,
      rules,
      detectionTables,
      detectionSections,
      sigmaValid: sigmaValidation.invalid.length === 0,
      sigmaErrors: sigmaValidation.errors,
      evasionConsiderations: this.extractEvasionNotes(skill),
      status: 'detect_complete',
    };
  }

  /** Build threat hunting queries. */
  hunt(ctx) {
    const { skill, technique } = ctx;
    if (!skill) throw new Error('No SKILL.md found');

    const queries = [];

    // Extract hunting-relevant code blocks
    const huntLangs = ['kql', 'kusto', 'spl', 'splunk', 'sql', 'powershell', 'bash'];
    for (const block of skill.codeBlocks) {
      if (huntLangs.includes(block.lang)) {
        queries.push({
          lang: block.lang,
          context: block.context,
          query: block.code,
        });
      }
    }

    // Extract indicators from tables
    const indicators = [];
    for (const table of skill.tables) {
      if (table.headers.some(h => /indicator|ioc|artifact|evidence|event/i.test(h))) {
        indicators.push(...table.rows.slice(0, 10));
      }
    }

    // Hunt hypothesis from sections
    const hypotheses = skill.sections
      .filter(s => /hunt|hypothesis|scenario|use.case/i.test(s.title))
      .map(s => ({
        title: s.title,
        content: s.content.slice(0, 300),
      }));

    return {
      action: 'hunt',
      technique,
      mode: 'BLUE',
      attackIds: skill.attackIds,
      tools: skill.tools,
      queries: queries.slice(0, 20),
      indicators: indicators.slice(0, 20),
      hypotheses: hypotheses.slice(0, 5),
      status: 'hunt_complete',
    };
  }

  /** Extract hardening recommendations. */
  harden(ctx) {
    const { skill, technique } = ctx;
    if (!skill) throw new Error('No SKILL.md found');

    const recommendations = this.extractRecommendations(skill);

    // Extract CIS/NIST references
    const controls = [];
    const controlRe = /(?:CIS|NIST|ISO)\s*[\w.-]+/g;
    let m;
    while ((m = controlRe.exec(skill.raw))) {
      controls.push(m[0]);
    }

    // Extract configuration code blocks
    const configs = skill.codeBlocks
      .filter(b => /config|harden|secure|policy|baseline/i.test(b.context))
      .map(b => ({
        lang: b.lang,
        context: b.context,
        config: b.code.slice(0, 500),
      }));

    return {
      action: 'harden',
      technique,
      mode: 'BLUE',
      attackIds: skill.attackIds,
      recommendations,
      controls: [...new Set(controls)],
      configs: configs.slice(0, 10),
      evasionConsiderations: this.extractEvasionNotes(skill),
      status: 'harden_complete',
    };
  }

  /** Validate Sigma rules for required structural fields. */
  validateSigmaRules(rules) {
    if (!Array.isArray(rules)) return { valid: [], invalid: [], errors: [] };
    const requiredFields = ['title:', 'logsource:', 'detection:', 'condition:'];
    const valid = [];
    const invalid = [];
    const errors = [];
    rules.forEach((rule, idx) => {
      const ruleText = typeof rule === 'string' ? rule : (rule && rule.rule) || '';
      const missing = requiredFields.filter(f => !ruleText.includes(f));
      if (missing.length === 0) {
        valid.push(idx);
      } else {
        invalid.push(idx);
        errors.push({ index: idx, missing: missing.map(f => f.replace(':', '')) });
      }
    });
    return { valid, invalid, errors };
  }

  /** PURPLE layer — every BLUE output includes evasion considerations. */
  extractEvasionNotes(skill) {
    const notes = [];
    for (const section of skill.sections) {
      if (/evas|bypass|limitation|blind.spot|false.negative|gap/i.test(section.title)) {
        notes.push({
          area: section.title,
          summary: section.content.slice(0, 200),
        });
      }
    }
    return notes.slice(0, 5);
  }
}
