// Tabnabbing Detector - Detects background tab navigation to phishing
// Browser Techniques Line 35: Tabnabbing & session re-auth deception
// Background tab changes into a phishing page; victim returns and re-authenticates

import { BaseDetector } from '../base-detector.js';

export class TabnabbingDetector extends BaseDetector {
  constructor(config = {}) {
    super({
      name: 'tabnabbing',
      category: 'social-engineering',
      version: '1.0.0',
      description: 'Detects tabnabbing attacks where background tabs change content',
      weight: 2,
      severity: 'medium',
      ...config
    });
  }

  async detect(target, ruleRegistry) {
    const findings = [];

    // This detector requires tab event tracking data
    const event = target.event || {};

    if (!event.wasBlurred && !event.navigationDuringBlur && !event.titleChanged) {
      return this.createResult(false, [], 0);
    }

    const rules = ruleRegistry.getRules('social-engineering', 'tabnabbing');

    if (!rules) {
      return this.createResult(false, [], 0);
    }

    // 1. Detect navigation during tab blur
    if (event.wasBlurred && event.navigationDuringBlur) {
      const timeSinceBlur = event.timeSinceBlur || 0;
      const isSuspicious = timeSinceBlur < (rules.rapidNavigationThreshold || 2000);

      if (isSuspicious) {
        findings.push(this.createFinding({
          pattern: 'navigation-during-blur',
          matched: `${event.originalUrl} → ${event.newUrl}`,
          description: 'Page navigated shortly after tab lost focus',
          severity: 'high',
          weight: 4,
          metadata: {
            originalUrl: event.originalUrl,
            newUrl: event.newUrl,
            timeSinceBlur,
            technique: 'tabnabbing-navigation'
          }
        }));
      }
    }

    // 2. Detect title change during blur
    if (event.wasBlurred && event.titleChanged) {
      findings.push(this.createFinding({
        pattern: 'title-change-during-blur',
        matched: `"${event.originalTitle}" → "${event.newTitle}"`,
        description: 'Page title changed while tab was inactive',
        severity: 'medium',
        weight: 3,
        metadata: {
          originalTitle: event.originalTitle,
          newTitle: event.newTitle,
          technique: 'tabnabbing-title'
        }
      }));
    }

    // 3. Detect favicon change during blur
    if (event.wasBlurred && event.faviconChanged) {
      findings.push(this.createFinding({
        pattern: 'favicon-change-during-blur',
        matched: 'Favicon changed during blur',
        description: 'Page favicon changed while tab was inactive',
        severity: 'medium',
        weight: 2,
        metadata: {
          technique: 'tabnabbing-favicon'
        }
      }));
    }

    // 4. Detect history.pushState manipulation during blur
    if (event.wasBlurred && event.historyManipulation) {
      findings.push(this.createFinding({
        pattern: 'history-manipulation-during-blur',
        matched: `URL changed to ${event.newUrl}`,
        description: 'History API used to change URL while tab was inactive',
        severity: 'high',
        weight: 3,
        metadata: {
          originalUrl: event.originalUrl,
          newUrl: event.newUrl,
          technique: 'history-api-abuse'
        }
      }));
    }

    // 5. Detect brand mismatch (original site vs new site)
    if (
      event.originalDomain &&
      event.newDomain &&
      event.originalDomain !== event.newDomain
    ) {
      // Check if new domain is trying to mimic original domain
      const isBrandMismatch = this.detectBrandMismatch(
        event.originalDomain,
        event.newDomain,
        event.newTitle,
        rules
      );

      if (isBrandMismatch) {
        findings.push(this.createFinding({
          pattern: 'brand-mismatch',
          matched: `${event.originalDomain} → ${event.newDomain}`,
          description: 'Page changed to different brand while tab was inactive',
          severity: 'high',
          weight: 4,
          metadata: {
            originalDomain: event.originalDomain,
            newDomain: event.newDomain,
            technique: 'brand-spoofing'
          }
        }));
      }
    }

    // 6. Detect login page appearance during blur
    if (event.wasBlurred && event.hasLoginForm && !event.hadLoginFormBefore) {
      findings.push(this.createFinding({
        pattern: 'login-form-appeared',
        matched: 'Login form appeared during blur',
        description: 'Login/authentication form appeared while tab was inactive',
        severity: 'high',
        weight: 4,
        metadata: {
          technique: 'session-hijacking-attempt'
        }
      }));
    }

    // 7. Detect combination attack (multiple indicators)
    const multipleIndicators = [
      event.navigationDuringBlur,
      event.titleChanged,
      event.faviconChanged,
      event.historyManipulation,
      event.hasLoginForm && !event.hadLoginFormBefore
    ].filter(Boolean).length;

    if (multipleIndicators >= 3) {
      findings.push(this.createFinding({
        pattern: 'multi-indicator-tabnabbing',
        matched: `${multipleIndicators} tabnabbing indicators`,
        description: 'Multiple tabnabbing indicators detected (high confidence attack)',
        severity: 'critical',
        weight: 5,
        metadata: {
          indicatorCount: multipleIndicators,
          technique: 'advanced-tabnabbing'
        }
      }));
    }

    // 8. Detect opener reference abuse
    if (event.hasOpenerReference && event.navigationDuringBlur) {
      findings.push(this.createFinding({
        pattern: 'opener-reference-abuse',
        matched: 'window.opener reference with navigation',
        description: 'Page has opener reference and navigated during blur (reverse tabnabbing)',
        severity: 'high',
        weight: 4,
        metadata: {
          technique: 'reverse-tabnabbing'
        }
      }));
    }

    const matched = findings.length > 0;
    const riskScore = findings.reduce((sum, f) => sum + (f.weight || 1), 0);

    return this.createResult(matched, findings, riskScore);
  }

  detectBrandMismatch(originalDomain, newDomain, newTitle, rules) {
    // Extract brand from original domain
    const originalBrand = originalDomain.split('.')[0];

    // Check if new domain/title contains original brand (potential spoofing)
    const newDomainLower = newDomain.toLowerCase();
    const newTitleLower = (newTitle || '').toLowerCase();

    // If new domain is completely different and mentions original brand in title
    if (
      !newDomainLower.includes(originalBrand.toLowerCase()) &&
      newTitleLower.includes(originalBrand.toLowerCase())
    ) {
      return true;
    }

    // Check against known brand domains
    if (rules.brandDomains && rules.brandDomains.includes(originalDomain)) {
      // Original was a known brand, new domain is not
      if (!rules.brandDomains.includes(newDomain)) {
        return true;
      }
    }

    return false;
  }
}
