const { useState, useEffect } = React;

function App() {
    const LOCAL_SCORES_KEY = 'aoe2CounterQuiz.localScores.v1';

    function getDifficultyConfig(mode) {
        if (mode === 'intermediate') {
            return {
                runLength: 15,
                timeLimitSec: 120,
                maxUniqueAsked: 5,
                minUniqueAsked: null,
                maxUniqueOptions: 1,
                excludeNaval: true,
                requireNonUniqueQuestions: false
            };
        }
        if (mode === 'expert') {
            return {
                runLength: 20,
                timeLimitSec: 150,
                maxUniqueAsked: null,
                minUniqueAsked: 10,
                maxUniqueOptions: null,
                excludeNaval: true,
                requireNonUniqueQuestions: false
            };
        }
        return {
            runLength: 10,
            timeLimitSec: 90,
            maxUniqueAsked: 0,
            minUniqueAsked: null,
            maxUniqueOptions: 0,
            excludeNaval: true,
            requireNonUniqueQuestions: true
        };
    }

    function getEmptyScores() {
        return {
            beginner: [],
            intermediate: [],
            expert: []
        };
    }

    function compareRuns(a, b) {
        if (b.score !== a.score) return b.score - a.score;
        if (a.durationMs !== b.durationMs) return a.durationMs - b.durationMs;
        return a.timestamp - b.timestamp;
    }

    function formatSeconds(total) {
        const mins = Math.floor(total / 60);
        const secs = total % 60;
        return `${mins}:${String(secs).padStart(2, '0')}`;
    }

    function formatMs(ms) {
        return formatSeconds(Math.max(0, Math.floor(ms / 1000)));
    }

    function getRunDurationMs(run) {
        if (!run) return 0;
        return Number(run.durationMs ?? run.duration_ms ?? 0);
    }

    function normalizeName(name) {
        return (name || '').toLowerCase().replace(/[^a-z0-9]+/g, ' ').trim();
    }

    function shuffleArray(arr) {
        return [...arr].sort(() => 0.5 - Math.random());
    }

    function pickRandomImage(urls) {
        if (!Array.isArray(urls) || urls.length === 0) return null;
        const index = Math.floor(Math.random() * urls.length);
        return urls[index] || null;
    }

    const [units, setUnits] = useState([]);
    const [imageMap, setImageMap] = useState({});
    const [unitUniqueMap, setUnitUniqueMap] = useState({});
    const [unitUniqueNameMap, setUnitUniqueNameMap] = useState({});
    const [screen, setScreen] = useState('home'); // 'home' | 'quiz' | 'summary' | 'highscores' | 'policy'
    const [difficulty, setDifficulty] = useState('beginner');
    const runConfig = getDifficultyConfig(difficulty);
    const [currentQuestion, setCurrentQuestion] = useState(null);
    const [selectedOption, setSelectedOption] = useState(null);
    const [score, setScore] = useState(0);
    const [totalQuestions, setTotalQuestions] = useState(0);
    const [showFeedback, setShowFeedback] = useState(false);
    const [loading, setLoading] = useState(true);
    const [usedSlugs, setUsedSlugs] = useState(new Set());
    const [elapsedSec, setElapsedSec] = useState(0);
    const [runStartedAt, setRunStartedAt] = useState(null);
    const [runDurationMs, setRunDurationMs] = useState(0);
    const [summaryPersisted, setSummaryPersisted] = useState(false);
    const [isNewLocalBest, setIsNewLocalBest] = useState(false);
    const [localScores, setLocalScores] = useState(getEmptyScores());
    const [viewer, setViewer] = useState(null);
    const [cloudScores, setCloudScores] = useState(getEmptyScores());
    const [runToken, setRunToken] = useState(null);
    const [currentRunHighlight, setCurrentRunHighlight] = useState(null);

    const isLoggedIn = !!viewer;

    // Load all unit data
    useEffect(() => {
        async function loadUnits() {
            try {
                // Load manifest of JSON files
                const manifestRes = await fetch('data/manifest.json');
                const files = await manifestRes.json();
                
                // Load all unit files
                const unitPromises = files.map(async (file) => {
                    const res = await fetch(`data/aoecompanion/${file}`);
                    return res.json();
                });
                
                const loadedUnits = await Promise.all(unitPromises);

                // Build slug → image_urls map from all countered_by and great_against entries
                const slugImages = {};
                const slugUnique = {};
                const nameUnique = {};
                loadedUnits.forEach(unit => {
                    const unitNameKey = (unit.name || '').toLowerCase().replace(/[^a-z0-9]+/g, ' ').trim();
                    if (unit.slug && unit.image_urls && unit.image_urls.length > 0 && !slugImages[unit.slug]) {
                        slugImages[unit.slug] = unit.image_urls;
                    }
                    if (unit.slug && typeof unit.is_unique === 'boolean') {
                        slugUnique[unit.slug] = unit.is_unique;
                    }
                    if (unitNameKey && typeof unit.is_unique === 'boolean') {
                        nameUnique[unitNameKey] = unit.is_unique;
                    }

                    [...(unit.countered_by || []), ...(unit.great_against || [])].forEach(entry => {
                        if (entry.slug && entry.image_urls && entry.image_urls.length > 0 && !slugImages[entry.slug]) {
                            slugImages[entry.slug] = entry.image_urls;
                        }
                    });
                });

                setImageMap(slugImages);
                setUnitUniqueMap(slugUnique);
                setUnitUniqueNameMap(nameUnique);
                setUnits(loadedUnits);
                setLoading(false);
            } catch (error) {
                console.error('Error loading units:', error);
                setLoading(false);
            }
        }
        
        loadUnits();
    }, []);

    useEffect(() => {
        try {
            const raw = localStorage.getItem(LOCAL_SCORES_KEY);
            if (!raw) return;
            const parsed = JSON.parse(raw);
            setLocalScores({
                beginner: Array.isArray(parsed?.beginner) ? parsed.beginner : [],
                intermediate: Array.isArray(parsed?.intermediate) ? parsed.intermediate : [],
                expert: Array.isArray(parsed?.expert) ? parsed.expert : []
            });
        } catch (error) {
            console.error('Failed to load local scores:', error);
        }
    }, []);

    useEffect(() => {
        async function loadViewer() {
            try {
                const res = await fetch('/api/auth/me');
                if (!res.ok) return;
                const data = await res.json();
                setViewer(data?.user || null);
            } catch (error) {
                console.error('Failed to load viewer:', error);
            }
        }
        loadViewer();
    }, []);

    useEffect(() => {
        if (!isLoggedIn) return;

        async function loadCloudScores() {
            try {
                const modes = ['beginner', 'intermediate', 'expert'];
                const controller = new AbortController();
                const timeout = setTimeout(() => controller.abort(), 5000);
                
                const results = await Promise.all(
                    modes.map(async mode => {
                        const res = await fetch(`/api/scores?mode=${mode}&limit=10`, {
                            signal: controller.signal
                        });
                        const data = await res.json();
                        return [mode, Array.isArray(data?.scores) ? data.scores : []];
                    })
                );
                clearTimeout(timeout);
                const nextScores = getEmptyScores();
                results.forEach(([mode, scores]) => {
                    nextScores[mode] = scores;
                });
                setCloudScores(nextScores);
            } catch (error) {
                console.error('Failed to load cloud scores:', error);
            }
        }

        loadCloudScores();
    }, [isLoggedIn]);

    // Generate a new question when units are loaded and quiz has started
    useEffect(() => {
        if (units.length > 0 && screen === 'quiz' && !currentQuestion) {
            generateQuestion(new Set());
        }
    }, [units, unitUniqueMap, unitUniqueNameMap, screen]);

    useEffect(() => {
        if (screen !== 'quiz') return;
        if (elapsedSec >= runConfig.timeLimitSec) {
            setRunDurationMs(runConfig.timeLimitSec * 1000);
            setScreen('summary');
            return;
        }

        const timer = setTimeout(() => {
            setElapsedSec(prev => prev + 1);
        }, 1000);

        return () => clearTimeout(timer);
    }, [screen, elapsedSec, runConfig.timeLimitSec]);

    useEffect(() => {
        if (screen !== 'summary' || summaryPersisted) return;

        const now = Date.now();
        const duration = runStartedAt ? now - runStartedAt : 0;
        setRunDurationMs(duration);

        const uniqueAskedCount = Array.from(usedSlugs).reduce((acc, slug) => {
            return acc + (unitUniqueMap[slug] === true ? 1 : 0);
        }, 0);

        if (isLoggedIn) {
            let cancelled = false;

            if (!runToken) {
                setSummaryPersisted(true);
                return;
            }

            (async () => {
                try {
                    const saveRes = await fetch('/api/scores', {
                        method: 'POST',
                        headers: { 'Content-Type': 'application/json' },
                        body: JSON.stringify({
                            mode: difficulty,
                            score,
                            questionCount: totalQuestions,
                            accuracy: runConfig.runLength > 0 ? Math.round((score / runConfig.runLength) * 100) : 0,
                            durationMs: duration,
                            uniqueAskedCount,
                            runToken
                        })
                    });

                    if (!saveRes.ok) {
                        const body = await saveRes.json().catch(() => ({}));
                        throw new Error(body?.error || 'Failed to save cloud score.');
                    }

                    const saveData = await saveRes.json().catch(() => ({}));
                    if (!cancelled) {
                        setCurrentRunHighlight({
                            source: 'cloud',
                            mode: difficulty,
                            id: saveData?.id || null
                        });
                    }

                    const refreshed = await fetch(`/api/scores?mode=${difficulty}&limit=10`);
                    const refreshedData = await refreshed.json();
                    if (!cancelled) {
                        setCloudScores(prev => ({
                            ...prev,
                            [difficulty]: Array.isArray(refreshedData?.scores) ? refreshedData.scores : []
                        }));
                    }
                } catch (error) {
                    console.error('Failed to save cloud score:', error);
                } finally {
                    if (!cancelled) {
                        setSummaryPersisted(true);
                    }
                }
            })();
            return () => {
                cancelled = true;
            };
        }

        if (!isLoggedIn) {
            const entry = {
                score,
                questionCount: totalQuestions,
                accuracy: runConfig.runLength > 0 ? Math.round((score / runConfig.runLength) * 100) : 0,
                durationMs: duration,
                uniqueAskedCount,
                timestamp: now
            };

            const current = {
                beginner: Array.isArray(localScores.beginner) ? localScores.beginner : [],
                intermediate: Array.isArray(localScores.intermediate) ? localScores.intermediate : [],
                expert: Array.isArray(localScores.expert) ? localScores.expert : []
            };

            const beforeBest = current[difficulty]?.[0] || null;
            const updatedModeRuns = [...current[difficulty], entry]
                .sort(compareRuns)
                .slice(0, 10);
            const afterBest = updatedModeRuns[0] || null;

            const updated = {
                ...current,
                [difficulty]: updatedModeRuns
            };

            try {
                localStorage.setItem(LOCAL_SCORES_KEY, JSON.stringify(updated));
            } catch (error) {
                console.error('Failed to save local scores:', error);
            }

            setLocalScores(updated);
            setCurrentRunHighlight({
                source: 'local',
                mode: difficulty,
                timestamp: entry.timestamp
            });
            setIsNewLocalBest(!beforeBest || (afterBest && afterBest.timestamp === entry.timestamp));
            setSummaryPersisted(true);
            return;
        }
        setSummaryPersisted(true);
    }, [
        screen,
        summaryPersisted,
        runStartedAt,
        isLoggedIn,
        score,
        totalQuestions,
        usedSlugs,
        unitUniqueMap,
        runConfig.runLength,
        difficulty,
        localScores,
        runToken
    ]);

    async function handleTwitchLogout() {
        try {
            await fetch('/api/auth/logout', { method: 'POST' });
        } catch (error) {
            console.error('Failed to log out:', error);
        }
        setViewer(null);
        setCloudScores(getEmptyScores());
        setRunToken(null);
        setCurrentRunHighlight(null);
    }

    function clearLocalData() {
        const confirmed = window.confirm('Clear all local high scores stored in this browser?');
        if (!confirmed) return;

        try {
            localStorage.removeItem(LOCAL_SCORES_KEY);
        } catch (error) {
            console.error('Failed to clear local scores:', error);
        }

        setLocalScores(getEmptyScores());
        setIsNewLocalBest(false);
        setCurrentRunHighlight(null);
    }

    function generateQuestion(overrideUsed) {
        const currentUsed = overrideUsed !== undefined ? overrideUsed : usedSlugs;
        if (units.length === 0) return;

        const runLength = runConfig.runLength;
        if (currentUsed.size >= runLength) {
            setScreen('summary');
            return;
        }

        function isEntryUnique(entry) {
            if (!entry) return false;
            const key = normalizeName(entry.name);
            const uniqueBySlug = entry.slug ? unitUniqueMap[entry.slug] : undefined;
            const uniqueByName = key ? unitUniqueNameMap[key] : undefined;
            return uniqueBySlug === true || uniqueByName === true || entry.is_unique === true;
        }

        function isUnitUnique(unit) {
            return isEntryUnique(unit);
        }

        function isNaval(entry) {
            return entry?.category === 'ship' || entry?.category === 'naval';
        }

        function isEntryAllowedByMode(entry) {
            if (!entry) return false;
            if (runConfig.excludeNaval && isNaval(entry)) return false;
            if (runConfig.maxUniqueOptions === 0 && isEntryUnique(entry)) return false;
            return true;
        }

        function isSameUnit(a, b) {
            if (!a || !b) return false;
            if (a.slug && b.slug && a.slug === b.slug) return true;
            return normalizeName(a.name) === normalizeName(b.name);
        }

        function pickDistractors(pool, needed, maxUniqueAllowed) {
            const shuffled = shuffleArray(pool);
            if (maxUniqueAllowed === null) {
                return shuffled.slice(0, needed);
            }

            let retries = 8;
            while (retries > 0) {
                const attempt = shuffleArray(pool);
                const picked = [];
                let uniquePicked = 0;

                for (const entry of attempt) {
                    const isUnique = isEntryUnique(entry);
                    if (isUnique && uniquePicked >= maxUniqueAllowed) {
                        continue;
                    }
                    picked.push(entry);
                    if (isUnique) {
                        uniquePicked += 1;
                    }
                    if (picked.length === needed) {
                        return picked;
                    }
                }
                retries -= 1;
            }

            return null;
        }

        const allAllowedBySlug = new Map();
        units.forEach(u => {
            (u.countered_by || []).forEach(c => {
                if (isEntryAllowedByMode(c) && c.slug && !allAllowedBySlug.has(c.slug)) {
                    allAllowedBySlug.set(c.slug, c);
                }
            });
        });

        const uniqueAskedSoFar = Array.from(currentUsed).reduce((acc, slug) => {
            return acc + (unitUniqueMap[slug] === true ? 1 : 0);
        }, 0);

        const remainingSlots = runLength - currentUsed.size;
        const neededUnique = runConfig.minUniqueAsked ? Math.max(0, runConfig.minUniqueAsked - uniqueAskedSoFar) : 0;
        const forceUniqueQuestion = runConfig.minUniqueAsked !== null && neededUnique >= remainingSlots;
        const uniqueQuestionCapReached = runConfig.maxUniqueAsked !== null && uniqueAskedSoFar >= runConfig.maxUniqueAsked;

        const candidateUnits = shuffleArray(units).filter(u => {
            if (!u?.slug) return false;
            if (currentUsed.has(u.slug)) return false;
            if (!u.countered_by || u.countered_by.length === 0) return false;
            if (runConfig.excludeNaval && isNaval(u)) return false;

            const uniqueUnit = isUnitUnique(u);
            if (runConfig.requireNonUniqueQuestions && uniqueUnit) return false;
            if (uniqueQuestionCapReached && uniqueUnit) return false;
            if (forceUniqueQuestion && !uniqueUnit) return false;
            return true;
        });

        for (const unit of candidateUnits) {
            const possibleCorrect = shuffleArray((unit.countered_by || []).filter(c => (
                isEntryAllowedByMode(c) && !isSameUnit(c, unit)
            )));

            if (possibleCorrect.length === 0) {
                continue;
            }

            const validCounterSlugs = new Set((unit.countered_by || []).map(c => c.slug));
            const distractorPool = Array.from(allAllowedBySlug.values()).filter(c => (
                !isSameUnit(c, unit) && !validCounterSlugs.has(c.slug)
            ));

            for (const correctCounter of possibleCorrect) {
                const uniqueInCorrect = isEntryUnique(correctCounter) ? 1 : 0;
                const maxUniqueDistractors = runConfig.maxUniqueOptions === null
                    ? null
                    : runConfig.maxUniqueOptions - uniqueInCorrect;

                if (maxUniqueDistractors !== null && maxUniqueDistractors < 0) {
                    continue;
                }

                const incorrectCounters = pickDistractors(distractorPool, 2, maxUniqueDistractors);
                if (!incorrectCounters || incorrectCounters.length < 2) {
                    continue;
                }

                const options = shuffleArray([correctCounter, ...incorrectCounters]).map(counter => ({
                    ...counter,
                    displayImage: pickRandomImage(counter.image_urls),
                    isCorrect: counter.slug === correctCounter.slug
                }));

                const nextUsed = new Set(currentUsed);
                nextUsed.add(unit.slug);
                setUsedSlugs(nextUsed);
                setCurrentQuestion({
                    unit,
                    unitDisplayImage: pickRandomImage(imageMap[unit.slug] || unit.image_urls),
                    options,
                    correctSlug: correctCounter.slug
                });
                setSelectedOption(null);
                setShowFeedback(false);
                return;
            }
        }

        // If no valid question can be generated under the current constraints,
        // finish the run early rather than leaving the UI stuck.
        setScreen('summary');
        setCurrentQuestion(null);
        setShowFeedback(false);
    }

    function handleOptionClick(option, index) {
        if (selectedOption !== null) return; // Already answered
        
        setSelectedOption(index);
        setShowFeedback(true);
        setTotalQuestions(prev => prev + 1);
        
        if (option.isCorrect) {
            setScore(prev => prev + 1);
        }
    }

    function handleNext() {
        if (totalQuestions >= runConfig.runLength) {
            setScreen('summary');
        } else {
            generateQuestion();
        }
    }

    async function requestRunToken(mode) {
        if (!isLoggedIn) return null;

        const res = await fetch('/api/runs/start', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ mode })
        });

        if (!res.ok) {
            const body = await res.json().catch(() => ({}));
            throw new Error(body?.error || 'Failed to create secure run token.');
        }

        const data = await res.json();
        return data?.runToken || null;
    }

    async function initializeRun(mode) {
        let nextToken = null;
        if (isLoggedIn) {
            try {
                nextToken = await requestRunToken(mode);
            } catch (error) {
                console.error(error);
                window.alert('Could not start a secure cloud run right now. Please try again.');
                return;
            }
        }

        setRunToken(nextToken);
        setDifficulty(mode);
        setScore(0);
        setTotalQuestions(0);
        setCurrentQuestion(null);
        const fresh = new Set();
        setUsedSlugs(fresh);
        setSummaryPersisted(false);
        setIsNewLocalBest(false);
        setRunDurationMs(0);
        setRunStartedAt(Date.now());
        setElapsedSec(0);
        setCurrentRunHighlight(null);
        setScreen('quiz');
    }

    function startNewRun() {
        initializeRun(difficulty);
    }

    function startQuiz(mode) {
        initializeRun(mode);
    }

    function goHome() {
        setScreen('home');
        setCurrentQuestion(null);
        setShowFeedback(false);
        setRunToken(null);
        setCurrentRunHighlight(null);
    }

    function getBestRun(mode) {
        const runs = isLoggedIn ? (cloudScores[mode] || []) : (localScores[mode] || []);
        return runs.length > 0 ? runs[0] : null;
    }

    function getViewerLabel(run) {
        return run?.twitch_display_name || run?.twitch_login || 'Unknown';
    }

    function isCurrentRun(run, source) {
        if (!currentRunHighlight || currentRunHighlight.mode !== difficulty || currentRunHighlight.source !== source) {
            return false;
        }

        if (source === 'cloud') {
            return Boolean(currentRunHighlight.id) && run?.id === currentRunHighlight.id;
        }

        return Boolean(currentRunHighlight.timestamp) && run?.timestamp === currentRunHighlight.timestamp;
    }

    function getLeaderboardRows(source) {
        const runs = source === 'cloud' ? (cloudScores[difficulty] || []) : (localScores[difficulty] || []);
        const topRuns = runs.slice(0, 5);
        const currentIndex = runs.findIndex(run => isCurrentRun(run, source));

        if (currentIndex > 4) {
            return topRuns.concat([{ ...runs[currentIndex], _rank: currentIndex + 1, _isCurrent: true }]);
        }

        return topRuns.map((run, index) => ({
            ...run,
            _rank: index + 1,
            _isCurrent: isCurrentRun(run, source)
        }));
    }

    function getTopRunsByMode(mode, source, limit = 10) {
        const runs = source === 'cloud' ? (cloudScores[mode] || []) : (localScores[mode] || []);
        return runs.slice(0, limit).map((run, index) => ({
            ...run,
            _rank: index + 1
        }));
    }

    function modeLabel(mode) {
        return mode.charAt(0).toUpperCase() + mode.slice(1);
    }

    const modeCards = [
        {
            key: 'beginner',
            title: 'Beginner',
            subtitle: 'Dark Age Drill',
            description: '10 questions, no unique units',
            image: 'img/base_dark_age.png'
        },
        {
            key: 'intermediate',
            title: 'Intermediate',
            subtitle: 'Feudal Pressure',
            description: '15 questions, some unique units',
            image: 'img/base_feudal_age.png'
        },
        {
            key: 'expert',
            title: 'Expert',
            subtitle: 'Castle Chaos',
            description: '20 questions, lots of unique units',
            image: 'img/base_castle_age.png'
        }
    ];

    const allModes = ['beginner', 'intermediate', 'expert'];

    if (screen === 'home') {
        return (
            <div className="home-container home-tech-tree">
                {isLoggedIn && (
                    <div className="auth-row">
                        <>
                            <div className="viewer-name">Signed in as {viewer.displayName || viewer.login}</div>
                            <button className="back-button" onClick={handleTwitchLogout}>Logout</button>
                        </>
                    </div>
                )}
                <h1 className="home-title">AoE2 Unit Counter Quiz</h1>
                <p className="home-subtitle">Counter mastery starts in the Dark Age and ends on top of the leaderboard.</p>

                <p className="home-description">
                    Built for beginners to practice core unit counters and for experts to sharpen decision speed under pressure.
                    Play across three difficulty tiers, race the clock, and chase clean runs. <a href="/api/auth/twitch/login">Sign in with Twitch</a> to lock in
                    your highscores so everyone can see where you rank.
                </p>

                {!isLoggedIn && (
                    <div className="pre-chooser-signin">
                        <a className="next-button login-link" href="/api/auth/twitch/login">Sign in with Twitch</a>
                    </div>
                )}

                {!isLoggedIn && <div className="chooser-separator" aria-hidden="true"><span></span></div>}

                <h2 className="chooser-title">Choose your quiz</h2>

                {loading ? (
                    <div className="mode-grid">
                        <div className="mode-loader-card">
                            <div className="mode-loader-spinner" aria-hidden="true"></div>
                            <div className="mode-loader-text">Loading unit data for quiz selector...</div>
                        </div>
                    </div>
                ) : (
                    <div className="mode-grid">
                        {modeCards.map((mode) => {
                            const bestRun = getBestRun(mode.key);
                            const config = getDifficultyConfig(mode.key);

                            return (
                                <div
                                    key={mode.key}
                                    className={`mode-card mode-${mode.key}`}
                                    onClick={() => startQuiz(mode.key)}
                                >
                                    <div className="mode-art-wrap">
                                        <img src={mode.image} alt={mode.title} className="mode-art" />
                                    </div>
                                    <div className="mode-name">{mode.title}</div>
                                    <div className="mode-age">{mode.subtitle}</div>
                                    <div className="mode-desc">{mode.description}</div>
                                    {bestRun && (
                                        <div className="mode-best">
                                            Best: {bestRun.score}/{config.runLength} in {formatMs(getRunDurationMs(bestRun))}
                                        </div>
                                    )}
                                </div>
                            );
                        })}
                    </div>
                )}
                <div className="chooser-actions">
                    <button className="back-button" onClick={() => setScreen('highscores')}>View Highscores</button>
                </div>
                {isLoggedIn ? (
                    <div className="anon-note">Signed in: scores sync to cloud leaderboard.</div>
                ) : (
                    <>
                        <div className="anon-note">Not signed in: high scores are stored locally in this browser.</div>
                        <div className="anon-note anon-note-secondary"><a href="/api/auth/twitch/login">Sign in with Twitch</a> to sync cloud highscores and appear on the public leaderboard.</div>
                    </>
                )}

                <div className="home-footer">
                    <h2 className="disclaimer-title">Disclaimer</h2>
                    <p>
                        This is a fan-made training tool and is not affiliated with Microsoft, Xbox Game Studios, or Forgotten Empires.
                        Unit data is for educational practice and may change with game updates.
                    </p>
                    <div className="footer-links">
                        <a href="mailto:hello@aoe-counters.com">Contact: hello@aoe-counters.com</a>
                        <button className="text-link-btn" onClick={() => setScreen('policy')}>Privacy / Usage Policy</button>
                        {isLoggedIn && <button className="text-link-btn" onClick={handleTwitchLogout}>Log out</button>}
                        <button className="text-link-btn danger" onClick={clearLocalData}>Clear local data</button>
                    </div>
                </div>
            </div>
        );
    }

    if (screen === 'highscores') {
        const source = isLoggedIn ? 'cloud' : 'local';

        return (
            <div className="home-container highscores-page">
                <h1 className="home-title">All Highscores</h1>
                <p className="home-subtitle">
                    Showing top runs from {isLoggedIn ? 'Cloud Leaderboards (Twitch account)' : 'Local Browser Storage'}.
                </p>

                {!isLoggedIn && (
                    <div className="anon-note">
                        <a href="/api/auth/twitch/login">Sign in with Twitch</a> to publish scores on the cloud leaderboard.
                    </div>
                )}

                <div className="all-highscores-grid">
                    {allModes.map((mode) => {
                        const rows = getTopRunsByMode(mode, source, 10);
                        const config = getDifficultyConfig(mode);

                        return (
                            <div key={mode} className="summary-highscores mode-board">
                                <h2 className="summary-hs-title">{modeLabel(mode)} ({config.runLength}Q)</h2>
                                {rows.length === 0 ? (
                                    <div className="empty-board">No runs yet.</div>
                                ) : (
                                    rows.map((run) => (
                                        <div key={`${mode}-${run.id || run.timestamp}-${run._rank}`} className={`summary-hs-row${source === 'cloud' ? ' summary-hs-row-cloud' : ''}`}>
                                            <span>#{run._rank}</span>
                                            {source === 'cloud' ? (
                                                <span className="summary-player-text">{getViewerLabel(run)}</span>
                                            ) : (
                                                <span>Local</span>
                                            )}
                                            <span>{run.score}/{config.runLength}</span>
                                            {source === 'cloud' ? (
                                                <span>{formatMs(getRunDurationMs(run))}</span>
                                            ) : null}
                                        </div>
                                    ))
                                )}
                            </div>
                        );
                    })}
                </div>

                <div className="summary-buttons">
                    <button className="next-button" onClick={() => startQuiz(difficulty)}>Play {modeLabel(difficulty)}</button>
                    <button className="back-button summary-menu-btn" onClick={goHome}>Main Menu</button>
                </div>
            </div>
        );
    }

    if (screen === 'policy') {
        return (
            <div className="home-container policy-page">
                <h1 className="home-title">Privacy & Usage Policy</h1>
                <div className="policy-section">
                    <h2>What this app stores</h2>
                    <p>When you are not signed in, highscores are stored locally in your browser only.</p>
                    <p>When signed in with Twitch, your submitted runs are stored in the cloud leaderboard with your Twitch login/display name.</p>
                </div>
                <div className="policy-section">
                    <h2>How scores are used</h2>
                    <p>Scores are used to build leaderboard rankings and compare run performance by mode.</p>
                    <p>We do not sell personal data. Data is used only for app functionality and leaderboard display.</p>
                </div>
                <div className="policy-section">
                    <h2>Analytics</h2>
                    <p>We use Fathom Analytics to measure overall site usage and improve the app experience.</p>
                    <p>Fathom is privacy-focused and does not use cookies or track you across websites.</p>
                </div>
                <div className="policy-section">
                    <h2>Fair usage</h2>
                    <p>Do not abuse API endpoints or intentionally spam score submissions.</p>
                    <p>Runs that look manipulated may be removed to keep competition fair.</p>
                </div>
                <div className="policy-section">
                    <h2>Contact</h2>
                    <p>Questions, correction requests, or removal requests: <a href="mailto:hello@aoe-counters.com">hello@aoe-counters.com</a></p>
                </div>
                <div className="summary-buttons">
                    <button className="back-button summary-menu-btn" onClick={goHome}>Back to Home</button>
                </div>
            </div>
        );
    }

    if (screen === 'summary') {
        return (
            <div className="home-container">
                <div className="summary-score">{score} / {runConfig.runLength}</div>
                <div className="summary-label">{difficulty.charAt(0).toUpperCase() + difficulty.slice(1)} Mode</div>
                <div className="summary-time">Run Time: {formatMs(runDurationMs)}</div>
                {isNewLocalBest && !isLoggedIn && (
                    <div className="summary-new-best">New Local Best!</div>
                )}
                {!isLoggedIn && (
                    <div className="summary-highscores">
                        <div className="summary-hs-title">Top Local Runs ({difficulty})</div>
                        {getLeaderboardRows('local').map((run, index) => (
                            <div key={`${run.timestamp}-${index}`} className={`summary-hs-row${run._isCurrent ? ' summary-hs-row-current' : ''}`}>
                                <span>#{run._rank || index + 1}</span>
                                <span>{run.score}/{runConfig.runLength}</span>
                                <span>{formatMs(run.durationMs)}</span>
                            </div>
                        ))}
                    </div>
                )}
                {isLoggedIn && (
                    <div className="summary-highscores">
                        <div className="summary-hs-title">Top Cloud Runs ({difficulty})</div>
                        {getLeaderboardRows('cloud').map((run, index) => (
                            <div key={`${run.id || run.timestamp}-${index}`} className={`summary-hs-row summary-hs-row-cloud${run._isCurrent ? ' summary-hs-row-current' : ''}`}>
                                <span className="summary-rank">#{run._rank || index + 1}</span>
                                <span className="summary-player">
                                    <span className="twitch-icon" aria-hidden="true">
                                        <svg viewBox="0 0 24 24" focusable="false">
                                            <path d="M3 2h18v12l-4 4h-4l-3 3v-3H6l-3-3V2zm2 2v9l2 2h4v2l2-2h4l2-2V4H5zm5 2h2v5h-2V6zm5 0h2v5h-2V6z" />
                                        </svg>
                                    </span>
                                    <span className="summary-player-text">{getViewerLabel(run)}</span>
                                </span>
                                <span className="summary-run-score">{run.score}/{runConfig.runLength}</span>
                                <span className="summary-run-time">{formatMs(getRunDurationMs(run))}</span>
                            </div>
                        ))}
                    </div>
                )}
                <div className="summary-buttons">
                    <button className="next-button" onClick={startNewRun}>Play Again</button>
                    <button className="back-button summary-menu-btn" onClick={goHome}>Main Menu</button>
                </div>
            </div>
        );
    }

    if (!currentQuestion) {
        return <div className="loading">Loading questions...</div>;
    }

    return (
        <div className="quiz-container">
            <div className="quiz-header">
                <button className="back-button" onClick={goHome}>← Menu</button>
                <div className="quiz-stats">
                    <div className="score">Progress: {totalQuestions} / {runConfig.runLength}</div>
                    <div className={`timer ${elapsedSec >= runConfig.timeLimitSec - 10 ? 'danger' : ''}`}>Time: {formatSeconds(Math.max(0, elapsedSec))}</div>
                </div>
            </div>
            
            <div className="question-section">
                <div className="question-text">
                    Which unit counters this unit?
                </div>
                
                <div className="unit-display">
                    {currentQuestion.unitDisplayImage && (
                        <img 
                            src={currentQuestion.unitDisplayImage}
                            alt={currentQuestion.unit.name}
                            className="unit-image"
                        />
                    )}
                    <div className="unit-name">{currentQuestion.unit.name}</div>
                </div>
            </div>

            {showFeedback && (
                <div className={`feedback ${selectedOption !== null && currentQuestion.options[selectedOption].isCorrect ? 'correct' : 'incorrect'}`}>
                    {selectedOption !== null && currentQuestion.options[selectedOption].isCorrect 
                        ? '✓ Correct!' 
                        : '✗ Incorrect!'}
                </div>
            )}
            
            <div className="options-grid">
                {currentQuestion.options.map((option, index) => {
                    let className = 'option-card';
                    
                    if (selectedOption !== null) {
                        className += ' disabled';
                        if (index === selectedOption) {
                            className += option.isCorrect ? ' correct' : ' incorrect';
                        } else if (option.isCorrect) {
                            className += ' correct';
                        }
                    }
                    
                    return (
                        <div
                            key={index}
                            className={className}
                            onClick={() => handleOptionClick(option, index)}
                        >
                            {option.displayImage && (
                                <img 
                                    src={option.displayImage}
                                    alt={option.name}
                                    className="option-image"
                                />
                            )}
                            <div className="option-name">{option.name}</div>
                        </div>
                    );
                })}
            </div>
            
            {showFeedback && (
                <button 
                    className="next-button"
                    onClick={handleNext}
                >
                    {totalQuestions >= runConfig.runLength ? 'See Results' : 'Next Question'}
                </button>
            )}
        </div>
    );
}

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