// PhishProtect Evidence: Technology Fingerprinter
// Runs in content script context per frame (document_idle)
// Detects CMS, frameworks, libraries, CDNs via client-side signals

(function() {
  'use strict';

  // Defer to avoid blocking page rendering
  setTimeout(function runFingerprint() {
    const results = {
      cms: [],
      httpServer: [],
      serverSide: [],
      clientLibs: [],
      proxies: [],
      frameworks: []
    };

    const seen = new Set(); // Deduplicate by "category:name"

    function addResult(category, name, version, confidence, signal) {
      const key = `${category}:${name}`;
      if (seen.has(key)) return;
      seen.add(key);
      if (!results[category]) results[category] = [];
      results[category].push({
        name,
        version: version || null,
        confidence: confidence || 'medium',
        signal
      });
    }

    // 1. Meta generator tag
    const generators = document.querySelectorAll('meta[name="generator"]');
    for (const gen of generators) {
      const content = gen.getAttribute('content') || '';

      // Check known CMS signatures
      const cmsSigs = [
        // WordPress is the most common CMS in the wild — and `<meta name="generator"
        // content="WordPress 6.9.4">` is by far the most reliable version source
        // (more reliable than ?ver= query params on enqueued scripts).
        { match: /WordPress\s*([\d.]+)/i, name: 'WordPress', cat: 'cms' },
        { match: /Drupal\s*([\d.]*)/i, name: 'Drupal', cat: 'cms' },
        { match: /Joomla[!]?\s*([\d.]*)/i, name: 'Joomla', cat: 'cms' },
        { match: /Hugo\s*([\d.]*)/i, name: 'Hugo', cat: 'cms' },
        { match: /Jekyll\s*v?([\d.]*)/i, name: 'Jekyll', cat: 'cms' },
        { match: /Ghost\s*([\d.]*)/i, name: 'Ghost', cat: 'cms' },
        { match: /Blogger/i, name: 'Blogger', cat: 'cms' },
        { match: /PrestaShop/i, name: 'PrestaShop', cat: 'cms' },
        { match: /Magento/i, name: 'Magento', cat: 'cms' },
        { match: /OpenCart/i, name: 'OpenCart', cat: 'cms' },
        { match: /Wix\.com/i, name: 'Wix', cat: 'cms' },
        { match: /Squarespace/i, name: 'Squarespace', cat: 'cms' }
      ];

      for (const sig of cmsSigs) {
        const m = content.match(sig.match);
        if (m) {
          addResult(sig.cat, sig.name, m[1] || null, 'high', `meta[generator]="${content}"`);
        }
      }
    }

    // 2. Script and link paths
    const resourceElements = document.querySelectorAll('script[src], link[href]');
    const pathChecks = [
      { match: /\/modules\/system\//i, name: 'Drupal', cat: 'cms' },
      { match: /\/sites\/default\/files\//i, name: 'Drupal', cat: 'cms' },
      { match: /\/media\/jui\//i, name: 'Joomla', cat: 'cms' },
      { match: /\/skin\/frontend\//i, name: 'Magento', cat: 'cms' },
      { match: /\/static\/frontend\//i, name: 'Magento', cat: 'cms' },
      { match: /cdn\.shopify\.com/i, name: 'Shopify', cat: 'cms' },
      { match: /\/_next\//i, name: 'Next.js', cat: 'frameworks' },
      { match: /\/__nuxt\//i, name: 'Nuxt.js', cat: 'frameworks' },
      { match: /gatsby-browser/i, name: 'Gatsby', cat: 'frameworks' },
      { match: /cdn\.jsdelivr\.net/i, name: 'jsDelivr CDN', cat: 'proxies' },
      { match: /cdnjs\.cloudflare\.com/i, name: 'Cloudflare CDN', cat: 'proxies' },
      { match: /unpkg\.com/i, name: 'unpkg CDN', cat: 'proxies' },
      { match: /ajax\.googleapis\.com/i, name: 'Google CDN', cat: 'proxies' },
      { match: /fonts\.googleapis\.com/i, name: 'Google Fonts', cat: 'clientLibs' },
      { match: /www\.googletagmanager\.com/i, name: 'Google Tag Manager', cat: 'clientLibs' },
      { match: /www\.google-analytics\.com/i, name: 'Google Analytics', cat: 'clientLibs' },
      { match: /connect\.facebook\.net/i, name: 'Facebook SDK', cat: 'clientLibs' }
    ];

    for (const el of resourceElements) {
      const url = el.src || el.href || '';
      for (const check of pathChecks) {
        if (check.match.test(url)) {
          addResult(check.cat, check.name, null, 'medium', `resource: ${url.substring(0, 100)}`);
        }
      }
    }

    // 3. Global JS variables (safe readonly checks)
    const varChecks = [
      { var: 'Drupal', name: 'Drupal', cat: 'cms' },
      { var: 'Joomla', name: 'Joomla', cat: 'cms' },
      { var: 'Shopify', name: 'Shopify', cat: 'cms' },
      { var: '__NEXT_DATA__', name: 'Next.js', cat: 'frameworks' },
      { var: '__NUXT__', name: 'Nuxt.js', cat: 'frameworks' },
      { var: '___gatsby', name: 'Gatsby', cat: 'frameworks' }
    ];

    for (const check of varChecks) {
      try {
        if (window[check.var] !== undefined) {
          addResult(check.cat, check.name, null, 'high', `window.${check.var}`);
        }
      } catch (e) {
        // Ignore access errors
      }
    }

    // jQuery version detection
    try {
      if (window.jQuery && window.jQuery.fn && window.jQuery.fn.jquery) {
        addResult('clientLibs', 'jQuery', window.jQuery.fn.jquery, 'high', 'window.jQuery.fn.jquery');
      }
    } catch (e) {}

    // React detection
    try {
      if (window.React || document.querySelector('[data-reactroot], [data-reactid]')) {
        const version = window.React && window.React.version;
        addResult('clientLibs', 'React', version || null, 'high', 'React detected');
      }
    } catch (e) {}

    // Vue detection
    try {
      if (window.Vue || document.querySelector('[data-v-]')) {
        const version = window.Vue && window.Vue.version;
        addResult('clientLibs', 'Vue.js', version || null, 'high', 'Vue detected');
      }
    } catch (e) {}

    // 4. DOM patterns
    const domChecks = [
      { sel: '.shopify-section', name: 'Shopify', cat: 'cms' },
      { sel: '[data-drupal-selector]', name: 'Drupal', cat: 'cms' },
      { sel: 'body.com-joomla', name: 'Joomla', cat: 'cms' },
      { sel: '[ng-app], [data-ng-app]', name: 'AngularJS', cat: 'clientLibs' },
      { sel: 'app-root', name: 'Angular', cat: 'frameworks' },
      { sel: '#__next', name: 'Next.js', cat: 'frameworks' },
      { sel: '#__nuxt', name: 'Nuxt.js', cat: 'frameworks' },
      { sel: '#___gatsby', name: 'Gatsby', cat: 'frameworks' },
      { sel: '[data-turbo-body]', name: 'Turbo/Hotwire', cat: 'frameworks' }
    ];

    for (const check of domChecks) {
      try {
        if (document.querySelector(check.sel)) {
          addResult(check.cat, check.name, null, 'medium', `DOM: ${check.sel}`);
        }
      } catch (e) {}
    }

    // 5. CSS framework detection via class patterns
    try {
      const body = document.body;
      if (body) {
        const html = body.innerHTML.substring(0, 50000); // Limit scan area
        if (/class="[^"]*col-(?:xs|sm|md|lg|xl)-\d/.test(html)) {
          addResult('clientLibs', 'Bootstrap', null, 'medium', 'CSS class pattern');
        }
        if (/class="[^"]*(?:tw-|flex |grid |text-\w+-\d{3})/.test(html)) {
          addResult('clientLibs', 'Tailwind CSS', null, 'low', 'CSS class pattern');
        }
        if (/class="[^"]*(?:mdc-|mat-)/.test(html)) {
          addResult('clientLibs', 'Material Design', null, 'medium', 'CSS class pattern');
        }
      }
    } catch (e) {}

    // 6. WordPress deep fingerprint — plugins, themes, core version from ?ver=
    try {
      const wpDetected = seen.has('cms:WordPress');
      const pageHost = window.location.hostname;
      const wpResources = document.querySelectorAll('script[src], link[rel="stylesheet"][href]');
      const pluginRe = /\/wp-content\/plugins\/([^/]+)\//;
      const themeRe = /\/wp-content\/themes\/([^/]+)\//;
      const wpIncludesRe = /\/wp-includes\//;
      const pluginMap = new Map();
      const themeMap = new Map();
      let coreVersion = null;
      let hasWpResources = false;

      for (const el of wpResources) {
        const raw = el.src || el.href || '';
        if (!raw) continue;
        let parsed;
        try { parsed = new URL(raw, window.location.href); } catch (_) { continue; }
        if (parsed.hostname !== pageHost) continue;
        const path = parsed.pathname;
        const ver = parsed.searchParams.get('ver') || null;

        // Check if this is a WP resource at all
        if (pluginRe.test(path) || themeRe.test(path) || wpIncludesRe.test(path)) {
          hasWpResources = true;
        }

        // Normalize version: keep only semver-like strings (e.g. 2.5.14, 6.9.1)
        // Reject build numbers, timestamps, hashes (e.g. 2171245, abc123)
        const isSemver = ver && /^\d+\.\d+/.test(ver);
        const cleanVer = isSemver ? ver : null;

        // Plugin extraction — prefer semver over null, update if we find a better version
        const pm = path.match(pluginRe);
        if (pm) {
          const existing = pluginMap.get(pm[1]);
          if (!existing || (!existing.match(/^\d+\.\d+/) && cleanVer)) {
            pluginMap.set(pm[1], cleanVer);
          }
        }

        // Theme extraction
        const tm = path.match(themeRe);
        if (tm) {
          const existing = themeMap.get(tm[1]);
          if (!existing || (!existing.match(/^\d+\.\d+/) && cleanVer)) {
            themeMap.set(tm[1], cleanVer);
          }
        }

        // Core version from wp-includes assets
        if (!coreVersion && wpIncludesRe.test(path) && ver && /^\d+\.\d+/.test(ver)) {
          coreVersion = ver;
        }
      }

      // Self-detect WordPress if dynamic rule hasn't flagged it yet
      if (!wpDetected && hasWpResources) {
        addResult('cms', 'WordPress', coreVersion, 'medium', 'resource path: /wp-content/ or /wp-includes/');
      }

      // Only populate deep fields if WordPress was detected
      if (wpDetected || hasWpResources) {
        // Set core version if found and not already set
        if (coreVersion) {
          const existing = results.cms.find(c => c.name === 'WordPress');
          if (existing && !existing.version) {
            existing.version = coreVersion;
            existing.signal += `, wp-includes ver=${coreVersion}`;
          }
        }

        results.wpPlugins = [];
        for (const [slug, ver] of pluginMap) {
          results.wpPlugins.push({
            name: slug,
            version: ver,
            confidence: 'high',
            signal: `resource path: /wp-content/plugins/${slug}/`
          });
        }

        results.wpThemes = [];
        for (const [slug, ver] of themeMap) {
          results.wpThemes.push({
            name: slug,
            version: ver,
            confidence: 'high',
            signal: `resource path: /wp-content/themes/${slug}/`
          });
        }
      }
    } catch (e) {}

    // Send report to background
    try {
      chrome.runtime.sendMessage({
        type: 'tech_fingerprint_report',
        data: results
      }).catch(() => {});
    } catch (e) {
      // Extension context invalidated
    }
  }, 0);
})();
