import { useEffect, useRef, useState } from 'react';
import { useQueryClient } from '@tanstack/react-query';

const SCAN_TIMEOUT_MS = 30000;

/**
 * Triggers a headless axe-core scan of a same-origin URL via a hidden iframe
 * with ?aai_scan=<id> (see overlay.js's auto-scan mode + class-admin-bar.php's
 * inject_overlay(), which loads overlay.js for logged-in admins on every
 * frontend page). The iframe's page posts back {type:'aai-scan-complete'|'aai-scan-error', scanId}
 * once axe-core finishes and the result is stored via /axe-scan.
 */
export function useHeadlessScan() {
  const qc = useQueryClient();
  const [scanning, setScanning]     = useState<Record<string, boolean>>({});
  const [scanErrors, setScanErrors] = useState<Record<string, string>>({});
  const scanIdToUrlRef = useRef<Record<string, string>>({});
  const iframesRef     = useRef<Record<string, HTMLIFrameElement>>({});
  const scanTimersRef  = useRef<Record<string, number>>({});
  // Populated only for scans started via scanUrlAsync() (sequential bulk scan) —
  // startScan()'s regular fire-and-forget callers never touch this.
  const resolversRef = useRef<Record<string, { resolve: () => void; reject: (e: Error) => void }>>({});

  function finishScan(scanId: string, error: string | null) {
    const url = scanIdToUrlRef.current[scanId];
    if (!url) return;
    delete scanIdToUrlRef.current[scanId];

    iframesRef.current[scanId]?.remove();
    delete iframesRef.current[scanId];

    window.clearTimeout(scanTimersRef.current[scanId]);
    delete scanTimersRef.current[scanId];

    setScanning((s) => { const n = { ...s }; delete n[url]; return n; });

    const resolver = resolversRef.current[scanId];
    delete resolversRef.current[scanId];

    if (error) {
      setScanErrors((s) => ({ ...s, [url]: error }));
      window.setTimeout(() => {
        setScanErrors((s) => { const n = { ...s }; delete n[url]; return n; });
      }, 4000);
      resolver?.reject(new Error(error));
    } else {
      qc.invalidateQueries({ queryKey: ['page-scans'] });
      resolver?.resolve();
    }
  }

  useEffect(() => {
    function onMessage(e: MessageEvent) {
      if (e.origin !== window.location.origin) return;
      const data = e.data as { type?: string; scanId?: string; error?: string } | undefined;
      if (!data?.scanId) return;
      if (data.type === 'aai-scan-complete') finishScan(data.scanId, null);
      else if (data.type === 'aai-scan-error') finishScan(data.scanId, data.error ?? 'Scan failed');
    }
    window.addEventListener('message', onMessage);
    return () => {
      window.removeEventListener('message', onMessage);
      Object.values(iframesRef.current).forEach((f) => f.remove());
      Object.values(scanTimersRef.current).forEach((t) => window.clearTimeout(t));
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  function beginScan(url: string): string {
    const scanId = `${Date.now()}-${Math.random().toString(36).slice(2)}`;
    scanIdToUrlRef.current[scanId] = url;
    setScanning((s) => ({ ...s, [url]: true }));
    setScanErrors((s) => { const n = { ...s }; delete n[url]; return n; });

    const iframe = document.createElement('iframe');
    iframe.src = url + (url.includes('?') ? '&' : '?') + 'aai_scan=' + encodeURIComponent(scanId);
    iframe.setAttribute('aria-hidden', 'true');
    iframe.style.cssText = 'position:fixed; top:-9999px; left:-9999px; width:1280px; height:900px; border:0;';
    document.body.appendChild(iframe);
    iframesRef.current[scanId] = iframe;

    scanTimersRef.current[scanId] = window.setTimeout(() => finishScan(scanId, 'Scan timed out'), SCAN_TIMEOUT_MS);
    return scanId;
  }

  function startScan(url: string) {
    if (scanning[url]) return;
    beginScan(url);
  }

  /** Sequential/bulk use — awaits a single scan's completion instead of firing and forgetting. */
  function scanUrlAsync(url: string): Promise<void> {
    if (scanning[url]) return Promise.resolve();
    return new Promise<void>((resolve, reject) => {
      const scanId = beginScan(url);
      resolversRef.current[scanId] = { resolve, reject };
    });
  }

  return { scanning, scanErrors, startScan, scanUrlAsync };
}
