// app.jsx — top-level state, routing, and API wiring.

// Convert a DB upload row to the shape the History component expects.
function adaptUpload(row) {
  const statusMap = {
    in_library: { label: 'In library', tone: 'ok' },
    queued:     { label: 'In queue',   tone: 'neutral' },
    scanning:   { label: 'Scanning…', tone: 'neutral' },
    infected:   { label: 'Rejected',   tone: 'danger' },
    scan_error: { label: 'Error',      tone: 'danger' },
  };
  const { label, tone } = statusMap[row.status] || { label: row.status, tone: 'neutral' };
  const gb    = row.size_bytes / (1024 ** 3);
  const size  = gb >= 1 ? gb.toFixed(1) + ' GB'
                        : Math.round(row.size_bytes / (1024 ** 2)) + ' MB';
  const ext   = (row.filename || '').split('.').pop().toLowerCase();
  const title = (row.filename || '').replace(/\.[^.]+$/, '').replace(/[._-]+/g, ' ');
  const dt    = new Date(row.queued_at.replace(' ', 'T'));
  return {
    id: row.id, title, runtime: '—', size, format: ext,
    when: formatWhen(dt), status: label, statusTone: tone,
    uploader: row.uploader, uploaderUsername: row.uploader,
  };
}

function formatWhen(dt) {
  const now       = new Date();
  const today     = new Date(now.getFullYear(), now.getMonth(), now.getDate());
  const yesterday = new Date(+today - 864e5);
  const day       = new Date(dt.getFullYear(), dt.getMonth(), dt.getDate());
  const hh = String(dt.getHours()).padStart(2, '0');
  const mm = String(dt.getMinutes()).padStart(2, '0');
  const t  = `${hh}:${mm}`;
  if (+day >= +today)     return `Today · ${t}`;
  if (+day >= +yesterday) return `Yesterday · ${t}`;
  const months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
  return `${dt.getDate()} ${months[dt.getMonth()]} · ${t}`;
}

function App() {
  // Auth + view
  const [user, setUser] = React.useState(null);
  const [view, setView] = React.useState('upload');

  // Upload state: idle | dragover | uploading | success | rejected | error
  const [upState,   setUpState]   = React.useState('idle');
  const [file,      setFile]      = React.useState(null);
  const [progress,  setProgress]  = React.useState(0);
  const [scanPhase, setScanPhase] = React.useState('idle');
  const [readyAt,   setReadyAt]   = React.useState('');

  // History — fetched from /api/uploads
  const [history, setHistory] = React.useState([]);

  // Follow OS dark/light: dark → Arthouse, light → Archive (default).
  const [osDark, setOsDark] = React.useState(
    () => typeof window !== 'undefined'
      && !!window.matchMedia?.('(prefers-color-scheme: dark)').matches,
  );
  React.useEffect(() => {
    const mq = window.matchMedia?.('(prefers-color-scheme: dark)');
    if (!mq) return;
    const onChange = (e) => setOsDark(e.matches);
    mq.addEventListener?.('change', onChange);
    return () => mq.removeEventListener?.('change', onChange);
  }, []);

  const effectivePalette = osDark ? 'arthouse' : 'archive';

  const rootRef = React.useRef(null);
  React.useEffect(() => {
    if (rootRef.current) applyPalette(rootRef.current, effectivePalette);
  }, [effectivePalette]);

  // On mount: restore session and load history.
  React.useEffect(() => {
    fetch('/api/me')
      .then(r => r.ok ? r.json() : null)
      .then(u => { if (u) setUser(u); });
    fetchHistory();
  }, []);

  const fetchHistory = () => {
    fetch('/api/uploads')
      .then(r => r.ok ? r.json() : [])
      .then(rows => setHistory(rows.map(adaptUpload)));
  };

  // Document-level drag guards — stop browser navigating away on a miss.
  React.useEffect(() => {
    const stop = (e) => { e.preventDefault(); e.stopPropagation(); };
    document.addEventListener('dragover', stop);
    document.addEventListener('drop',    stop);
    return () => {
      document.removeEventListener('dragover', stop);
      document.removeEventListener('drop',    stop);
    };
  }, []);

  const startUpload = (f) => {
    setFile(f);
    setUpState('uploading');
    setProgress(0);
    setScanPhase('idle');

    const xhr = new XMLHttpRequest();
    xhr.upload.onprogress = (e) => {
      if (e.lengthComputable) setProgress((e.loaded / e.total) * 100);
    };
    xhr.onload = () => {
      if (xhr.status === 202) {
        setProgress(100);
        setScanPhase('scanning');
        pollUntilDone(JSON.parse(xhr.responseText).id);
      } else if (xhr.status === 403) {
        try {
          if (JSON.parse(xhr.responseText).error === 'quota_exceeded') {
            setUpState('quota');
            return;
          }
        } catch {}
        setUpState('error');
      } else {
        setUpState('error');
      }
    };
    xhr.onerror = () => setUpState('error');
    xhr.open('POST', '/api/upload');
    const fd = new FormData();
    fd.append('file', f);
    xhr.send(fd);
  };

  const pollUntilDone = async (id) => {
    while (true) {
      await new Promise(r => setTimeout(r, 3000));
      let res;
      try { res = await fetch(`/api/uploads/${id}`).then(r => r.json()); }
      catch { continue; }

      if (res.status === 'in_library') {
        setScanPhase('finalizing');
        setTimeout(() => {
          const ready = new Date(Date.now() + 60_000);
          const hh   = ready.getHours();
          const mm   = String(ready.getMinutes()).padStart(2, '0');
          const ampm = hh >= 12 ? 'pm' : 'am';
          const hh12 = ((hh + 11) % 12) + 1;
          setReadyAt(`${hh12}:${mm} ${ampm}`);
          fetchHistory();
          setUpState('success');
        }, 800);
        return;
      } else if (res.status === 'infected') {
        setUpState('rejected');
        return;
      } else if (res.status === 'scan_error') {
        setUpState('error');
        return;
      }
      // still queued or scanning — keep polling
    }
  };

  const onPickFile = (f) => startUpload(f);

  const onDragOver = (e) => {
    e.preventDefault();
    if (upState === 'idle') setUpState('dragover');
  };
  const onDragLeave = (e) => {
    e.preventDefault();
    if (upState === 'dragover') {
      const r = e.currentTarget.getBoundingClientRect();
      if (e.clientX <= r.left || e.clientX >= r.right ||
          e.clientY <= r.top  || e.clientY >= r.bottom) {
        setUpState('idle');
      }
    }
  };
  const onDrop = (e) => {
    e.preventDefault();
    if (upState !== 'idle' && upState !== 'dragover') return;
    const f = e.dataTransfer?.files?.[0];
    if (f) startUpload(f);
    else setUpState('idle');
  };

  const onReset = () => {
    setUpState('idle');
    setFile(null);
    setProgress(0);
    setScanPhase('idle');
    setReadyAt('');
  };

  const onSignOut = async () => {
    await fetch('/api/logout', { method: 'POST' }).catch(() => {});
    setUser(null);
    setView('upload');
    onReset();
  };

  const paletteName = window.PALETTES?.[effectivePalette]?.name || effectivePalette;

  return (
    <div className="ef-root" ref={rootRef}>
      {!user ? (
        <Login onAuthed={setUser} />
      ) : (
        <>
          <TopBar user={user} isAdmin={user.isAdmin} view={view} onView={setView}
                  onSignOut={onSignOut} />

          <main className="ef-main" data-view={view}>
            {view === 'upload' && (
              <div className="ef-stack">
                <DropZone state={upState} file={file} progress={progress}
                          scanPhase={scanPhase} readyAt={readyAt}
                          onPickFile={onPickFile}
                          onDragOver={onDragOver}
                          onDragLeave={onDragLeave}
                          onDrop={onDrop}
                          onReset={onReset} />
              </div>
            )}
            {view === 'history' && (
              <History items={history} user={user} />
            )}
            {view === 'admin' && user.isAdmin && (
              <Admin user={user} />
            )}
          </main>

          <Footer palette={paletteName} />
        </>
      )}
    </div>
  );
}

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