// PhishProtect Content Script - Optimized Edition
// Lightweight monitoring with background delegation
// Reduced from 427 lines to ~150 lines

// Inject page-world scripts into main world context
function injectPageScript(path) {
  const script = document.createElement('script');
  script.src = chrome.runtime.getURL(path);
  script.onload = function() { this.remove(); };
  (document.head || document.documentElement).appendChild(script);
}

// Inject immediately (before any page scripts run)
// Script tracker must go first so it hooks appendChild before clipboard interceptor loads
injectPageScript('injected/script-tracker.js');
injectPageScript('injected/clipboard-interceptor.js');
console.log('PhishProtect: Page-world scripts injected');

// Bridge: page-world clipboard-interceptor posts results via window.postMessage,
// but content scripts run in an isolated world — the background can only see
// chrome.runtime messages. Forward the relevant page-world events across.
// Without this, every JS-driven clipboard hijack (the classic clickfix vector)
// gets blocked but never reaches the portal's Hunting feed.
window.addEventListener('message', (event) => {
  // Only trust same-window messages from our own origin
  if (event.source !== window) return;
  const msg = event.data;
  if (!msg || typeof msg !== 'object') return;

  if (msg.type === 'phishprotect-clipboard-blocked') {
    try {
      chrome.runtime.sendMessage({
        type: 'clipboard_injection_blocked',
        data: {
          threats:        msg.threats || [],
          source:         msg.source || 'unknown',
          timestamp:      msg.timestamp || Date.now(),
          contentPreview: msg.contentPreview || null,
          stackTrace:     msg.stackTrace || null,
          inlineScripts:  msg.inlineScripts || null,
          dynamicScripts: msg.dynamicScripts || null,
          sourceSnippets: msg.sourceSnippets || null,
          url:            location.href
        }
      });
    } catch (e) {
      // Extension may be reloading — ignore.
    }
  }
});

// Initialize protection
// ============================================
// User settings — cached + reactive
// ============================================
// Detection always runs (telemetry/dashboard need it). What these flags control:
//   - overlays.enabled : whether the visual indicators (rectangles, badges,
//                        corner brackets, image outline) are SHOWN on the page
//   - detection.{text,image,video} : whether the local content script even
//                                    *queues* a scan for that modality
const NITHIC_DEFAULT_SETTINGS = {
  overlays: { enabled: true, snoozeUntil: null },
  detection: { image: true, text: true, video: true }
};
let _nithicSettings = JSON.parse(JSON.stringify(NITHIC_DEFAULT_SETTINGS));

function nsOverlaysEnabled() {
  const o = _nithicSettings.overlays || {};
  if (o.enabled === false) return false;
  if (o.snoozeUntil && Date.now() < o.snoozeUntil) return false;
  return true;
}
function nsDetectionEnabled(modality) {
  return _nithicSettings.detection?.[modality] !== false;
}

function nsSyncOverlayVisibility() {
  // content_scripts run at document_start, before <body> exists. If we try
  // to flip the class too early we silently no-op and the toggle appears
  // dead after every reload. Defer until body is in the tree, then re-sync.
  if (!document.body) {
    if (document.readyState === 'loading') {
      document.addEventListener('DOMContentLoaded', nsSyncOverlayVisibility, { once: true });
    } else {
      // documentElement exists but no body yet (unusual) — try next microtask
      queueMicrotask(nsSyncOverlayVisibility);
    }
    return;
  }
  document.body.classList.toggle('ns-overlays-off', !nsOverlaysEnabled());
}

(async function loadNithicSettings() {
  try {
    const result = await chrome.storage.local.get('nithicSettings');
    if (result.nithicSettings) {
      _nithicSettings = { ..._nithicSettings, ...result.nithicSettings };
      // Deep-merge nested
      if (result.nithicSettings.overlays)  _nithicSettings.overlays  = { ...NITHIC_DEFAULT_SETTINGS.overlays,  ...result.nithicSettings.overlays  };
      if (result.nithicSettings.detection) _nithicSettings.detection = { ...NITHIC_DEFAULT_SETTINGS.detection, ...result.nithicSettings.detection };
    }
    nsSyncOverlayVisibility();
  } catch (_) {}
})();

// Live-update on settings change — toggle in popup applies without page reload
try {
  chrome.storage.onChanged.addListener((changes, area) => {
    if (area !== 'local' || !changes.nithicSettings) return;
    const next = changes.nithicSettings.newValue || NITHIC_DEFAULT_SETTINGS;
    _nithicSettings = {
      overlays:  { ...NITHIC_DEFAULT_SETTINGS.overlays,  ...(next.overlays  || {}) },
      detection: { ...NITHIC_DEFAULT_SETTINGS.detection, ...(next.detection || {}) }
    };
    nsSyncOverlayVisibility();
  });
} catch (_) {}

function initializeProtection() {
  // Monitor clipboard operations (backup monitoring for manual copy/paste)
  const clipboardMonitor = new ClipboardMonitor();
  clipboardMonitor.initialize();

  // Monitor DOM for dynamic content
  monitorDOMChanges();

  // Initialize HTTP protection
  initializeHttpProtection();

  // CVE-aware credential-form gate (non-blocking on safe sites)
  interceptCveFormSubmissions();
}

// CVE gate on sensitive form submissions
// IMPORTANT: never call preventDefault() on a 'maybe-safe' form — that breaks
// React/SPA forms (their onSubmit must run synchronously; a manual form.submit()
// afterward bypasses React entirely, causing a native page POST that loses state).
// Pre-fetch CVE status once per page; on submit, only block if confirmed risk.
let _cachedCveStatus = null;
function refreshCveStatus() {
  try {
    chrome.runtime.sendMessage({ type: 'getCveStatus' }, (result) => {
      if (chrome.runtime.lastError) return;
      _cachedCveStatus = result || null;
    });
  } catch { /* ignore */ }
}
function interceptCveFormSubmissions() {
  refreshCveStatus();
  setInterval(refreshCveStatus, 30000);

  document.addEventListener('submit', (event) => {
    const form = event.target;
    if (!form || form.tagName !== 'FORM') return;
    if (window.location.protocol !== 'https:') return;

    const inputs = Array.from(form.getElementsByTagName('input'));
    const hasPassword = inputs.some(i => i.type === 'password');
    const hasEmail = inputs.some(i => i.type === 'email');
    if (!hasPassword && !hasEmail) return;

    if (_cachedCveStatus && _cachedCveStatus.hasCriticalCves) {
      const total = _cachedCveStatus.totalCves || 0;
      const crit = _cachedCveStatus.criticalCount || 0;
      const high = _cachedCveStatus.highCount || 0;
      const proceed = confirm(
        '⚠️ VULNERABILITY WARNING\n\n' +
        'This site has ' + total + ' known CVE' + (total !== 1 ? 's' : '') +
        ' (' + crit + ' critical, ' + high + ' high).\n\n' +
        'Submitting credentials to a site with known vulnerabilities is risky.\n' +
        'Your data may be exposed due to unpatched software.\n\n' +
        'Do you want to proceed?'
      );
      if (!proceed) {
        event.preventDefault();
        event.stopPropagation();
      }
    }
  }, true);
}

// Monitor DOM for dynamically added forms and links
function monitorDOMChanges() {
  const observer = new MutationObserver((mutations) => {
    for (const mutation of mutations) {
      for (const node of mutation.addedNodes) {
        if (node.nodeType === Node.ELEMENT_NODE) {
          // Extract form data for background analysis
          if (node.tagName === 'FORM') {
            const formData = extractFormData(node);
            notifyBackground('dom_form_added', formData);
          }
          // Extract link data for background analysis
          if (node.tagName === 'A') {
            const linkData = extractLinkData(node);
            notifyBackground('dom_link_added', linkData);
          }
        }
      }
    }
  });

  // Start observing when body is ready
  if (document.body) {
    observer.observe(document.body, {
      childList: true,
      subtree: true
    });
  } else {
    // Wait for body
    document.addEventListener('DOMContentLoaded', () => {
      if (document.body) {
        observer.observe(document.body, {
          childList: true,
          subtree: true
        });
      }
    });
  }
}

// Extract form data for background analysis (NO analysis here)
function extractFormData(form) {
  const inputs = Array.from(form.getElementsByTagName('input'));
  return {
    url: window.location.href,
    action: form.action,
    method: form.method,
    inputCount: inputs.length,
    inputTypes: inputs.map(i => ({ type: i.type, name: i.name })),
    hasPassword: inputs.some(i => i.type === 'password'),
    hasEmail: inputs.some(i => i.type === 'email')
  };
}

// Extract link data for background analysis (NO analysis here)
function extractLinkData(link) {
  return {
    pageUrl: window.location.href,
    href: link.href,
    text: link.textContent.trim(),
    rel: link.rel,
    target: link.target
  };
}

// Capture a clean stack trace at the current point
function captureStackTrace() {
  try {
    const stack = new Error().stack;
    return stack.split('\n')
      .filter(line => !line.includes('chrome-extension://'))
      .slice(0, 10)
      .map(line => line.trim());
  } catch (e) {
    return null;
  }
}

// Notify background script
function notifyBackground(type, data) {
  chrome.runtime.sendMessage({
    type: type,
    data: data
  }).catch(() => {
    // Ignore errors if background is unavailable
  });
}

// Show warning overlay (from background decision)
function showWarningOverlay(data) {
  // Remove existing overlay if present
  const existing = document.getElementById('phishprotect-warning-overlay');
  if (existing) {
    existing.remove();
  }

  // Create overlay
  const overlay = document.createElement('div');
  overlay.id = 'phishprotect-warning-overlay';
  overlay.style.cssText = `
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(0, 0, 0, 0.8);
    z-index: 2147483647;
    display: flex;
    justify-content: center;
    align-items: center;
    font-family: Arial, sans-serif;
  `;

  // Create warning box
  const box = document.createElement('div');
  box.style.cssText = `
    background: #fff;
    padding: 30px;
    border-radius: 8px;
    max-width: 500px;
    box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
  `;

  // Title
  const title = document.createElement('h2');
  title.textContent = '⚠️ Security Warning';
  title.style.cssText = 'color: #ea4335; margin: 0 0 15px 0;';
  box.appendChild(title);

  // Message
  const message = document.createElement('p');
  message.textContent = data.message || 'This page may contain security threats.';
  message.style.cssText = 'margin: 0 0 15px 0; line-height: 1.5;';
  box.appendChild(message);

  // Risk score
  if (data.riskScore) {
    const riskInfo = document.createElement('p');
    riskInfo.textContent = `Risk Score: ${data.riskScore}`;
    riskInfo.style.cssText = 'margin: 0 0 20px 0; font-weight: bold; color: #666;';
    box.appendChild(riskInfo);
  }

  // Close button
  const closeBtn = document.createElement('button');
  closeBtn.textContent = 'I Understand';
  closeBtn.style.cssText = `
    background: #1a73e8;
    color: #fff;
    border: none;
    padding: 10px 20px;
    border-radius: 4px;
    cursor: pointer;
    font-size: 14px;
  `;
  closeBtn.onclick = () => overlay.remove();
  box.appendChild(closeBtn);

  overlay.appendChild(box);
  document.body.appendChild(overlay);
}

// Initialize HTTP protection
function initializeHttpProtection() {
  // Check if current page is HTTP (not HTTPS)
  if (window.location.protocol !== 'http:') return;

  // Skip localhost
  if (window.location.hostname === 'localhost' ||
      window.location.hostname === '127.0.0.1' ||
      window.location.hostname === '::1') return;

  console.log('PhishProtect: HTTP site detected, initializing protection');

  // Show persistent HTTP warning banner
  showHttpWarningBanner();

  // Intercept form submissions
  interceptFormSubmissions();

  // Warn about file inputs
  warnAboutFileInputs();
}

// Show persistent warning banner for HTTP sites
function showHttpWarningBanner() {
  const banner = document.createElement('div');
  banner.id = 'phishprotect-http-banner';
  banner.style.cssText = `
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
    color: #fff;
    padding: 12px 20px;
    z-index: 2147483646;
    font-family: Arial, sans-serif;
    font-size: 14px;
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
  `;

  banner.innerHTML = `
    <span style="margin-right: 10px;">🔓</span>
    <strong>Insecure Connection (HTTP)</strong>
    <span style="margin: 0 10px;">•</span>
    Your data is not encrypted on this site
  `;

  // Insert at the beginning of body
  if (document.body) {
    document.body.insertBefore(banner, document.body.firstChild);
  } else {
    document.addEventListener('DOMContentLoaded', () => {
      if (document.body) {
        document.body.insertBefore(banner, document.body.firstChild);
      }
    });
  }
}

// Intercept form submissions with password fields
function interceptFormSubmissions() {
  document.addEventListener('submit', (event) => {
    const form = event.target;
    if (!form || form.tagName !== 'FORM') return;

    // Check if form contains password or sensitive inputs
    const inputs = Array.from(form.getElementsByTagName('input'));
    const hasPassword = inputs.some(input => input.type === 'password');
    const hasEmail = inputs.some(input => input.type === 'email');
    const hasSensitiveData = hasPassword || hasEmail;

    if (hasSensitiveData) {
      event.preventDefault();
      event.stopPropagation();

      // Show warning dialog
      const proceed = confirm(
        '⚠️ SECURITY WARNING\n\n' +
        'You are about to submit sensitive information (password/email) over an INSECURE HTTP connection.\n\n' +
        'Risks:\n' +
        '• Your password will be transmitted in plain text\n' +
        '• Anyone on your network can intercept your credentials\n' +
        '• This connection is NOT encrypted\n\n' +
        'PhishProtect has BLOCKED this form submission for your safety.\n\n' +
        'Recommendation: Only enter passwords on secure HTTPS sites.\n\n' +
        'Do you really want to proceed? (NOT RECOMMENDED)'
      );

      if (!proceed) {
        console.log('PhishProtect: Blocked HTTP form submission with password');
        return false;
      }

      // User chose to proceed anyway - show one more warning
      const finalConfirm = confirm(
        'FINAL WARNING\n\n' +
        'Are you absolutely sure you want to send your password over an unencrypted connection?\n\n' +
        'This is extremely dangerous and NOT recommended.\n\n' +
        'Click OK to proceed at your own risk.'
      );

      if (finalConfirm) {
        console.log('PhishProtect: User confirmed HTTP form submission');
        // Remove the event listener temporarily to allow submission
        form.removeEventListener('submit', arguments.callee);
        form.submit();
      }

      return false;
    }
  }, true);
}

// Warn about file inputs on HTTP sites
function warnAboutFileInputs() {
  document.addEventListener('change', (event) => {
    const input = event.target;
    if (!input || input.type !== 'file') return;

    if (input.files && input.files.length > 0) {
      alert(
        '⚠️ File Upload Warning\n\n' +
        'You are about to upload a file over an INSECURE HTTP connection.\n\n' +
        'Your file will NOT be encrypted during transmission.\n\n' +
        'Recommendation: Only upload files on secure HTTPS sites.'
      );
    }
  }, true);
}

// (interceptCveFormSubmissions defined above)

// Clipboard Monitor - Lightweight with background delegation
class ClipboardMonitor {
  constructor() {
    this.enabled = true;
  }

  initialize() {
    document.addEventListener('copy', this.handleCopy.bind(this), true);
    document.addEventListener('paste', this.handlePaste.bind(this), true);
  }

  async handleCopy(event) {
    if (!this.enabled) return;

    const text = window.getSelection().toString();
    if (!text || text.length < 10) return;

    const threat = this.detectThreats(text);
    if (threat) {
      event.preventDefault();
      this.showClipboardAlert('Suspicious content blocked from clipboard');
      notifyBackground('suspicious_copy', this._buildEvidence(text, threat, 'user-copy'));
    }
  }

  async handlePaste(event) {
    if (!this.enabled) return;

    try {
      const text = event.clipboardData.getData('text');
      if (!text || text.length < 10) return;

      const threat = this.detectThreats(text);
      if (threat) {
        event.preventDefault();
        this.showClipboardAlert('Suspicious paste operation blocked');
        notifyBackground('suspicious_paste', this._buildEvidence(text, threat, 'user-paste'));
      }
    } catch (error) {
      // Ignore clipboard access errors
    }
  }

  // Build the rich evidence payload the portal expects (matches the older
  // clipboard-injection schema so Hunting renders the full review panel).
  _buildEvidence(text, threat, source) {
    // Capture up to 3 inline-script previews from the page — many clickfix
    // pages stage the payload via inline JS, so the analyst can correlate.
    let inlineScripts = [];
    try {
      const scripts = Array.from(document.scripts).filter(s => !s.src && s.textContent);
      inlineScripts = scripts.slice(0, 3).map((s, idx) => ({
        index: idx,
        preview: s.textContent.slice(0, 300),
        lineCount: s.textContent.split('\n').length
      }));
    } catch (_) {}

    return {
      // Core
      length: text.length,
      patternId: threat.id,
      patternName: threat.name,
      // Full clipboard content (capped at 2KB so we always get the whole command)
      fullText: text.length <= 2000 ? text : text.slice(0, 2000) + '... [truncated]',
      textPreview: text.slice(0, 500),
      // Page context
      pageUrl: location.href,
      pageTitle: document.title,
      pageHost: location.hostname,
      // Selection context
      selectionType: window.getSelection().type,
      activeTag: document.activeElement?.tagName?.toLowerCase() || null,
      // Evidence of staging
      inlineScripts,
      clipboardSource: source,
      // Timestamps
      timestamp: Date.now()
    };
  }

  // Quick threat detection — returns the matched pattern (id + name) or null.
  // Background uses this metadata to build a telemetry event for the portal.
  detectThreats(text) {
    const host = (window.location.hostname || '').toLowerCase();
    // Microsoft sign-in origins where this code is legitimately rendered.
    // Outside these origins, a Microsoft OAuth response URL in the clipboard
    // is the core ConsentFix signal — the attacker site is asking the user to
    // paste it into a form so they can exchange the code for tokens.
    const microsoftOrigins = [
      'login.microsoftonline.com', 'login.live.com', 'login.microsoft.com',
      'login.windows.net', 'microsoft.com', 'microsoftonline.com', 'live.com',
      'office.com', 'office365.com', 'outlook.com', 'sharepoint.com', 'azure.com'
    ];
    const onMicrosoft = microsoftOrigins.some(d => host === d || host.endsWith('.' + d));

    const patterns = [
      { id: 'clickfix-powershell-http',  name: 'PowerShell + HTTP invocation',   re: /powershell.*invoke.*http/i },
      { id: 'clickfix-certutil-urlcache',name: 'certutil URL cache (LOLBin)',    re: /certutil.*urlcache/i },
      { id: 'clickfix-private-key',      name: 'Private key in clipboard',       re: /-----BEGIN.*PRIVATE KEY-----/ },
      { id: 'clickfix-cmd-http',         name: 'cmd.exe /c + HTTP',              re: /cmd\.exe.*\/c.*http/i }
    ];
    for (const p of patterns) {
      if (p.re.test(text)) return p;
    }

    // ConsentFix — only flag when we're NOT on a Microsoft sign-in origin.
    // Pattern matches Microsoft Entra ID and live.com OAuth response shapes:
    //   http://localhost:PORT/?code=0.AXoA…&state=…  (Entra desktop-app flow)
    //   …nativeclient#code=0.A…&state=…
    //   ?code=M.C…_…&state=…  (Microsoft account)
    //   ?code=0.A[A-Za-z0-9_.-]+&state=…
    if (!onMicrosoft) {
      const consentFixRe = /([?&#]code=(0\.A[A-Za-z0-9_.-]{20,}|M\.C[A-Za-z0-9_-]{20,})).{0,200}[?&]state=[A-Za-z0-9_-]{6,}/i;
      const localhostRedirectRe = /https?:\/\/(localhost|127\.0\.0\.1)(:\d+)?\/[^ \n]*[?&]code=0\.A/i;
      if (consentFixRe.test(text) || localhostRedirectRe.test(text)) {
        return {
          id: 'consentfix-oauth-paste',
          name: 'ConsentFix: Microsoft OAuth code on non-Microsoft origin'
        };
      }
    }
    return null;
  }

  // Back-compat shim (other code paths may still call this)
  hasObviousThreats(text) { return !!this.detectThreats(text); }

  showClipboardAlert(message) {
    const alert = document.createElement('div');
    alert.style.cssText = `
      position: fixed;
      top: 20px;
      right: 20px;
      background: #ea4335;
      color: #fff;
      padding: 15px 20px;
      border-radius: 4px;
      box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
      z-index: 2147483647;
      font-family: Arial, sans-serif;
      font-size: 14px;
    `;
    alert.textContent = message;
    document.body.appendChild(alert);

    // Auto-remove after 5 seconds
    setTimeout(() => alert.remove(), 5000);
  }
}

// ============================================
// Identity Detector - Login Form & OAuth Monitor
// ============================================
// Detects user identity (domain only) on ALL sites for AI visibility policy enforcement

const KNOWN_IDP_PATTERNS = [
  /accounts\.google\.com/, /login\.microsoftonline\.com/, /.*\.okta\.com/,
  /login\.live\.com/, /github\.com\/login/, /auth0\.com/
];

const OAUTH_BUTTON_PATTERNS = [
  /sign\s*in\s*with\s*(google|microsoft|github|apple|facebook)/i,
  /continue\s*with\s*(google|microsoft|github|apple|facebook)/i,
  /log\s*in\s*with\s*(google|microsoft|github|apple|facebook)/i
];

function initializeIdentityDetector() {
  // A. Form-based login detection
  document.addEventListener('submit', (event) => {
    const form = event.target;
    if (!form || form.tagName !== 'FORM') return;

    const inputs = Array.from(form.getElementsByTagName('input'));
    const hasPassword = inputs.some(i => i.type === 'password');
    if (!hasPassword) return;

    // Find email/username field
    const emailInput = inputs.find(i =>
      i.type === 'email' ||
      /email|username|user|login|account/i.test(i.name) ||
      /email|username|user|login|account/i.test(i.id) ||
      /email|username|user|login|account/i.test(i.getAttribute('autocomplete') || '')
    );

    if (!emailInput || !emailInput.value) return;

    const value = emailInput.value.trim();
    const atIndex = value.indexOf('@');
    if (atIndex === -1) return; // No domain to extract

    // Extract domain only (discard username for privacy)
    const identityDomain = value.substring(atIndex + 1).toLowerCase();
    if (!identityDomain || !identityDomain.includes('.')) return;

    notifyBackground('identity_detected', {
      identityDomain,
      site: window.location.hostname,
      method: 'form_login'
    });
  }, true);

  // B. OAuth/SSO button click detection
  document.addEventListener('click', (event) => {
    const el = event.target?.closest?.('a, button, [role="button"]');
    if (!el) return;

    const text = (el.textContent || '').trim();
    const isOAuth = OAUTH_BUTTON_PATTERNS.some(p => p.test(text));
    if (!isOAuth) return;

    notifyBackground('identity_detected', {
      identityDomain: null, // Domain will be resolved after OAuth redirect
      site: window.location.hostname,
      method: 'oauth_click',
      provider: text
    });
  }, true);

  // C. IdP URL detection (if we're ON an IdP page)
  const currentHost = window.location.hostname;
  const isIdP = KNOWN_IDP_PATTERNS.some(p => p.test(currentHost));
  if (isIdP) {
    notifyBackground('identity_detected', {
      identityDomain: null,
      site: currentHost,
      method: 'idp_visit',
      idpDomain: currentHost
    });
  }
}

// ============================================
// AI Visibility - Policy Enforcement (Real-time)
// ============================================

function showPolicyBlockToast(message, policyName) {
  const toast = document.createElement('div');
  toast.style.cssText = `
    position: fixed;
    top: 20px;
    right: 20px;
    background: linear-gradient(135deg, #dc2626 0%, #991b1b 100%);
    color: #fff;
    padding: 16px 20px;
    border-radius: 8px;
    box-shadow: 0 4px 20px rgba(220, 38, 38, 0.4);
    z-index: 2147483647;
    font-family: Arial, sans-serif;
    font-size: 14px;
    max-width: 380px;
    line-height: 1.4;
  `;
  toast.innerHTML = `
    <div style="font-weight: 700; margin-bottom: 6px;">Action Blocked</div>
    <div style="opacity: 0.9;">${message}</div>
    <div style="margin-top: 8px; font-size: 11px; opacity: 0.7;">Policy: ${policyName}</div>
  `;
  document.body.appendChild(toast);
  setTimeout(() => toast.remove(), 6000);
}

// ============================================
// AI Visibility - User Action Monitor
// ============================================

// Inline AI domain list (lightweight check, no module import needed)
const AI_DOMAIN_MAP = {
  'chatgpt.com': 'ChatGPT', 'chat.openai.com': 'ChatGPT', 'openai.com': 'ChatGPT',
  'copilot.microsoft.com': 'Copilot Cloud', 'copilot.cloud.microsoft': 'Copilot Cloud',
  'github.copilot.com': 'Copilot Cloud', 'copilot.github.com': 'Copilot Cloud',
  'gemini.google.com': 'Gemini', 'bard.google.com': 'Gemini', 'aistudio.google.com': 'Gemini',
  'grok.com': 'Grok', 'grok.x.ai': 'Grok', 'x.ai': 'Grok',
  'perplexity.ai': 'Perplexity', 'www.perplexity.ai': 'Perplexity',
  'claude.ai': 'Claude', 'console.anthropic.com': 'Claude',
  'meta.ai': 'Meta AI', 'www.meta.ai': 'Meta AI',
  'chat.mistral.ai': 'Mistral', 'mistral.ai': 'Mistral',
  'huggingface.co': 'Hugging Face',
  'chat.deepseek.com': 'DeepSeek', 'deepseek.com': 'DeepSeek',
  'poe.com': 'Poe', 'you.com': 'You.com',
  'coral.cohere.com': 'Cohere', 'dashboard.cohere.com': 'Cohere',
  'app.jasper.ai': 'Jasper', 'cursor.sh': 'Cursor', 'replit.com': 'Replit'
};

function getAIService() {
  const host = window.location.hostname.toLowerCase();
  if (AI_DOMAIN_MAP[host]) return { domain: host, service: AI_DOMAIN_MAP[host] };
  const noWww = host.replace(/^www\./, '');
  if (AI_DOMAIN_MAP[noWww]) return { domain: noWww, service: AI_DOMAIN_MAP[noWww] };
  const parts = host.split('.');
  if (parts.length > 2) {
    const parent = parts.slice(1).join('.');
    if (AI_DOMAIN_MAP[parent]) return { domain: parent, service: AI_DOMAIN_MAP[parent] };
  }
  return null;
}

function initializeAIVisibilityMonitor() {
  const aiInfo = getAIService();
  if (!aiInfo) return; // Not an AI domain, skip

  console.log(`PhishProtect AI Visibility: Monitoring ${aiInfo.service} (${aiInfo.domain})`);

  function sendAIEvent(eventType, extra = {}) {
    notifyBackground('ai_visibility_event', {
      eventType,
      domain: aiInfo.domain,
      service: aiInfo.service,
      contentSize: extra.contentSize || 0,
      metadata: extra.metadata || {}
    });
  }

  // Policy check helper: asks background if action is allowed
  function checkPolicyAndBlock(eventType, event) {
    return new Promise((resolve) => {
      try {
        chrome.runtime.sendMessage({
          type: 'ai_visibility_policy_check',
          data: {
            aiService: aiInfo.service,
            action: eventType,
            siteDomain: window.location.hostname
          }
        }, (response) => {
          if (response && !response.allowed) {
            event.preventDefault();
            event.stopImmediatePropagation();
            showPolicyBlockToast(response.reason, response.policyName);
            // Record violation event
            notifyBackground('ai_visibility_event', {
              eventType,
              domain: aiInfo.domain,
              service: aiInfo.service,
              contentSize: 0,
              isViolation: true,
              policyName: response.policyName,
              alertType: eventType
            });
            resolve(false);
          } else {
            resolve(true);
          }
        });
      } catch (e) {
        resolve(true); // Allow on error
      }
    });
  }

  // Monitor paste events (with policy enforcement)
  document.addEventListener('paste', async (event) => {
    const allowed = await checkPolicyAndBlock('paste', event);
    if (!allowed) return;
    try {
      const text = event.clipboardData?.getData('text') || '';
      sendAIEvent('paste', { contentSize: text.length });
    } catch (e) {
      sendAIEvent('paste');
    }
  }, true);

  // Monitor copy events
  document.addEventListener('copy', () => {
    const text = window.getSelection()?.toString() || '';
    sendAIEvent('copy', { contentSize: text.length });
  }, true);

  // Monitor file uploads (input[type=file] change events, with policy enforcement)
  document.addEventListener('change', async (event) => {
    const input = event.target;
    if (input?.type === 'file' && input.files?.length > 0) {
      const allowed = await checkPolicyAndBlock('file_upload', event);
      if (!allowed) {
        input.value = ''; // Clear the file input
        return;
      }
      const totalSize = Array.from(input.files).reduce((sum, f) => sum + f.size, 0);
      sendAIEvent('file_upload', {
        contentSize: totalSize,
        metadata: { fileCount: input.files.length, fileNames: Array.from(input.files).map(f => f.name) }
      });
    }
  }, true);

  // Monitor drag-and-drop file uploads
  document.addEventListener('drop', (event) => {
    const files = event.dataTransfer?.files;
    if (files && files.length > 0) {
      const totalSize = Array.from(files).reduce((sum, f) => sum + f.size, 0);
      sendAIEvent('file_upload', {
        contentSize: totalSize,
        metadata: { fileCount: files.length, method: 'drag-drop' }
      });
    }
  }, true);

  // Monitor keyboard input in text areas / contenteditable (manual input)
  let inputDebounce = null;
  let inputCharCount = 0;

  function handleManualInput(event) {
    // Only track actual typing in input/textarea/contenteditable
    const el = event.target;
    if (!el) return;
    const isInput = el.tagName === 'TEXTAREA' || el.tagName === 'INPUT' ||
                    el.isContentEditable || el.getAttribute('contenteditable') === 'true';
    if (!isInput) return;

    inputCharCount += (event.data?.length || 1);

    // Debounce: report after 3 seconds of inactivity
    clearTimeout(inputDebounce);
    inputDebounce = setTimeout(() => {
      if (inputCharCount > 5) { // Ignore trivial input
        sendAIEvent('manual_input', { contentSize: inputCharCount });
      }
      inputCharCount = 0;
    }, 3000);
  }
  document.addEventListener('input', handleManualInput, true);

  // Monitor right-click (context menu)
  document.addEventListener('contextmenu', () => {
    sendAIEvent('right_click');
  }, true);

  // Monitor downloads (listen for link clicks with download attribute)
  document.addEventListener('click', (event) => {
    const link = event.target?.closest?.('a[href]');
    if (!link) return;

    if (link.hasAttribute('download') || link.href?.match(/\.(zip|tar|gz|pdf|csv|json|txt|py|js|ts|xlsx|docx|pptx)(\?|$)/i)) {
      sendAIEvent('file_download', {
        metadata: { href: link.href, download: link.getAttribute('download') || true }
      });
    }
  }, true);

  // Monitor Print Screen / screen capture attempts
  document.addEventListener('keydown', (event) => {
    if (event.key === 'PrintScreen' ||
        (event.ctrlKey && event.shiftKey && event.key === 'S') || // Win+Shift+S snipping
        (event.metaKey && event.shiftKey && (event.key === '3' || event.key === '4' || event.key === '5'))) { // Mac screenshots
      sendAIEvent('screen_capture');
    }
  }, true);

}

// ============================================
// AI Content Detection - Runs on ALL sites
// ============================================
// Detects AI-generated text and images everywhere, not just on AI domains.
// The value is catching AI content when it leaves AI services and appears
// in Google Docs, Slack, LinkedIn, email, etc.

function initializeContentScanning() {
  const hostname = window.location.hostname;

  // Skip scanning on our own infrastructure — avoids feedback loops where the
  // gallery's rendered thumbnails get re-scanned and re-ingested back into it.
  // Covers all six Nithic TLDs and any subdomain (portal, api, cloud, etc.)
  const NITHIC_HOST_RE = /(^|\.)nithic\.(ai|com|io|info|net|org)$/i;
  if (NITHIC_HOST_RE.test(hostname)) {
    console.log('[Nithic] Skipping content scan on own domain:', hostname);
    return;
  }

  const scannedImages = new WeakSet();
  let scannedTextHashes = new Set();

  // Determine if we're on an AI domain (for context tagging, not gating)
  const aiInfo = getAIService(); // may be null on non-AI sites

  // --- Text scanning: paste events on ALL sites ---
  document.addEventListener('paste', (event) => {
    if (!nsDetectionEnabled('text')) return; // user disabled text analysis
    try {
      const text = event.clipboardData?.getData('text') || '';
      if (text.length < 200) return; // Skip short pastes

      // Simple hash to avoid re-scanning same text
      const hash = text.substring(0, 100) + text.length;
      if (scannedTextHashes.has(hash)) return;
      scannedTextHashes.add(hash);
      if (scannedTextHashes.size > 50) scannedTextHashes = new Set([...scannedTextHashes].slice(-30));

      chrome.runtime.sendMessage({
        type: 'content_scan_text',
        data: {
          text,
          url: window.location.href,
          domain: hostname,
          aiService: aiInfo?.service || null
        }
      }).catch(() => {});
    } catch (e) {
      // Ignore clipboard access errors
    }
  }, true);

  // --- Text scanning: copy events (detect copying AI output from any page) ---
  document.addEventListener('copy', () => {
    if (!nsDetectionEnabled('text')) return;
    try {
      const text = window.getSelection()?.toString() || '';
      if (text.length < 200) return;

      const hash = text.substring(0, 100) + text.length;
      if (scannedTextHashes.has(hash)) return;
      scannedTextHashes.add(hash);

      chrome.runtime.sendMessage({
        type: 'content_scan_text',
        data: {
          text,
          url: window.location.href,
          domain: hostname,
          aiService: aiInfo?.service || null
        }
      }).catch(() => {});
    } catch (e) {
      // Ignore
    }
  }, true);

  // --- Image overlay effects (futuristic / glowy) ---
  // SAFE: Only applies outline + box-shadow to the img element itself.
  // Badge floats from document.body using fixed positioning.
  // We NEVER modify any parent element or reparent the image.

  const activeOverlays = new Map(); // img → { badge, cleanup }

  function applyImageOverlay(img, verdict, confidence, manipulationType) {
    let color1, glowRgba, badgeText;

    if (verdict === 'ai_generated') {
      // Wholly synthetic — entire image is AI. Solid red outline, no regions.
      color1 = '#ef4444';
      glowRgba = 'rgba(239, 68, 68, 0.4)';
      badgeText = 'AI GENERATED';
    } else if (verdict === 'manipulated') {
      // Real photo with edits — outline + region rectangles indicate edits.
      color1 = '#ef4444';
      glowRgba = 'rgba(239, 68, 68, 0.4)';
      badgeText = manipulationType === 'splice'  ? 'SPLICE'
                : manipulationType === 'inpaint' ? 'INPAINT'
                : manipulationType === 'clone'   ? 'CLONE'
                : 'ALTERED';
    } else if (verdict === 'mixed') {
      color1 = '#ef4444';
      glowRgba = 'rgba(239, 68, 68, 0.4)';
      badgeText = 'AI + ALTERED';
    } else if (verdict === 'uncertain') {
      color1 = '#f59e0b';
      glowRgba = 'rgba(245, 158, 11, 0.3)';
      badgeText = '?';
    } else {
      return;
    }

    const pct = Math.round((confidence || 0) * 100);

    // Style the image itself — safe, no layout impact
    img.style.outline = `3px solid ${color1}`;
    img.style.outlineOffset = '-3px';
    img.style.setProperty('--ns-glow-color', glowRgba);
    img.style.animation = 'ns-glow-pulse 3s ease-in-out infinite';
    img.dataset.nsVerdict = verdict;

    // Floating badge — appended to document.body with fixed positioning
    const badge = document.createElement('div');
    badge.className = 'ns-float-badge';
    badge.style.cssText = `
      position:fixed; z-index:2147483646; pointer-events:none;
      background:rgba(0,0,0,0.8); backdrop-filter:blur(6px);
      border:1px solid ${color1}; border-radius:4px;
      padding:2px 7px;
      font-family:Arial,sans-serif; font-size:10px; font-weight:700;
      color:${color1}; letter-spacing:0.5px;
      box-shadow:0 0 10px ${glowRgba};
      display:flex; align-items:center; gap:4px;
      transition:top 0.1s,left 0.1s;
      opacity:0;
    `;
    badge.innerHTML = `<span style="width:5px;height:5px;border-radius:50%;background:${color1};box-shadow:0 0 4px ${color1};"></span>${badgeText}<span style="opacity:0.6;font-size:9px;">${pct}%</span>`;
    document.body.appendChild(badge);

    // Position the badge relative to the image
    function positionBadge() {
      const rect = img.getBoundingClientRect();
      if (rect.width === 0 || rect.height === 0) { badge.style.opacity = '0'; return; }
      badge.style.top = `${rect.top + 4}px`;
      badge.style.left = `${rect.left + 4}px`;
      badge.style.opacity = '1';
    }

    positionBadge();

    // Reposition on scroll/resize
    const reposition = () => requestAnimationFrame(positionBadge);
    window.addEventListener('scroll', reposition, { passive: true });
    window.addEventListener('resize', reposition, { passive: true });

    // Cleanup function
    const cleanup = () => {
      badge.remove();
      window.removeEventListener('scroll', reposition);
      window.removeEventListener('resize', reposition);
    };

    // Clean up previous overlay if re-scanning
    if (activeOverlays.has(img)) activeOverlays.get(img).cleanup();
    activeOverlays.set(img, { badge, cleanup });

    // Clean up if image is removed from DOM
    const observer = new MutationObserver(() => {
      if (!document.contains(img)) { cleanup(); observer.disconnect(); activeOverlays.delete(img); }
    });
    observer.observe(document.body, { childList: true, subtree: true });
  }

  /**
   * Draw manipulation region rectangles as floating divs over the image.
   * Only for images large enough (>200px displayed).
   */
  function applyManipulationRegions(img, regions) {
    if (!regions || regions.length === 0) return;

    const displayW = img.clientWidth || img.offsetWidth;
    const displayH = img.clientHeight || img.offsetHeight;
    if (displayW < 120 || displayH < 120) return;

    const scaleX = displayW / (img.naturalWidth || displayW);
    const scaleY = displayH / (img.naturalHeight || displayH);

    // Cap to the most-significant regions: rank by confidence × area, then take top N
    const MAX_REGIONS = 5;
    const ranked = regions
      .map(r => ({ region: r, score: (r.confidence ?? 0.5) * (r.w || 0) * (r.h || 0) }))
      .sort((a, b) => b.score - a.score)
      .slice(0, MAX_REGIONS)
      .map(x => x.region);

    const rects = [];

    ranked.forEach((region, i) => {
      const rect = document.createElement('div');
      rect.className = 'ns-float-region';
      const isClone = region.type && region.type.indexOf('clone') !== -1;
      const stroke = isClone ? 'rgba(245, 158, 11, 0.95)' : 'rgba(239, 68, 68, 0.95)';
      const glow   = isClone ? 'rgba(245, 158, 11, 0.4)'  : 'rgba(239, 68, 68, 0.45)';
      rect.style.cssText = `
        position:fixed; z-index:2147483645; pointer-events:none;
        border:2px solid ${stroke}; border-radius:2px;
        box-shadow:0 0 8px ${glow}, inset 0 0 8px ${glow};
        animation:ns-corner-markers 2.5s ease-in-out infinite;
        animation-delay:${i * 0.3}s;
        opacity:0;
      `;
      // Tiny label for the region type — hidden by default, fades in on image hover
      let label = null;
      if (region.type) {
        label = document.createElement('span');
        label.className = 'ns-float-region-label';
        label.style.cssText = `
          position:absolute; top:-18px; left:0;
          font-family:Arial,sans-serif; font-size:9px; font-weight:700;
          letter-spacing:0.6px; color:${stroke}; text-transform:uppercase;
          background:rgba(0,0,0,0.78); padding:1px 5px; border-radius:2px;
          white-space:nowrap;
          opacity:0; transform:translateY(2px);
          transition:opacity 0.18s ease, transform 0.18s ease;
        `;
        label.textContent = region.type.replace(/_/g, ' ');
        rect.appendChild(label);
      }
      document.body.appendChild(rect);
      rects.push({ el: rect, region, label });
    });

    // Hover labels — geometry-based check via document mousemove (rAF-throttled)
    // Robust against pages that intercept mouseenter on images.
    let labelsVisible = false;
    let mmPending = false;
    let lastClientX = 0, lastClientY = 0;
    const showLabels = () => rects.forEach(r => {
      if (r.label) { r.label.style.opacity = '1'; r.label.style.transform = 'translateY(0)'; }
    });
    const hideLabels = () => rects.forEach(r => {
      if (r.label) { r.label.style.opacity = '0'; r.label.style.transform = 'translateY(2px)'; }
    });
    const onMouseMove = (e) => {
      lastClientX = e.clientX; lastClientY = e.clientY;
      if (mmPending) return;
      mmPending = true;
      requestAnimationFrame(() => {
        mmPending = false;
        const r = img.getBoundingClientRect();
        const inside = r.width > 0 && r.height > 0
          && lastClientX >= r.left && lastClientX <= r.right
          && lastClientY >= r.top  && lastClientY <= r.bottom;
        if (inside !== labelsVisible) {
          labelsVisible = inside;
          if (inside) showLabels(); else hideLabels();
        }
      });
    };
    document.addEventListener('mousemove', onMouseMove, { passive: true });

    // Hidden-image guard — used both initially and during scroll
    function isImgVisible() {
      if (!img.isConnected) return false;
      if (img.clientWidth === 0 || img.clientHeight === 0) return false;
      const cs = getComputedStyle(img);
      if (cs.display === 'none' || cs.visibility === 'hidden' || cs.opacity === '0') return false;
      return true;
    }

    function positionRects() {
      if (!isImgVisible()) {
        rects.forEach(r => r.el.style.opacity = '0');
        return;
      }
      const imgRect = img.getBoundingClientRect();
      if (imgRect.width === 0 || imgRect.height === 0) {
        rects.forEach(r => r.el.style.opacity = '0');
        return;
      }
      rects.forEach(({ el, region }) => {
        const x = imgRect.left + region.x * scaleX;
        const y = imgRect.top  + region.y * scaleY;
        // GPU-accelerated transform avoids layout/paint per scroll frame (much smoother than left/top)
        el.style.transform = `translate3d(${x}px, ${y}px, 0)`;
        el.style.width = `${region.w * scaleX}px`;
        el.style.height = `${region.h * scaleY}px`;
        el.style.opacity = '1';
      });
    }

    // GPU hint — keeps the rectangle in its own composited layer
    rects.forEach(r => {
      r.el.style.willChange = 'transform';
      r.el.style.left = '0';
      r.el.style.top = '0';
    });

    positionRects();
    const reposition = () => requestAnimationFrame(positionRects);
    window.addEventListener('scroll', reposition, { passive: true });
    window.addEventListener('resize', reposition, { passive: true });
  }

  /**
   * Fallback: when verdict is "ai_generated" or "manipulated" but the cloud
   * returned no specific regions, draw four corner brackets around the image.
   * Communicates "the entire image is implicated" without misleading rectangles.
   */
  function applyCornerBrackets(img, color) {
    const displayW = img.clientWidth || img.offsetWidth;
    const displayH = img.clientHeight || img.offsetHeight;
    if (displayW < 80 || displayH < 80) return;

    const sz = Math.max(14, Math.min(28, Math.round(displayW * 0.07)));
    const corners = ['tl', 'tr', 'bl', 'br'];
    const els = corners.map(pos => {
      const c = document.createElement('div');
      c.className = 'ns-corner';
      const bw = '3px';
      const css = `position:fixed; z-index:2147483645; pointer-events:none;
        width:${sz}px; height:${sz}px; opacity:0;
        animation:ns-corner-markers 2.5s ease-in-out infinite;`;
      const borders = {
        tl: `border-top:${bw} solid ${color}; border-left:${bw} solid ${color};`,
        tr: `border-top:${bw} solid ${color}; border-right:${bw} solid ${color};`,
        bl: `border-bottom:${bw} solid ${color}; border-left:${bw} solid ${color};`,
        br: `border-bottom:${bw} solid ${color}; border-right:${bw} solid ${color};`,
      };
      c.style.cssText = css + borders[pos];
      document.body.appendChild(c);
      return { el: c, pos };
    });

    // Cache for hidden-image check
    function cornerImgVisible() {
      if (!img.isConnected) return false;
      if (img.clientWidth === 0 || img.clientHeight === 0) return false;
      const cs = getComputedStyle(img);
      return !(cs.display === 'none' || cs.visibility === 'hidden' || cs.opacity === '0');
    }
    // GPU hint
    els.forEach(({ el }) => { el.style.willChange = 'transform'; el.style.left = '0'; el.style.top = '0'; });

    function positionCorners() {
      if (!cornerImgVisible()) { els.forEach(e => e.el.style.opacity = '0'); return; }
      const r = img.getBoundingClientRect();
      if (r.width === 0 || r.height === 0) { els.forEach(e => e.el.style.opacity = '0'); return; }
      els.forEach(({ el, pos }) => {
        let x, y;
        if (pos === 'tl') { x = r.left;       y = r.top; }
        if (pos === 'tr') { x = r.right - sz; y = r.top; }
        if (pos === 'bl') { x = r.left;       y = r.bottom - sz; }
        if (pos === 'br') { x = r.right - sz; y = r.bottom - sz; }
        el.style.transform = `translate3d(${x}px, ${y}px, 0)`;
        el.style.opacity = '1';
      });
    }
    positionCorners();
    const reposition = () => requestAnimationFrame(positionCorners);
    window.addEventListener('scroll', reposition, { passive: true });
    window.addEventListener('resize', reposition, { passive: true });
  }

  // Inject keyframe animations (once)
  if (!document.getElementById('ns-scan-styles')) {
    const style = document.createElement('style');
    style.id = 'ns-scan-styles';
    style.textContent = `
      @keyframes ns-scanline {
        0% { top: -2px; opacity: 0; }
        10% { opacity: 1; }
        90% { opacity: 0.6; }
        100% { top: calc(100% + 2px); opacity: 0; }
      }
      @keyframes ns-glow-pulse {
        0%, 100% { box-shadow: 0 0 8px 2px var(--ns-glow-color); }
        50% { box-shadow: 0 0 20px 6px var(--ns-glow-color); }
      }
      @keyframes ns-border-rotate {
        0% { background-position: 0% 50%; }
        100% { background-position: 200% 50%; }
      }
      @keyframes ns-badge-appear {
        0% { transform: translateY(-8px) scale(0.8); opacity: 0; }
        100% { transform: translateY(0) scale(1); opacity: 1; }
      }
      @keyframes ns-corner-markers {
        0%, 100% { opacity: 0.5; }
        50% { opacity: 1; }
      }
      /* Snooze: overlays toggled off via popup hide instantly without reload.
         Detection still runs in the background; only the visual overlays go
         dark. Inline styles need !important to lose to the class rule. */
      body.ns-overlays-off .ns-float-badge,
      body.ns-overlays-off .ns-float-region,
      body.ns-overlays-off .ns-corner {
        display: none !important;
      }
      body.ns-overlays-off img[data-ns-verdict] {
        outline: none !important;
        animation: none !important;
        box-shadow: none !important;
      }
    `;
    (document.head || document.documentElement).appendChild(style);
  }

  // --- Image scanning: all images on the page ---
  // Per-skip logging is gated by NITHIC_DEBUG=true on window so it stays quiet
  // by default. Toggle in DevTools: window.NITHIC_DEBUG = true; then reload.
  const skipLog = (reason, img) => {
    if (window.NITHIC_DEBUG) {
      console.log('[Nithic] Skip image:', reason, img.src?.slice(0, 80), `${img.naturalWidth}x${img.naturalHeight}`);
    }
  };
  function scanImage(img) {
    if (!nsDetectionEnabled('image')) return; // user disabled image analysis
    if (scannedImages.has(img)) return;
    if (!img.src) { skipLog('no src', img); return; }
    if (img.src.startsWith('data:') && img.src.length < 1000) { skipLog('tiny data URL', img); return; }
    if (img.naturalWidth < 200 || img.naturalHeight < 200) { skipLog('too small', img); return; }

    if (!img.isConnected) { skipLog('detached', img); return; }
    const cs = getComputedStyle(img);
    if (cs.display === 'none' || cs.visibility === 'hidden' || cs.opacity === '0') {
      skipLog('hidden (' + cs.display + '/' + cs.visibility + '/' + cs.opacity + ')', img);
      return;
    }
    if (img.clientWidth === 0 || img.clientHeight === 0) { skipLog('zero box', img); return; }

    scannedImages.add(img);

    chrome.runtime.sendMessage({
      type: 'content_scan_image',
      data: {
        imageData: {
          src: img.src,
          naturalWidth: img.naturalWidth,
          naturalHeight: img.naturalHeight,
          alt: img.alt || '',
          fileSize: 0
        },
        url: window.location.href,
        domain: hostname,
        aiService: aiInfo?.service || null
      }
    }, (result) => {
      if (chrome.runtime.lastError) return;
      if (!result) return;

      // Apply visual overlay based on verdict
      const verdict = result.cloud?.cloudVerdict || result.client?.verdict;
      const confidence = result.cloud?.cloudConfidence || result.client?.confidence || 0;
      const manipType = result.cloud?.manipulationType;
      const regions = result.cloud?.manipulationRegions || [];

      // Diagnostic so verdicts/regions are inspectable from DevTools
      console.log('[Nithic] Image scan:', {
        src: img.src.slice(0, 80),
        verdict, confidence, manipType,
        regionCount: regions.length,
        regions: regions.slice(0, 3)
      });

      if (verdict && verdict !== 'human') {
        applyImageOverlay(img, verdict, confidence, manipType);
        if (regions.length > 0) {
          applyManipulationRegions(img, regions);
        } else if (verdict === 'ai_generated' || verdict === 'manipulated' || verdict === 'mixed') {
          // No specific altered regions returned — communicate "whole image" via corner brackets
          applyCornerBrackets(img, '#ef4444');
        }
      }
    });
  }

  // IntersectionObserver triggers scans when an image enters the viewport.
  // This handles lazy-loaded images, hidden tabs/panels that become visible,
  // infinite scroll, etc. — patterns used by Gemini, ChatGPT, photoai, etc.
  // The visibility guard inside scanImage() then gates the cloud request itself.
  const tryScan = (img) => {
    if (img.complete && img.naturalWidth > 0) {
      scanImage(img);
    } else {
      img.addEventListener('load', () => scanImage(img), { once: true });
    }
  };

  const intersectionObserver = new IntersectionObserver((entries) => {
    for (const entry of entries) {
      if (entry.isIntersecting) tryScan(entry.target);
    }
  }, { rootMargin: '300px', threshold: 0.01 });

  // Watch existing imgs for src changes (lazy-loading via src swap on the same element)
  const srcChangeObserver = new MutationObserver((mutations) => {
    for (const m of mutations) {
      if (m.type === 'attributes' && m.attributeName === 'src' && m.target.tagName === 'IMG') {
        // Allow re-scan: same element, new src is a new piece of content
        scannedImages.delete(m.target);
        tryScan(m.target);
      }
    }
  });

  const watchImg = (img) => {
    intersectionObserver.observe(img);
    srcChangeObserver.observe(img, { attributes: true, attributeFilter: ['src'] });
  };

  // Observe all existing images
  document.querySelectorAll('img').forEach(watchImg);

  // Watch for dynamically added images (DOM insertions)
  const imageObserver = new MutationObserver((mutations) => {
    for (const mutation of mutations) {
      for (const node of mutation.addedNodes) {
        if (node.nodeType !== Node.ELEMENT_NODE) continue;
        if (node.tagName === 'IMG') watchImg(node);
        const imgs = node.querySelectorAll?.('img');
        if (imgs) imgs.forEach(watchImg);
      }
    }
  });

  if (document.body) {
    imageObserver.observe(document.body, { childList: true, subtree: true });
  }

  // --- Image scanning: file uploads (input[type=file]) on any site ---
  document.addEventListener('change', (event) => {
    const input = event.target;
    if (input?.type !== 'file' || !input.files?.length) return;

    for (const file of input.files) {
      if (!file.type.startsWith('image/')) continue;
      chrome.runtime.sendMessage({
        type: 'content_scan_image',
        data: {
          imageData: {
            src: file.name,
            naturalWidth: 0,
            naturalHeight: 0,
            alt: file.name,
            fileSize: file.size
          },
          url: window.location.href,
          domain: hostname,
          aiService: aiInfo?.service || null
        }
      }).catch(() => {});
    }
  }, true);
}

// Initialize when DOM is ready
if (document.readyState === 'loading') {
  document.addEventListener('DOMContentLoaded', () => {
    initializeProtection();
    initializeIdentityDetector();
    initializeContentScanning();
    initializeAIVisibilityMonitor();
  });
} else {
  initializeProtection();
  initializeIdentityDetector();
  initializeContentScanning();
  initializeAIVisibilityMonitor();
}

console.log('PhishProtect content script loaded (optimized)');
