// app.jsx — Root component. Owns view routing, auth state, project history.
// In production, auth flows through /api/login + httpOnly session cookie.
// In this in-browser demo, we keep a hardcoded shortlist matching the three
// users (yaroslav / lina / svetlana) with the demo password "ape-demo".

const DEMO_USERS = ['yaroslav', 'lina', 'svetlana'];
const DEMO_PASSWORD = 'ape-demo';

// Global 401 interceptor — if any /api/ call returns 401, force re-login.
// Container rebuilds wipe the in-memory session store, so stale cookies
// must be handled gracefully instead of showing "Not authenticated" errors.
if (window.__APE_USE_SERVER) {
  const _origFetch = window.fetch;
  window.fetch = async function (url, opts) {
    const res = await _origFetch.call(this, url, opts);
    if (res.status === 401 && typeof url === 'string' && url.startsWith('/api/') && !url.includes('/api/login')) {
      localStorage.removeItem('ape.user');
      window.location.reload();
    }
    return res;
  };
}

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "theme": "editorial",
  "density": "regular"
}/*EDITMODE-END*/;

// ── localStorage helpers (artifact-only persistence) ──────────────────────
const STORAGE = {
  user: 'ape.user',
  history: 'ape.history',
};

function loadHistory() {
  try { return JSON.parse(localStorage.getItem(STORAGE.history) || '[]'); }
  catch (e) { return []; }
}
function saveHistory(h) {
  try { localStorage.setItem(STORAGE.history, JSON.stringify(h)); } catch (e) {}
}

function App() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  React.useEffect(() => applyTheme(t.theme), [t.theme]);
  React.useEffect(() => applyDensity(t.density), [t.density]);

  // Auth: { username } or null
  const [user, setUser] = React.useState(() => {
    try { return JSON.parse(localStorage.getItem(STORAGE.user) || 'null'); }
    catch (e) { return null; }
  });

  // In server mode, validate the session on mount — the httpOnly cookie
  // may have expired even though localStorage still has a user object.
  React.useEffect(() => {
    if (!window.__APE_USE_SERVER || !user) return;
    fetch('/api/me', { credentials: 'include' }).then((r) => {
      if (!r.ok) {
        localStorage.removeItem(STORAGE.user);
        setUser(null);
      }
    }).catch(() => {});
  }, []); // eslint-disable-line react-hooks/exhaustive-deps

  // View: 'dashboard' | 'upload' | 'processing' | 'results'
  const [view, setView] = React.useState('dashboard');
  const [project, setProject] = React.useState(null);
  const [data, setData] = React.useState(null);
  const [history, setHistory] = React.useState(loadHistory);
  const [toast, setToast] = React.useState('');
  const [rerunning, setRerunning] = React.useState(false);

  React.useEffect(() => { saveHistory(history); }, [history]);

  const flashToast = (msg) => {
    setToast(msg);
    setTimeout(() => setToast(''), 2400);
  };

  const handleLogin = async (username, password) => {
    if (window.__APE_USE_SERVER) {
      const res = await fetch('/api/login', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        credentials: 'include',
        body: JSON.stringify({ username, password }),
      });
      const body = await res.json();
      if (!res.ok) throw new Error(body.error || 'Invalid credentials');
      const u = { username: body.username };
      localStorage.setItem(STORAGE.user, JSON.stringify(u));
      setUser(u);
      setView('dashboard');
    } else {
      // In-browser demo: accept the three users + demo password.
      await new Promise((r) => setTimeout(r, 300));
      if (!DEMO_USERS.includes(username) || password !== DEMO_PASSWORD) {
        throw new Error('Invalid credentials');
      }
      const u = { username };
      localStorage.setItem(STORAGE.user, JSON.stringify(u));
      setUser(u);
      setView('dashboard');
    }
  };

  const handleLogout = async () => {
    if (window.__APE_USE_SERVER) {
      try { await fetch('/api/logout', { method: 'POST' }); } catch (e) {}
    }
    localStorage.removeItem(STORAGE.user);
    setUser(null);
    setView('dashboard');
    setProject(null); setData(null);
  };

  const handleNewProject = () => {
    setProject(null); setData(null);
    setView('upload');
  };

  const handleRun = (proj) => {
    setProject({ ...proj, id: 'p_' + Date.now() });
    setData(null);
    setView('processing');
  };

  const handleProcessingDone = (assessment) => {
    setData(assessment);
    const entry = {
      id: project.id,
      name: project.name,
      createdAt: Date.now(),
      project,
      data: assessment,
    };
    setHistory((H) => [entry, ...H].slice(0, 200));
    setView('results');
    flashToast('Assessment complete');
  };

  const handleProcessingError = (e) => {
    flashToast('Error: ' + (e.message || e));
    setView('upload');
  };

  const handleOpenProject = (id) => {
    const p = history.find((p) => p.id === id);
    if (!p) return;
    setProject(p.project);
    setData(p.data);
    setView('results');
  };

  const handleDeleteProject = (id) => {
    if (!confirm('Delete this assessment from history? This does not undo the Claude call.')) return;
    setHistory((H) => H.filter((p) => p.id !== id));
  };

  const handleDownloadXlsx = async () => {
    try {
      await window.exportXlsx(data, { projectName: project.name });
      flashToast('XLSX downloaded');
    } catch (e) { alert('XLSX export failed: ' + e.message); }
  };
  const handleDownloadDocx = async () => {
    try {
      await window.exportDocx(data, { projectName: project.name });
      flashToast('DOCX downloaded');
    } catch (e) { alert('DOCX export failed: ' + e.message); }
  };

  const handleRerunSelected = async (segmentIds) => {
    if (!segmentIds.length) return;
    setRerunning(true);
    try {
      const segs = (data.segments || []).filter((s) => segmentIds.includes(s.id));
      const sourceText = segs.map((s) => s.source).join('\n\n');
      const targetText = segs.map((s) => s.target).join('\n\n');
      const fresh = await window.runMqmAssessment({
        sourceText, targetText,
        glossary: project.glossary,
        onLog: () => {},
      });
      // Merge: replace errors / corrections for those segment IDs only.
      const newSegMap = {};
      fresh.segments.forEach((s, i) => { newSegMap[segmentIds[i % segmentIds.length]] = s; });
      const mergedSegs = (data.segments || []).map((s) => {
        const ns = newSegMap[s.id];
        if (!ns) return s;
        return { ...s, corrected: ns.corrected, lev_distance: ns.lev_distance, lev_norm: ns.lev_norm };
      });
      // Drop old errors for those segments, add fresh
      const keepErrors = (data.errors || []).filter((e) => !segmentIds.includes(e.segment_id));
      const newErrors = (fresh.errors || []).map((e, i) => ({
        ...e, segment_id: segmentIds[i % segmentIds.length],
      }));
      const merged = { ...data, segments: mergedSegs, errors: [...keepErrors, ...newErrors] };
      // Recompute metrics
      const counts = { critical: 0, major: 0, minor: 0 };
      let penalty = 0;
      for (const e of merged.errors) {
        const sev = (e.severity || 'minor').toLowerCase();
        if (counts[sev] === undefined) continue;
        counts[sev]++; penalty += window.SEVERITY_WEIGHTS[sev];
      }
      const dimCounts = {};
      window.MQM_DIMENSIONS.forEach((d) => { dimCounts[d.label] = 0; });
      for (const e of merged.errors) { if (dimCounts[e.category] !== undefined) dimCounts[e.category]++; }
      const wc = merged.metrics.source_word_count;
      merged.metrics = {
        counts, penalty, quality_score: Math.max(0, (1 - penalty / wc) * 100),
        source_word_count: wc, dimension_counts: dimCounts,
      };
      setData(merged);
      // Update history entry too
      setHistory((H) => H.map((p) => p.id === project.id ? { ...p, data: merged } : p));
      flashToast(`Re-ran ${segmentIds.length} segment${segmentIds.length === 1 ? '' : 's'}`);
    } catch (e) {
      alert('Re-run failed: ' + e.message);
    } finally {
      setRerunning(false);
    }
  };

  // ── Render ────────────────────────────────────────────────────────────
  if (!user) {
    return (
      <>
        <LoginView onLogin={handleLogin} />
        <TweaksPanel>
          <TweakSection label="Variation" />
          <TweakRadio label="Theme" value={t.theme}
                     options={[{ value: 'editorial', label: 'Editorial' }, { value: 'lab', label: 'Lab' }, { value: 'studio', label: 'Studio' }]}
                     onChange={(v) => setTweak('theme', v)} />
          <TweakRadio label="Density" value={t.density}
                     options={['compact', 'regular', 'roomy']}
                     onChange={(v) => setTweak('density', v)} />
        </TweaksPanel>
      </>
    );
  }

  return (
    <div className="app">
      <Topbar user={user.username} view={view} onNav={(v) => setView(v)} onLogout={handleLogout} />
      <div className="main">
        {view === 'dashboard' && (
          <DashboardView
            history={history}
            onNewProject={handleNewProject}
            onOpenProject={handleOpenProject}
            onDeleteProject={handleDeleteProject}
          />
        )}
        {view === 'upload' && <UploadView onRun={handleRun} />}
        {view === 'processing' && project && (
          <ProcessingView project={project} onDone={handleProcessingDone} onError={handleProcessingError} />
        )}
        {view === 'results' && project && data && (
          <ResultsView
            project={project} data={data}
            onDownloadXlsx={handleDownloadXlsx}
            onDownloadDocx={handleDownloadDocx}
            onRerunSelected={handleRerunSelected}
            rerunning={rerunning}
          />
        )}
      </div>

      {toast && <div className="toast">{toast}</div>}

      <TweaksPanel>
        <TweakSection label="Variation" />
        <TweakRadio label="Theme" value={t.theme}
                   options={[{ value: 'editorial', label: 'Editorial' }, { value: 'lab', label: 'Lab' }, { value: 'studio', label: 'Studio' }]}
                   onChange={(v) => setTweak('theme', v)} />
        <TweakRadio label="Density" value={t.density}
                   options={['compact', 'regular', 'roomy']}
                   onChange={(v) => setTweak('density', v)} />
        <TweakSection label="Demo helpers" />
        <TweakButton label="Load demo project (RU\u2192EN paper)" onClick={() => {
          // Quick sample so reviewers can land on the results screen without uploading.
          const sample = window.__APE_DEMO_PROJECT;
          if (!sample) { flashToast('Demo project unavailable'); return; }
          setProject(sample.project);
          setData(sample.data);
          setView('results');
        }} />
        <TweakButton label="Clear history" secondary onClick={() => {
          if (confirm('Clear all saved projects?')) setHistory([]);
        }} />
      </TweaksPanel>
    </div>
  );
}

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