// useLiveScan — drives the mock scan simulation using rAF.
// Emits scan events: 'start' | 'progress' | 'finding' | 'done' | 'error'.

function useLiveScan() {
  const { React } = window;
  const { useState, useRef, useCallback } = React;

  const [scan, setScan] = useState({
    status: 'idle',        // idle | running | done | error | rate-limited
    repoUrl: null,
    progress: 0,           // 0..1
    filesScanned: 0, totalFiles: 0,
    etaMs: 0, elapsedMs: 0,
    findings: [],
    log: [],
    error: null,
    cancelToken: 0,
  });

  const rafRef = useRef(0);
  const startedRef = useRef(0);
  const cacheRef = useRef(new Map()); // repoUrl → full result

  const cancel = useCallback(() => {
    cancelAnimationFrame(rafRef.current);
    setScan(s => ({ ...s, status: 'idle', cancelToken: s.cancelToken + 1 }));
  }, []);

  const run = useCallback((repoUrlRaw) => {
    const v = window.githubLib.validate(repoUrlRaw);
    if (!v.ok) { setScan(s => ({ ...s, status: 'error', error: v.error })); return; }
    const repoUrl = v.canonical;

    // cached?
    if (cacheRef.current.has(repoUrl)) {
      const cached = cacheRef.current.get(repoUrl);
      setScan({ ...cached, status: 'done' });
      return;
    }

    const plan = window.scanRunner.plan(repoUrl);
    startedRef.current = performance.now();
    cancelAnimationFrame(rafRef.current);

    // schedule finding appearances proportionally through the scan
    const findingTimings = plan.findings.map((f, i) =>
      ({ f, t: Math.floor(plan.totalMs * (0.18 + 0.72 * (i / Math.max(1, plan.findings.length - 1)))) })
    );

    const tools = ['semgrep', 'bandit', 'gitleaks', 'trivy', 'njsscan', 'checkov'];
    const logPhases = [
      { t: 0,     text: `cloning ${repoUrl} --depth 1` },
      { t: 400,   text: 'repo size: 142MB · ok · 2184 files' },
      { t: 700,   text: 'phase 1 / 6 · whitebox + sast' },
      { t: 900,   text: 'semgrep: loading ruleset p/owasp-top-10,p/python · 312 rules' },
      { t: 1400,  text: 'bandit: python ast · B-series checks' },
      { t: 1900,  text: 'gitleaks: commit history scan depth=500' },
      { t: 2500,  text: 'trivy: dependency + container scan' },
    ];

    setScan(s => ({
      ...s, status: 'running', repoUrl, progress: 0,
      filesScanned: 0, totalFiles: plan.totalFiles,
      etaMs: plan.totalMs, elapsedMs: 0,
      findings: [], log: [], error: null,
    }));

    const tick = (now) => {
      const elapsed = now - startedRef.current;
      const p = Math.min(1, elapsed / plan.totalMs);
      const filesScanned = Math.min(plan.totalFiles, Math.floor(plan.totalFiles * p));
      const etaMs = Math.max(0, plan.totalMs - elapsed);

      // accumulate log phases and a streaming per-file line every ~180ms
      const logs = [];
      for (const lp of logPhases) if (lp.t <= elapsed) logs.push(lp.text);
      // streaming tool-heartbeat lines
      const beats = Math.floor(elapsed / 350);
      for (let i = 0; i < Math.min(beats, 18); i++) {
        const tool = tools[i % tools.length];
        logs.push(`${tool} ▸ scanned ${Math.floor(filesScanned * (i / 18))} files`);
      }
      // findings appearing on schedule
      const activeFindings = findingTimings.filter(x => x.t <= elapsed).map(x => x.f);

      setScan(s => ({
        ...s,
        progress: p,
        filesScanned, etaMs, elapsedMs: elapsed,
        log: logs.slice(-14),
        findings: activeFindings,
      }));

      if (p < 1) {
        rafRef.current = requestAnimationFrame(tick);
      } else {
        const done = {
          status: 'done', repoUrl, progress: 1,
          filesScanned: plan.totalFiles, totalFiles: plan.totalFiles,
          etaMs: 0, elapsedMs: plan.totalMs,
          log: [...logs.slice(-13), 'scan complete · writing sarif'],
          findings: plan.findings,
          error: null, cancelToken: 0,
        };
        cacheRef.current.set(repoUrl, done);
        setScan(done);
      }
    };
    rafRef.current = requestAnimationFrame(tick);
  }, []);

  return { scan, run, cancel };
}
window.useLiveScan = useLiveScan;
