import React, { useEffect, useRef, useState } from 'react';
import { createRoot } from 'react-dom/client';
import { parseQuote, raceResponses, transition, unsafeAmount, type State } from './domain';
import { mountCodeViewer } from '../web/code-viewer.mjs';
const samples = {
  valid: { id: 'Q-001', customerId: 'C-001', totalCents: 12500 },
  price: { id: 'Q-001', customerId: 'C-001', totalCents: 'free today' },
  identity: { id: 'C-001', customerId: 'Q-001', totalCents: 12500 }
};
function App() {
  const [sample, setSample] = useState<keyof typeof samples>('price');
  const [guarded, setGuarded] = useState(true);
  const [state, setState] = useState<State>({ status: 'idle' });
  const [trace, setTrace] = useState<string[]>([]);
  const [busy, setBusy] = useState(false);
  const active = useRef<AbortController | null>(null);
  useEffect(() => () => active.current?.abort(), []);
  function reset() { active.current?.abort(); active.current = null; setBusy(false); setState({ status: 'idle' }); setTrace([]); }
  function run() {
    reset(); const payload = samples[sample];
    if (!guarded) {
      const amount = unsafeAmount(payload);
      setTrace(['The payload arrived from the sample API.', 'An assertion told the compiler to trust it.', `The application displayed $${amount.toFixed(2)}.`, Number.isFinite(amount) ? 'An ID or state can still be wrong even when the price looks right.' : 'NaN means the price calculation failed after the compiler had finished.']);
      setState({ status: 'error', message: Number.isFinite(amount) ? 'Unchecked data passed through.' : 'Broken display: $NaN' });
      return;
    }
    const parsed = parseQuote(payload);
    setState(parsed.ok ? transition({ status: 'idle' }, { type: 'received', quote: parsed.value }) : { status: 'error', message: parsed.error });
    setTrace(['The payload arrived as unknown.', parsed.ok ? 'The validator checked IDs and whole-number cents.' : 'The validator stopped invalid data before it reached the price display.', parsed.ok ? 'A ready state now contains a verified quote.' : parsed.error]);
  }
  async function race() {
    reset(); const controller = new AbortController(); active.current = controller; setBusy(true);
    setTrace(['Older request starts first; newer request starts second.', 'The newer response will arrive first.']);
    try {
      const result = await raceResponses(guarded, 200, controller.signal);
      if (active.current !== controller) return;
      setTrace(result.trace.concat('Final selection: ' + result.selected));
    } catch (e) { if (!controller.signal.aborted) setTrace(['The request could not finish. Try again.']); }
    finally { if (active.current === controller) { setBusy(false); active.current = null; } }
  }
  return <>
    <div className="lab-control-row">
      <label>Sample response<select aria-label="Sample response" value={sample} onChange={e => { reset(); setSample(e.target.value as keyof typeof samples); }}><option value="price">A price sent as text</option><option value="identity">Mixed-up identifiers</option><option value="valid">A valid quote</option></select></label>
      <label>Implementation<select aria-label="Implementation" value={guarded ? 'fixed' : 'broken'} onChange={e => { reset(); setGuarded(e.target.value === 'fixed'); }}><option value="fixed">With safeguards</option><option value="broken">Without safeguards</option></select></label>
    </div>
    <div className="lab-actions"><button onClick={run} disabled={busy}>Run this example</button><button className="secondary" onClick={() => void race()} disabled={busy}>Try competing responses</button><button className="secondary" onClick={reset}>Reset environment</button><button className="secondary" data-view-source="domain.ts">View the code</button></div>
    <div className="experiment-grid"><section className="result-card" aria-labelledby="result-heading"><h3 id="result-heading">What happened</h3><div role="status" aria-live="polite" aria-atomic="true"><p className="result-state">{busy ? 'Two requests are running…' : state.status === 'idle' ? 'Ready to try' : state.status}</p>{state.status === 'error' && <p>{state.message}</p>}{(state.status === 'ready' || state.status === 'approved') && <p className="result-number">${(state.quote.totalCents / 100).toFixed(2)}</p>}</div><button className="secondary" onClick={() => setState(transition(state, { type: 'approve', approver: 'Sample reviewer' }))} disabled={busy}>Try approving the quote</button></section>
    <section className="result-card"><h3>Follow the decision</h3><ol className="trace" aria-label="Execution trace">{trace.map((line, i) => <li key={i}>{line}</li>)}</ol>{trace.length === 0 && <p>Choose a sample and run it. Every action uses invented, in-memory data.</p>}</section></div>
    <details><summary>Inspect the sample API response</summary><pre><code>{JSON.stringify(samples[sample], null, 2)}</code></pre></details>
  </>;
}
createRoot(document.getElementById('experiment')!).render(<App />);
mountCodeViewer();
