const { useState, useEffect, useRef, useCallback } = React;

const api = {
    async get(url) {
        const r = await fetch(url);
        if (!r.ok) throw new Error((await r.json().catch(() => ({}))).error || r.statusText);
        return r.json();
    },
    async post(url, data) {
        const r = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) });
        if (!r.ok) throw new Error((await r.json().catch(() => ({}))).error || r.statusText);
        return r.json();
    },
    async put(url, data) {
        const r = await fetch(url, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) });
        if (!r.ok) throw new Error((await r.json().catch(() => ({}))).error || r.statusText);
        return r.json();
    },
};

function wsUrl() { return `${location.protocol === 'https:' ? 'wss:' : 'ws:'}//${location.host}`; }

function shuffleArray(arr) {
    const a = [...arr];
    for (let i = a.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1));[a[i], a[j]] = [a[j], a[i]]; }
    return a;
}

function fieldVal(field, fallback) {
    if (!field) return fallback || '';
    if (typeof field === 'string') return field;
    if (typeof field === 'object' && field.value !== undefined) return field.value;
    return String(field);
}

function normalizeText(t) {
    return (t || '').toLowerCase()
        .replace(/[äÄ]/g, 'ae').replace(/[öÖ]/g, 'oe').replace(/[üÜ]/g, 'ue').replace(/ß/g, 'ss')
        .replace(/[^a-z0-9]/g, '');
}

function Logo({ size }) {
    const cls = size === 'lg' ? 'w-24 h-24' : size === 'md' ? 'w-16 h-16' : 'w-10 h-10';
    return <img src="/img/logo.png" alt="Quizalarm" className={`${cls} mx-auto`} />;
}

/* ================================================================== */
/*  Shared Components                                                  */
/* ================================================================== */

function ImageModal({ src, onClose }) {
    if (!src) return null;
    return (
        <div className="fixed inset-0 bg-black bg-opacity-90 flex items-center justify-center z-50 p-4" onClick={onClose}>
            <img src={src} className="max-w-full max-h-full object-contain rounded-lg" alt="" />
        </div>
    );
}

function Timer({ timeLeft, total }) {
    if (!total) return null;
    const pct = Math.max(0, (timeLeft / total) * 100);
    const color = pct > 50 ? 'bg-green-500' : pct > 25 ? 'bg-yellow-500' : 'bg-red-500';
    return (
        <div className="w-full mb-4">
            <div className="w-full bg-gray-700 rounded-full h-4 overflow-hidden">
                <div className={`${color} h-full rounded-full transition-all duration-200`} style={{ width: `${pct}%` }} />
            </div>
            <div className="text-center text-sm mt-1 text-gray-300">{Math.ceil(timeLeft)}s</div>
        </div>
    );
}

function Leaderboard({ entries, highlight, compact }) {
    if (!entries || !entries.length) return null;
    const show = compact ? entries.slice(0, 5) : entries;
    return (
        <div className="bg-gray-800 rounded-xl p-4 mt-4 w-full">
            <h3 className="text-lg font-bold mb-2 text-center">🏆 Leaderboard</h3>
            {show.map((e, i) => (
                <div key={e.name} className={`flex justify-between py-2 px-3 rounded-lg mb-1 ${e.name === highlight ? 'bg-red-700' : i === 0 ? 'bg-amber-700' : 'bg-gray-700'}`}>
                    <span className="font-medium">{i === 0 ? '🥇' : i === 1 ? '🥈' : i === 2 ? '🥉' : `${e.rank}.`} {e.name}</span>
                    <span className="font-bold">{e.score}</span>
                </div>
            ))}
        </div>
    );
}

const ANSWER_COLORS = { A: 'answer-a', B: 'answer-b', C: 'answer-c', D: 'answer-d' };

function AnswerGrid({ question, selected, onToggle, disabled, feedback }) {
    const typ = (question.typ || 'MC').toLowerCase();
    const isWF = typ === 'wahr/falsch';

    if (typ === 'freitext') {
        if (feedback) {
            return (
                <div className="space-y-3">
                    <div className={`p-4 rounded-xl text-center text-xl font-bold text-white ${feedback.isCorrect ? 'bg-green-600 ring-4 ring-green-400' : 'bg-red-700 ring-4 ring-red-400'}`}>
                        {feedback.isCorrect ? '✓ ' : '✗ '}{selected[0] || '(keine Antwort)'}
                    </div>
                    {!feedback.isCorrect && feedback.correctText && (
                        <div className="p-4 rounded-xl text-center text-xl font-bold bg-green-600 text-white ring-4 ring-green-400">✓ {feedback.correctText}</div>
                    )}
                </div>
            );
        }
        return (
            <input type="text" className="w-full p-4 rounded-xl bg-gray-700 text-white text-xl text-center border-2 border-gray-600 focus:border-red-500 outline-none"
                placeholder="Deine Antwort..." value={selected[0] || ''} onChange={(e) => onToggle([e.target.value])} disabled={disabled} autoFocus />
        );
    }

    return (
        <div className={`grid gap-3 ${question.antworten.length <= 2 ? 'grid-cols-1 sm:grid-cols-2' : 'grid-cols-1 sm:grid-cols-2'}`}>
            {question.antworten.map((a) => {
                const isSelected = selected.includes(a.key);
                let cls, icon = '';

                if (feedback) {
                    const isCorrectAnswer = feedback.correctKeys.includes(a.key);
                    if (isCorrectAnswer) { cls = 'bg-green-600 text-white ring-4 ring-green-400'; icon = '✓ '; }
                    else if (isSelected) { cls = 'bg-red-700 text-white ring-4 ring-red-400'; icon = '✗ '; }
                    else { cls = 'bg-gray-700 text-gray-500 opacity-40'; }
                    cls += ' font-bold py-4 px-6 rounded-xl text-lg text-center cursor-default';
                } else {
                    cls = `${ANSWER_COLORS[a.key]} ${isSelected ? 'selected' : ''} ${disabled ? 'disabled' : ''} text-white font-bold py-4 px-6 rounded-xl text-lg cursor-pointer transition-all text-center`;
                }

                return (
                    <button key={a.key} className={cls} disabled={disabled || !!feedback}
                        onClick={() => {
                            if (disabled || feedback) return;
                            if (isWF) onToggle([a.key]);
                            else onToggle(isSelected ? selected.filter((s) => s !== a.key) : [...selected, a.key]);
                        }}>
                        {icon}{a.text}
                    </button>
                );
            })}
        </div>
    );
}

function BackButton({ onClick, label }) {
    return (
        <button onClick={onClick} className="text-gray-400 hover:text-white text-sm flex items-center gap-1">
            <span>←</span> <span>{label || 'Beenden'}</span>
        </button>
    );
}

/* ================================================================== */
/*  Home Page                                                          */
/* ================================================================== */

function HomePage({ navigate }) {
    return (
        <div className="min-h-screen flex flex-col items-center justify-center p-6">
            <Logo size="lg" />
            <h1 className="text-5xl font-extrabold mb-2 mt-4 fire-gradient">Quizalarm</h1>
            <p className="text-gray-400 mb-10 text-lg">Lernen. Quizzen. Wissen.</p>
            <div className="w-full max-w-sm space-y-4">
                <button onClick={() => navigate('wettkampf-select')} className="w-full py-4 bg-emerald-600 hover:bg-emerald-700 rounded-xl text-xl font-bold transition-all shadow-lg shadow-emerald-900/30">⚔️ Wettkampf</button>
                <button onClick={() => navigate('solo-select')} className="w-full py-4 bg-amber-600 hover:bg-amber-700 rounded-xl text-xl font-bold transition-all shadow-lg shadow-amber-900/30">📚 Solo-Lernen</button>
                <button onClick={() => navigate('join')} className="w-full py-4 bg-red-600 hover:bg-red-700 rounded-xl text-xl font-bold transition-all shadow-lg shadow-red-900/30">🎮 Live-Quiz beitreten</button>
                <button onClick={() => navigate('my-results')} className="w-full py-4 bg-gray-700 hover:bg-gray-600 rounded-xl text-xl font-bold transition-all">📊 Meine Ergebnisse</button>
            </div>
            <a href="/admin" className="mt-8 text-gray-500 hover:text-gray-300 text-sm">Admin-Bereich →</a>
        </div>
    );
}

/* ================================================================== */
/*  Join Page                                                          */
/* ================================================================== */

function JoinPage({ navigate }) {
    const [code, setCode] = useState('');
    const [name, setName] = useState('');
    const [error, setError] = useState('');
    const handleJoin = () => {
        if (!code.trim() || !name.trim()) return setError('Code und Name erforderlich');
        navigate('live-play', { code: code.trim().toUpperCase(), name: name.trim() });
    };
    return (
        <div className="min-h-screen flex flex-col items-center justify-center p-6">
            <h2 className="text-3xl font-bold mb-6">🎮 Live-Quiz beitreten</h2>
            <div className="w-full max-w-sm space-y-4">
                <input className="w-full p-4 rounded-xl bg-gray-800 text-white text-center text-2xl tracking-widest border-2 border-gray-700 focus:border-red-500 outline-none uppercase" placeholder="CODE" value={code} onChange={(e) => setCode(e.target.value)} maxLength={20} />
                <input className="w-full p-4 rounded-xl bg-gray-800 text-white text-center text-xl border-2 border-gray-700 focus:border-red-500 outline-none" placeholder="Dein Name" value={name} onChange={(e) => setName(e.target.value)} maxLength={20} onKeyDown={(e) => e.key === 'Enter' && handleJoin()} />
                {error && <p className="text-red-400 text-center">{error}</p>}
                <button onClick={handleJoin} className="w-full py-4 bg-red-600 hover:bg-red-700 rounded-xl text-xl font-bold shadow-lg shadow-red-900/30">Beitreten</button>
                <button onClick={() => navigate('home')} className="w-full py-3 text-gray-400 hover:text-white">← Zurück</button>
            </div>
        </div>
    );
}

/* ================================================================== */
/*  Live Play Page                                                     */
/* ================================================================== */

function LivePlayPage({ params, navigate }) {
    const [phase, setPhase] = useState('connecting');
    const [session, setSession] = useState(null);
    const [question, setQuestion] = useState(null);
    const [qIndex, setQIndex] = useState(0);
    const [qTotal, setQTotal] = useState(0);
    const [timeLimit, setTimeLimit] = useState(0);
    const [timeLeft, setTimeLeft] = useState(0);
    const [selected, setSelected] = useState([]);
    const [answered, setAnswered] = useState(false);
    const [result, setResult] = useState(null);
    const [leaderboardData, setLeaderboardData] = useState([]);
    const [error, setError] = useState('');
    const [enlargedImg, setEnlargedImg] = useState(null);
    const wsRef = useRef(null);
    const timerRef = useRef(null);
    const phaseRef = useRef(phase);
    phaseRef.current = phase;

    useEffect(() => {
        const ws = new WebSocket(wsUrl());
        wsRef.current = ws;
        ws.onopen = () => ws.send(JSON.stringify({ type: 'join', code: params.code, name: params.name }));
        ws.onmessage = (e) => {
            const msg = JSON.parse(e.data);
            switch (msg.type) {
                case 'joined': setSession(msg.session); setPhase('waiting'); break;
                case 'player-joined': case 'player-left':
                    setSession((s) => s ? { ...s, players: msg.players, playerCount: msg.playerCount } : s); break;
                case 'game-start': setQTotal(msg.totalQuestions); setPhase('playing'); break;
                case 'question':
                    setQuestion(msg.question); setQIndex(msg.index); setQTotal(msg.total);
                    setTimeLimit(msg.timeLimit); setTimeLeft(msg.timeLimit);
                    setSelected([]); setAnswered(false); setResult(null); setPhase('question'); break;
                case 'answer-received': setAnswered(true); break;
                case 'time-up': setAnswered(true); clearInterval(timerRef.current); break;
                case 'question-result':
                    setResult(msg); setLeaderboardData(msg.leaderboard); setPhase('result'); clearInterval(timerRef.current); break;
                case 'game-end':
                    setLeaderboardData(msg.leaderboard); setPhase('final'); clearInterval(timerRef.current); break;
                case 'error': setError(msg.message); setPhase('error'); break;
            }
        };
        ws.onclose = () => { if (phaseRef.current !== 'final' && phaseRef.current !== 'error') setPhase('disconnected'); };
        return () => { clearInterval(timerRef.current); ws.close(); };
    }, []);

    useEffect(() => {
        clearInterval(timerRef.current);
        if (phase === 'question' && timeLimit > 0 && !answered) {
            const start = Date.now();
            timerRef.current = setInterval(() => {
                const left = Math.max(0, timeLimit - (Date.now() - start) / 1000);
                setTimeLeft(left);
                if (left <= 0) clearInterval(timerRef.current);
            }, 100);
        }
    }, [phase, timeLimit, answered]);

    const submitAnswer = () => {
        if (answered || !wsRef.current) return;
        wsRef.current.send(JSON.stringify({ type: 'answer', answers: selected }));
        setAnswered(true);
    };

    const leave = () => { if (wsRef.current) wsRef.current.close(); navigate('home'); };

    if (phase === 'error') return (
        <div className="min-h-screen flex flex-col items-center justify-center p-6">
            <p className="text-red-400 text-xl mb-4">❌ {error}</p>
            <button onClick={() => navigate('join')} className="py-3 px-6 bg-gray-700 rounded-xl">Zurück</button>
        </div>
    );

    if (phase === 'disconnected') return (
        <div className="min-h-screen flex flex-col items-center justify-center p-6">
            <p className="text-yellow-400 text-xl mb-4">Verbindung verloren</p>
            <p className="text-gray-400 mb-4">Du kannst mit demselben Namen wieder beitreten.</p>
            <button onClick={() => navigate('join')} className="py-3 px-6 bg-red-600 rounded-xl font-bold">Erneut beitreten</button>
        </div>
    );

    if (phase === 'connecting') return <div className="min-h-screen flex items-center justify-center"><p className="text-xl animate-pulse text-amber-400">Verbinde...</p></div>;

    if (phase === 'waiting') return (
        <div className="min-h-screen flex flex-col items-center justify-center p-6">
            <div className="self-start mb-4"><BackButton onClick={leave} label="Verlassen" /></div>
            <h2 className="text-3xl font-bold mb-2">⏳ Warteraum</h2>
            <p className="text-gray-400 mb-4">{session?.setName}</p>
            <p className="text-lg mb-4">{session?.playerCount || 0} Teilnehmer</p>
            <div className="flex flex-wrap gap-2 justify-center max-w-md">
                {(session?.players || []).map((n) => (
                    <span key={n} className={`px-3 py-1 rounded-full text-sm ${n === params.name ? 'bg-red-600' : 'bg-gray-700'}`}>{n}</span>
                ))}
            </div>
            <p className="mt-6 text-gray-500 animate-pulse">Warte auf den Start...</p>
        </div>
    );

    if (phase === 'question') return (
        <div className="min-h-screen flex flex-col p-4 max-w-2xl mx-auto">
            <div className="flex justify-between items-center mb-2">
                <BackButton onClick={leave} label="Verlassen" />
                <span className="text-sm text-gray-400">Frage {qIndex + 1} / {qTotal}</span>
            </div>
            {timeLimit > 0 && <Timer timeLeft={timeLeft} total={timeLimit} />}
            <h2 className="text-xl font-bold mb-4 text-center">{question?.frage}</h2>
            {question?.bild && <img src={question.bild} className="max-h-48 mx-auto rounded-lg mb-4 cursor-pointer" alt="" onClick={() => setEnlargedImg(question.bild)} />}
            <AnswerGrid question={question} selected={selected} onToggle={setSelected} disabled={answered} />
            {!answered && <button onClick={submitAnswer} disabled={selected.length === 0} className="mt-4 py-3 bg-red-600 hover:bg-red-700 disabled:opacity-40 rounded-xl text-lg font-bold w-full">Antwort senden</button>}
            {answered && <p className="mt-4 text-center text-amber-400 animate-pulse">✓ Antwort gesendet — warte auf Ergebnis...</p>}
            <ImageModal src={enlargedImg} onClose={() => setEnlargedImg(null)} />
        </div>
    );

    if (phase === 'result') {
        const liveFeedback = result ? {
            correctKeys: result.correctAnswer || [],
            correctText: question?.typ?.toLowerCase() === 'freitext' ? (result.correctAnswer || [])[0] : null,
            isCorrect: result.yourResult?.correct,
        } : null;
        return (
            <div className="min-h-screen flex flex-col p-4 max-w-2xl mx-auto">
                <div className="text-center text-sm text-gray-400 mb-2">Frage {qIndex + 1} / {qTotal}</div>
                <h2 className="text-xl font-bold mb-4 text-center">{question?.frage}</h2>
                {question?.bild && <img src={question.bild} className="max-h-36 mx-auto rounded-lg mb-4" alt="" />}
                {question && <AnswerGrid question={question} selected={selected} disabled feedback={liveFeedback} />}
                <div className={`text-3xl text-center mt-4 font-bold ${result?.yourResult?.correct ? 'text-green-400' : 'text-red-400'}`}>
                    {result?.yourResult?.correct ? '✅ Richtig!' : '❌ Falsch'}
                </div>
                <p className="text-xl text-center font-bold mt-1">+{result?.yourResult?.points || 0} Punkte</p>
                <p className="text-gray-400 text-center mb-2">Gesamt: {result?.yourResult?.totalScore || 0}</p>
                <Leaderboard entries={leaderboardData} highlight={params.name} compact />
                <p className="mt-4 text-gray-500 animate-pulse text-center">Warte auf nächste Frage...</p>
            </div>
        );
    }

    if (phase === 'final') return (
        <div className="min-h-screen flex flex-col items-center justify-center p-4">
            <h2 className="text-3xl font-bold mb-6">🏁 Quiz beendet!</h2>
            <Leaderboard entries={leaderboardData} highlight={params.name} />
            <button onClick={() => navigate('home')} className="mt-6 py-3 px-6 bg-gray-700 hover:bg-gray-600 rounded-xl">Zur Startseite</button>
        </div>
    );

    return null;
}

/* ================================================================== */
/*  Solo Select Page                                                   */
/* ================================================================== */

function SoloSelectPage({ navigate }) {
    const [sets, setSets] = useState([]);
    const [name, setName] = useState('');
    const [doShuffle, setDoShuffle] = useState(true);
    const [error, setError] = useState('');
    const [checking, setChecking] = useState(false);
    const [progressDialog, setProgressDialog] = useState(null);

    useEffect(() => { api.get('/api/sets').then(setSets).catch((e) => setError(e.message)); }, []);

    const startSet = async (setId, setName2, color) => {
        if (!name.trim()) return setError('Bitte Name eingeben');
        setChecking(true);
        setError('');
        try {
            const progress = await api.get(`/api/progress/${encodeURIComponent(name.trim())}/${setId}`);
            if (progress.hasProgress) {
                setProgressDialog({ setId, setName: setName2, color, ...progress });
            } else {
                navigate('solo-quiz', { setId, setName: setName2, name: name.trim(), shuffle: doShuffle, color });
            }
        } catch (e) {
            navigate('solo-quiz', { setId, setName: setName2, name: name.trim(), shuffle: doShuffle, color });
        }
        setChecking(false);
    };

    const continueSession = () => {
        navigate('solo-quiz', {
            setId: progressDialog.setId, setName: progressDialog.setName,
            name: name.trim(), shuffle: doShuffle, color: progressDialog.color,
            continueSessionId: progressDialog.sessionId,
            priorAnswers: progressDialog.answers,
        });
        setProgressDialog(null);
    };

    const startFresh = () => {
        navigate('solo-quiz', {
            setId: progressDialog.setId, setName: progressDialog.setName,
            name: name.trim(), shuffle: doShuffle, color: progressDialog.color,
        });
        setProgressDialog(null);
    };

    return (
        <div className="min-h-screen flex flex-col items-center p-6 pt-12">
            <div className="w-full max-w-md"><BackButton onClick={() => navigate('home')} label="Zurück" /></div>
            <h2 className="text-3xl font-bold mb-6 mt-2">📚 Solo-Lernen</h2>
            <div className="w-full max-w-md space-y-4">
                <input className="w-full p-4 rounded-xl bg-gray-800 text-white text-center text-xl border-2 border-gray-700 focus:border-amber-500 outline-none" placeholder="Dein Name" value={name} onChange={(e) => setName(e.target.value)} />
                <label className="flex items-center gap-3 text-gray-300 cursor-pointer">
                    <input type="checkbox" checked={doShuffle} onChange={() => setDoShuffle(!doShuffle)} className="w-5 h-5 accent-amber-500" />
                    Fragen mischen
                </label>
                {error && <p className="text-red-400">{error}</p>}

                {progressDialog && (
                    <div className="bg-gray-800 rounded-xl p-5 border-2 border-amber-500 space-y-3">
                        <h3 className="font-bold text-lg text-amber-400">📌 Gespeicherter Fortschritt</h3>
                        <p>{progressDialog.setName}: <strong>{progressDialog.totalAnswered}</strong> / {progressDialog.totalQuestions} Fragen beantwortet</p>
                        <p className="text-sm text-gray-400">{progressDialog.totalCorrect} davon richtig</p>
                        <div className="flex gap-3">
                            <button onClick={continueSession} className="flex-1 py-3 bg-amber-600 hover:bg-amber-700 rounded-xl font-bold">Weitermachen</button>
                            <button onClick={startFresh} className="flex-1 py-3 bg-gray-600 hover:bg-gray-500 rounded-xl font-bold">Neue Runde</button>
                        </div>
                    </div>
                )}

                {!progressDialog && (
                    <>
                        <h3 className="text-lg font-semibold mt-4">Fragenset wählen:</h3>
                        {sets.length === 0 && <p className="text-gray-500">Keine Fragensets konfiguriert.</p>}
                        {sets.map((s) => {
                            const bg = s.color || '#d97706';
                            return (
                                <button key={s.id} onClick={() => startSet(s.id, s.name, s.color)} disabled={checking}
                                    style={{ backgroundColor: bg }}
                                    className="w-full py-4 rounded-xl text-lg font-bold transition-all shadow-lg text-white hover:opacity-90 disabled:opacity-50">
                                    {checking ? 'Prüfe...' : s.name}
                                </button>
                            );
                        })}
                    </>
                )}
            </div>
        </div>
    );
}

/* ================================================================== */
/*  Solo Quiz Page                                                     */
/* ================================================================== */

function SoloQuizPage({ params, navigate }) {
    const [allQuestions, setAllQuestions] = useState([]);
    const [questions, setQuestions] = useState([]);
    const [index, setIndex] = useState(0);
    const [selected, setSelected] = useState([]);
    const [showFeedback, setShowFeedback] = useState(false);
    const [isCorrect, setIsCorrect] = useState(false);
    const [correctKeys, setCorrectKeys] = useState([]);
    const [correctText, setCorrectText] = useState('');
    const [results, setResults] = useState([]);
    const [priorResults] = useState(params.priorAnswers || []);
    const [error, setError] = useState('');
    const [enlargedImg, setEnlargedImg] = useState(null);
    const [shuffledAnswers, setShuffledAnswers] = useState([]);
    const [saving, setSaving] = useState(false);
    const sessionId = useRef(params.continueSessionId || 'SOLO-' + Date.now().toString(36).toUpperCase());
    const priorIds = useRef(new Set((params.priorAnswers || []).map((a) => a.Frage_ID)));

    useEffect(() => {
        api.get(`/api/sets/${params.setId}/questions`).then((qs) => {
            setAllQuestions(qs);
            let remaining = qs.filter((q) => !priorIds.current.has(q.id));
            if (params.shuffle && remaining.length > 0) remaining = shuffleArray(remaining);
            setQuestions(remaining);
        }).catch((e) => setError(e.message));
    }, []);

    useEffect(() => {
        if (questions.length === 0 || index >= questions.length) return;
        const q = questions[index];
        const answers = [];
        ['A', 'B', 'C', 'D'].forEach((k) => { if (q[`Antwort ${k}`]) answers.push({ key: k, text: q[`Antwort ${k}`] }); });
        setShuffledAnswers(shuffleArray(answers));
    }, [index, questions]);

    if (error) return <div className="min-h-screen flex items-center justify-center text-red-400 text-xl p-6 text-center">{error}</div>;
    if (!questions.length && allQuestions.length > 0) {
        const allResults = priorResults.map((a) => ({ Frage_ID: a.Frage_ID, Richtig: a.Richtig, Punkte: a.Punkte }));
        return (
            <div className="min-h-screen flex flex-col items-center justify-center p-6">
                <h2 className="text-2xl font-bold mb-4">✅ Alle Fragen bereits beantwortet!</h2>
                <div className="space-y-3 w-full max-w-sm">
                    <button onClick={() => navigate('solo-result', { results: allResults, setName: params.setName, name: params.name, questions: allQuestions, color: params.color })}
                        className="w-full py-3 bg-amber-600 rounded-xl font-bold">Ergebnis anzeigen</button>
                    <button onClick={() => navigate('solo-quiz', { ...params, continueSessionId: null, priorAnswers: null })}
                        className="w-full py-3 bg-gray-600 rounded-xl font-bold">Neue Runde starten</button>
                    <button onClick={() => navigate('home')} className="w-full py-3 text-gray-400 hover:text-white">← Zur Startseite</button>
                </div>
            </div>
        );
    }
    if (!questions.length) return <div className="min-h-screen flex items-center justify-center animate-pulse text-xl text-amber-400">Lade Fragen...</div>;

    const q = questions[index];
    const typ = fieldVal(q['Typ'], 'MC').toLowerCase();
    const sanitized = {
        frage: q['Frage'] || '(Kein Fragetext)',
        typ: fieldVal(q['Typ'], 'MC'),
        bild: q['Bild'] && Array.isArray(q['Bild']) && q['Bild'].length ? q['Bild'][0].url : null,
        antworten: shuffledAnswers,
    };

    const feedback = showFeedback ? { correctKeys, correctText: typ === 'freitext' ? correctText : null, isCorrect } : null;
    const totalProgress = priorIds.current.size + index + 1;
    const totalAll = allQuestions.length;

    const checkAnswer = () => {
        const rawCorrect = (q['Richtige Antwort'] || '').trim();
        let correctKeysResolved, ok;

        if (typ === 'freitext') {
            correctKeysResolved = [];
            setCorrectText(rawCorrect);
            ok = normalizeText(selected[0]) === normalizeText(rawCorrect);
        } else {
            // Resolve correct answer: could be keys ("A","B") or text ("Wahr","Falsch")
            const parts = rawCorrect.split(',').map((s) => s.trim()).filter(Boolean);
            const validKeys = new Set(['A', 'B', 'C', 'D']);
            const allAreKeys = parts.length > 0 && parts.every((p) => validKeys.has(p.toUpperCase()));

            if (allAreKeys) {
                correctKeysResolved = parts.map((p) => p.toUpperCase());
            } else {
                // Match text against answer options
                correctKeysResolved = [];
                for (const part of parts) {
                    const partLower = part.toLowerCase().trim();
                    // Search in all answers (including shuffled ones which keep their original keys)
                    const allAnswers = [];
                    ['A', 'B', 'C', 'D'].forEach((k) => {
                        if (q[`Antwort ${k}`]) allAnswers.push({ key: k, text: q[`Antwort ${k}`] });
                    });
                    for (const ans of allAnswers) {
                        if (ans.text.toLowerCase().trim() === partLower) {
                            correctKeysResolved.push(ans.key);
                            break;
                        }
                    }
                }
                // Fallback
                if (correctKeysResolved.length === 0) {
                    correctKeysResolved = parts.map((p) => p.toUpperCase());
                }
            }

            const correctSet = new Set(correctKeysResolved);
            const selectedSet = new Set(selected.map((s) => s.toUpperCase()));
            ok = correctKeysResolved.length === selectedSet.size && correctKeysResolved.every((c) => selectedSet.has(c));
        }

        setCorrectKeys(correctKeysResolved);
        setIsCorrect(ok);
        setShowFeedback(true);

        const answerText = selected.map((key) => {
            const ans = shuffledAnswers.find((a) => a.key === key);
            return ans ? ans.text : key;
        }).join(', ');

        const answerData = {
            Nutzername: params.name, Frage_ID: q.id, Fragenset: params.setName,
            Antwort: answerText, Richtig: ok, Punkte: ok ? 1000 : 0,
            Session_ID: sessionId.current, Modus: 'Solo', Zeitstempel: new Date().toISOString(),
        };

        setSaving(true);
        api.post('/api/results/single', answerData)
            .catch((e) => console.error('Save error:', e))
            .finally(() => setSaving(false));

        setResults((prev) => [...prev, { Frage_ID: q.id, Richtig: ok, Punkte: ok ? 1000 : 0 }]);
    };

    const nextQuestion = () => {
        if (index + 1 >= questions.length) {
            const allResults = [
                ...priorResults.map((a) => ({ Frage_ID: a.Frage_ID, Richtig: a.Richtig, Punkte: a.Punkte })),
                ...results,
            ];
            navigate('solo-result', { results: allResults, setName: params.setName, name: params.name, questions: allQuestions, color: params.color });
            return;
        }
        setIndex(index + 1);
        setSelected([]);
        setShowFeedback(false);
        setCorrectKeys([]);
        setCorrectText('');
    };

    return (
        <div className="min-h-screen flex flex-col p-4 max-w-2xl mx-auto">
            <div className="flex justify-between items-center mb-2">
                <BackButton onClick={() => navigate('home')} />
                <span className="text-sm text-gray-400">Frage {totalProgress} / {totalAll}</span>
            </div>
            <div className="w-full bg-gray-700 rounded-full h-2 mb-4">
                <div className="h-full rounded-full transition-all" style={{ width: `${(totalProgress / totalAll) * 100}%`, backgroundColor: params.color || '#f59e0b' }} />
            </div>
            <h2 className="text-xl font-bold mb-4 text-center">{sanitized.frage}</h2>
            {sanitized.bild && <img src={sanitized.bild} className="max-h-48 mx-auto rounded-lg mb-4 cursor-pointer" alt="" onClick={() => setEnlargedImg(sanitized.bild)} />}
            <AnswerGrid question={sanitized} selected={selected} onToggle={setSelected} disabled={showFeedback} feedback={feedback} />
            {!showFeedback && (
                <button onClick={checkAnswer} disabled={selected.length === 0}
                    style={{ backgroundColor: params.color || '#d97706' }}
                    className="mt-4 py-3 hover:opacity-90 disabled:opacity-40 rounded-xl text-lg font-bold w-full shadow-lg text-white">Prüfen</button>
            )}
            {showFeedback && (
                <div className="mt-4 text-center space-y-3">
                    <div className={`text-2xl font-bold ${isCorrect ? 'text-green-400' : 'text-red-400'}`}>
                        {isCorrect ? '✅ Richtig!' : '❌ Falsch'}
                    </div>
                    {saving && <p className="text-gray-500 text-sm animate-pulse">Speichere...</p>}
                    <button onClick={nextQuestion} disabled={saving} className="w-full py-3 bg-red-600 hover:bg-red-700 disabled:opacity-50 rounded-xl text-lg font-bold shadow-lg shadow-red-900/30">
                        {index + 1 >= questions.length ? 'Ergebnis anzeigen' : 'Nächste Frage →'}
                    </button>
                </div>
            )}
            <ImageModal src={enlargedImg} onClose={() => setEnlargedImg(null)} />
        </div>
    );
}

/* ================================================================== */
/*  Solo Result Page                                                   */
/* ================================================================== */

function SoloResultPage({ params, navigate }) {
    const { results, setName, name, questions } = params;
    const total = results.length;
    const correct = results.filter((r) => r.Richtig).length;
    const pct = total > 0 ? Math.round((correct / total) * 100) : 0;

    const qMap = {};
    (questions || []).forEach((q) => { qMap[q.id] = q; });

    const byCategory = {};
    results.forEach((r) => {
        const q = qMap[r.Frage_ID];
        const cat = fieldVal(q?.['Kategorie'], 'Ohne Kategorie') || 'Ohne Kategorie';
        if (!byCategory[cat]) byCategory[cat] = { total: 0, correct: 0 };
        byCategory[cat].total++;
        if (r.Richtig) byCategory[cat].correct++;
    });

    return (
        <div className="min-h-screen flex flex-col items-center p-6 pt-12">
            <h2 className="text-3xl font-bold mb-2">📊 Ergebnis</h2>
            <p className="text-gray-400 mb-4">{setName} — {name}</p>
            <div className={`text-6xl font-extrabold mb-2 ${pct >= 70 ? 'text-green-400' : pct >= 40 ? 'text-yellow-400' : 'text-red-400'}`}>{pct}%</div>
            <p className="text-xl mb-6">{correct} / {total} richtig</p>
            {Object.keys(byCategory).length > 1 && (
                <div className="w-full max-w-md bg-gray-800 rounded-xl p-4 mb-4">
                    <h3 className="font-bold mb-2">Nach Kategorie:</h3>
                    {Object.entries(byCategory).map(([cat, v]) => (
                        <div key={cat} className="flex justify-between py-1 border-b border-gray-700 last:border-0">
                            <span>{cat}</span>
                            <span className={v.correct / v.total >= 0.7 ? 'text-green-400' : 'text-red-400'}>{v.correct}/{v.total}</span>
                        </div>
                    ))}
                </div>
            )}
            <div className="w-full max-w-md space-y-3">
                <button onClick={() => navigate('solo-select')}
                    style={{ backgroundColor: params.color || '#d97706' }}
                    className="w-full py-3 hover:opacity-90 rounded-xl font-bold shadow-lg text-white">Nochmal spielen</button>
                <button onClick={() => navigate('home')} className="w-full py-3 bg-gray-700 hover:bg-gray-600 rounded-xl font-bold">Zur Startseite</button>
            </div>
        </div>
    );
}

/* ================================================================== */
/*  Wettkampf Select Page                                              */
/* ================================================================== */

function WettkampfSelectPage({ navigate }) {
    const [sets, setSets] = useState([]);
    const [name, setName] = useState('');
    const [selectedSet, setSelectedSet] = useState(null);
    const [totalQuestions, setTotalQuestions] = useState(null);
    const [error, setError] = useState('');
    const [checking, setChecking] = useState(false);
    const [progressDialog, setProgressDialog] = useState(null);

    useEffect(() => {
        api.get('/api/sets').then((allSets) => {
            // Only show sets with a configured wettkampfCount
            setSets(allSets.filter((s) => s.wettkampfCount && s.wettkampfCount > 0));
        }).catch((e) => setError(e.message));
    }, []);

    const selectSet = async (set) => {
        setSelectedSet(set);
        try {
            const data = await api.get(`/api/sets/${set.id}/count`);
            setTotalQuestions(data.count);
        } catch (e) {
            setError(e.message);
        }
    };

    const startWettkampf = async () => {
        if (!name.trim()) return setError('Bitte Name eingeben');
        const count = selectedSet.wettkampfCount;
        setChecking(true);
        setError('');
        try {
            const progress = await api.get(`/api/wettkampf-progress/${encodeURIComponent(name.trim())}/${selectedSet.id}`);
            if (progress.hasProgress) {
                setProgressDialog({ ...progress, count });
            } else {
                navigate('wettkampf-quiz', { setId: selectedSet.id, setName: selectedSet.name, name: name.trim(), questionCount: count, color: selectedSet.color, timer: selectedSet.wettkampfTimer });
            }
        } catch (e) {
            navigate('wettkampf-quiz', { setId: selectedSet.id, setName: selectedSet.name, name: name.trim(), questionCount: count, color: selectedSet.color, timer: selectedSet.wettkampfTimer });
        }
        setChecking(false);
    };

    const continueSession = () => {
        navigate('wettkampf-quiz', {
            setId: selectedSet.id, setName: selectedSet.name,
            name: name.trim(), questionCount: selectedSet.wettkampfCount,
            color: selectedSet.color, timer: selectedSet.wettkampfTimer,
            continueSessionId: progressDialog.sessionId,
            priorAnswers: progressDialog.answers,
        });
        setProgressDialog(null);
    };

    const startFresh = () => {
        navigate('wettkampf-quiz', {
            setId: selectedSet.id, setName: selectedSet.name,
            name: name.trim(), questionCount: selectedSet.wettkampfCount,
            color: selectedSet.color, timer: selectedSet.wettkampfTimer,
        });
        setProgressDialog(null);
    };

    return (
        <div className="min-h-screen flex flex-col items-center p-6 pt-12">
            <div className="w-full max-w-md"><BackButton onClick={() => navigate('home')} label="Zurück" /></div>
            <h2 className="text-3xl font-bold mb-6 mt-2">⚔️ Wettkampf</h2>
            <div className="w-full max-w-md space-y-4">
                <input className="w-full p-4 rounded-xl bg-gray-800 text-white text-center text-xl border-2 border-gray-700 focus:border-emerald-500 outline-none" placeholder="Dein Name" value={name} onChange={(e) => setName(e.target.value)} />

                {progressDialog && (
                    <div className="bg-gray-800 rounded-xl p-5 border-2 border-emerald-500 space-y-3">
                        <h3 className="font-bold text-lg text-emerald-400">📌 Gespeicherter Fortschritt</h3>
                        <p>{selectedSet.name}: <strong>{progressDialog.totalAnswered}</strong> Fragen beantwortet</p>
                        <div className="flex gap-3">
                            <button onClick={continueSession} className="flex-1 py-3 bg-emerald-600 hover:bg-emerald-700 rounded-xl font-bold">Weitermachen</button>
                            <button onClick={startFresh} className="flex-1 py-3 bg-gray-600 hover:bg-gray-500 rounded-xl font-bold">Neue Runde</button>
                        </div>
                    </div>
                )}

                {!progressDialog && (
                    <>
                        {!selectedSet ? (
                            <>
                                <h3 className="text-lg font-semibold mt-4">Fragenset wählen:</h3>
                                {sets.length === 0 && <p className="text-gray-500">Keine Wettkampf-Sets konfiguriert. Bitte im Admin-Bereich konfigurieren.</p>}
                                {sets.map((s) => {
                                    const bg = s.color || '#059669';
                                    return (
                                        <button key={s.id} onClick={() => selectSet(s)}
                                            style={{ backgroundColor: bg }}
                                            className="w-full py-4 rounded-xl text-lg font-bold transition-all shadow-lg text-white hover:opacity-90">
                                            {s.name} <span className="opacity-70 text-sm">({s.wettkampfCount} Fragen)</span>
                                        </button>
                                    );
                                })}
                            </>
                        ) : (
                            <>
                                <div className="bg-gray-800 rounded-xl p-4 flex justify-between items-center">
                                    <div>
                                        <span className="text-sm text-gray-400">Fragenset:</span>
                                        <p className="font-bold text-emerald-400">{selectedSet.name}</p>
                                        <p className="text-sm text-gray-400">{selectedSet.wettkampfCount} Fragen{totalQuestions ? ` (von ${totalQuestions} gesamt)` : ''}</p>
                                    </div>
                                    <button onClick={() => { setSelectedSet(null); setTotalQuestions(null); }} className="text-sm text-gray-400 hover:text-white">Ändern</button>
                                </div>

                                {error && <p className="text-red-400">{error}</p>}

                                <button onClick={startWettkampf} disabled={checking}
                                    style={{ backgroundColor: selectedSet.color || '#059669' }}
                                    className="w-full py-4 hover:opacity-90 disabled:opacity-40 rounded-xl text-xl font-bold transition-all shadow-lg text-white">
                                    {checking ? 'Prüfe...' : '⚔️ Wettkampf starten'}
                                </button>
                            </>
                        )}
                    </>
                )}
            </div>
        </div>
    );
}

/* ================================================================== */
/*  Wettkampf Quiz Page                                                */
/* ================================================================== */

function WettkampfQuizPage({ params, navigate }) {
    const [questions, setQuestions] = useState([]);
    const [allQuestionsRaw, setAllQuestionsRaw] = useState([]);
    const [index, setIndex] = useState(0);
    const [answers, setAnswers] = useState({}); // { questionId: { selected: [...], rowId: null } }
    const [error, setError] = useState('');
    const [enlargedImg, setEnlargedImg] = useState(null);
    const [shuffledAnswersMap, setShuffledAnswersMap] = useState({});
    const [saving, setSaving] = useState(false);
    const [confirming, setConfirming] = useState(false);
    const [timeLeft, setTimeLeft] = useState(null); // seconds remaining
    const [timerExpired, setTimerExpired] = useState(false);
    const sessionId = useRef(params.continueSessionId || 'WK-' + Date.now().toString(36).toUpperCase());
    const timerRef = useRef(null);
    const confirmRef = useRef(null);
    const accentColor = params.color || '#059669';

    // Type-aware random selection
    const selectQuestions = useCallback((allQuestions, count) => {
        const shuffled = shuffleArray(allQuestions);

        // Detect all unique types dynamically
        const typeGroups = {};
        shuffled.forEach((q) => {
            const typ = fieldVal(q['Typ'], 'MC');
            if (!typeGroups[typ]) typeGroups[typ] = [];
            typeGroups[typ].push(q);
        });

        const typeKeys = Object.keys(typeGroups);
        const selected = [];
        const usedIds = new Set();

        // If count >= number of types, pick one of each type first
        if (typeKeys.length > 1 && count >= typeKeys.length) {
            for (const typ of typeKeys) {
                const q = typeGroups[typ][0];
                selected.push(q);
                usedIds.add(q.id);
            }
        }

        // Fill remaining from shuffled pool
        for (const q of shuffled) {
            if (selected.length >= count) break;
            if (!usedIds.has(q.id)) {
                selected.push(q);
                usedIds.add(q.id);
            }
        }

        return shuffleArray(selected); // Shuffle final order
    }, []);

    useEffect(() => {
        api.get(`/api/sets/${params.setId}/questions`).then((qs) => {
            setAllQuestionsRaw(qs);

            if (params.priorAnswers && params.priorAnswers.length > 0) {
                // Resuming: find the questions that were already answered
                const priorIds = new Set(params.priorAnswers.map((a) => Number(a.Frage_ID)));
                const resumeQuestions = qs.filter((q) => priorIds.has(q.id));
                
                // Fill the rest to match configure questionCount
                const needed = params.questionCount - resumeQuestions.length;
                if (needed > 0) {
                    const unpicked = qs.filter((q) => !priorIds.has(q.id));
                    const additional = shuffleArray(unpicked).slice(0, needed);
                    resumeQuestions.push(...additional);
                }
                
                setQuestions(shuffleArray(resumeQuestions));

                // Restore answers state
                const restored = {};
                params.priorAnswers.forEach((a) => {
                    const ansStr = a.Antwort || '';
                    restored[Number(a.Frage_ID)] = {
                        selected: ansStr ? ansStr.split(',').map((s) => s.trim()) : [],
                        rowId: a.rowId,
                    };
                });
                setAnswers(restored);
            } else {
                const picked = selectQuestions(qs, params.questionCount);
                setQuestions(picked);
            }
        }).catch((e) => setError(e.message));
    }, []);

    // Build shuffled answers for each question
    useEffect(() => {
        if (questions.length === 0) return;
        const map = {};
        questions.forEach((q) => {
            const typ = fieldVal(q['Typ'], 'MC').toLowerCase();
            if (typ !== 'freitext') {
                const antworten = [];
                ['A', 'B', 'C', 'D'].forEach((k) => { if (q[`Antwort ${k}`]) antworten.push({ key: k, text: q[`Antwort ${k}`] }); });
                map[q.id] = shuffleArray(antworten);
            } else {
                map[q.id] = [];
            }
        });
        setShuffledAnswersMap(map);
    }, [questions]);

    const q = questions[index];
    const answeredCount = Object.keys(answers).filter((qid) => {
        const a = answers[qid];
        return a && a.selected && a.selected.length > 0 && (a.selected[0] !== '');
    }).length;
    const allAnswered = answeredCount >= questions.length;

    const saveAnswer = async (questionId, selected) => {
        const existing = answers[questionId];
        const answerText = selected.join(',');

        if (existing && existing.rowId) {
            // Update existing answer
            try {
                await api.put('/api/results/update', {
                    sessionId: sessionId.current,
                    frageId: questionId,
                    nutzername: params.name,
                    antwort: answerText,
                });
            } catch (e) {
                console.error('Update error:', e);
            }
        } else {
            // Create new answer
            try {
                setSaving(true);
                const row = await api.post('/api/results/single', {
                    Nutzername: params.name,
                    Frage_ID: questionId,
                    Fragenset: params.setName,
                    Antwort: answerText,
                    Richtig: false,
                    Punkte: 0,
                    Session_ID: sessionId.current,
                    Modus: 'Wettkampf',
                    Zeitstempel: new Date().toISOString(),
                });
                setAnswers((prev) => ({
                    ...prev,
                    [questionId]: { selected, rowId: row.id },
                }));
                setSaving(false);
                return;
            } catch (e) {
                console.error('Save error:', e);
                setSaving(false);
            }
        }

        setAnswers((prev) => ({
            ...prev,
            [questionId]: { ...prev[questionId], selected },
        }));
    };

    const handleSelect = (questionId, selected) => {
        setAnswers((prev) => ({
            ...prev,
            [questionId]: { ...(prev[questionId] || {}), selected },
        }));
    };

    const submitCurrent = () => {
        if (!q) return;
        const currentAnswer = answers[q.id];
        if (currentAnswer && currentAnswer.selected && currentAnswer.selected.length > 0) {
            saveAnswer(q.id, currentAnswer.selected);
        }
        // Move to next unanswered or stay
        goToNextUnanswered();
    };

    const goToNextUnanswered = () => {
        // Find next unanswered starting from current index
        for (let i = 1; i <= questions.length; i++) {
            const nextIdx = (index + i) % questions.length;
            const qId = questions[nextIdx].id;
            const a = answers[qId];
            if (!a || !a.selected || a.selected.length === 0 || a.selected[0] === '') {
                setIndex(nextIdx);
                return;
            }
        }
        // All answered; stay on current
    };

    const skipQuestion = () => {
        // Just go to next question (wrapping)
        const nextIdx = (index + 1) % questions.length;
        setIndex(nextIdx);
    };

    const confirmResults = async () => {
        setConfirming(true);
        // Calculate scores for all questions
        const finalResults = [];
        questions.forEach((question) => {
            const a = answers[question.id];
            if (!a || !a.selected || a.selected.length === 0 || a.selected[0] === '') {
                // Unanswered — counts as wrong
                finalResults.push({ rowId: null, questionId: question.id, richtig: false, punkte: 0 });
                return;
            }

            const typ = fieldVal(question['Typ'], 'MC').toLowerCase();
            const rawCorrect = (question['Richtige Antwort'] || '').trim();
            let ok = false;

            if (typ === 'freitext') {
                ok = normalizeText(a.selected[0]) === normalizeText(rawCorrect);
            } else {
                // Resolve correct answers
                const parts = rawCorrect.split(',').map((s) => s.trim()).filter(Boolean);
                const validKeys = new Set(['A', 'B', 'C', 'D']);
                const allAreKeys = parts.length > 0 && parts.every((p) => validKeys.has(p.toUpperCase()));

                let correctKeysResolved;
                if (allAreKeys) {
                    correctKeysResolved = parts.map((p) => p.toUpperCase());
                } else {
                    correctKeysResolved = [];
                    for (const part of parts) {
                        const partLower = part.toLowerCase().trim();
                        const allAntworten = [];
                        ['A', 'B', 'C', 'D'].forEach((k) => {
                            if (question[`Antwort ${k}`]) allAntworten.push({ key: k, text: question[`Antwort ${k}`] });
                        });
                        for (const ans of allAntworten) {
                            if (ans.text.toLowerCase().trim() === partLower) {
                                correctKeysResolved.push(ans.key);
                                break;
                            }
                        }
                    }
                    if (correctKeysResolved.length === 0) correctKeysResolved = parts.map((p) => p.toUpperCase());
                }

                const correctSet = new Set(correctKeysResolved);
                const selectedSet = new Set(a.selected.map((s) => s.toUpperCase()));
                ok = correctKeysResolved.length === selectedSet.size && correctKeysResolved.every((c) => selectedSet.has(c));
            }

            finalResults.push({
                rowId: a.rowId,
                questionId: question.id,
                richtig: ok,
                punkte: ok ? 1000 : 0,
            });
        });

        // Save final scores to backend
        try {
            const rowsToUpdate = finalResults.filter((r) => r.rowId);
            if (rowsToUpdate.length > 0) {
                await api.put('/api/results/finalize', { results: rowsToUpdate });
            }
        } catch (e) {
            console.error('Finalize error:', e);
        }

        navigate('wettkampf-result', {
            results: finalResults,
            questions: questions,
            answers: answers,
            setName: params.setName,
            name: params.name,
            allQuestionsRaw: allQuestionsRaw,
            shuffledAnswersMap: shuffledAnswersMap,
            color: params.color,
        });
    };

    // Store confirmResults in ref for timer access
    confirmRef.current = confirmResults;

    // Countdown timer
    useEffect(() => {
        if (!params.timer || questions.length === 0) return;
        const totalSeconds = params.timer * 60;
        
        let startTimestamp = Date.now();
        if (params.priorAnswers && params.priorAnswers.length > 0) {
            let earliest = null;
            for (const a of params.priorAnswers) {
                if (a.Zeitstempel) {
                    const ts = new Date(a.Zeitstempel).getTime();
                    if (!earliest || ts < earliest) earliest = ts;
                }
            }
            if (earliest) startTimestamp = earliest;
        }

        const runTimer = () => {
            const elapsed = (Date.now() - startTimestamp) / 1000;
            const remaining = Math.max(0, totalSeconds - elapsed);
            setTimeLeft(remaining);
            if (remaining <= 0) {
                clearInterval(timerRef.current);
                setTimerExpired(true);
                // Auto-confirm
                if (confirmRef.current) confirmRef.current();
            }
        };

        runTimer(); // Initial check
        timerRef.current = setInterval(runTimer, 200);
        return () => clearInterval(timerRef.current);
    }, [questions.length, params.timer, params.priorAnswers]);

    if (error) return <div className="min-h-screen flex items-center justify-center text-red-400 text-xl p-6 text-center">{error}</div>;
    if (!questions.length) return <div className="min-h-screen flex items-center justify-center animate-pulse text-xl text-emerald-400">Lade Fragen...</div>;

    const currentAnswer = answers[q?.id];
    const currentSelected = currentAnswer?.selected || [];
    const typ = fieldVal(q['Typ'], 'MC').toLowerCase();
    const isWF = typ === 'wahr/falsch';
    const currentShuffledAnswers = shuffledAnswersMap[q?.id] || [];

    const sanitized = {
        frage: q['Frage'] || '(Kein Fragetext)',
        typ: fieldVal(q['Typ'], 'MC'),
        bild: q['Bild'] && Array.isArray(q['Bild']) && q['Bild'].length ? q['Bild'][0].url : null,
        antworten: currentShuffledAnswers,
    };

    const hasCurrentAnswer = currentSelected.length > 0 && currentSelected[0] !== '';

    return (
        <div className="min-h-screen flex flex-col p-4 max-w-2xl mx-auto">
            {/* Header */}
            <div className="flex justify-between items-center mb-2">
                <BackButton onClick={() => { if (confirm('Wettkampf wirklich verlassen? Dein Fortschritt bleibt gespeichert.')) navigate('home'); }} label="Verlassen" />
                <span className="text-sm text-gray-400">Frage {index + 1} / {questions.length}</span>
            </div>

            {/* Timer */}
            {timeLeft !== null && (
                <div className="w-full mb-3">
                    <div className="w-full bg-gray-700 rounded-full h-4 overflow-hidden">
                        <div className={`h-full rounded-full transition-all duration-200 ${timeLeft < 60 ? 'bg-red-500' : timeLeft < params.timer * 30 ? 'bg-yellow-500' : ''}`}
                            style={{ width: `${Math.max(0, (timeLeft / (params.timer * 60)) * 100)}%`, backgroundColor: timeLeft >= params.timer * 30 ? accentColor : undefined }} />
                    </div>
                    <div className={`text-center text-lg font-bold mt-1 ${timeLeft < 60 ? 'text-red-400 animate-pulse' : 'text-gray-300'}`}>
                        {Math.floor(timeLeft / 60)}:{String(Math.floor(timeLeft % 60)).padStart(2, '0')}
                    </div>
                </div>
            )}

            {/* Progress bar */}
            <div className="w-full bg-gray-700 rounded-full h-2 mb-3">
                <div className="h-full rounded-full transition-all" style={{ width: `${(answeredCount / questions.length) * 100}%`, backgroundColor: accentColor }} />
            </div>

            <div className="mb-4">
                {(() => {
                    const N = questions.length;
                    if (N === 0) return null;
                    const R = Math.ceil(N / 20);
                    const chunks = [];
                    const baseSize = Math.floor(N / R);
                    let remainder = N % R;
                    let startIndex = 0;
                    for (let i = 0; i < R; i++) {
                        const chunkSize = baseSize + (remainder > 0 ? 1 : 0);
                        remainder--;
                        const chunk = [];
                        for (let j = 0; j < chunkSize; j++) {
                            chunk.push({ q: questions[startIndex + j], origIndex: startIndex + j });
                        }
                        chunks.push(chunk);
                        startIndex += chunkSize;
                    }
                    return (
                        <div className="flex flex-col gap-1.5 sm:gap-2 items-center w-full">
                            {chunks.map((chunk, rowIdx) => (
                                <div key={rowIdx} className="flex gap-1 sm:gap-1.5 justify-center w-full flex-nowrap">
                                    {chunk.map(({ q, origIndex }) => {
                                        const a = answers[q.id];
                                        const isAnswered = a && a.selected && a.selected.length > 0 && a.selected[0] !== '';
                                        const isCurrent = origIndex === index;
                                        const style = {};
                                        // flex-nowrap with flex-1 aspect-square forces it on one row safely shrinking on mobile
                                        let cls = 'flex-1 max-w-[2.25rem] aspect-square rounded-md sm:rounded-lg text-xs sm:text-sm font-bold transition-all flex items-center justify-center min-w-0 overflow-hidden shrink ';
                                        if (isCurrent) { cls += 'text-white ring-2'; style.backgroundColor = '#f59e0b'; style.boxShadow = '0 0 0 2px #fcd34d'; }
                                        else if (isAnswered) { cls += 'text-white'; style.backgroundColor = accentColor; }
                                        else cls += 'bg-gray-700 text-gray-400 hover:bg-gray-600';
                                        return <button key={q.id} onClick={() => setIndex(origIndex)} className={cls} style={style}>{origIndex + 1}</button>;
                                    })}
                                </div>
                            ))}
                        </div>
                    );
                })()}
            </div>

            {/* Status */}
            <div className="text-center text-sm mb-3">
                <span style={{ color: accentColor }} className="font-semibold">{answeredCount}</span>
                <span className="text-gray-500"> / {questions.length} beantwortet</span>
            </div>

            {/* Question */}
            <h2 className="text-xl font-bold mb-4 text-center">{sanitized.frage}</h2>
            {sanitized.bild && <img src={sanitized.bild} className="max-h-48 mx-auto rounded-lg mb-4 cursor-pointer" alt="" onClick={() => setEnlargedImg(sanitized.bild)} />}

            {/* Answer area — no feedback, just selection */}
            {typ === 'freitext' ? (
                <input type="text" className="w-full p-4 rounded-xl bg-gray-700 text-white text-xl text-center border-2 border-gray-600 focus:border-emerald-500 outline-none"
                    placeholder="Deine Antwort..." value={currentSelected[0] || ''}
                    onChange={(e) => handleSelect(q.id, [e.target.value])} autoFocus />
            ) : (
                <div className={`grid gap-3 grid-cols-1 sm:grid-cols-2`}>
                    {sanitized.antworten.map((a) => {
                        const isSelected = currentSelected.includes(a.key);
                        const cls = `${ANSWER_COLORS[a.key]} ${isSelected ? 'selected' : ''} text-white font-bold py-4 px-6 rounded-xl text-lg cursor-pointer transition-all text-center`;
                        return (
                            <button key={a.key} className={cls}
                                onClick={() => {
                                    if (isWF) handleSelect(q.id, [a.key]);
                                    else handleSelect(q.id, isSelected ? currentSelected.filter((s) => s !== a.key) : [...currentSelected, a.key]);
                                }}>
                                {a.text}
                            </button>
                        );
                    })}
                </div>
            )}

            {/* Action buttons */}
            <div className="mt-4 space-y-3">
                {hasCurrentAnswer && (
                    <button onClick={submitCurrent} disabled={saving || timerExpired}
                        style={{ backgroundColor: accentColor }}
                        className="w-full py-3 hover:opacity-90 disabled:opacity-50 rounded-xl text-lg font-bold shadow-lg text-white">
                        {saving ? 'Speichere...' : 'Antwort speichern →'}
                    </button>
                )}
                <button onClick={skipQuestion}
                    className="w-full py-3 bg-gray-700 hover:bg-gray-600 rounded-xl text-lg font-bold">
                    Überspringen →
                </button>
            </div>

            {/* Confirm button */}
            <div className="mt-6 border-t border-gray-700 pt-4">
                {allAnswered ? (
                    <button onClick={confirmResults} disabled={confirming}
                        className="w-full py-4 bg-red-600 hover:bg-red-700 disabled:opacity-50 rounded-xl text-xl font-bold shadow-lg shadow-red-900/30 animate-pulse">
                        {confirming ? 'Werte aus...' : '⚔️ Ergebnis bestätigen'}
                    </button>
                ) : (
                    <button disabled className="w-full py-4 bg-gray-800 opacity-40 rounded-xl text-xl font-bold cursor-not-allowed">
                        ⚔️ Noch {questions.length - answeredCount} Fragen offen
                    </button>
                )}
            </div>

            <ImageModal src={enlargedImg} onClose={() => setEnlargedImg(null)} />
        </div>
    );
}

/* ================================================================== */
/*  Wettkampf Result Page                                              */
/* ================================================================== */

function WettkampfResultPage({ params, navigate }) {
    const { results, questions, answers, setName, name, shuffledAnswersMap } = params;
    const total = results.length;
    const correct = results.filter((r) => r.richtig).length;
    const pct = total > 0 ? Math.round((correct / total) * 100) : 0;

    const qMap = {};
    questions.forEach((q) => { qMap[q.id] = q; });

    // Separate into wrong and right
    const wrongResults = results.filter((r) => !r.richtig);
    const rightResults = results.filter((r) => r.richtig);

    // Resolve correct answers for a question
    const resolveCorrectKeys = (question) => {
        const rawCorrect = (question['Richtige Antwort'] || '').trim();
        const typ = fieldVal(question['Typ'], 'MC').toLowerCase();
        if (typ === 'freitext') return { keys: [], text: rawCorrect };

        const parts = rawCorrect.split(',').map((s) => s.trim()).filter(Boolean);
        const validKeys = new Set(['A', 'B', 'C', 'D']);
        const allAreKeys = parts.length > 0 && parts.every((p) => validKeys.has(p.toUpperCase()));

        if (allAreKeys) return { keys: parts.map((p) => p.toUpperCase()), text: null };

        const resolved = [];
        for (const part of parts) {
            const partLower = part.toLowerCase().trim();
            ['A', 'B', 'C', 'D'].forEach((k) => {
                if ((question[`Antwort ${k}`] || '').toLowerCase().trim() === partLower) resolved.push(k);
            });
        }
        return { keys: resolved.length > 0 ? resolved : parts.map((p) => p.toUpperCase()), text: null };
    };

    const QuestionReview = ({ result }) => {
        const question = qMap[result.questionId];
        if (!question) return null;
        const typ = fieldVal(question['Typ'], 'MC').toLowerCase();
        const userAnswer = answers[result.questionId];
        const selected = userAnswer?.selected || [];
        const { keys: correctKeys, text: correctText } = resolveCorrectKeys(question);
        const shuffledAntworten = shuffledAnswersMap[question.id] || [];

        return (
            <div className={`bg-gray-800 rounded-xl p-4 border-l-4 ${result.richtig ? 'border-green-500' : 'border-red-500'}`}>
                <h4 className="font-bold mb-2">{question['Frage']}</h4>
                {question['Bild'] && Array.isArray(question['Bild']) && question['Bild'].length > 0 && (
                    <img src={question['Bild'][0].url} className="max-h-32 rounded-lg mb-2" alt="" />
                )}
                {typ === 'freitext' ? (
                    <div className="space-y-2">
                        <div className={`p-3 rounded-lg text-center font-bold ${result.richtig ? 'bg-green-600 ring-2 ring-green-400' : 'bg-red-700 ring-2 ring-red-400'}`}>
                            {result.richtig ? '✓ ' : '✗ '}{selected[0] || '(keine Antwort)'}
                        </div>
                        {!result.richtig && correctText && (
                            <div className="p-3 rounded-lg text-center font-bold bg-green-600 ring-2 ring-green-400">✓ {correctText}</div>
                        )}
                    </div>
                ) : (
                    <div className="grid gap-2 grid-cols-1 sm:grid-cols-2">
                        {shuffledAntworten.map((a) => {
                            const isSelected = selected.includes(a.key);
                            const isCorrectAnswer = correctKeys.includes(a.key);
                            let cls = 'py-3 px-4 rounded-lg text-center font-bold ';
                            let icon = '';
                            if (isCorrectAnswer) { cls += 'bg-green-600 text-white ring-2 ring-green-400'; icon = '✓ '; }
                            else if (isSelected) { cls += 'bg-red-700 text-white ring-2 ring-red-400'; icon = '✗ '; }
                            else { cls += 'bg-gray-700 text-gray-500 opacity-40'; }
                            return <div key={a.key} className={cls}>{icon}{a.text}</div>;
                        })}
                    </div>
                )}
            </div>
        );
    };

    // Category breakdown
    const byCategory = {};
    results.forEach((r) => {
        const q = qMap[r.questionId];
        const cat = fieldVal(q?.['Kategorie'], 'Ohne Kategorie') || 'Ohne Kategorie';
        if (!byCategory[cat]) byCategory[cat] = { total: 0, correct: 0 };
        byCategory[cat].total++;
        if (r.richtig) byCategory[cat].correct++;
    });

    return (
        <div className="min-h-screen flex flex-col items-center p-6 pt-12">
            {/* Overall Score */}
            <h2 className="text-3xl font-bold mb-2">⚔️ Wettkampf-Ergebnis</h2>
            <p className="text-gray-400 mb-4">{setName} — {name}</p>
            <div className={`text-6xl font-extrabold mb-2 ${pct >= 70 ? 'text-green-400' : pct >= 40 ? 'text-yellow-400' : 'text-red-400'}`}>{pct}%</div>
            <p className="text-xl mb-6">{correct} / {total} richtig</p>

            {/* Category breakdown */}
            {Object.keys(byCategory).length > 1 && (
                <div className="w-full max-w-md bg-gray-800 rounded-xl p-4 mb-6">
                    <h3 className="font-bold mb-2">Nach Kategorie:</h3>
                    {Object.entries(byCategory).map(([cat, v]) => (
                        <div key={cat} className="flex justify-between py-1 border-b border-gray-700 last:border-0">
                            <span>{cat}</span>
                            <span className={v.correct / v.total >= 0.7 ? 'text-green-400' : 'text-red-400'}>{v.correct}/{v.total}</span>
                        </div>
                    ))}
                </div>
            )}

            {/* Wrong answers */}
            {wrongResults.length > 0 && (
                <div className="w-full max-w-md mb-6">
                    <h3 className="text-xl font-bold mb-3 text-red-400">❌ Falsch beantwortet ({wrongResults.length})</h3>
                    <div className="space-y-3">
                        {wrongResults.map((r) => <QuestionReview key={r.questionId} result={r} />)}
                    </div>
                </div>
            )}

            {/* Right answers */}
            {rightResults.length > 0 && (
                <div className="w-full max-w-md mb-6">
                    <h3 className="text-xl font-bold mb-3 text-green-400">✅ Richtig beantwortet ({rightResults.length})</h3>
                    <div className="space-y-3">
                        {rightResults.map((r) => <QuestionReview key={r.questionId} result={r} />)}
                    </div>
                </div>
            )}

            <div className="w-full max-w-md space-y-3">
                <button onClick={() => navigate('wettkampf-select')}
                    style={{ backgroundColor: params.color || '#059669' }}
                    className="w-full py-3 hover:opacity-90 rounded-xl font-bold shadow-lg text-white">Nochmal spielen</button>
                <button onClick={() => navigate('home')} className="w-full py-3 bg-gray-700 hover:bg-gray-600 rounded-xl font-bold">Zur Startseite</button>
            </div>
        </div>
    );
}

/* ================================================================== */
/*  My Results Page                                                    */
/* ================================================================== */

function MyResultsPage({ navigate }) {
    const [name, setName] = useState('');
    const [results, setResults] = useState(null);
    const [error, setError] = useState('');

    const search = () => {
        if (!name.trim()) return;
        setError('');
        api.get(`/api/results/${encodeURIComponent(name.trim())}`)
            .then(setResults).catch((e) => setError(e.message));
    };

    const sessions = {};
    if (results) {
        results.forEach((r) => {
            const sid = r['Session_ID'] || 'Unbekannt';
            if (!sessions[sid]) sessions[sid] = { modus: fieldVal(r['Modus'], ''), fragenset: r['Fragenset'], date: r['Zeitstempel'], answers: [] };
            sessions[sid].answers.push(r);
        });
    }

    return (
        <div className="min-h-screen flex flex-col items-center p-6 pt-12">
            <div className="w-full max-w-md"><BackButton onClick={() => navigate('home')} label="Zurück" /></div>
            <h2 className="text-3xl font-bold mb-6 mt-2">📊 Meine Ergebnisse</h2>
            <div className="w-full max-w-md space-y-3">
                <input className="w-full p-4 rounded-xl bg-gray-800 text-white text-center border-2 border-gray-700 focus:border-red-500 outline-none" placeholder="Dein Name" value={name} onChange={(e) => setName(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && search()} />
                <button onClick={search} className="w-full py-3 bg-red-600 hover:bg-red-700 rounded-xl font-bold shadow-lg shadow-red-900/30">Suchen</button>
                {error && <p className="text-red-400 text-center">{error}</p>}
            </div>
            {results && results.length === 0 && <p className="mt-4 text-gray-500">Keine Ergebnisse gefunden.</p>}
            {results && results.length > 0 && (
                <div className="w-full max-w-md mt-6 space-y-4">
                    {Object.entries(sessions).reverse().map(([sid, s]) => {
                        const total = s.answers.length;
                        const cor = s.answers.filter((a) => {
                            const v = a['Richtig'];
                            return v === true || v === 'true' || v === 1;
                        }).length;
                        const points = s.answers.reduce((sum, a) => sum + (Number(a['Punkte']) || 0), 0);
                        return (
                            <div key={sid} className="bg-gray-800 rounded-xl p-4">
                                <div className="flex justify-between items-start mb-2">
                                    <div>
                                        <span className={`text-xs px-2 py-0.5 rounded-full text-white ${s.modus === 'Live' ? 'bg-red-600' : s.modus === 'Wettkampf' ? 'bg-emerald-600' : 'bg-amber-600'}`}>{s.modus}</span>
                                        <span className="ml-2 font-semibold">{s.fragenset}</span>
                                    </div>
                                    <span className="text-sm text-gray-400">{s.date ? new Date(s.date).toLocaleDateString('de') : ''}</span>
                                </div>
                                <p>{cor}/{total} richtig — {points.toLocaleString('de')} Punkte</p>
                            </div>
                        );
                    })}
                </div>
            )}
        </div>
    );
}

/* ================================================================== */
/*  App Router                                                         */
/* ================================================================== */

function App() {
    const [page, setPage] = useState('home');
    const [params, setParams] = useState({});
    const navigate = useCallback((p, par = {}) => { setPage(p); setParams(par); window.scrollTo(0, 0); }, []);

    switch (page) {
        case 'home': return <HomePage navigate={navigate} />;
        case 'join': return <JoinPage navigate={navigate} />;
        case 'live-play': return <LivePlayPage params={params} navigate={navigate} />;
        case 'solo-select': return <SoloSelectPage navigate={navigate} />;
        case 'solo-quiz': return <SoloQuizPage params={params} navigate={navigate} />;
        case 'solo-result': return <SoloResultPage params={params} navigate={navigate} />;
        case 'wettkampf-select': return <WettkampfSelectPage navigate={navigate} />;
        case 'wettkampf-quiz': return <WettkampfQuizPage params={params} navigate={navigate} />;
        case 'wettkampf-result': return <WettkampfResultPage params={params} navigate={navigate} />;
        case 'my-results': return <MyResultsPage navigate={navigate} />;
        default: return <HomePage navigate={navigate} />;
    }
}

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