// Main app — assembles all views, manages tabs, fetches data from API.
const { useState: useStateA, useEffect: useEffectA, useCallback: useCallbackA, useRef: useRefA } = React;

const BASE_TABS = [
  { id: 'dashboard',    label: 'Dashboard' },
  { id: 'dictionary',   label: 'Dictionary' },
  { id: 'conjugations', label: 'Conjugations' },
  { id: 'grammar',      label: 'Grammar' },
  { id: 'examples',     label: 'Examples' },
  { id: 'teach',        label: 'Teach me',     pill: 'cool' },
];

function App() {
  const [data, setData] = useStateA(null);
  const [error, setError] = useStateA('');
  const [tab, setTab] = useStateA('dashboard');
  const [toasts, setToasts] = useStateA([]);
  const [pulseId, setPulseId] = useStateA(null);
  const [settingsOpen, setSettingsOpen] = useStateA(false);
  const [autoCycleEnabled, setAutoCycleEnabled] = useStateA(true);
  const [autoCycleIntervalH, setAutoCycleIntervalH] = useStateA(24);
  const [syncing, setSyncing] = useStateA(false);
  const [reanalyzing, setReanalyzing] = useStateA(false);
  const [batchSyncEnabled, setBatchSyncEnabled] = useStateA(false);
  const [batchReanalysisEnabled, setBatchReanalysisEnabled] = useStateA(false);
  const [reanalysisFullContext, setReanalysisFullContext] = useStateA(false);
  const [reanalysisRagContext, setReanalysisRagContext] = useStateA(false);
  const [reanalysisAllowConfirmed, setReanalysisAllowConfirmed] = useStateA(false);
  const [reprocessing, setReprocessing] = useStateA(false);
  const [syncModel, setSyncModel] = useStateA('claude-sonnet-4-6');
  const [reanalysisModel, setReanalysisModel] = useStateA('claude-sonnet-4-6');
  const [correctionModel, setCorrectionModel] = useStateA('claude-sonnet-4-6');
  const [, force] = useStateA(0);

  const pushToast = useCallbackA((t) => {
    const id = Math.random().toString(36).slice(2);
    setToasts(prev => [...prev, { ...t, id }]);
    setTimeout(() => setToasts(prev => prev.filter(x => x.id !== id)), 5500);
  }, []);

  const fetchDictionary = useCallbackA(async () => {
    try {
      const res = await fetch('/api/dictionary', { cache: 'no-store' });
      if (!res.ok) {
        throw new Error(res.status === 503 ? 'Dictionary not yet generated — waiting for first bot sync.' : `Fetch failed: ${res.status}`);
      }
      const d = await res.json();
      setData(d);
      setAutoCycleEnabled(d.meta?.auto_cycle_enabled ?? true);
      setAutoCycleIntervalH(d.meta?.auto_cycle_interval_hours ?? 24);
      setBatchSyncEnabled(d.meta?.batch_sync_enabled ?? false);
      setBatchReanalysisEnabled(d.meta?.batch_reanalysis_enabled ?? false);
      setReanalysisFullContext(d.meta?.reanalysis_full_context ?? false);
      setReanalysisRagContext(d.meta?.reanalysis_rag_context ?? false);
      setReanalysisAllowConfirmed(d.meta?.reanalysis_allow_confirmed ?? false);
      setSyncModel(d.meta?.sync_model || 'claude-sonnet-4-6');
      setReanalysisModel(d.meta?.reanalysis_model || 'claude-sonnet-4-6');
      setCorrectionModel(d.meta?.correction_model || 'claude-sonnet-4-6');
      setError('');
    } catch (err) {
      setError(err.message);
    }
  }, []);

  // Initial fetch
  useEffectA(() => { fetchDictionary(); }, [fetchDictionary]);

  // SSE subscription
  useEffectA(() => {
    let es;
    try {
      es = new EventSource('/api/events');
      es.addEventListener('dictionary-updated', () => {
        fetchDictionary();
        pushToast({ kind: 'confirmed', title: 'Wire · updated', body: 'Dictionary refreshed from Discord.' });
      });
      es.onerror = () => {};
    } catch {
      // Fallback: poll every 60s
      const id = setInterval(fetchDictionary, 60_000);
      return () => clearInterval(id);
    }
    return () => es?.close();
  }, [fetchDictionary, pushToast]);

  // Re-render relative times every 30s
  useEffectA(() => {
    const id = setInterval(() => force(x => x + 1), 30_000);
    return () => clearInterval(id);
  }, []);

  function dismissToast(id) {
    setToasts(prev => prev.filter(x => x.id !== id));
  }

  async function patchMeta(body) {
    try {
      const res = await fetch('/api/meta', {
        method: 'PATCH',
        headers: adminHeaders({ 'Content-Type': 'application/json' }),
        body: JSON.stringify(body),
      });
      if (res.status === 401) {
        promptAdminToken();
        return false;
      }
      if (!res.ok) {
        const err = await res.json().catch(() => ({}));
        pushToast({ kind: 'error', title: 'Error', body: err.error || 'Request failed' });
        return false;
      }
      await fetchDictionary();
      return true;
    } catch (err) {
      pushToast({ kind: 'error', title: 'Error', body: err.message });
      return false;
    }
  }

  async function handleAutoCycleEnabledChange(v) {
    setAutoCycleEnabled(v);
    await patchMeta({ auto_cycle_enabled: v });
  }

  async function handleSaveAutoCycleInterval() {
    await patchMeta({ auto_cycle_interval_hours: autoCycleIntervalH });
    pushToast({ kind: 'confirmed', title: 'Saved', body: `Auto-cycle interval set to ${autoCycleIntervalH}h.` });
  }

  async function syncNow() {
    setSyncing(true);
    pushToast({ kind: 'info', title: 'Sync · requested', body: 'Asking the bot to wake up…' });
    const ok = await patchMeta({ force_sync: true });
    setSyncing(false);
    if (ok) {
      pushToast({ kind: 'confirmed', title: 'Sync · flagged', body: 'Bot will sync on next poll (~5s).' });
    }
  }

  async function handleBatchSyncChange(v) { setBatchSyncEnabled(v); await patchMeta({ batch_sync_enabled: v }); }
  async function handleBatchReanalysisChange(v) { setBatchReanalysisEnabled(v); await patchMeta({ batch_reanalysis_enabled: v }); }
  async function handleReanalysisFullContextChange(v) { setReanalysisFullContext(v); await patchMeta({ reanalysis_full_context: v }); }
  async function handleReanalysisRagContextChange(v) { setReanalysisRagContext(v); await patchMeta({ reanalysis_rag_context: v }); }
  async function handleReanalysisAllowConfirmedChange(v) { setReanalysisAllowConfirmed(v); await patchMeta({ reanalysis_allow_confirmed: v }); }
  async function handleSyncModelChange(v) { setSyncModel(v); await patchMeta({ sync_model: v }); }
  async function handleReanalysisModelChange(v) { setReanalysisModel(v); await patchMeta({ reanalysis_model: v }); }
  async function handleCorrectionModelChange(v) { setCorrectionModel(v); await patchMeta({ correction_model: v }); }

  async function fullReprocess() {
    const msgCount = data.meta?.last_sync_message_count || 1800;
    const chunks = Math.ceil(msgCount / 50);
    const dictChars = JSON.stringify(data.entries || []).length + JSON.stringify(data.grammar_rules || []).length + JSON.stringify(data.example_sentences || []).length;
    const dictTokens = Math.round(dictChars / 4);
    const tokensPerChunk = dictTokens + 2000 + 3000;
    const totalInputTokens = tokensPerChunk * chunks;
    const totalOutputTokens = chunks * 2000;
    const model = data.meta?.sync_model || 'claude-sonnet-4-6';
    const isOpus = model.includes('opus');
    const isHaiku = model.includes('haiku');
    const inputRate = isOpus ? 5 : isHaiku ? 1 : 3;
    const outputRate = isOpus ? 25 : isHaiku ? 5 : 15;
    const batchDiscount = batchSyncEnabled ? 0.5 : 1;
    const cost = ((totalInputTokens * inputRate + totalOutputTokens * outputRate) / 1_000_000 * batchDiscount);
    const costStr = '$' + cost.toFixed(2);
    if (!window.confirm(`⚠️ FULL REPROCESS\n\nThis fetches EVERY message from the Discord channel and re-analyzes from scratch.\n\n~${msgCount} messages · ${chunks} chunks · ${model}\nEstimated cost: ${costStr}${batchSyncEnabled ? ' (batch 50% off)' : ''}\n\nContinue?`)) return;
    setReprocessing(true);
    pushToast({ kind: 'info', title: 'Full reprocess', body: 'Fetching all channel messages…' });
    const ok = await patchMeta({ force_full_reprocess: true });
    setReprocessing(false);
    if (ok) pushToast({ kind: 'confirmed', title: 'Full reprocess · flagged', body: 'Bot will reprocess on next poll (~60s).' });
  }

  async function reanalyzeNow() {
    setReanalyzing(true);
    pushToast({ kind: 'info', title: 'Re-analysis · requested', body: 'Asking the bot to re-analyze…' });
    const ok = await patchMeta({ force_reanalysis: true });
    setReanalyzing(false);
    if (ok) {
      pushToast({ kind: 'confirmed', title: 'Re-analysis · flagged', body: 'Bot will re-analyze on next poll (~60s).' });
    }
  }

  // Loading state
  if (!data) {
    return (
      <div className="app-shell">
        <div className="loading-shell">
          <div className="loading-spinner"></div>
          <div className="serif" style={{ fontSize: 24, color: 'var(--paper)', fontStyle: 'italic' }}>Loading the lexicon…</div>
          {error && <div style={{ color: 'var(--accent-warm)', marginTop: 8, maxWidth: '40ch', textAlign: 'center' }}>{error}</div>}
        </div>
      </div>
    );
  }

  return (
    <div className="app-shell">
      <Masthead
        meta={data.meta}
        lastSyncedRel={relTime(data.meta?.last_updated || 0)}
        onOpenSettings={() => setSettingsOpen(true)}
      />
      <Tabs tabs={isAdminMode() ? [...BASE_TABS, { id: 'botcentral', label: 'Bot Central', pill: 'admin', noNum: true }] : BASE_TABS} active={tab} onChange={setTab} />

      <LiveTicker items={data.ticker} />

      <main className="page">
        {tab === 'dashboard' && (
          <DashboardView
            data={data}
            onGoTeach={() => setTab('teach')}
            onGoDictionary={() => setTab('dictionary')}
          />
        )}
        {tab === 'dictionary' && (
          <DictionaryView
            data={data}
            pulseId={pulseId}
            onPulseClear={() => setPulseId(null)}
            pushToast={pushToast}
            onRefresh={fetchDictionary}
          />
        )}
        {tab === 'conjugations' && <ConjugationsView data={data} pushToast={pushToast} onRefresh={fetchDictionary} />}
        {tab === 'grammar' && <GrammarView data={data} pushToast={pushToast} onRefresh={fetchDictionary} />}
        {tab === 'examples' && <ExamplesView data={data} pushToast={pushToast} onRefresh={fetchDictionary} />}
        {tab === 'teach' && <TeachMeView data={data} />}
        {tab === 'botcentral' && <BotCentralView
          autoCycleEnabled={autoCycleEnabled} setAutoCycleEnabled={handleAutoCycleEnabledChange}
          autoCycleIntervalH={autoCycleIntervalH} setAutoCycleIntervalH={setAutoCycleIntervalH}
          onSaveAutoCycleInterval={handleSaveAutoCycleInterval}
          lastAutoCycle={data.meta?.last_auto_cycle_at || 0}
          onSyncNow={syncNow} syncing={syncing}
          onReanalyzeNow={reanalyzeNow} reanalyzing={reanalyzing}
          batchSyncEnabled={batchSyncEnabled} setBatchSyncEnabled={handleBatchSyncChange}
          batchReanalysisEnabled={batchReanalysisEnabled} setBatchReanalysisEnabled={handleBatchReanalysisChange}
          reanalysisFullContext={reanalysisFullContext} setReanalysisFullContext={handleReanalysisFullContextChange}
          reanalysisRagContext={reanalysisRagContext} setReanalysisRagContext={handleReanalysisRagContextChange}
          reanalysisAllowConfirmed={reanalysisAllowConfirmed} setReanalysisAllowConfirmed={handleReanalysisAllowConfirmedChange}
          onFullReprocess={fullReprocess} reprocessing={reprocessing}
          syncModel={syncModel} setSyncModel={handleSyncModelChange}
          reanalysisModel={reanalysisModel} setReanalysisModel={handleReanalysisModelChange}
          correctionModel={correctionModel} setCorrectionModel={handleCorrectionModelChange}
        />}
      </main>

      <footer className="page-foot">
        <div className="divider-orn"><span className="glyph">❦</span></div>
        <p className="serif" style={{ fontStyle: 'italic', color: 'var(--fg-mute)', textAlign: 'center', fontSize: 14 }}></p>
        <p className="hand" style={{ textAlign: 'center', fontSize: 18, color: 'var(--accent-warm)', marginTop: 4 }}>
          do not feed the seamen.
        </p>
      </footer>

      <ToastStack toasts={toasts} onDismiss={dismissToast} />
      <SettingsDrawer
        open={settingsOpen}
        onClose={() => setSettingsOpen(false)}
      />
    </div>
  );
}

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