// Clipboard Injection Signal Providers
// Extracts typed features from clipboard content for use by dynamic rules.
// Each provider returns { value, metadata? } — no decisions, no severity, no findings.

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

  // ── clipboard.* providers ──────────────────────────────

  /**
   * Detect the overall threat type(s) present in clipboard content.
   * Returns an array of threat type strings (empty if clean).
   */
  registry.register('clipboard', 'threat_types', (target) => {
    const text = target.clipboard || target.clipboardData || target.content || '';
    if (!text) return { value: [] };

    const types = [];
    if (_hasCommandPattern(text)) types.push('malicious-command');
    if (_hasCryptoAddress(text)) types.push('crypto-address-swap');
    if (_hasSensitiveData(text)) types.push('sensitive-data-injection');
    if (_hasPhishingUrl(text)) types.push('phishing-url-injection');
    return { value: types };
  }, { returnType: 'string[]', scope: 'tab', description: 'Array of threat types found in clipboard content' });

  /**
   * Detect PowerShell patterns in clipboard/content text.
   * Returns an array of matched PowerShell technique names.
   */
  registry.register('clipboard', 'has_powershell_pattern', (target) => {
    const text = target.clipboard || target.clipboardData || target.content || '';
    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: { techniques: matched, count: matched.length },
    };
  }, { returnType: 'boolean', scope: 'tab', description: 'Whether PowerShell command patterns are present' });

  /**
   * Detect cryptocurrency addresses in clipboard content.
   * Returns { detected: boolean, currencies: string[], count: number }
   */
  registry.register('clipboard', 'has_crypto_address', (target) => {
    const text = target.clipboard || target.clipboardData || target.content || '';
    if (!text) return { value: false };

    const currencies = [];
    for (const [currency, pattern] of Object.entries(CRYPTO_PATTERNS)) {
      if (pattern.test(text)) currencies.push(currency);
    }
    return {
      value: currencies.length > 0,
      metadata: { currencies, count: currencies.length },
    };
  }, { returnType: 'boolean', scope: 'tab', description: 'Whether cryptocurrency addresses are present' });

  /**
   * Calculate command severity (0-10 scale).
   * Considers command types, obfuscation, chaining complexity.
   */
  registry.register('clipboard', 'command_severity', (target) => {
    const text = target.clipboard || target.clipboardData || target.content || '';
    if (!text) return { value: 0 };

    let severity = 0;
    const analysis = _analyzeCommandSeverity(text);
    severity = analysis.score;
    return {
      value: Math.min(severity, 10),
      metadata: {
        commandTypes: analysis.commandTypes,
        obfuscationScore: analysis.obfuscationScore,
        chainComplexity: analysis.chainComplexity,
      },
    };
  }, { returnType: 'number', scope: 'tab', description: 'Command severity score (0-10)' });

  /**
   * Calculate obfuscation score for clipboard content.
   * Higher = more obfuscation techniques detected.
   */
  registry.register('clipboard', 'obfuscation_score', (target) => {
    const text = target.clipboard || target.clipboardData || target.content || '';
    if (!text) return { value: 0 };

    let score = 0;
    for (const { pattern } of OBFUSCATION_INDICATORS) {
      const matches = text.match(pattern);
      if (matches && matches.length > 2) score++;
    }
    return { value: score };
  }, { returnType: 'number', scope: 'tab', description: 'Obfuscation technique count' });

  // ── text.* providers (generic text analysis) ────────────

  /**
   * Detect base64-encoded commands in text.
   */
  registry.register('text', 'has_base64_command', (target) => {
    const text = target.clipboard || target.clipboardData || target.content || '';
    if (!text) return { value: false };

    const hasEncodedPS = /(?:-enc(?:odedcommand)?|-e)\s+[A-Za-z0-9+\/=]{20,}/i.test(text);
    const hasBase64Pipe = /base64\s+-d.*?\|.*?(?:bash|sh)/i.test(text);
    const hasFromBase64 = /frombase64string/i.test(text);

    return {
      value: hasEncodedPS || hasBase64Pipe || hasFromBase64,
      metadata: {
        encodedPowerShell: hasEncodedPS,
        base64Pipe: hasBase64Pipe,
        fromBase64String: hasFromBase64,
      },
    };
  }, { returnType: 'boolean', scope: 'tab', description: 'Whether base64-encoded commands are present' });

  /**
   * Detect LOLBin (Living Off The Land Binary) usage.
   */
  registry.register('text', 'has_lolbin', (target) => {
    const text = target.clipboard || target.clipboardData || target.content || '';
    if (!text) return { value: false };

    const matched = [];
    for (const { pattern, name } of LOLBIN_PATTERNS) {
      if (pattern.test(text)) matched.push(name);
    }
    // Parity with the page-world interceptor: also flag command-execution
    // patterns that aren't a named LOLBin binary — registry persistence,
    // temp/appdata executables, bash pipe-to-shell, VBScript droppers.
    for (const { pattern, name } of COMMAND_EXEC_EXTRA) {
      if (pattern.test(text)) matched.push(name);
    }
    return {
      value: matched.length > 0,
      metadata: { lolbins: matched, count: matched.length },
    };
  }, { returnType: 'boolean', scope: 'tab', description: 'Whether LOLBin / command-execution patterns are present' });

  /**
   * Detect UNC/WebDAV remote paths (\\server@port\share).
   */
  registry.register('text', 'has_unc_path', (target) => {
    const text = target.clipboard || target.clipboardData || target.content || '';
    if (!text) return { value: false };

    const uncPattern = /\\\\[^\s\\]+@\d+\\[^\s]+/;
    const match = uncPattern.test(text);
    return {
      value: match,
      metadata: { pattern: 'UNC/WebDAV remote path' },
    };
  }, { returnType: 'boolean', scope: 'tab', description: 'Whether UNC/WebDAV remote paths are present' });

  /**
   * Detect sensitive data patterns (passwords, API keys, SSNs, etc.)
   */
  registry.register('text', 'has_sensitive_data', (target) => {
    const text = target.clipboard || target.clipboardData || target.content || '';
    if (!text) return { value: false };

    const detected = [];
    for (const { pattern, type } of SENSITIVE_DATA_PATTERNS) {
      if (pattern.test(text)) detected.push(type);
    }
    return {
      value: detected.length > 0,
      metadata: { dataTypes: detected, count: detected.length },
    };
  }, { returnType: 'boolean', scope: 'tab', description: 'Whether sensitive data patterns are present' });

  /**
   * Count suspicious URLs in text (IP addresses, free TLDs, punycode, non-standard ports).
   */
  registry.register('text', 'suspicious_url_count', (target) => {
    const text = target.clipboard || target.clipboardData || target.content || '';
    if (!text) return { value: 0 };

    const urlPattern = /https?:\/\/[^\s]+/gi;
    const urls = text.match(urlPattern) || [];
    let suspiciousCount = 0;

    for (const url of urls) {
      try {
        const urlObj = new URL(url);
        if (/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(urlObj.hostname)) suspiciousCount++;
        else if (urlObj.port && !['80', '443', '8080'].includes(urlObj.port)) suspiciousCount++;
        else if (/\.(tk|ml|ga|cf|gq)$/i.test(urlObj.hostname)) suspiciousCount++;
        else if (urlObj.hostname.startsWith('xn--')) suspiciousCount++;
      } catch {}
    }

    return {
      value: suspiciousCount,
      metadata: { totalUrls: urls.length },
    };
  }, { returnType: 'number', scope: 'tab', description: 'Count of suspicious URLs in content' });
}

// ── Private Pattern Constants ────────────────────────────

const POWERSHELL_PATTERNS = [
  { pattern: /powershell(?:\.exe)?.*?(?:Invoke-WebRequest|iwr|wget|curl)/i, type: 'Download' },
  { pattern: /powershell(?:\.exe)?.*?(?:Invoke-Expression|iex)/i, type: 'RemoteExec' },
  { pattern: /powershell(?:\.exe)?.*?(?:Start-Process|start)/i, type: 'ProcessStart' },
  { pattern: /powershell(?:\.exe)?.*?(?:-enc(?:odedcommand)?|-e)\s+[A-Za-z0-9+\/=]{20,}/i, type: 'EncodedCommand' },
  { pattern: /powershell(?:\.exe)?.*?(?:-w(?:indowstyle)?\s+hidden|-nop|-ep\s+bypass)/i, type: 'HiddenBypass' },
  { pattern: /powershell(?:\.exe)?.*?(?:DownloadString|DownloadFile)/i, type: 'DownloadStringFile' },
  { pattern: /powershell(?:\.exe)?.*?(?:Add-MpPreference.*?-ExclusionPath)/i, type: 'DisableDefender' },
];

const CRYPTO_PATTERNS = {
  bitcoin: /\b[13][a-km-zA-HJ-NP-Z1-9]{25,34}\b/,
  ethereum: /\b0x[a-fA-F0-9]{40}\b/,
  litecoin: /\b[LM3][a-km-zA-HJ-NP-Z1-9]{26,33}\b/,
  monero: /\b4[0-9AB][1-9A-HJ-NP-Za-km-z]{93}\b/,
};

const LOLBIN_PATTERNS = [
  { pattern: /\brundll32(?:\.exe)?\s/i, name: 'rundll32' },
  { pattern: /\bregsvr32(?:\.exe)?\s/i, name: 'regsvr32' },
  { pattern: /\bmshta(?:\.exe)?\s/i, name: 'mshta' },
  { pattern: /\bcertutil(?:\.exe)?\s/i, name: 'certutil' },
  { pattern: /\bbitsadmin(?:\.exe)?\s/i, name: 'bitsadmin' },
  { pattern: /\bwmic(?:\.exe)?\s/i, name: 'wmic' },
  { pattern: /\bcmstp(?:\.exe)?\s/i, name: 'cmstp' },
  { pattern: /\binstallutil(?:\.exe)?\s/i, name: 'installutil' },
  { pattern: /\bregasm(?:\.exe)?\s/i, name: 'regasm' },
  { pattern: /\bregsvcs(?:\.exe)?\s/i, name: 'regsvcs' },
  { pattern: /\bmsbuild(?:\.exe)?\s/i, name: 'msbuild' },
  { pattern: /\bcsc(?:\.exe)?\s/i, name: 'csc' },
  { pattern: /\bieexec(?:\.exe)?\s/i, name: 'ieexec' },
  { pattern: /\bodbcconf(?:\.exe)?\s/i, name: 'odbcconf' },
  { pattern: /\bpcalua(?:\.exe)?\s/i, name: 'pcalua' },
  { pattern: /\bmavinject(?:\.exe)?\s/i, name: 'mavinject' },
  { pattern: /\bSyncAppvPublishingServer(?:\.exe)?\s/i, name: 'SyncAppvPublishingServer' },
  { pattern: /\bforfiles(?:\.exe)?\s/i, name: 'forfiles' },
  { pattern: /\bhh(?:\.exe)?\s/i, name: 'hh' },
  { pattern: /\bdfsvc(?:\.exe)?\s/i, name: 'dfsvc' },
  { pattern: /\bpresentationhost(?:\.exe)?\s/i, name: 'presentationhost' },
  { pattern: /\bwinrm(?:\.exe)?\s/i, name: 'winrm' },
  { pattern: /\bmsiexec(?:\.exe)?\s/i, name: 'msiexec' },
  { pattern: /\bmsdt(?:\.exe)?\s/i, name: 'msdt' },
  { pattern: /\bdnscmd(?:\.exe)?\s/i, name: 'dnscmd' },
  { pattern: /\bcontrol(?:\.exe)?\s/i, name: 'control' },
  { pattern: /\binfdefaultinstall(?:\.exe)?\s/i, name: 'infdefaultinstall' },
  { pattern: /\bdesktopimgdownldr(?:\.exe)?\s/i, name: 'desktopimgdownldr' },
  { pattern: /\beudcedit(?:\.exe)?\s/i, name: 'eudcedit' },
  { pattern: /\bbash(?:\.exe)?\s/i, name: 'bash' },
  { pattern: /\bwsl(?:\.exe)?\s/i, name: 'wsl' },
  { pattern: /\bxwizard(?:\.exe)?\s/i, name: 'xwizard' },
  { pattern: /\bverclsid(?:\.exe)?\s/i, name: 'verclsid' },
  { pattern: /\bbginfo(?:\.exe)?\s/i, name: 'bginfo' },
  { pattern: /\bdiskshadow(?:\.exe)?\s/i, name: 'diskshadow' },
  { pattern: /\breplace(?:\.exe)?\s/i, name: 'replace' },
  { pattern: /\brpcping(?:\.exe)?\s/i, name: 'rpcping' },
  { pattern: /\bftp(?:\.exe)?\s/i, name: 'ftp' },
  { pattern: /\bexplorer(?:\.exe)?\s/i, name: 'explorer' },
];

// Command-execution patterns that aren't a named LOLBin binary. Ported from
// the page-world interceptor so the v3 ruleset reaches detection parity.
const COMMAND_EXEC_EXTRA = [
  // cmd /c piping a download utility to fetch a remote payload
  { pattern: /cmd(?:\.exe)?.*?\/c.*?(?:curl|certutil|bitsadmin).*?http/i, name: 'cmd-http-download' },
  // Registry persistence / security-software tampering
  { pattern: /reg(?:\.exe)?.*?add.*?(?:HKCU|HKLM).*?(?:Run|RunOnce)/i, name: 'reg-persistence' },
  { pattern: /reg(?:\.exe)?.*?delete.*?(?:Windows Defender|Security)/i, name: 'reg-security-removal' },
  // Executables staged in user-writable dirs
  { pattern: /%(?:temp|appdata|programdata)%.*?\.(?:exe|bat|cmd|ps1|vbs|js)/i, name: 'env-exec' },
  // Bash / *nix download-and-run
  { pattern: /(?:curl|wget).*?(?:http|ftp).*?\|.*?(?:bash|sh|python|perl|ruby)/i, name: 'pipe-to-shell' },
  { pattern: /(?:curl|wget).*?-o.*?\/tmp.*?;.*?chmod.*?\+x/i, name: 'download-exec' },
  { pattern: /base64\s+-d.*?\|.*?(?:bash|sh)/i, name: 'base64-pipe-exec' },
  { pattern: /nc\s+-[el].*?\d+.*?-e.*?(?:\/bin\/(?:ba)?sh|cmd\.exe)/i, name: 'netcat-revshell' },
  { pattern: /\/bin\/(?:ba)?sh\s+-i\s+>&\s+\/dev\/tcp/i, name: 'bash-revshell' },
  // VBScript / JScript droppers
  { pattern: /CreateObject\s*\(\s*["'](?:WinHttp\.WinHttpRequest|MSXML2\.(?:Server)?XMLHTTP|Scripting\.FileSystemObject)/i, name: 'vbs-createobject' },
  { pattern: /(?:WinHttpRequest|XMLHTTP).*?\.Open.*?http/i, name: 'vbs-http' },
  { pattern: /Execute\s+(?:h|response|obj)\.ResponseText/i, name: 'vbs-execute-response' },
  { pattern: /cmd(?:\.exe)?.*?\/c.*?echo.*?(?:>|>>).*?%temp%.*?\.(?:bat|cmd|vbs|js|ps1)/i, name: 'cmd-echo-script' },
];

const SENSITIVE_DATA_PATTERNS = [
  { pattern: /(?:password|passwd|pwd)\s*[:=]\s*\S+/i, type: 'Password' },
  { pattern: /(?:api[_-]?key|apikey)\s*[:=]\s*[\w-]+/i, type: 'API Key' },
  { pattern: /(?:token|bearer)\s*[:=]\s*[\w.-]+/i, type: 'Auth Token' },
  { pattern: /-----BEGIN (?:RSA |DSA )?PRIVATE KEY-----/, type: 'Private Key' },
  { pattern: /\b\d{3}-\d{2}-\d{4}\b/, type: 'SSN' },
  { pattern: /\b(?:\d{4}[-\s]?){3}\d{4}\b/, type: 'Credit Card' },
];

const OBFUSCATION_INDICATORS = [
  { pattern: /\^/g, name: 'Caret Escaping' },
  { pattern: /`/g, name: 'Backtick Escaping' },
  { pattern: /\$\{[^}]+\}/g, name: 'Variable Expansion' },
  { pattern: /\[char\]/gi, name: 'Character Code' },
  { pattern: /-join/gi, name: 'String Joining' },
  { pattern: /frombase64string|base64/gi, name: 'Base64 Encoding' },
  { pattern: /\\x[0-9a-f]{2}/gi, name: 'Hex Encoding' },
  { pattern: /\\u[0-9a-f]{4}/gi, name: 'Unicode Encoding' },
];

// ── Private Helpers ──────────────────────────────────────

function _hasCommandPattern(text) {
  const commandPatterns = [
    /powershell(?:\.exe)?.*?(?:Invoke-|iex|iwr|wget|curl|DownloadString|Start-Process|-enc)/i,
    /cmd(?:\.exe)?.*?\/c.*?(?:curl|certutil|bitsadmin|mshta).*?http/i,
    /(?:curl|wget).*?(?:http|ftp).*?\|.*?(?:bash|sh|python|perl)/i,
    /nc\s+-[el].*?\d+.*?-e.*?(?:\/bin\/(?:ba)?sh|cmd\.exe)/i,
    /reg(?:\.exe)?.*?add.*?(?:HKCU|HKLM).*?(?:Run|RunOnce)/i,
  ];
  return commandPatterns.some(p => p.test(text));
}

function _hasCryptoAddress(text) {
  return Object.values(CRYPTO_PATTERNS).some(p => p.test(text));
}

function _hasSensitiveData(text) {
  return SENSITIVE_DATA_PATTERNS.some(({ pattern }) => pattern.test(text));
}

function _hasPhishingUrl(text) {
  const urlPattern = /https?:\/\/[^\s]+/gi;
  const urls = text.match(urlPattern) || [];
  for (const url of urls) {
    try {
      const urlObj = new URL(url);
      if (/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(urlObj.hostname)) return true;
      if (urlObj.port && !['80', '443', '8080'].includes(urlObj.port)) return true;
      if (/\.(tk|ml|ga|cf|gq)$/i.test(urlObj.hostname)) return true;
      if (urlObj.hostname.startsWith('xn--')) return true;
    } catch {}
  }
  return false;
}

function _analyzeCommandSeverity(text) {
  let score = 0;
  const commandTypes = [];

  // Critical patterns (3 points each)
  const critical = [
    { pattern: /powershell(?:\.exe)?.*?(?:Invoke-Expression|iex).*?(?:DownloadString|http)/i, type: 'PS Remote Exec' },
    { pattern: /powershell(?:\.exe)?.*?(?:-enc(?:odedcommand)?|-e)\s+[A-Za-z0-9+\/=]{20,}/i, type: 'PS Encoded' },
    { pattern: /(?:curl|wget).*?(?:http|ftp).*?\|.*?(?:bash|sh|python)/i, type: 'Pipe Exec' },
    { pattern: /nc\s+-[el].*?-e.*?(?:\/bin\/(?:ba)?sh|cmd\.exe)/i, type: 'Reverse Shell' },
    { pattern: /certutil(?:\.exe)?.*?-urlcache.*?http/i, type: 'Certutil Download' },
    { pattern: /mshta(?:\.exe)?.*?http/i, type: 'MSHTA Exec' },
  ];

  for (const { pattern, type } of critical) {
    if (pattern.test(text)) {
      score += 3;
      commandTypes.push(type);
    }
  }

  // High patterns (2 points each)
  const high = [
    { pattern: /powershell(?:\.exe)?.*?(?:Invoke-WebRequest|iwr|wget)/i, type: 'PS Download' },
    { pattern: /powershell(?:\.exe)?.*?(?:-w(?:indowstyle)?\s+hidden|-nop|-ep\s+bypass)/i, type: 'PS Evasion' },
    { pattern: /reg(?:\.exe)?.*?add.*?(?:HKCU|HKLM).*?(?:Run|RunOnce)/i, type: 'Persistence' },
    { pattern: /wmic.*?process.*?call.*?create/i, type: 'WMIC Process' },
  ];

  for (const { pattern, type } of high) {
    if (pattern.test(text)) {
      score += 2;
      commandTypes.push(type);
    }
  }

  // Obfuscation bonus
  let obfuscationScore = 0;
  for (const { pattern } of OBFUSCATION_INDICATORS) {
    const matches = text.match(pattern);
    if (matches && matches.length > 2) obfuscationScore++;
  }
  if (obfuscationScore >= 2) score += 2;

  // Chain complexity
  const chainPatterns = [/\|\|/g, /&&/g, /;/g, /\|/g];
  let chainComplexity = 0;
  for (const p of chainPatterns) {
    const m = text.match(p);
    if (m) chainComplexity += m.length;
  }
  if (chainComplexity >= 3) score += 1;

  return { score, commandTypes, obfuscationScore, chainComplexity };
}
