import React, { useEffect, useState } from 'react';
import { createRoot } from 'react-dom/client';
import { QRCodeSVG } from 'qrcode.react';
import './styles.css';

const api = async (path, options = {}) => {
  const response = await fetch(`/api${path}`, { credentials: 'include', headers: { Accept: 'application/json', ...(options.body ? { 'Content-Type': 'application/json' } : {}), ...options.headers }, ...options });
  const payload = await response.json();
  if (!response.ok || !payload.success) throw new Error(payload.error?.message || 'Request failed');
  return payload.data;
};

function useSession() {
  const [session, setSession] = useState(null);
  useEffect(() => { api('/auth/session').then(setSession).catch(() => setSession({ user: null })); }, []);
  return [session, setSession];
}

function Login({ onLogin }) {
  const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [error, setError] = useState('');
  const login = async e => { e.preventDefault(); try { const data = await api('/auth/login', { method: 'POST', headers: { 'X-CSRF-Token': (await api('/auth/session')).csrfToken }, body: JSON.stringify({ email, password }) }); onLogin(data); } catch (err) { setError(err.message); } };
  return <main className="card narrow"><h1>ExamFlow Control</h1><p>Sign in to manage classroom sessions.</p><form onSubmit={login}><input placeholder="Email" type="email" value={email} onChange={e => setEmail(e.target.value)} required/><input placeholder="Password" type="password" value={password} onChange={e => setPassword(e.target.value)} required/>{error && <p className="error">{error}</p>}<button>Sign in</button></form></main>;
}

function Admin() {
  const [auth, setAuth] = useSession(); const [sessions, setSessions] = useState([]); const [exams, setExams] = useState([]); const [rooms, setRooms] = useState([]); const [message, setMessage] = useState('');
  const load = async () => { try { const token = auth?.csrfToken || (await api('/auth/session')).csrfToken; const options = { method: 'POST', headers: { 'X-CSRF-Token': token }, body: JSON.stringify({ action: 'select', limit: 500 }) }; const [s, e, r] = await Promise.all([api('/admin/exam-sessions'), api('/admin/data/exams', options), api('/admin/data/organization_units', options)]); setSessions(s); setExams(e.rows.filter(x => ['published', 'active'].includes(x.status))); setRooms(r.rows.filter(x => x.unit_type === 'classroom')); } catch (err) { setMessage(err.message); } };
  useEffect(() => { if (auth?.user) load(); }, [auth?.user]);
  if (!auth) return <main className="card narrow">Loading…</main>;
  if (!auth.user) return <Login onLogin={setAuth}/>;
  const create = async e => { e.preventDefault(); const f = new FormData(e.target); try { await api('/admin/exam-sessions', { method: 'POST', headers: { 'X-CSRF-Token': auth.csrfToken }, body: JSON.stringify(Object.fromEntries(f)) }); e.target.reset(); setMessage('Classroom QR session created.'); load(); } catch (err) { setMessage(err.message); } };
  const control = async (id, action) => { try { await api(`/admin/exam-sessions/${id}/control`, { method: 'POST', headers: { 'X-CSRF-Token': auth.csrfToken }, body: JSON.stringify({ action }) }); load(); } catch (err) { setMessage(err.message); } };
  return <main className="shell"><header><div><strong>ExamFlow</strong><span>Classroom session control</span></div><button className="secondary" onClick={() => { api('/auth/logout', { method: 'POST', headers: { 'X-CSRF-Token': auth.csrfToken } }).finally(() => setAuth({ user: null })); }}>Sign out</button></header><section className="card"><h1>Create classroom session</h1><p>Each session gets a distinct QR, live board, and result reveal.</p><form className="grid" onSubmit={create}><select name="exam_id" required><option value="">Select published exam</option>{exams.map(x => <option key={x.id} value={x.id}>{x.name}</option>)}</select><select name="classroom_id" required><option value="">Select classroom</option>{rooms.map(x => <option key={x.id} value={x.id}>{x.name}</option>)}</select><input name="scheduled_start" type="datetime-local"/><input name="scheduled_end" type="datetime-local"/><button>Create QR session</button></form></section>{message && <p className="notice">{message}</p>}<section><h2>Sessions</h2><div className="session-grid">{sessions.map(s => <SessionCard key={s.id} session={s} control={control}/>)}</div></section></main>;
}

function SessionCard({ session, control }) { const root = window.location.origin; const student = `${root}/session/${session.public_token}`; const board = `${root}/live/${session.public_token}`; return <article className="card session"><div className="status">{session.status.replace('_', ' ')}</div><h3>{session.name}</h3><p>{session.exam_name} · {session.classroom_name}</p><QRCodeSVG value={student} size={132}/><a href={student} target="_blank">Open student QR link</a><a href={board} target="_blank">Open live display</a><div className="actions">{session.status === 'scheduled' && <button onClick={() => control(session.id, 'launch')}>Launch class</button>}{session.status === 'live' && <button onClick={() => control(session.id, 'close')}>Close exam</button>}{session.status === 'closed' && <button onClick={() => control(session.id, 'reveal_results')}>Launch results</button>}{session.status === 'results_revealed' && <button onClick={() => control(session.id, 'hide_results')}>Hide results</button>}</div></article>; }

function Student({ token }) { const [session, setSession] = useState(null); const [registration, setRegistration] = useState(null); const [otp, setOtp] = useState(''); const [notice, setNotice] = useState(''); useEffect(() => { api(`/public/exam-sessions/${token}`).then(setSession).catch(e => setNotice(e.message)); }, [token]); const enroll = async e => { e.preventDefault(); try { const csrfToken = (await api('/auth/session')).csrfToken; const data = await api(`/public/exam-sessions/${token}/registrations`, { method: 'POST', headers: { 'X-CSRF-Token': csrfToken }, body: JSON.stringify(Object.fromEntries(new FormData(e.target))) }); setRegistration(data); setNotice('OTP sent to your mobile.'); } catch (err) { setNotice(err.message); } }; const verify = async e => { e.preventDefault(); try { const csrfToken = (await api('/auth/session')).csrfToken; await api(`/public/registrations/${registration.id}/otp/verify`, { method: 'POST', headers: { 'X-CSRF-Token': csrfToken }, body: JSON.stringify({ otp }) }); const attempt = await api(`/public/registrations/${registration.id}/attempts`, { method: 'POST', headers: { 'X-CSRF-Token': csrfToken }, body: JSON.stringify({}) }); window.location.assign(`/exam/take/${attempt.id}`); } catch (err) { setNotice(err.message); } }; if (!session) return <main className="card narrow"><h1>{notice || 'Loading classroom exam…'}</h1></main>; return <main className="card narrow"><p className="eyebrow">{session.classroom_name}</p><h1>{session.exam_name}</h1><p>{session.duration_minutes} minutes · {session.total_marks} marks</p>{!registration ? <form onSubmit={enroll}><input name="name" placeholder="Full name" required/><input name="mobile" placeholder="10-digit mobile number" inputMode="numeric" required/><input name="email" placeholder="Email (optional)" type="email"/><button>Send OTP</button></form> : <form onSubmit={verify}><input value={otp} onChange={e => setOtp(e.target.value)} placeholder="6-digit OTP" inputMode="numeric" required/><button>Verify and start exam</button></form>}{notice && <p className="notice">{notice}</p>}</main>; }

function LiveBoard({ token }) { const [data, setData] = useState(null); const [error, setError] = useState(''); useEffect(() => { const load = () => api(`/public/exam-sessions/${token}/live-board`).then(setData).catch(e => setError(e.message)); load(); const timer = setInterval(load, 5000); return () => clearInterval(timer); }, [token]); if (!data) return <main className="board"><h1>{error || 'Loading live results…'}</h1></main>; const medals = ['🥇', '🥈', '🥉']; return <main className="board"><p>{data.session.classroom_name}</p><h1>{data.session.exam_name}</h1><div className="metrics">{Object.entries(data.stats).map(([k,v]) => <div key={k}><b>{v}</b><span>{k.replace('_', ' ')}</span></div>)}</div>{data.results_revealed ? <section className="podium"><h2>Classroom champions</h2>{data.top_10.slice(0,3).map((x,i) => <article key={x.name}><strong>{medals[i]} #{i + 1}</strong><h3>{x.name}</h3><p>{x.percentage}% · {x.score} marks</p></article>)}<h2>Top 10</h2><ol>{data.top_10.map(x => <li key={x.name}>{x.name}<b>{x.percentage}%</b></li>)}</ol></section> : <section className="waiting"><h2>Results will be revealed shortly</h2><p>The live display updates every 5 seconds.</p></section>}</main>; }

function TakeExam({ attemptId }) { const [data, setData] = useState(null); const [answers, setAnswers] = useState({}); const [error, setError] = useState(''); const [submitting, setSubmitting] = useState(false); useEffect(() => { api(`/public/attempts/${attemptId}`).then(setData).catch(e => setError(e.message)); }, [attemptId]); const submit = async () => { if (!data || submitting) return; setSubmitting(true); try { const csrfToken = (await api('/auth/session')).csrfToken; const rows = Object.entries(answers).map(([question_id, selected_answer]) => ({ question_id, selected_answer })); await api(`/public/attempts/${attemptId}/answers`, { method: 'PUT', headers: { 'X-CSRF-Token': csrfToken }, body: JSON.stringify({ answers: rows }) }); await api(`/public/attempts/${attemptId}/submit`, { method: 'POST', headers: { 'X-CSRF-Token': csrfToken }, body: JSON.stringify({}) }); window.location.assign(`/exam/result/${attemptId}`); } catch (e) { setError(e.message); setSubmitting(false); } }; if (!data) return <main className="card narrow"><h1>{error || 'Loading exam...'}</h1></main>; return <main className="shell"><header><div><strong>{data.exam.name}</strong><span>{data.exam.duration_minutes} minutes · {data.questions.length} questions</span></div><button onClick={submit} disabled={submitting}>Submit exam</button></header>{data.questions.map((q, index) => <article className="card question" key={q.id}><p className="eyebrow">Question {index + 1} · {q.marks} marks</p><h2>{q.question_text}</h2>{q.question_type === 'numerical' ? <input inputMode="decimal" value={answers[q.id] || ''} onChange={e => setAnswers({ ...answers, [q.id]: e.target.value })}/> : <div className="options">{q.options.map(option => <label key={option.id}><input type={q.question_type === 'multiple' ? 'checkbox' : 'radio'} name={q.id} checked={q.question_type === 'multiple' ? (answers[q.id] || []).includes(option.id) : answers[q.id] === option.id} onChange={() => setAnswers({ ...answers, [q.id]: q.question_type === 'multiple' ? ((answers[q.id] || []).includes(option.id) ? answers[q.id].filter(x => x !== option.id) : [...(answers[q.id] || []), option.id]) : option.id })}/>{option.text}</label>)}</div>}</article>)}{error && <p className="error">{error}</p>}<button className="wide" onClick={submit} disabled={submitting}>{submitting ? 'Submitting...' : 'Finish and submit exam'}</button></main>; }

function ExamResult({ attemptId }) { const [result, setResult] = useState(null); const [error, setError] = useState(''); useEffect(() => { api(`/public/attempts/${attemptId}/result`).then(setResult).catch(e => setError(e.message)); }, [attemptId]); if (!result) return <main className="card narrow"><h1>{error || 'Calculating result...'}</h1></main>; return <main className="card narrow result"><p className="eyebrow">{result.exam.name}</p><h1>{result.attempt.passed ? 'Congratulations!' : 'Exam completed'}</h1><div className="score">{result.attempt.percentage}%</div><p>{result.attempt.score} marks · Rank #{result.attempt.rank || '—'}</p><p>{result.attempt.correct_count} correct · {result.attempt.wrong_count} incorrect · {result.attempt.skipped_count} skipped</p></main>; }

const path = window.location.pathname.split('/').filter(Boolean); const screen = path[0] === 'session' ? <Student token={path[1]}/> : path[0] === 'live' ? <LiveBoard token={path[1]}/> : path[0] === 'exam' && path[1] === 'take' ? <TakeExam attemptId={path[2]}/> : path[0] === 'exam' && path[1] === 'result' ? <ExamResult attemptId={path[2]}/> : <Admin/>;
createRoot(document.getElementById('root')).render(screen);
