/* Scanify — fully wired SPA: auth, URL/file/IP scans, reports, account, settings.
   Same look as the design handoff, with real backend integration. */

const { useState, useEffect, useRef, useCallback } = React;

// Pull cross-script components/helpers off window (each <script type="text/babel">
// runs in its own scope under @babel/standalone, so bare names don't leak).
const { GlobeLoader, ScanWhirl } = window;
const {
  useTweaks, TweaksPanel, TweakSection, TweakRow,
  TweakSlider, TweakToggle, TweakRadio, TweakSelect,
  TweakText, TweakNumber, TweakColor, TweakButton,
} = window;

// ---------- inline icons ----------
const I = {
  search:   <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5"><circle cx="7" cy="7" r="4.5"/><path d="m13.5 13.5-3-3" strokeLinecap="round"/></svg>,
  url:      <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5"><circle cx="7" cy="7" r="4.5"/><path d="m13.5 13.5-3-3" strokeLinecap="round"/></svg>,
  file:     <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5"><path d="M3 2h6l4 4v8H3z" strokeLinejoin="round"/><path d="M9 2v4h4"/></svg>,
  ip:       <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5"><rect x="2" y="3" width="12" height="10" rx="1.5"/><path d="M2 6h12M5 9h2M9 9h2"/></svg>,
  reports:  <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5"><path d="M3 13V5l5-3 5 3v8" strokeLinejoin="round"/><path d="M3 13h10M6 13V8h4v5"/></svg>,
  user:     <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5"><circle cx="8" cy="6" r="2.5"/><path d="M3 13c.5-2.5 2.5-4 5-4s4.5 1.5 5 4" strokeLinecap="round"/></svg>,
  settings: <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5"><path d="M8 2v2M8 12v2M2 8h2M12 8h2M3.8 3.8l1.4 1.4M10.8 10.8l1.4 1.4M3.8 12.2l1.4-1.4M10.8 5.2l1.4-1.4"/><circle cx="8" cy="8" r="2"/></svg>,
  bolt:     <svg viewBox="0 0 16 16" width="14" height="14" fill="currentColor"><path d="M9 1 3 9h4l-1 6 6-8H8z"/></svg>,
  globe:    <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5"><circle cx="8" cy="8" r="6"/><path d="M2 8h12M8 2c2 2 2 10 0 12M8 2c-2 2-2 10 0 12"/></svg>,
  shield:   <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5"><path d="M8 1.5 13 4v4c0 3-2 5-5 6.5-3-1.5-5-3.5-5-6.5V4z" strokeLinejoin="round"/></svg>,
  download: <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5"><path d="M8 2v8m-3-3 3 3 3-3M3 13h10" strokeLinecap="round" strokeLinejoin="round"/></svg>,
  flag:     <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5"><path d="M3 14V2M3 3h8l-1.5 3L11 9H3" strokeLinejoin="round"/></svg>,
  back:     <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5"><path d="M9 3 4 8l5 5M4 8h9" strokeLinecap="round" strokeLinejoin="round"/></svg>,
  copy:     <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5"><rect x="5" y="5" width="9" height="9" rx="1.5"/><path d="M2 10V3a1 1 0 0 1 1-1h7"/></svg>,
  trash:    <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5"><path d="M3 4h10M6 4V2.5h4V4M5 4l1 9h4l1-9"/></svg>,
  eye:      <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5"><path d="M1 8s2.5-5 7-5 7 5 7 5-2.5 5-7 5-7-5-7-5z"/><circle cx="8" cy="8" r="2"/></svg>,
};

// ---------- canvas background ----------
function BackgroundCanvas() {
  const ref = useRef(null);
  useEffect(() => {
    const cv = ref.current; if (!cv) return;
    const ctx = cv.getContext('2d');
    const dpr = Math.min(window.devicePixelRatio || 1, 2);
    function resize() {
      const r = cv.getBoundingClientRect();
      cv.width = r.width * dpr; cv.height = r.height * dpr;
      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
    }
    resize();
    const ro = new ResizeObserver(resize); ro.observe(cv);

    const blobs = Array.from({length: 4}, () => ({
      x: Math.random(), y: Math.random(),
      vx: (Math.random()-0.5)*0.00006, vy: (Math.random()-0.5)*0.00006,
      r: 280 + Math.random()*220,
      hue: Math.random() < 0.5 ? 38 : 140,
      sat: 30, light: 80,
      a: 0.10 + Math.random()*0.06,
    }));
    const dots = Array.from({length: 38}, () => ({
      x: Math.random(), y: Math.random(),
      vx: (Math.random()-0.5)*0.00004, vy: (Math.random()-0.5)*0.00004,
      r: 1 + Math.random()*1.6,
      a: 0.08 + Math.random()*0.10,
    }));

    let t0 = performance.now();
    let raf;
    function draw(now) {
      const dt = Math.min(40, now - t0); t0 = now;
      const W = cv.clientWidth, H = cv.clientHeight;
      ctx.clearRect(0,0,W,H);
      for (const b of blobs) {
        b.x += b.vx*dt; b.y += b.vy*dt;
        if (b.x<-0.2) b.x=1.2; if (b.x>1.2) b.x=-0.2;
        if (b.y<-0.2) b.y=1.2; if (b.y>1.2) b.y=-0.2;
        const cx = b.x*W, cy = b.y*H;
        const g = ctx.createRadialGradient(cx, cy, 0, cx, cy, b.r);
        g.addColorStop(0, `hsla(${b.hue}, ${b.sat}%, ${b.light}%, ${b.a})`);
        g.addColorStop(1, `hsla(${b.hue}, ${b.sat}%, ${b.light}%, 0)`);
        ctx.fillStyle = g;
        ctx.fillRect(cx-b.r, cy-b.r, b.r*2, b.r*2);
      }
      for (const d of dots) {
        d.x += d.vx*dt; d.y += d.vy*dt;
        if (d.x<0) d.x=1; if (d.x>1) d.x=0;
        if (d.y<0) d.y=1; if (d.y>1) d.y=0;
        ctx.fillStyle = `rgba(80, 100, 80, ${d.a})`;
        ctx.beginPath(); ctx.arc(d.x*W, d.y*H, d.r, 0, Math.PI*2); ctx.fill();
      }
      ctx.strokeStyle = 'rgba(60, 70, 60, 0.04)';
      ctx.lineWidth = 1;
      const gs = 48;
      for (let x = 0; x < W; x += gs) { ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, H); ctx.stroke(); }
      for (let y = 0; y < H; y += gs) { ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(W, y); ctx.stroke(); }

      raf = requestAnimationFrame(draw);
    }
    raf = requestAnimationFrame(draw);
    return () => { cancelAnimationFrame(raf); ro.disconnect(); };
  }, []);
  return <canvas ref={ref} className="bg-canvas" />;
}

// ---------- helpers ----------
function classifyLevel(score) {
  if (score >= 70) return { label: 'CRITICAL', cls: 'danger' };
  if (score >= 40) return { label: 'HIGH',     cls: 'danger' };
  if (score >= 20) return { label: 'MEDIUM',   cls: 'warn' };
  if (score >= 5)  return { label: 'LOW',      cls: 'warn' };
  return { label: 'SAFE', cls: 'safe' };
}
function formatTime(d) {
  const dt = d instanceof Date ? d : new Date(d);
  return `${dt.toLocaleDateString()} ${dt.toLocaleTimeString([], {hour:'2-digit', minute:'2-digit'})}`;
}

// ---------- TopBar ----------
function TopBar({ user, onSignOut, onNav }) {
  const initial = (user?.name || user?.email || 'U').trim().charAt(0).toUpperCase();
  return (
    <div className="topbar">
      <div className="brand" onClick={() => onNav('url')} style={{cursor:'pointer'}}>
        <span className="brand-name">scanify<em>.to</em></span>
        <span className="brand-pill mono">THREAT INTELLIGENCE</span>
      </div>
      <div className="topbar-right">
        <span className="tb-chip"><span className="tb-dot"/>All engines operational</span>
        {user ? (
          <>
            <div className="tb-user">
              <div className="avatar">{initial}</div>
              <span className="user-name">{user.name?.split(' ')[0] || user.email}</span>
              {user.plan === 'pro' && <span className="pro-tag">PRO</span>}
            </div>
            <a className="tb-link" href="#" onClick={e => { e.preventDefault(); onSignOut(); }}>Sign out</a>
          </>
        ) : (
          <a className="tb-link" href="/api/auth/google">Sign in</a>
        )}
        <a className="tb-about" href="#/about" onClick={e => { e.preventDefault(); onNav('about'); }}>About Us</a>
      </div>
    </div>
  );
}

// ---------- Sidebar ----------
function Sidebar({ active, onNav }) {
  const item = (key, icon, label) => (
    <div className={`sb-item ${active === key ? 'active' : ''}`} onClick={() => onNav(key)}>
      <span className="sb-icon">{icon}</span> {label}
    </div>
  );
  return (
    <aside className="sidebar">
      <div className="sb-section">
        <div className="sb-label">Analysis</div>
        {item('url',  I.url,  'URL Scanner')}
        {item('file', I.file, 'File Scanner')}
        {item('ip',   I.ip,   'IP / Domain')}
      </div>
      <div className="sb-section">
        <div className="sb-label">Reports</div>
        {item('reports', I.reports, 'Export Reports')}
      </div>
      <div className="sb-section">
        <div className="sb-label">Account</div>
        {item('account',  I.user,     'My Account')}
        {item('settings', I.settings, 'Settings')}
      </div>

      <div className="sb-bottom">
        <div className="sb-card">
          <div className="engines-status">
            <span className="dot"/>
            <div>
              <div className="sb-card-title">Engines connected</div>
              <div className="sb-card-body">95 AV Engines · Safe<br/>Browsing</div>
            </div>
          </div>
        </div>
        <div className="sb-card dev" onClick={() => onNav('settings')} style={{cursor:'pointer'}}>
          <span className="icon">{I.bolt}</span>
          <div>
            <div className="sb-card-title">For Developers</div>
            <div className="sb-card-body">REST API · 15 free scans/mo</div>
          </div>
        </div>
        <div className="sb-foot-links">
          <a href="/pricing">Pricing</a>
          <a href="/privacy">Privacy</a>
          <a href="/terms">Terms</a>
        </div>
      </div>
    </aside>
  );
}

// ---------- shared progress card ----------
// Advances through the first N-1 steps on a timer, then pauses at the last step
// (showing "Finalizing analysis…") indefinitely until the parent unmounts this
// card by switching phase to 'done' or 'error' once the real fetch resolves.
function ScanProgressCard({ steps, active, resetKey }) {
  const [done, setDone] = useState(0);
  useEffect(() => {
    setDone(0);
    if (!active) return;
    let i = 0;
    const id = setInterval(() => {
      i = Math.min(i + 1, steps.length - 1); // stop short of last step
      setDone(i);
      if (i >= steps.length - 1) clearInterval(id);
    }, 900);
    return () => clearInterval(id);
  }, [active, resetKey]);

  const finalizing = done >= steps.length - 1;
  const lastDoneName = done > 0 ? steps[done - 1] : null;
  return (
    <div className="card">
      <div className="scan-progress-head">
        <h3 className="card-title" style={{margin:0}}>Scan in Progress</h3>
        <div className="scan-progress-status">
          {finalizing ? 'Finalizing analysis…' : lastDoneName ? <>{lastDoneName} <span className="ok">→ done</span></> : 'Initializing engines…'}
        </div>
      </div>
      <div className="scan-tiles">
        {steps.map((s, i) => (
          <div key={i} className={`scan-tile ${i < done ? 'done' : i === done ? 'active' : ''}`}>
            <div className="scan-tile-head">
              <span className="scan-tile-spinner"/>
              <span className="scan-tile-name">{s}</span>
            </div>
            <span className="scan-tile-status">{i < done ? 'Complete' : i === done ? 'Scanning…' : 'Queued'}</span>
          </div>
        ))}
      </div>
      <div className="scan-progress-foot">
        <div className="scan-progress-bar">
          <div className="scan-progress-fill" style={{width: `${Math.max(8, (done/steps.length)*100 + (finalizing ? 10 : 0))}%`}}/>
        </div>
        <div className="scan-progress-count">{finalizing ? 'Awaiting results…' : `${done} / ${steps.length} checks`}</div>
      </div>
    </div>
  );
}

// ---------- shared verdict card ----------
function VerdictCard({ target, score, scannedAt, engines, protocol, headline, body, level }) {
  const lv = level || classifyLevel(score);
  return (
    <div className={`card verdict-card lvl-${lv.cls}`}>
      <div className="verdict-score-block">
        <div className="verdict-num">{score}</div>
        <div className="verdict-score-label">Risk Score</div>
        <div className={`verdict-pill verdict-pill-${lv.cls}`}>{lv.label}</div>
      </div>
      <div>
        <div className="verdict-target">{target}</div>
        <h2 className="verdict-headline">{headline}</h2>
        <p className="verdict-body">{body}</p>
      </div>
      <div className="verdict-meta">
        <div className="row"><span className="label">Scanned</span><span>{formatTime(scannedAt)}</span></div>
        {engines  && <div className="row"><span className="label">Engines</span><span>{engines}</span></div>}
        {protocol && <div className="row"><span className="label">Protocol</span><span>{protocol}</span></div>}
      </div>
    </div>
  );
}

function PreviewCard({ target, screenshotUrl, user, onUpgrade }) {
  const isPro = user?.plan === 'pro';
  const [attempt, setAttempt] = useState(0);
  const [failed, setFailed] = useState(false);

  // URLScan's CDN is sometimes 1-10s slow after the result API says "done".
  // Retry with backoff: 2s, 5s, 10s. After all fail, show a clean fallback.
  function handleImgError() {
    if (attempt >= 3) { setFailed(true); return; }
    const delay = [2000, 5000, 10000][attempt] || 10000;
    const next = attempt + 1;
    setAttempt(next);
    setTimeout(() => {
      const img = document.getElementById('ss-preview-img');
      if (img && screenshotUrl) img.src = screenshotUrl + '?r=' + next; // cache-bust
    }, delay);
  }

  const chromeBar = (
    <div className="preview-chrome">
      <span className="ic" style={{display:'inline-flex', gap:4}}>
        <span style={{width:8,height:8,borderRadius:'50%',background:'#e35b50'}}/>
        <span style={{width:8,height:8,borderRadius:'50%',background:'#e8b13a'}}/>
        <span style={{width:8,height:8,borderRadius:'50%',background:'#3eb265'}}/>
      </span>
      <span className="mono" style={{fontSize:12, color:'#6b7269'}}>{target}</span>
    </div>
  );

  // Free users — Pro upsell card
  if (!isPro) {
    return (
      <div className="card">
        <h3 className="card-title">
          <span className="ic">{I.globe}</span> Live Website Preview
          <span style={{
            fontSize:9, letterSpacing:'0.12em', padding:'2px 7px', borderRadius:3,
            background:'rgba(58,138,85,0.12)', color:'#286a40', marginLeft:8,
            fontWeight:600, verticalAlign:'middle',
          }}>PRO</span>
        </h3>
        <div className="preview-frame">
          {chromeBar}
          <div className="preview-body" style={{
            display:'flex', flexDirection:'column', alignItems:'center', justifyContent:'center',
            gap:14, padding:'48px 24px', textAlign:'center', minHeight:280,
          }}>
            <svg width="38" height="38" viewBox="0 0 24 24" fill="none" stroke="#6b7269" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
              <rect x="3" y="11" width="18" height="11" rx="2"/>
              <path d="M7 11V7a5 5 0 0 1 10 0v4"/>
            </svg>
            <div style={{fontSize:15, fontWeight:600, color:'#3a3833'}}>See the page before you visit it</div>
            <div style={{fontSize:13, color:'#6b7269', maxWidth:360, lineHeight:1.55}}>
              Pro users get a live screenshot of every URL they scan — spot fake login screens, brand impersonation, and suspicious layouts without ever clicking the link.
            </div>
            <button className="run-btn" onClick={onUpgrade} style={{marginTop:6}}>
              Upgrade to Pro — $9.99/mo →
            </button>
          </div>
        </div>
      </div>
    );
  }

  // Reset retry state when a new screenshot URL arrives (new scan)
  useEffect(() => { setAttempt(0); setFailed(false); }, [screenshotUrl]);

  const resultUrl = screenshotUrl
    ? screenshotUrl.replace('/screenshots/', '/result/').replace('.png', '/')
    : null;

  // Pro users — actual screenshot with retry-on-error
  return (
    <div className="card">
      <h3 className="card-title"><span className="ic">{I.globe}</span> Live Website Preview</h3>
      <div className="preview-frame">
        {chromeBar}
        {screenshotUrl && !failed ? (
          <img
            id="ss-preview-img"
            src={screenshotUrl}
            alt="Live page screenshot"
            onError={handleImgError}
            style={{display:'block',width:'100%',maxHeight:480,objectFit:'cover',objectPosition:'top',background:'#f3efe8'}}
          />
        ) : (
          <div className="preview-body" style={{padding:'48px 24px', textAlign:'center', color:'#6b7269', fontSize:13}}>
            {!screenshotUrl ? (
              '[ no screenshot available ]'
            ) : (
              <span>
                Screenshot capture timed out.{' '}
                <a href={resultUrl} target="_blank" rel="noopener noreferrer" style={{color:'#3a8a55', textDecoration:'underline'}}>
                  View on URLScan ↗
                </a>
              </span>
            )}
          </div>
        )}
        <div className="preview-caption">Live capture taken at scan time — shows the actual page a visitor would see</div>
      </div>
    </div>
  );
}

function DetailsCard({ checks }) {
  return (
    <div className="card">
      <h3 className="card-title">Detailed Check Results</h3>
      <div className="checks">
        <div className="checks-head">
          <span>Check</span><span>Engine / Source</span><span>Finding</span><span>Status</span>
        </div>
        {checks.map((c, i) => (
          <div key={i} className="checks-row">
            <span className="check-name">{c.name}</span>
            <span className="check-source">{c.source}</span>
            <span className="check-finding">{c.finding}</span>
            <span className="check-status">{c.status === 'flag' ? '⚠ Flagged' : '✓ Clean'}</span>
          </div>
        ))}
      </div>
    </div>
  );
}

function AICard({ text }) {
  return (
    <div className="card">
      <h3 className="card-title">AI Threat Assessment</h3>
      <div className="ai-block">{text}</div>
    </div>
  );
}

// Build a "checks" list and AI summary from the backend scan result.
function detailsFromScan(data) {
  const vt   = data?.details?.virustotal;
  const gsb  = data?.details?.safebrowsing;
  const ssl  = data?.details?.ssl;
  const wh   = data?.details?.whois;
  const us   = data?.details?.urlscan;
  const checks = [];
  if (vt) {
    const flagged = vt.detections?.malicious || 0;
    const total   = vt.totalEngines || 95;
    checks.push({
      name: '95 AV Engines', source: 'VirusTotal',
      finding: `${flagged}/${total} engines flagged`,
      status: flagged > 0 ? 'flag' : 'ok',
    });
  }
  if (gsb) {
    checks.push({
      name: 'Phishing & Malware Check', source: 'Google Safe Browsing',
      finding: gsb.matches?.length ? `Match: ${gsb.matches[0].threatType}` : 'No matches in threat database',
      status: gsb.matches?.length ? 'flag' : 'ok',
    });
  }
  if (ssl) {
    checks.push({
      name: 'SSL Certificate', source: 'TLS Inspector',
      finding: ssl.valid ? 'Valid HTTPS connection' : (ssl.error || 'Invalid certificate'),
      status: ssl.valid ? 'ok' : 'flag',
    });
  }
  if (us) {
    checks.push({
      name: 'Page Screenshot', source: 'urlscan.io',
      finding: us.score != null ? `Score: ${us.score}/100 · HTTP ${us.statusCode || '—'}` : 'Captured',
      status: 'ok',
    });
  }
  if (wh) {
    checks.push({
      name: 'Domain Age & Registration', source: 'RDAP / WHOIS',
      finding: `${wh.ageDays != null ? Math.round(wh.ageDays/365) + ' years old · ' : ''}${wh.creationDate ? 'Registered ' + new Date(wh.creationDate).toLocaleDateString() + ' · ' : ''}${wh.registrar || ''}`.trim() || 'Lookup completed',
      status: wh.ageDays != null && wh.ageDays < 30 ? 'flag' : 'ok',
    });
  }
  const flagged = vt?.detections?.malicious || 0;
  const total   = vt?.totalEngines || 95;
  const aiText = flagged > 0
    ? `Malware scan: ${flagged}/${total} engines flagged. Treat this URL as untrusted and avoid visiting unless you understand the risk. Confidence: High.`
    : `Malware scan: 0/${total} engines flagged. Phishing & Malware Check: clean. No indicators of compromise found. Confidence: High.`;
  return { checks, aiText };
}

// Reusable sign-in modal triggered from scanner pages when scan is attempted while signed out
function SignInModal({ open, onClose, beforeRedirect }) {
  useEffect(() => {
    if (!open) return;
    const onKey = e => { if (e.key === 'Escape') onClose(); };
    document.addEventListener('keydown', onKey);
    const prevOverflow = document.body.style.overflow;
    document.body.style.overflow = 'hidden';
    return () => {
      document.removeEventListener('keydown', onKey);
      document.body.style.overflow = prevOverflow;
    };
  }, [open, onClose]);

  if (!open) return null;
  return (
    <div onClick={onClose} style={{
      position:'fixed', inset:0,
      background:'rgba(15, 14, 12, 0.55)',
      backdropFilter:'blur(3px)',
      WebkitBackdropFilter:'blur(3px)',
      display:'flex', alignItems:'center', justifyContent:'center',
      zIndex:1000, padding:20,
      animation: 'ssModalFade 0.2s ease-out',
    }}>
      <style>{`
        @keyframes ssModalFade { from { opacity: 0 } to { opacity: 1 } }
        @keyframes ssModalPop {
          from { opacity: 0; transform: scale(0.94) translateY(8px) }
          to   { opacity: 1; transform: scale(1) translateY(0) }
        }
      `}</style>
      <div onClick={e => e.stopPropagation()} className="card" style={{
        maxWidth:420, width:'100%', margin:0,
        animation: 'ssModalPop 0.25s cubic-bezier(0.16, 1, 0.3, 1)',
      }}>
        <h3 className="card-title">Sign in to scan</h3>
        <p className="card-sub">Free accounts get 1 scan per month. No credit card required.</p>
        <div style={{display:'flex', gap:8, marginTop:14, flexWrap:'wrap'}}>
          <a href="/api/auth/google" className="run-btn" onClick={() => { try { beforeRedirect?.(); } catch {} }} style={{textDecoration:'none', flex:1, textAlign:'center', display:'inline-block', minWidth:160}}>
            Sign in with Google →
          </a>
          <button className="btn" onClick={onClose}>Cancel</button>
        </div>
      </div>
    </div>
  );
}

// ====================================================================
// PAGE: URL SCANNER
// ====================================================================
function PageURL({ user, authReady, initialQuery, onScanConsumed, onUpgrade }) {
  const [url, setUrl] = useState('');
  const [phase, setPhase] = useState('idle'); // idle | scanning | done | error
  const [error, setError] = useState(null);
  const [data, setData]   = useState(null);
  const [resetKey, setResetKey] = useState(0);
  const [recents, setRecents] = useState([]);
  const [showSignIn, setShowSignIn] = useState(false);
  const deepLinkRef = useRef(false);

  // Prefill from deep link, then auto-scan once auth resolves
  useEffect(() => {
    if (!initialQuery) return;
    setUrl(initialQuery);
  }, [initialQuery]);

  useEffect(() => {
    if (deepLinkRef.current) return;
    if (!initialQuery) return;
    if (!authReady) return; // wait until /api/auth/me has resolved
    deepLinkRef.current = true;
    onScanConsumed?.();
    if (user) {
      setTimeout(() => startScan(initialQuery), 150);
    } else {
      setShowSignIn(true);
    }
  }, [authReady, user, initialQuery]);

  async function startScan(overrideUrl) {
    if (!user) { setShowSignIn(true); return; }
    const target = (overrideUrl ?? url).trim();
    if (!target) return;
    setError(null);
    setPhase('scanning');
    setResetKey(k => k + 1);
    try {
      const res = await fetch('/api/scan', {
        method: 'POST',
        credentials: 'include',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ url: target }),
      });
      const j = await res.json();
      if (!j.success) {
        if (res.status === 401) setError('Sign in to run scans.');
        else setError(j.error || 'Scan failed');
        setPhase('error');
        return;
      }
      setData({ ...j.data, preview: j.preview, scansUsed: j.scansUsed, scansLimit: j.scansLimit });
      setPhase('done');
      setRecents(prev => [{
        target: j.data.url,
        score: j.data.riskScore,
        finishedAt: Date.now(),
      }, ...prev].slice(0, 6));
    } catch (e) {
      setError('Network error — try again');
      setPhase('error');
    }
  }
  function newScan() { setPhase('idle'); setData(null); setError(null); }

  const target = data?.url || (url.startsWith('http') ? url : 'https://' + url);
  const screenshotUrl = data?.details?.urlscan?.screenshotUrl || null;
  const verdictHeadline = data && data.riskScore < 5
    ? 'No threats detected'
    : data?.riskScore < 20 ? 'Low risk indicators'
    : data?.riskScore < 40 ? 'Medium-risk signals found'
    : 'High-risk indicators detected';
  const verdictBody = data && data.riskScore < 5
    ? 'This URL passed all checks. The domain has a clean history and no malicious patterns were found across any scanning engine.'
    : 'Review the detailed checks below. One or more sources flagged this target — proceed with caution.';
  const { checks, aiText } = data ? detailsFromScan(data) : { checks: [], aiText: '' };

  return (
    <>
      <div className="page-kicker">Website Analysis</div>
      <h1 className="page-title">Threat <em>Detection</em></h1>
      <p className="page-sub">Submit any website URL for real-time analysis across malware databases, phishing registries, and domain intelligence.</p>

      {!user && (
        <div className="card" style={{borderColor:'#d4ccba'}}>
          <h3 className="card-title">Sign in to run a scan</h3>
          <p className="card-sub">Free accounts get 1 scan per month. No credit card required.</p>
          <a className="run-btn" href="/api/auth/google" style={{textDecoration:'none', display:'inline-block'}}>Sign in with Google →</a>
        </div>
      )}

      <div className="card">
        <h3 className="card-title"><span className="ic">{I.search}</span> Submit Website URL for Analysis</h3>
        <p className="card-sub">Supports HTTP and HTTPS</p>
        <div className="url-row">
          <span className="url-prefix-box">TARGET WEBSITE</span>
          <label className="url-input-box">
            <span className="pfx">https://</span>
            <input
              type="text"
              value={url}
              onChange={e => setUrl(e.target.value)}
              onKeyDown={e => e.key === 'Enter' && phase !== 'scanning' && url.trim() && startScan()}
              placeholder="example.com"
              spellCheck={false}
            />
          </label>
          <button className="run-btn" onClick={startScan} disabled={user ? (phase === 'scanning' || !url.trim()) : false}>
            {phase === 'scanning' ? 'Scanning…' : 'Run Scan'} →
          </button>
        </div>
        <div className="opts">
          {['95 AV Engines', 'Phishing & Malware Check', 'Page Screenshot', 'Domain Age & Registration'].map((o, i) => (
            <label key={i} className="opt">
              <input type="checkbox" defaultChecked />
              <span className="opt-box">
                <svg viewBox="0 0 12 12" width="9" height="9" fill="none" stroke="currentColor" strokeWidth="2"><path d="M2 6 5 9 10 3" strokeLinecap="round" strokeLinejoin="round"/></svg>
              </span>
              <span>{o}</span>
            </label>
          ))}
        </div>
      </div>

      {phase === 'error' && (
        <div className="card" style={{borderColor:'#b8453d'}}>
          <h3 className="card-title" style={{color:'#b8453d'}}>{error}</h3>
        </div>
      )}

      {phase === 'scanning' && (
        <ScanProgressCard
          steps={['95 AV Engines','Phishing & Malware Check','Page Screenshot','Domain Age & Registration']}
          active resetKey={resetKey}
        />
      )}

      {phase === 'done' && data && (
        <>
          {data.preview && (
            <div className="card" style={{borderColor:'#d4ccba'}}>
              <h3 className="card-title">You've used your free scans this month</h3>
              <p className="card-sub">Showing a preview only. Upgrade to Pro for unlimited scans.</p>
              <button className="run-btn" onClick={onUpgrade}>Upgrade to Pro — $9.99/mo →</button>
            </div>
          )}
          <VerdictCard
            target={target}
            score={data.riskScore}
            scannedAt={data.scannedAt}
            engines={data.details?.virustotal?.totalEngines ? `${data.details.virustotal.totalEngines} engines` : '—'}
            protocol={target.startsWith('https') ? 'HTTPS / TLS' : 'HTTP'}
            headline={verdictHeadline}
            body={verdictBody}
          />
          <PreviewCard target={target} screenshotUrl={screenshotUrl} user={user} onUpgrade={onUpgrade}/>
          <DetailsCard checks={checks}/>
          <AICard text={aiText}/>
          <div className="actions">
            <button className="btn" onClick={newScan}><span className="ic">{I.back}</span> New Scan</button>
            {user?.plan === 'pro'
              ? <a className="btn" href={`/api/report/url?url=${encodeURIComponent(target)}`}><span className="ic">{I.download}</span> Download PDF Report</a>
              : <button className="btn" onClick={onUpgrade}><span className="ic">{I.download}</span> Download PDF (Pro)</button>}
          </div>
        </>
      )}

      {recents.length > 0 && (
        <div className="card">
          <h3 className="card-title" style={{marginBottom: 8}}>Recent Scans</h3>
          <div className="recents">
            {recents.map((s, i) => (
              <div key={i} className="recents-row">
                <span className="recent-target">
                  <span className="recent-dot"/>
                  <span className="recent-url">{s.target}</span>
                </span>
                <span className="recent-score">{s.score}/100</span>
                <span className="recent-time">{formatTime(s.finishedAt)}</span>
              </div>
            ))}
          </div>
        </div>
      )}
      <SignInModal
        open={showSignIn}
        onClose={() => setShowSignIn(false)}
        beforeRedirect={() => {
          try { sessionStorage.setItem('pendingScan', JSON.stringify({ type: 'url', q: url })); } catch {}
        }}
      />
    </>
  );
}

// ====================================================================
// PAGE: FILE SCANNER
// ====================================================================
function PageFile({ user, onUpgrade }) {
  const [file, setFile] = useState(null);
  const [phase, setPhase] = useState('idle');
  const [error, setError] = useState(null);
  const [data, setData] = useState(null);
  const [resetKey, setResetKey] = useState(0);
  const [showSignIn, setShowSignIn] = useState(false);

  async function startScan() {
    if (!user) { setShowSignIn(true); return; }
    if (!file) return;
    setError(null);
    setPhase('scanning');
    setResetKey(k => k + 1);
    try {
      const fd = new FormData();
      fd.append('file', file);
      const res = await fetch('/api/scan/file', { method: 'POST', credentials: 'include', body: fd });
      const j = await res.json();
      if (!j.success) {
        if (res.status === 401) setError('Sign in to run scans.');
        else setError(j.error || 'Scan failed');
        setPhase('error'); return;
      }
      setData({ ...j.data, preview: j.preview });
      setPhase('done');
    } catch { setError('Network error — try again'); setPhase('error'); }
  }
  function newScan() { setPhase('idle'); setData(null); setError(null); setFile(null); }

  return (
    <>
      <div className="page-kicker">File Analysis</div>
      <h1 className="page-title">File <em>Scanner</em></h1>
      <p className="page-sub">Upload any file — executable, PDF, document, script — to scan against 95+ antivirus engines via VirusTotal.</p>

      {!user && (
        <div className="card" style={{borderColor:'#d4ccba'}}>
          <h3 className="card-title">Sign in to scan files</h3>
          <a className="run-btn" href="/api/auth/google" style={{textDecoration:'none', display:'inline-block'}}>Sign in with Google →</a>
        </div>
      )}

      <div className="card">
        <h3 className="card-title"><span className="ic">{I.file}</span> Upload File for Analysis</h3>
        <p className="card-sub">Max 32 MB. Common formats supported.</p>
        <div className="url-row">
          <label className="url-input-box" style={{cursor:'pointer'}}>
            <input type="file" style={{display:'none'}} onChange={e => setFile(e.target.files?.[0] || null)} disabled={!user}/>
            <span style={{fontSize:13, color:'#6b7269'}}>{file ? `${file.name} · ${(file.size/1024).toFixed(1)} KB` : 'Choose a file…'}</span>
          </label>
          <button className="run-btn" onClick={startScan} disabled={user ? (phase === 'scanning' || !file) : false}>
            {phase === 'scanning' ? 'Scanning…' : 'Run Scan'} →
          </button>
        </div>
      </div>

      {phase === 'error' && (
        <div className="card" style={{borderColor:'#b8453d'}}>
          <h3 className="card-title" style={{color:'#b8453d'}}>{error}</h3>
        </div>
      )}

      {phase === 'scanning' && (
        <ScanProgressCard steps={['Hash Calculation','Database Lookup','95 AV Engines']} active resetKey={resetKey}/>
      )}

      {phase === 'done' && data && (
        <>
          {data.preview && (
            <div className="card" style={{borderColor:'#d4ccba'}}>
              <h3 className="card-title">Free scan limit reached this month</h3>
              <button className="run-btn" onClick={onUpgrade}>Upgrade to Pro — $9.99/mo →</button>
            </div>
          )}
          <VerdictCard
            target={data.filename}
            score={data.riskScore}
            scannedAt={data.scannedAt}
            engines={data.details?.virustotal?.totalEngines ? `${data.details.virustotal.totalEngines} engines` : '—'}
            protocol={`${(data.fileSize/1024).toFixed(1)} KB`}
            headline={data.riskScore < 5 ? 'File is clean' : 'Threats detected'}
            body={data.riskScore < 5 ? 'No engines flagged this file as malicious.' : 'One or more antivirus engines flagged this file. Do not execute.'}
          />
          <DetailsCard checks={detailsFromScan(data).checks}/>
          <div className="actions">
            <button className="btn" onClick={newScan}><span className="ic">{I.back}</span> New Scan</button>
          </div>
        </>
      )}
      <SignInModal
        open={showSignIn}
        onClose={() => setShowSignIn(false)}
        beforeRedirect={() => {
          try { sessionStorage.setItem('pendingScan', JSON.stringify({ type: 'file' })); } catch {}
        }}
      />
    </>
  );
}

// ====================================================================
// PAGE: IP / DOMAIN SCANNER
// ====================================================================
function PageIP({ user, authReady, initialQuery, onScanConsumed, onUpgrade }) {
  const [target, setTarget] = useState('');
  const [phase, setPhase] = useState('idle');
  const [error, setError] = useState(null);
  const [data, setData] = useState(null);
  const [resetKey, setResetKey] = useState(0);
  const [showSignIn, setShowSignIn] = useState(false);
  const deepLinkRef = useRef(false);

  useEffect(() => {
    if (!initialQuery) return;
    setTarget(initialQuery);
  }, [initialQuery]);

  useEffect(() => {
    if (deepLinkRef.current) return;
    if (!initialQuery) return;
    if (!authReady) return;
    deepLinkRef.current = true;
    onScanConsumed?.();
    if (user) {
      setTimeout(() => startScan(initialQuery), 150);
    } else {
      setShowSignIn(true);
    }
  }, [authReady, user, initialQuery]);

  async function startScan(overrideTarget) {
    if (!user) { setShowSignIn(true); return; }
    const t = (overrideTarget ?? target).trim();
    if (!t) return;
    setError(null);
    setPhase('scanning');
    setResetKey(k => k + 1);
    try {
      const res = await fetch('/api/scan/ip-domain', {
        method: 'POST',
        credentials: 'include',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ target: t }),
      });
      const j = await res.json();
      if (!j.success) {
        if (res.status === 401) setError('Sign in to run scans.');
        else setError(j.error || 'Scan failed');
        setPhase('error'); return;
      }
      setData({ ...j.data, preview: j.preview });
      setPhase('done');
    } catch { setError('Network error — try again'); setPhase('error'); }
  }
  function newScan() { setPhase('idle'); setData(null); setError(null); }

  return (
    <>
      <div className="page-kicker">Network Intelligence</div>
      <h1 className="page-title">IP &amp; <em>Domain</em></h1>
      <p className="page-sub">Look up reputation, geolocation, and threat history for any IPv4 address or domain name.</p>

      {!user && (
        <div className="card" style={{borderColor:'#d4ccba'}}>
          <h3 className="card-title">Sign in to run lookups</h3>
          <a className="run-btn" href="/api/auth/google" style={{textDecoration:'none', display:'inline-block'}}>Sign in with Google →</a>
        </div>
      )}

      <div className="card">
        <h3 className="card-title"><span className="ic">{I.ip}</span> IP Address or Domain</h3>
        <p className="card-sub">Examples: 8.8.8.8 · github.com</p>
        <div className="url-row">
          <span className="url-prefix-box">TARGET</span>
          <label className="url-input-box">
            <input
              type="text"
              value={target}
              onChange={e => setTarget(e.target.value)}
              onKeyDown={e => e.key === 'Enter' && phase !== 'scanning' && target.trim() && startScan()}
              placeholder="8.8.8.8 or example.com"
              spellCheck={false}
            />
          </label>
          <button className="run-btn" onClick={startScan} disabled={user ? (phase === 'scanning' || !target.trim()) : false}>
            {phase === 'scanning' ? 'Scanning…' : 'Run Scan'} →
          </button>
        </div>
      </div>

      {phase === 'error' && (
        <div className="card" style={{borderColor:'#b8453d'}}>
          <h3 className="card-title" style={{color:'#b8453d'}}>{error}</h3>
        </div>
      )}

      {phase === 'scanning' && (
        <ScanProgressCard steps={['Reputation','Threat History','Geolocation','Registration']} active resetKey={resetKey}/>
      )}

      {phase === 'done' && data && (
        <>
          {data.preview && (
            <div className="card" style={{borderColor:'#d4ccba'}}>
              <h3 className="card-title">Free scan limit reached</h3>
              <button className="run-btn" onClick={onUpgrade}>Upgrade to Pro →</button>
            </div>
          )}
          <VerdictCard
            target={data.target || target}
            score={data.riskScore}
            scannedAt={data.scannedAt}
            engines={data.details?.virustotal?.totalEngines ? `${data.details.virustotal.totalEngines} engines` : '—'}
            protocol={data.type === 'ip' ? 'IPv4' : 'Domain'}
            headline={data.riskScore < 5 ? 'Target appears clean' : 'Suspicious indicators found'}
            body={data.riskScore < 5 ? 'No threat intelligence sources flagged this target.' : 'Review the detailed results — some sources reported risk indicators.'}
          />
          <DetailsCard checks={detailsFromScan(data).checks}/>
          <div className="actions">
            <button className="btn" onClick={newScan}><span className="ic">{I.back}</span> New Scan</button>
          </div>
        </>
      )}
      <SignInModal
        open={showSignIn}
        onClose={() => setShowSignIn(false)}
        beforeRedirect={() => {
          try { sessionStorage.setItem('pendingScan', JSON.stringify({ type: 'ip', q: target })); } catch {}
        }}
      />
    </>
  );
}

// ====================================================================
// PAGE: REPORTS
// ====================================================================
function PageReports({ user }) {
  return (
    <>
      <div className="page-kicker">Reports</div>
      <h1 className="page-title">Export <em>Reports</em></h1>
      <p className="page-sub">Download branded PDF security reports for any URL, file, or IP scan. Pro tier required.</p>

      <div className="card">
        <h3 className="card-title">PDF Security Reports</h3>
        <p className="card-sub">Run a scan first, then click "Download PDF Report" on the result. Reports include full risk breakdown, SSL analysis, domain age, IP geolocation, and methodology — ready to share with clients.</p>
        {user?.plan !== 'pro' && (
          <a className="run-btn" href="/pricing" style={{textDecoration:'none', display:'inline-block'}}>Upgrade to Pro to enable PDF exports →</a>
        )}
      </div>

      <div className="card">
        <h3 className="card-title">What's in a report</h3>
        <ul style={{margin:0, paddingLeft:20, color:'#6b7269', fontSize:13, lineHeight:1.7}}>
          <li>Executive risk summary &amp; verdict</li>
          <li>VirusTotal 95+ engine breakdown</li>
          <li>Google Safe Browsing &amp; phishing-registry checks</li>
          <li>SSL/TLS certificate inspection</li>
          <li>Domain registration &amp; WHOIS history</li>
          <li>IP geolocation &amp; reputation</li>
          <li>Live page screenshot</li>
          <li>Methodology &amp; confidence rating</li>
        </ul>
      </div>
    </>
  );
}

// ====================================================================
// PAGE: ABOUT
// ====================================================================
function PageAbout() {
  const tools = [
    { name: 'URL Scanner', desc: 'Real-time analysis across 95+ antivirus engines, Google Safe Browsing, phishing registries, SSL inspection, redirect chain tracking, and live page screenshot capture.' },
    { name: 'File Scanner', desc: 'Upload any file — executables, PDFs, Office documents, scripts — and scan it instantly against the world’s largest antivirus coalition powered by VirusTotal.' },
    { name: 'IP & Domain Intelligence', desc: 'Look up any IPv4 address or domain for reputation history, threat classifications, geolocation data, and registration age — all sourced from VirusTotal intelligence.' },
  ];
  const why = [
    ['01', 'Free to start', 'Run scans on the web, in your browser via the Chrome extension, or programmatically via the REST API — at no cost. Sign in with Google to unlock the API key.'],
    ['02', 'Pro for power users', 'Upgrade to Pro for $9.99/month for unlimited scans, downloadable PDF security reports, and up to 5 API keys. Cancel anytime — no commitment.'],
    ['03', '95+ AV engines', 'Powered by VirusTotal, Google Safe Browsing, and urlscan.io. We aggregate results from dozens of vendors so you get a complete picture — not just one opinion.'],
    ['04', 'See before you click', 'Every URL scan captures a live screenshot of the target page. Spot fake login screens, brand impersonation, and suspicious layouts without ever visiting the site.'],
  ];
  return (
    <>
      <div className="page-kicker">About Us</div>
      <h1 className="page-title">Built to protect <em>everyone</em> online</h1>
      <p className="page-sub">Scanify is a threat intelligence platform that gives anyone — security professionals and everyday internet users alike — the power to verify links, files, and IP addresses in seconds. Use the web app, our Chrome extension, or the REST API. Free to get started, no credit card required.</p>

      <div className="card">
        <h3 className="card-title">What we scan</h3>
        <div className="about-grid">
          {tools.map((t, i) => (
            <div key={i} className="about-tile">
              <div className="about-tile-name">{t.name}</div>
              <div className="about-tile-desc">{t.desc}</div>
            </div>
          ))}
        </div>
      </div>

      <div className="card">
        <h3 className="card-title">Why Scanify</h3>
        <div className="about-grid">
          {why.map(([n, label, text]) => (
            <div key={n} className="about-tile">
              <div className="about-tile-num">{n}</div>
              <div className="about-tile-name">{label}</div>
              <div className="about-tile-desc">{text}</div>
            </div>
          ))}
        </div>
      </div>

      <div className="card">
        <h3 className="card-title">A message from the creator</h3>
        <div style={{color:'var(--text-dim)', fontSize:13, lineHeight:1.7}}>
          <p style={{marginTop:0}}>I built Scanify because I was tired of security being something only certain people could afford or understand. Every day, regular people get hit by phishing links, fake websites, and malicious files — and most of them don’t have a team or a tool to turn to.</p>
          <p>So I made one. Scanify is simple on purpose. You paste a link, you get an answer. No jargon, no fine print. Just the truth about whether something is safe or not.</p>
          <p>The free tier is here to stay — anyone can scan a URL, file, or IP address without paying a cent. Pro exists for power users and businesses who need unlimited scans, downloadable reports, and higher API limits, and the revenue from Pro is what keeps the free tier running for everyone else.</p>
          <p style={{marginBottom:0}}>Stay safe out there.</p>
        </div>
        <div style={{display:'flex', alignItems:'center', gap:14, marginTop:18, paddingTop:18, borderTop:'1px solid var(--border)'}}>
          <img src="/karim.jpg" alt="Karim Al Anani" style={{width:48, height:48, borderRadius:'50%', objectFit:'cover'}}/>
          <div style={{flex:1}}>
            <div style={{fontWeight:600, fontSize:14}}>Karim Al Anani</div>
            <div style={{fontSize:12, color:'var(--text-dim)'}}>Creator · Scanify</div>
          </div>
          <a className="btn" href="https://www.linkedin.com/in/karim-al-anani-a94b622ba/" target="_blank" rel="noopener noreferrer">LinkedIn</a>
          <a className="btn" href="https://github.com/techkar1m" target="_blank" rel="noopener noreferrer">GitHub</a>
        </div>
      </div>
    </>
  );
}

// ====================================================================
// CancelSubscriptionCard — survey → win-back offer → pause option → cancel
function CancelSubscriptionCard() {
  // 'idle' | 'survey' | 'offer' | 'pause' | 'confirm' | 'loading' | 'done' | 'discounted' | 'paused'
  const [step, setStep] = useState('idle');
  const [error, setError] = useState(null);
  const [reason, setReason] = useState('');
  const [note, setNote] = useState('');
  const [endsAt, setEndsAt] = useState(null);
  const [pausedUntil, setPausedUntil] = useState(null);
  const [pauseMonths, setPauseMonths] = useState(1);
  const [offerAvailable, setOfferAvailable] = useState(true); // disabled if backend says no discount configured

  async function submitSurvey() {
    // Fire and forget — survey is optional, never blocks
    try {
      await fetch('/api/billing/cancel-survey', {
        method: 'POST', credentials: 'include',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ reason, note }),
      });
    } catch {}
    setStep('offer');
  }

  async function acceptDiscount() {
    setStep('loading');
    setError(null);
    try {
      const r = await fetch('/api/billing/discount', { method: 'POST', credentials: 'include' });
      const j = await r.json();
      if (!j.success) {
        if (/not configured/i.test(j.error || '')) {
          setOfferAvailable(false);
          setStep('pause');
          return;
        }
        setError(j.error || 'Could not apply discount.');
        setStep('offer');
        return;
      }
      setStep('discounted');
    } catch {
      setError('Network error. Please try again.');
      setStep('offer');
    }
  }

  async function acceptPause() {
    setStep('loading');
    setError(null);
    try {
      const r = await fetch('/api/billing/pause', {
        method: 'POST', credentials: 'include',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ months: pauseMonths }),
      });
      const j = await r.json();
      if (!j.success) {
        setError(j.error || 'Could not pause.');
        setStep('pause');
        return;
      }
      setPausedUntil(j.pausedUntil);
      setStep('paused');
    } catch {
      setError('Network error. Please try again.');
      setStep('pause');
    }
  }

  async function doCancel() {
    setStep('loading');
    setError(null);
    try {
      const r = await fetch('/api/billing/cancel', { method: 'POST', credentials: 'include' });
      const j = await r.json();
      if (!j.success) {
        setError(j.error || 'Could not cancel.');
        setStep('confirm');
        return;
      }
      setEndsAt(j.endsAt);
      setStep('done');
    } catch {
      setError('Network error. Please try again.');
      setStep('confirm');
    }
  }

  // Final states
  if (step === 'done') {
    return (
      <div className="card">
        <h3 className="card-title">Subscription cancelled</h3>
        <p className="card-sub">
          Your Pro plan will end{endsAt ? ` on ${new Date(endsAt).toLocaleDateString()}` : ' at the close of your current billing period'}.
          You keep full access until then. No further charges will be made.
        </p>
      </div>
    );
  }

  if (step === 'discounted') {
    return (
      <div className="card">
        <h3 className="card-title">Discount applied</h3>
        <p className="card-sub">Your next billing period will be 50% off. You'll keep all Pro features and your subscription continues as normal.</p>
      </div>
    );
  }

  if (step === 'paused') {
    return (
      <div className="card">
        <h3 className="card-title">Subscription paused</h3>
        <p className="card-sub">
          Pro will pause at the end of your current billing period and automatically resume{pausedUntil ? ` on ${new Date(pausedUntil).toLocaleDateString()}` : ' after the pause window'}.
          You won't be billed during the pause.
        </p>
      </div>
    );
  }

  // Step 1 — Survey (skippable)
  if (step === 'survey') {
    const reasons = [
      ['too_expensive', 'Too expensive'],
      ['not_using', 'Not using it enough'],
      ['found_alternative', 'Found an alternative'],
      ['missing_feature', 'Missing a feature I need'],
      ['other', 'Other'],
    ];
    return (
      <div className="card">
        <h3 className="card-title">Quick question — why are you cancelling?</h3>
        <p className="card-sub">Optional. Helps me improve Scanify.</p>
        <div style={{display:'flex', flexDirection:'column', gap:6, margin:'10px 0'}}>
          {reasons.map(([val, label]) => (
            <label key={val} style={{display:'flex', alignItems:'center', gap:8, fontSize:13, cursor:'pointer'}}>
              <input type="radio" name="cancel-reason" value={val} checked={reason === val} onChange={() => setReason(val)} />
              <span>{label}</span>
            </label>
          ))}
        </div>
        {reason === 'other' && (
          <textarea
            value={note}
            onChange={e => setNote(e.target.value)}
            placeholder="Tell us more (optional)…"
            style={{width:'100%', minHeight:60, padding:8, border:'1px solid #d4ccba', borderRadius:6, fontFamily:'inherit', fontSize:12, marginBottom:10}}
            maxLength={500}
          />
        )}
        <div style={{display:'flex', gap:8, flexWrap:'wrap'}}>
          <button className="run-btn" onClick={() => setStep('idle')}>Keep Pro</button>
          <button className="btn" onClick={() => setStep('offer')}>Skip</button>
          <button className="btn" onClick={submitSurvey}>Continue →</button>
        </div>
      </div>
    );
  }

  // Step 2 — Win-back offer (skippable, only shown if backend has discount configured)
  if (step === 'offer') {
    return (
      <div className="card">
        <style>{`
          @keyframes ssStrike { to { width: 100%; } }
          @keyframes ssFadeIn { from { opacity: 0; } to { opacity: 1; } }
          @keyframes ssFadeSlide { from { opacity: 0; transform: translateX(-10px) scale(0.92); } to { opacity: 1; transform: translateX(0) scale(1); } }
          @keyframes ssPulseGreen {
            0% { transform: scale(1); }
            40% { transform: scale(1.08); }
            100% { transform: scale(1); }
          }
        `}</style>
        <h3 className="card-title">Wait — try one month at 50% off?</h3>
        {offerAvailable && (
          <div style={{
            display:'flex', alignItems:'center', justifyContent:'center', gap:14,
            margin:'18px 0 22px', fontSize:26, fontWeight:700,
            fontFamily:'JetBrains Mono, monospace',
          }}>
            <span style={{position:'relative', color:'#6b7269', display:'inline-block'}}>
              $9.99/mo
              <span style={{
                position:'absolute', top:'52%', left:0, height:3,
                background:'#b8453d', borderRadius:2, width:0,
                animation: 'ssStrike 0.9s ease-out 0.4s forwards',
              }}/>
            </span>
            <span style={{
              fontSize:20, color:'#6b7269', opacity:0,
              animation: 'ssFadeIn 0.4s ease-out 1.3s forwards',
            }}>→</span>
            <span style={{
              color:'#3a8a55', display:'inline-block', opacity:0,
              animation: 'ssFadeSlide 0.55s cubic-bezier(0.34, 1.56, 0.64, 1) 1.5s forwards, ssPulseGreen 0.6s ease-out 2.1s',
            }}>$4.99/mo</span>
          </div>
        )}
        <p className="card-sub">{offerAvailable
          ? 'One-time discount on your next billing period. Cancels nothing — your subscription continues as normal at the discounted rate, then back to $9.99/mo.'
          : 'No win-back offer available right now. Continue to pause or cancel options.'}</p>
        {error && <div style={{color:'#b8453d', fontSize:12, marginBottom:10}}>{error}</div>}
        <div style={{display:'flex', gap:8, flexWrap:'wrap'}}>
          {offerAvailable && <button className="run-btn" onClick={acceptDiscount}>Yes, give me 50% off</button>}
          <button className="btn" onClick={() => setStep('pause')}>No thanks</button>
        </div>
      </div>
    );
  }

  // Step 3 — Pause option
  if (step === 'pause') {
    return (
      <div className="card">
        <h3 className="card-title">Pause instead of cancel?</h3>
        <p className="card-sub">Pause your subscription for 1–3 months — no billing during the pause, automatic resume after. Your account, history, and API keys stay exactly as they are.</p>
        <div style={{display:'flex', gap:8, margin:'10px 0', flexWrap:'wrap'}}>
          {[1, 2, 3].map(m => (
            <button
              key={m}
              className={`btn ${pauseMonths === m ? '' : ''}`}
              style={{borderColor: pauseMonths === m ? '#3a8a55' : undefined, color: pauseMonths === m ? '#3a8a55' : undefined}}
              onClick={() => setPauseMonths(m)}
            >{m} month{m > 1 ? 's' : ''}</button>
          ))}
        </div>
        {error && <div style={{color:'#b8453d', fontSize:12, marginBottom:10}}>{error}</div>}
        <div style={{display:'flex', gap:8, flexWrap:'wrap'}}>
          <button className="run-btn" onClick={acceptPause}>Pause for {pauseMonths} month{pauseMonths > 1 ? 's' : ''}</button>
          <button className="btn" onClick={() => setStep('confirm')}>No, cancel anyway</button>
        </div>
      </div>
    );
  }

  // Step 4 — Final confirm
  if (step === 'confirm' || step === 'loading') {
    const loading = step === 'loading';
    return (
      <div className="card">
        <h3 className="card-title">Cancel Pro?</h3>
        <p className="card-sub">You'll lose unlimited scans, PDF security reports, priority speed, and up to 5 API keys. You'll keep Pro access until the end of your current billing period — then drop to the Free plan.</p>
        {error && <div style={{color:'#b8453d', fontSize:12, marginBottom:10}}>{error}</div>}
        <div style={{display:'flex', gap:8, flexWrap:'wrap'}}>
          <button className="run-btn" onClick={() => setStep('idle')} disabled={loading}>Keep Pro</button>
          <button className="btn warn" onClick={doCancel} disabled={loading}>
            {loading ? 'Cancelling…' : 'Yes, cancel'}
          </button>
        </div>
      </div>
    );
  }

  // Step 0 — idle entry point
  return (
    <div className="card">
      <h3 className="card-title">Manage subscription</h3>
      <p className="card-sub">Cancellation takes effect at the end of your current billing period. You keep Pro access until then — no partial refunds, no further charges.</p>
      {error && <div style={{color:'#b8453d', fontSize:12, marginBottom:10}}>{error}</div>}
      <button className="btn" onClick={() => setStep('survey')}>Cancel subscription →</button>
    </div>
  );
}

// PAGE: ACCOUNT
// ====================================================================
function PageAccount({ user, onUpgrade, onSignOut, onSignIn }) {
  if (!user) {
    return (
      <>
        <div className="page-kicker">Account</div>
        <h1 className="page-title">Sign <em>In</em></h1>
        <p className="page-sub">Sign in with Google to track your scans, download PDF reports, and manage API keys.</p>
        <div className="card">
          <a className="run-btn" href="/api/auth/google" style={{textDecoration:'none', display:'inline-block'}}>Sign in with Google →</a>
        </div>
      </>
    );
  }
  return (
    <>
      <div className="page-kicker">Account</div>
      <h1 className="page-title">My <em>Account</em></h1>
      <p className="page-sub">Manage your plan, view usage, and download invoices.</p>

      <div className="card">
        <h3 className="card-title">Profile</h3>
        <div style={{display:'grid', gridTemplateColumns:'140px 1fr', gap:'10px 16px', fontSize:13}}>
          <span style={{color:'#6b7269'}}>Name</span><span>{user.name || '—'}</span>
          <span style={{color:'#6b7269'}}>Email</span><span>{user.email}</span>
          <span style={{color:'#6b7269'}}>Plan</span>
          <span>{user.plan === 'pro' ? <span className="pro-tag">PRO</span> : 'Free'}</span>
        </div>
      </div>

      {user.plan !== 'pro' && (
        <div className="card">
          <h3 className="card-title">Upgrade to Pro</h3>
          <p className="card-sub">Unlimited scans, downloadable PDF reports, priority speed, 5 API keys — $9.99/month, cancel anytime.</p>
          <button className="run-btn" onClick={onUpgrade}>Upgrade to Pro →</button>
        </div>
      )}

      {user.plan === 'pro' && <CancelSubscriptionCard />}

      <div className="card">
        <h3 className="card-title">Sign out</h3>
        <button className="btn" onClick={onSignOut}>Sign out of Scanify</button>
      </div>
    </>
  );
}

// ====================================================================
// PAGE: SETTINGS (API keys)
// ====================================================================
function PageSettings({ user }) {
  const [keys, setKeys] = useState([]);
  const [loading, setLoading] = useState(false);
  const [newName, setNewName] = useState('');
  const [showKey, setShowKey] = useState(null);
  const [error, setError] = useState(null);
  const [revealed, setRevealed] = useState({});

  const reload = useCallback(async () => {
    if (!user) return;
    setLoading(true);
    try {
      const r = await fetch('/api/keys', { credentials:'include' });
      const j = await r.json();
      if (j.success) setKeys(j.keys || []);
    } finally { setLoading(false); }
  }, [user]);

  useEffect(() => { reload(); }, [reload]);

  async function createKey() {
    setError(null);
    const r = await fetch('/api/keys', {
      method: 'POST', credentials:'include',
      headers: {'Content-Type':'application/json'},
      body: JSON.stringify({ name: newName.trim() || 'My Key' }),
    });
    const j = await r.json();
    if (!j.success) { setError(j.error || 'Could not create key'); return; }
    setShowKey({ id: j.keyId, key: j.key });
    setNewName('');
    reload();
  }
  async function revoke(id) {
    if (!confirm('Revoke this key? Apps using it will stop working.')) return;
    const r = await fetch(`/api/keys/${id}`, { method:'DELETE', credentials:'include' });
    const j = await r.json();
    if (j.success) reload();
  }

  if (!user) {
    return (
      <>
        <div className="page-kicker">Settings</div>
        <h1 className="page-title"><em>Settings</em></h1>
        <p className="page-sub">Sign in to manage API keys.</p>
        <div className="card">
          <a className="run-btn" href="/api/auth/google" style={{textDecoration:'none', display:'inline-block'}}>Sign in with Google →</a>
        </div>
      </>
    );
  }

  return (
    <>
      <div className="page-kicker">Settings</div>
      <h1 className="page-title">API <em>Keys</em></h1>
      <p className="page-sub">Use these keys with the public REST API. Free plan: 15 scans/month per user. Pro: unlimited.</p>

      <div className="card">
        <h3 className="card-title">Create new key</h3>
        <div className="url-row">
          <label className="url-input-box">
            <input type="text" value={newName} onChange={e => setNewName(e.target.value)} placeholder="Key name (e.g. Production)" />
          </label>
          <button className="run-btn" onClick={createKey} disabled={keys.length >= (user.plan === 'pro' ? 5 : 1)}>Generate Key →</button>
        </div>
        {error && <div style={{color:'#b8453d', fontSize:12, marginTop:8}}>{error}</div>}
        {showKey && (
          <div style={{background:'#f3efe8', border:'1px solid #d4ccba', borderRadius:8, padding:12, marginTop:12, fontFamily:'JetBrains Mono, monospace', fontSize:11, wordBreak:'break-all'}}>
            <div style={{fontSize:11, color:'#6b7269', marginBottom:6}}>Save this key — it will not be shown again:</div>
            <div>{showKey.key}</div>
            <button className="btn" style={{marginTop:8}} onClick={() => { navigator.clipboard.writeText(showKey.key); }}><span className="ic">{I.copy}</span> Copy</button>
          </div>
        )}
      </div>

      <div className="card">
        <h3 className="card-title">Your keys</h3>
        {loading ? <div style={{fontSize:12, color:'#6b7269'}}>Loading…</div>
          : keys.length === 0 ? <div style={{fontSize:13, color:'#6b7269'}}>No keys yet. Create one above.</div>
          : (
            <div className="recents">
              {keys.map(k => (
                <div key={k.keyId} className="recents-row" style={{gridTemplateColumns:'1fr auto auto auto'}}>
                  <span>
                    <div style={{fontWeight:500, fontSize:13}}>{k.name}</div>
                    <div style={{fontFamily:'JetBrains Mono, monospace', fontSize:10, color:'#6b7269'}}>{k.keyId}</div>
                  </span>
                  <span style={{fontSize:11, color:'#6b7269'}}>{k.tier === 'pro' ? 'PRO' : 'FREE'}</span>
                  <span style={{fontSize:11, color:'#6b7269'}}>{k.monthlyUsage || 0} this mo.</span>
                  <button className="btn warn" onClick={() => revoke(k.keyId)}><span className="ic">{I.trash}</span> Revoke</button>
                </div>
              ))}
            </div>
          )}
      </div>

      <div className="card">
        <h3 className="card-title">API Documentation</h3>
        <p className="card-sub">Full reference, code examples, and rate limits at <a href="/api" style={{color:'#3a8a55'}}>scanify.to/api</a>.</p>
      </div>
    </>
  );
}

// ====================================================================
// MAIN APP
// ====================================================================
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "globeSpeed": 1.0,
  "whirlSpeed": 1.0,
  "showWhirl": true,
  "scansLive": 1284
}/*EDITMODE-END*/;

function App() {
  const [tweaks, setTweak] = useTweaks(TWEAK_DEFAULTS);

  // Parse deep-link query params + sessionStorage (post-OAuth) on first mount.
  // Priority: URL ?q= overrides sessionStorage so a fresh landing-page submit
  // always wins over a stale stored value.
  const initialDeepLink = (() => {
    try {
      const params = new URLSearchParams(location.search);
      const q = params.get('q');
      const type = params.get('type');
      if (q) return { type: type === 'ip' ? 'ip' : 'url', q };
      // No URL param — check sessionStorage (set by SignInModal before OAuth)
      const stored = sessionStorage.getItem('pendingScan');
      if (stored) {
        sessionStorage.removeItem('pendingScan'); // consume once
        const parsed = JSON.parse(stored);
        if (parsed && (parsed.q || parsed.type === 'file')) {
          const t = parsed.type === 'ip' ? 'ip' : parsed.type === 'file' ? 'file' : 'url';
          return { type: t, q: parsed.q || '' };
        }
      }
      return null;
    } catch { return null; }
  })();

  const [page, setPage] = useState(() => {
    const m = location.hash.match(/^#\/(\w+)/);
    if (m) return m[1];
    if (initialDeepLink) return initialDeepLink.type;
    return 'url';
  });
  const [pendingQuery, setPendingQuery] = useState(initialDeepLink?.q || '');
  const [user, setUser] = useState(null);
  const [authReady, setAuthReady] = useState(false);

  // Strip ?q= and ?type= from address bar after consuming them
  useEffect(() => {
    if (!initialDeepLink) return;
    const u = new URL(location.href);
    u.searchParams.delete('q');
    u.searchParams.delete('type');
    window.history.replaceState({}, '', u.pathname + u.hash);
  }, []);

  useEffect(() => {
    fetch('/api/auth/me', { credentials: 'include' })
      .then(r => r.json())
      .then(d => { setUser(d.user || null); setAuthReady(true); })
      .catch(() => setAuthReady(true));
  }, []);

  useEffect(() => {
    location.hash = '#/' + page;
  }, [page]);

  async function signOut() {
    try { await fetch('/api/auth/logout', { method:'POST', credentials:'include' }); } catch {}
    setUser(null);
    setPage('url');
  }
  function startUpgrade() {
    location.href = '/pricing';
  }

  const Page = {
    url:      <PageURL      user={user} authReady={authReady} initialQuery={page === 'url' ? pendingQuery : ''} onScanConsumed={() => setPendingQuery('')} onUpgrade={startUpgrade}/>,
    file:     <PageFile     user={user} onUpgrade={startUpgrade}/>,
    ip:       <PageIP       user={user} authReady={authReady} initialQuery={page === 'ip' ? pendingQuery : ''} onScanConsumed={() => setPendingQuery('')} onUpgrade={startUpgrade}/>,
    reports:  <PageReports  user={user}/>,
    account:  <PageAccount  user={user} onUpgrade={startUpgrade} onSignOut={signOut}/>,
    settings: <PageSettings user={user}/>,
    about:    <PageAbout/>,
  }[page] || <PageURL user={user} onUpgrade={startUpgrade}/>;

  return (
    <>
      <TopBar user={user} onSignOut={signOut} onNav={setPage}/>
      <div className="shell">
        <Sidebar active={page} onNav={setPage}/>
        <div className="content-wrap">
          <BackgroundCanvas/>
          <div className="content">
            <div className="content-main">{Page}</div>

            <aside className="right-rail">
              <div className="rail-globe-card">
                <div className="label">Network online</div>
                <GlobeLoader size={200} speed={tweaks.globeSpeed} paused={false}/>
                <div className="target">{user ? `Hi ${(user.name || user.email).split(' ')[0]}` : 'Sign in to scan'}</div>
              </div>
              {tweaks.showWhirl && (
                <div className="rail-whirl">
                  <ScanWhirl size={240} speed={tweaks.whirlSpeed} paused={false}/>
                  <div className="rail-whirl-cap">
                    <div className="num">{tweaks.scansLive.toLocaleString()}</div>
                    <div className="lbl">Live scans now</div>
                  </div>
                </div>
              )}
            </aside>
          </div>
        </div>
      </div>

      <TweaksPanel title="Tweaks" defaultPosition={{ right: 24, bottom: 24 }}>
        <TweakSection title="Right rail">
          <TweakSlider label="Globe spin speed" min={0.2} max={3} step={0.1}
            value={tweaks.globeSpeed} onChange={v => setTweak('globeSpeed', v)}/>
          <TweakSlider label="Whirl speed" min={0.2} max={3} step={0.1}
            value={tweaks.whirlSpeed} onChange={v => setTweak('whirlSpeed', v)}/>
          <TweakToggle label="Show scan whirl" value={tweaks.showWhirl}
            onChange={v => setTweak('showWhirl', v)}/>
          <TweakNumber label="Live scan counter" value={tweaks.scansLive}
            onChange={v => setTweak('scansLive', v)}/>
        </TweakSection>
      </TweaksPanel>
    </>
  );
}

ReactDOM.createRoot(document.getElementById('root')).render(<App/>);
