// Keyboard-Instruction Signal Providers (v3)
// Typed features for "ClickFix" / fake-instruction social engineering — pages
// that instruct the user to run commands via keyboard shortcuts (Win+R, paste,
// Enter). Ported from the legacy keyboard-instruction detector. Providers
// return { value, metadata? } only — no decisions.

/**
 * @param {import('../../core/signal-provider.js').SignalProviderRegistry} registry
 */
export function registerKeyboardInstructionSignals(registry) {
  const norm = (target) => {
    const t = target.content || target.text || target.clipboard || '';
    return typeof t === 'string' ? t : '';
  };

  // Win+R / Run dialog instruction — the highest-signal ClickFix indicator.
  registry.register('text', 'run_dialog_instruction', (target) => {
    const text = norm(target).toLowerCase().replace(/\s+/g, ' ');
    if (!text) return { value: false };
    const matched = RUN_DIALOG_PATTERNS.some(p => p.test(text));
    return { value: matched };
  }, { returnType: 'boolean', scope: 'tab', description: 'Win+R / Run-dialog instruction present' });

  // "Paste this command and press Enter" style instruction.
  registry.register('text', 'paste_run_instruction', (target) => {
    const text = norm(target).toLowerCase().replace(/\s+/g, ' ');
    if (!text) return { value: false };
    const matched = PASTE_RUN_PATTERNS.some(p => p.test(text));
    return { value: matched };
  }, { returnType: 'boolean', scope: 'tab', description: 'Paste-and-run instruction present' });

  // Direct "run/open command prompt / type this command" instruction.
  registry.register('text', 'command_run_instruction', (target) => {
    const text = norm(target).toLowerCase().replace(/\s+/g, ' ');
    if (!text) return { value: false };
    const matched = COMMAND_INSTRUCTION_PATTERNS.some(p => p.test(text));
    return { value: matched };
  }, { returnType: 'boolean', scope: 'tab', description: 'Command-execution instruction present' });

  // How many distinct keyboard-shortcut instructions appear (Win+R, Ctrl+V,
  // Enter, …). 3+ usually means a multi-step attack walkthrough.
  registry.register('text', 'shortcut_instruction_count', (target) => {
    const text = norm(target).toLowerCase().replace(/\s+/g, ' ');
    if (!text) return { value: 0 };
    const hits = new Set();
    for (const { pattern, shortcut } of SHORTCUT_PATTERNS) {
      if (pattern.test(text)) hits.add(shortcut);
    }
    return { value: hits.size, metadata: hits.size ? { shortcuts: [...hits] } : null };
  }, { returnType: 'number', scope: 'tab', description: 'Distinct keyboard-shortcut instructions' });

  // Urgency / pressure phrases (social-engineering pressure).
  registry.register('text', 'urgency_phrase_count', (target) => {
    const text = norm(target).toLowerCase();
    if (!text) return { value: 0 };
    let n = 0;
    for (const p of URGENCY_PATTERNS) if (p.test(text)) n++;
    return { value: n };
  }, { returnType: 'number', scope: 'tab', description: 'Count of urgency/pressure phrases' });

  // Is there an actual command payload near the instructions? (powershell, cmd,
  // LOLBins, curl|bash…). Distinguishes "ClickFix with a real payload" from a
  // benign mention of a shortcut.
  registry.register('text', 'has_command_payload', (target) => {
    const text = norm(target);
    if (!text) return { value: false };
    return { value: COMMAND_PAYLOAD.test(text) };
  }, { returnType: 'boolean', scope: 'tab', description: 'Executable command payload present' });

  // Visual obfuscation: zero-width chars, RTL override, homoglyphs, excessive
  // whitespace. Used to hide the real instruction/command from the user.
  registry.register('text', 'visual_obfuscation_count', (target) => {
    const text = norm(target);
    if (!text) return { value: 0 };
    let n = 0;
    const zwc = text.match(/[​‌‍﻿]/g);
    if (zwc && zwc.length > 5) n++;
    if (/[‮]/.test(text)) n++;                     // RTL override
    if (/[аеорсхһ]/.test(text)) n++; // Cyrillic homoglyphs
    const ws = text.match(/\s{10,}/g);
    if (ws && ws.length > 3) n++;
    return { value: n };
  }, { returnType: 'number', scope: 'tab', description: 'Visual obfuscation technique count' });
}

// ── Pattern constants (ported from the legacy detector) ──────────────────────

const RUN_DIALOG_PATTERNS = [
  /(?:press|hit|type|click|push)\s+(?:the\s+)?(?:windows|win)\s*\+\s*r\b/i,
  /(?:press|hit|type|click|push)\s+(?:the\s+)?(?:windows|win)\s+key\s+and\s+r\b/i,
  /(?:windows|win)\s*\+\s*r\s+(?:key|shortcut|combination)/i,
  /open\s+(?:the\s+)?run\s+(?:dialog|window|box)/i,
  /(?:press|hit|type)\s+(?:windows|win)\s+(?:key\s+)?\+\s*x\b/i,
];

const PASTE_RUN_PATTERNS = [
  /paste\s+(?:the\s+)?(?:following|this|below)\s+(?:command|code|script|text)/i,
  /copy\s+(?:and\s+)?paste\s+(?:the\s+)?(?:following|this|below)/i,
  /paste\s+(?:it\s+)?(?:into|in)\s+(?:the\s+)?(?:run|command|terminal|powershell|cmd)/i,
  /(?:ctrl|cmd|control)\s*\+\s*(?:v|c)\s+(?:and|then)\s+(?:enter|return)/i,
  /paste\s+(?:and\s+)?(?:press\s+)?enter/i,
  /paste\s+(?:and\s+)?(?:hit\s+)?return/i,
];

const COMMAND_INSTRUCTION_PATTERNS = [
  /(?:type|enter|run|execute|copy|paste)\s+(?:this\s+)?(?:command|code|script):?\s*(?:powershell|cmd|certutil|mshta|regsvr32)/i,
  /(?:run|execute|open)\s+(?:the\s+)?(?:command\s+prompt|cmd|powershell|terminal)/i,
  /(?:type|paste|enter)\s+(?:this\s+)?in\s+(?:the\s+)?(?:run\s+(?:dialog|window|box)|command\s+prompt|terminal)/i,
  /copy\s+and\s+paste\s+(?:this\s+)?(?:command|code|script)/i,
  /(?:download|save)\s+and\s+(?:run|execute|open)\s+(?:this\s+)?(?:file|program|script)/i,
];

const SHORTCUT_PATTERNS = [
  { pattern: /(?:windows|win)\s*\+\s*r\b/i, shortcut: 'Win+R' },
  { pattern: /(?:ctrl|control|cmd)\s*\+\s*v\b/i, shortcut: 'Ctrl+V' },
  { pattern: /(?:press|hit)\s+(?:the\s+)?(?:enter|return)\b/i, shortcut: 'Enter' },
  { pattern: /(?:ctrl|control)\s*\+\s*shift\s*\+\s*esc\b/i, shortcut: 'Ctrl+Shift+Esc' },
  { pattern: /(?:ctrl|control|cmd)\s*\+\s*r\b/i, shortcut: 'Ctrl+R' },
  { pattern: /\bf11\b/i, shortcut: 'F11' },
];

const URGENCY_PATTERNS = [
  /urgent(?:ly)?|immediately|right\s+now|asap|hurry/i,
  /(?:your\s+)?account\s+(?:will\s+be\s+)?(?:suspended|locked|closed|terminated)/i,
  /within\s+\d+\s+(?:hours?|minutes?|days?)/i,
  /limited\s+time|time\s+(?:is\s+)?running\s+out/i,
  /verify\s+(?:your\s+)?(?:account|identity|information)\s+(?:now|immediately)/i,
  /security\s+(?:alert|warning|notice)/i,
  /unauthorized\s+(?:access|activity|login)/i,
  /click\s+(?:here|below|this)\s+(?:now|immediately)/i,
];

// A *malicious* command payload — not just a binary name (so "open PowerShell
// and run Get-Process" in a legit tutorial doesn't look like an attack). Needs
// download/execute/encoded/LOLBin-over-http context.
const COMMAND_PAYLOAD = new RegExp([
  'powershell[^\\n]{0,100}(?:-e(?:nc(?:odedcommand)?)?\\b|iex|invoke-expression|iwr|invoke-webrequest|downloadstring|downloadfile|-w\\s*hidden|-nop|-ep\\s*bypass)',
  'certutil[^\\n]{0,60}-urlcache',
  'mshta\\s+https?:',
  'rundll32[^\\n]{0,60}(?:https?:|javascript:)',
  'regsvr32[^\\n]{0,60}(?:/i:|https?:)',
  'bitsadmin[^\\n]{0,60}/transfer',
  'msiexec[^\\n]{0,60}/i\\s+https?:',
  '(?:curl|wget)[^\\n]{0,80}\\|\\s*(?:ba)?sh',
  'iwr[^\\n]{0,80}\\|\\s*iex',
].join('|'), 'i');
