// Command Pattern Signal Providers
// Extracts typed features from text/clipboard content for command pattern detection.
// Each provider returns { value, metadata? } — no decisions, no severity, no findings.

// ── Obfuscation indicators ──────────────────────
const OBFUSCATION_INDICATORS = [
  { pattern: /\^/g, type: 'caret-concat' },
  { pattern: /`/g, type: 'backtick-escape' },
  { pattern: /\$\{[^}]+\}/g, type: 'variable-expansion' },
  { pattern: /\[char\]/gi, type: 'char-code' },
  { pattern: /-join/gi, type: 'join-obfuscation' },
  { pattern: /-f\s+['"][^'"]+['"]/gi, type: 'format-string' },
  { pattern: /\+\s*['"][a-z]['"][\s\+]/gi, type: 'string-concat' },
];

// ── PowerShell patterns ─────────────────────────
const POWERSHELL_PATTERNS = [
  { pattern: /(?:powershell|pwsh).*(?:invoke-webrequest|iwr|wget|curl)/i, type: 'ps-download' },
  { pattern: /invoke-expression|iex/i, type: 'ps-iex' },
  { pattern: /(?:powershell|pwsh).*-enc(?:odedcommand)?/i, type: 'ps-encoded' },
  { pattern: /(?:powershell|pwsh).*(?:-w(?:indowstyle)?\s+hidden|-ep(?:olicy)?\s+bypass)/i, type: 'ps-hidden-bypass' },
  { pattern: /new-object\s+(?:net\.webclient|system\.net)/i, type: 'ps-net-object' },
  { pattern: /start-process|start-bitstransfer/i, type: 'ps-start' },
  { pattern: /\[System\.Convert\]::FromBase64/i, type: 'ps-base64-decode' },
  { pattern: /Set-MpPreference.*-DisableRealtimeMonitoring/i, type: 'ps-disable-defender' },
];

// ── Pipe-to-shell patterns ──────────────────────
const PIPE_PATTERNS = [
  { pattern: /curl\s+.*\|\s*(?:ba)?sh/i, type: 'curl-pipe-sh' },
  { pattern: /wget\s+.*\|\s*(?:ba)?sh/i, type: 'wget-pipe-sh' },
  { pattern: /curl\s+.*\|\s*python/i, type: 'curl-pipe-python' },
  { pattern: /wget\s+.*\|\s*python/i, type: 'wget-pipe-python' },
  { pattern: /curl\s+.*\|\s*perl/i, type: 'curl-pipe-perl' },
];

// ── LOLBin patterns ─────────────────────────────
const LOLBIN_PATTERNS = [
  { pattern: /mshta\s+(?:http|vbscript:|javascript:)/i, binary: 'mshta', type: 'mshta-remote' },
  { pattern: /rundll32.*(?:http|javascript:|shell32)/i, binary: 'rundll32', type: 'rundll32-abuse' },
  { pattern: /regsvr32.*(?:\/s|\/i:http|scrobj)/i, binary: 'regsvr32', type: 'regsvr32-squiblydoo' },
  { pattern: /certutil.*(?:-urlcache|-split)/i, binary: 'certutil', type: 'certutil-download' },
  { pattern: /bitsadmin.*(?:\/transfer|\/create)/i, binary: 'bitsadmin', type: 'bitsadmin-transfer' },
  { pattern: /wmic\s+.*(?:process\s+call\s+create|os\s+get)/i, binary: 'wmic', type: 'wmic-process' },
  { pattern: /cmstp.*\/s.*\.inf/i, binary: 'cmstp', type: 'cmstp-bypass' },
];

// ── Script engine patterns ──────────────────────
const SCRIPT_ENGINE_PATTERNS = [
  { pattern: /wscript(?:\.exe)?\s+/i, engine: 'wscript', type: 'wscript-exec' },
  { pattern: /cscript(?:\.exe)?\s+/i, engine: 'cscript', type: 'cscript-exec' },
  { pattern: /msiexec.*(?:\/i|\/q).*http/i, engine: 'msiexec', type: 'msiexec-remote' },
];

// ── Suspicious keywords ─────────────────────────
const SUSPICIOUS_KEYWORDS = [
  'invoke-webrequest', 'invoke-expression', 'downloadstring',
  'downloadfile', 'start-process', 'new-object',
  'system.net.webclient', 'bitstransfer', 'certutil',
  'hidden', 'bypass', 'unrestricted', 'noprofile',
  '-enc', 'frombase64', 'decompress',
  'reverse', 'shell', 'payload', 'exploit',
  'nc -e', 'netcat', '/dev/tcp',
];

/**
 * Register all command-pattern signal providers on a SignalProviderRegistry.
 * @param {import('../../core/signal-provider.js').SignalProviderRegistry} registry
 */
export function registerCommandPatternSignals(registry) {

  // ── text.has_powershell_command ────────────────
  registry.register('text', 'has_powershell_command', (target) => {
    const text = target.content || target.text || target.clipboard || '';
    if (!text) return { value: false };

    const matched = [];
    for (const { pattern, type } of POWERSHELL_PATTERNS) {
      if (pattern.test(text)) matched.push(type);
    }
    return {
      value: matched.length > 0,
      metadata: matched.length > 0 ? { techniques: matched, matchCount: matched.length } : null,
    };
  }, { returnType: 'boolean', scope: 'frame', description: 'PowerShell command patterns detected' });

  // ── text.has_pipe_to_shell ────────────────────
  registry.register('text', 'has_pipe_to_shell', (target) => {
    const text = target.content || target.text || target.clipboard || '';
    if (!text) return { value: false };

    const matched = [];
    for (const { pattern, type } of PIPE_PATTERNS) {
      if (pattern.test(text)) matched.push(type);
    }
    return {
      value: matched.length > 0,
      metadata: matched.length > 0 ? { techniques: matched } : null,
    };
  }, { returnType: 'boolean', scope: 'frame', description: 'Curl/wget piped to shell detected' });

  // ── text.has_lolbin_abuse ─────────────────────
  registry.register('text', 'has_lolbin_abuse', (target) => {
    const text = target.content || target.text || target.clipboard || '';
    if (!text) return { value: false };

    const matched = [];
    const binaries = [];
    for (const { pattern, binary, type } of LOLBIN_PATTERNS) {
      if (pattern.test(text)) {
        matched.push(type);
        binaries.push(binary);
      }
    }
    return {
      value: matched.length > 0,
      metadata: matched.length > 0 ? { techniques: matched, binaries } : null,
    };
  }, { returnType: 'boolean', scope: 'frame', description: 'Windows LOLBin abuse detected' });

  // ── text.has_script_engine_exec ───────────────
  registry.register('text', 'has_script_engine_exec', (target) => {
    const text = target.content || target.text || target.clipboard || '';
    if (!text) return { value: false };

    const matched = [];
    for (const { pattern, engine, type } of SCRIPT_ENGINE_PATTERNS) {
      if (pattern.test(text)) matched.push(type);
    }
    return {
      value: matched.length > 0,
      metadata: matched.length > 0 ? { techniques: matched } : null,
    };
  }, { returnType: 'boolean', scope: 'frame', description: 'Script engine (wscript/cscript) execution detected' });

  // ── text.obfuscation_score ────────────────────
  registry.register('text', 'obfuscation_score', (target) => {
    const text = target.content || target.text || target.clipboard || '';
    if (!text) return { value: 0 };

    let score = 0;
    const types = [];
    for (const { pattern, type } of OBFUSCATION_INDICATORS) {
      const matches = text.match(pattern);
      if (matches && matches.length > 2) {
        score++;
        types.push(type);
      }
    }
    return {
      value: score,
      metadata: score > 0 ? { types, isObfuscated: score >= 2 } : null,
    };
  }, { returnType: 'number', scope: 'frame', description: 'Command obfuscation score (0-7)' });

  // ── text.suspicious_keyword_count ─────────────
  registry.register('text', 'suspicious_keyword_count', (target) => {
    const text = target.content || target.text || target.clipboard || '';
    if (!text) return { value: 0 };

    const normalized = text.toLowerCase();
    const matched = [];
    for (const keyword of SUSPICIOUS_KEYWORDS) {
      if (normalized.includes(keyword.toLowerCase())) {
        matched.push(keyword);
      }
    }
    return {
      value: matched.length,
      metadata: matched.length > 0 ? { keywords: matched } : null,
    };
  }, { returnType: 'number', scope: 'frame', description: 'Count of suspicious command keywords found' });

  // ── text.has_base64_encoded_command ────────────
  registry.register('text', 'has_base64_encoded_command', (target) => {
    const text = target.content || target.text || target.clipboard || '';
    if (!text) return { value: false };

    const b64Matches = text.match(/[A-Za-z0-9+\/]{20,}={0,2}/g);
    if (!b64Matches) return { value: false };

    const longBlocks = b64Matches.filter(b => b.length > 50);
    return {
      value: longBlocks.length > 0,
      metadata: longBlocks.length > 0 ? {
        count: longBlocks.length,
        maxLength: Math.max(...longBlocks.map(b => b.length)),
      } : null,
    };
  }, { returnType: 'boolean', scope: 'frame', description: 'Long base64-encoded blocks found (likely encoded commands)' });
}
