// Malvertising Detector - Detects malicious advertising redirect chains
// Browser Techniques Line 67: Redirect chains to exploit kit
// High-risk redirect patterns and known exploit-kit infra

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

export class MalvertisingDetector extends BaseDetector {
  constructor(config = {}) {
    super({
      name: 'malvertising',
      category: 'code-injection',
      version: '1.0.0',
      description: 'Detects malvertising redirect chains and forced downloads',
      weight: 3,
      severity: 'high',
      ...config
    });
  }

  async detect(target, ruleRegistry) {
    const findings = [];
    const url = target.url;
    const redirectChain = target.redirectChain || [];
    const referrer = target.referrer;

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

    const rules = ruleRegistry.getRules('code-injection', 'malvertising');

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

    // 1. Detect excessive redirect chains
    const redirectThreshold = rules.redirectChainThreshold || 3;

    if (redirectChain.length > redirectThreshold) {
      const hasSuspiciousDomains = redirectChain.some(redirectUrl =>
        this.isSuspiciousDomain(redirectUrl, rules)
      );

      findings.push(this.createFinding({
        pattern: 'excessive-redirect-chain',
        matched: `${redirectChain.length} redirects`,
        description: `Suspicious redirect chain (${redirectChain.length} hops)`,
        severity: hasSuspiciousDomains ? 'high' : 'medium',
        weight: hasSuspiciousDomains ? 4 : 3,
        metadata: {
          redirectCount: redirectChain.length,
          chain: redirectChain,
          hasSuspiciousDomains,
          technique: 'malvertising-redirect-chain'
        }
      }));
    }

    // 2. Detect known exploit kit domains
    const isExploitKit = this.isExploitKitDomain(url, rules);

    if (isExploitKit) {
      findings.push(this.createFinding({
        pattern: 'exploit-kit-domain',
        matched: url,
        description: 'Domain matches known exploit kit infrastructure',
        severity: 'critical',
        weight: 5,
        metadata: {
          url,
          exploitKit: isExploitKit.kitName,
          technique: 'malvertising-exploit-kit'
        }
      }));
    }

    // 3. Detect forced download patterns
    if (target.hasAutoDownload || target.downloadTriggered) {
      const isFromAd = this.isAdNetwork(referrer, rules) ||
                       redirectChain.some(r => this.isAdNetwork(r, rules));

      if (isFromAd) {
        findings.push(this.createFinding({
          pattern: 'ad-forced-download',
          matched: target.downloadFile || 'auto-download',
          description: 'Automatic download triggered from advertising network',
          severity: 'critical',
          weight: 5,
          metadata: {
            downloadFile: target.downloadFile,
            referrer,
            technique: 'malvertising-forced-download'
          }
        }));
      }
    }

    // 4. Detect suspicious meta refresh
    if (target.hasMetaRefresh) {
      const refreshDelay = target.metaRefreshDelay || 0;
      const refreshUrl = target.metaRefreshUrl;

      // Instant or very fast meta refresh is suspicious
      if (refreshDelay < (rules.suspiciousMetaRefreshDelay || 2)) {
        findings.push(this.createFinding({
          pattern: 'suspicious-meta-refresh',
          matched: `${refreshDelay}s delay to ${refreshUrl}`,
          description: 'Suspicious fast meta refresh redirect',
          severity: 'medium',
          weight: 3,
          metadata: {
            delay: refreshDelay,
            targetUrl: refreshUrl,
            technique: 'malvertising-meta-refresh'
          }
        }));
      }
    }

    // 5. Detect ad network domain hopping
    if (redirectChain.length > 0) {
      const adNetworkHops = this.countAdNetworkHops(redirectChain, rules);

      if (adNetworkHops > (rules.maxAdNetworkHops || 2)) {
        findings.push(this.createFinding({
          pattern: 'ad-network-hopping',
          matched: `${adNetworkHops} ad network redirects`,
          description: 'Multiple ad network redirects (potential malvertising)',
          severity: 'high',
          weight: 4,
          metadata: {
            adNetworkHops,
            chain: redirectChain,
            technique: 'malvertising-ad-hopping'
          }
        }));
      }
    }

    // 6. Detect cloaking (different content for crawlers vs users)
    if (target.hasCloaking) {
      findings.push(this.createFinding({
        pattern: 'ad-cloaking',
        matched: 'User-agent based content delivery',
        description: 'Content cloaking detected (shows different content to crawlers)',
        severity: 'high',
        weight: 4,
        metadata: {
          technique: 'malvertising-cloaking'
        }
      }));
    }

    // 7. Detect popup chains
    if (target.popupCount && target.popupCount > (rules.suspiciousPopupCount || 2)) {
      findings.push(this.createFinding({
        pattern: 'popup-chain',
        matched: `${target.popupCount} popups`,
        description: 'Multiple rapid popup windows (malvertising pattern)',
        severity: 'high',
        weight: 3,
        metadata: {
          popupCount: target.popupCount,
          technique: 'malvertising-popup-chain'
        }
      }));
    }

    // 8. Detect suspicious URL parameters
    const hasSuspiciousParams = this.detectSuspiciousUrlParameters(url, rules);

    if (hasSuspiciousParams) {
      findings.push(this.createFinding({
        pattern: 'suspicious-ad-params',
        matched: hasSuspiciousParams.params.join(', '),
        description: 'URL contains suspicious advertising tracking parameters',
        severity: 'medium',
        weight: 2,
        metadata: {
          suspiciousParams: hasSuspiciousParams.params,
          technique: 'malvertising-tracking'
        }
      }));
    }

    // 9. Detect landing page abuse
    if (target.isLandingPage && target.hasAggressiveBehavior) {
      findings.push(this.createFinding({
        pattern: 'aggressive-landing-page',
        matched: url,
        description: 'Aggressive behavior on advertising landing page',
        severity: 'high',
        weight: 4,
        metadata: {
          behaviors: target.aggressiveBehaviors || [],
          technique: 'malvertising-aggressive-landing'
        }
      }));
    }

    // 10. Detect domain reputation abuse
    if (redirectChain.length > 0) {
      const lowReputationCount = redirectChain.filter(redirectUrl =>
        this.isLowReputationDomain(redirectUrl, rules)
      ).length;

      if (lowReputationCount > 0) {
        findings.push(this.createFinding({
          pattern: 'low-reputation-redirect',
          matched: `${lowReputationCount} low-reputation domains in chain`,
          description: 'Redirect chain includes low-reputation domains',
          severity: 'high',
          weight: 4,
          metadata: {
            lowReputationCount,
            technique: 'malvertising-low-reputation'
          }
        }));
      }
    }

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

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

  isSuspiciousDomain(url, rules) {
    try {
      const urlObj = new URL(url);
      const hostname = urlObj.hostname;

      const suspiciousTLDs = rules.suspiciousTLDs || [
        '.tk', '.ml', '.ga', '.cf', '.gq', '.pw', '.top', '.xyz'
      ];

      return suspiciousTLDs.some(tld => hostname.endsWith(tld));
    } catch {
      return false;
    }
  }

  isExploitKitDomain(url, rules) {
    const exploitKitPatterns = rules.exploitKitPatterns || [];

    try {
      const urlObj = new URL(url);
      const hostname = urlObj.hostname;
      const path = urlObj.pathname;
      const fullUrl = hostname + path;

      for (const pattern of exploitKitPatterns) {
        if (pattern.regex) {
          const regex = new RegExp(pattern.regex, 'i');
          if (regex.test(fullUrl)) {
            return { kitName: pattern.name };
          }
        }
      }

      return null;
    } catch {
      return null;
    }
  }

  isAdNetwork(url, rules) {
    if (!url) return false;

    const adNetworks = rules.adNetworks || [
      'doubleclick.net',
      'googleadservices.com',
      'googlesyndication.com',
      'adnxs.com',
      'advertising.com',
      'criteo.com',
      'outbrain.com',
      'taboola.com',
      'revcontent.com',
      'mgid.com'
    ];

    try {
      const urlObj = new URL(url);
      const hostname = urlObj.hostname;

      return adNetworks.some(adNetwork => hostname.includes(adNetwork));
    } catch {
      return false;
    }
  }

  countAdNetworkHops(redirectChain, rules) {
    return redirectChain.filter(url => this.isAdNetwork(url, rules)).length;
  }

  detectSuspiciousUrlParameters(url, rules) {
    const suspiciousParams = rules.suspiciousUrlParams || [
      'malware', 'exploit', 'payload', 'shell', 'backdoor',
      'download_force', 'auto_download', 'force_install'
    ];

    try {
      const urlObj = new URL(url);
      const params = Array.from(urlObj.searchParams.keys());

      const foundSuspicious = params.filter(param =>
        suspiciousParams.some(suspicious => param.toLowerCase().includes(suspicious))
      );

      if (foundSuspicious.length > 0) {
        return { params: foundSuspicious };
      }

      return null;
    } catch {
      return null;
    }
  }

  isLowReputationDomain(url, rules) {
    try {
      const urlObj = new URL(url);
      const hostname = urlObj.hostname;

      // Check against low reputation TLDs
      const lowReputationTLDs = rules.lowReputationTLDs || [
        '.tk', '.ml', '.ga', '.cf', '.gq'
      ];

      if (lowReputationTLDs.some(tld => hostname.endsWith(tld))) {
        return true;
      }

      // Check for very new or suspicious domain patterns
      const suspiciousPatterns = rules.suspiciousDomainPatterns || [
        /\d{5,}/,  // Many consecutive digits
        /[a-z]{20,}/,  // Very long random string
        /-{2,}/  // Multiple consecutive hyphens
      ];

      return suspiciousPatterns.some(pattern => pattern.test(hostname));
    } catch {
      return false;
    }
  }
}
