const express = require('express');
const http = require('http');
const path = require('path');
const { getEnvConfig, readConfig, writeConfig } = require('./config');
const { listRows, createRow, batchCreateRows, updateRow } = require('./baserow');
const { setupWebSocket, getSessions } = require('./live-session');
const { extractFieldValue } = require('./quiz-logic');

const app = express();
const server = http.createServer(app);

app.use(express.json({ limit: '5mb' }));
app.use(express.static(path.join(__dirname, '..', 'public')));

function requireAdmin(req, res, next) {
    if (req.headers['x-admin-token'] !== getEnvConfig().adminPassword) {
        return res.status(401).json({ error: 'Nicht autorisiert' });
    }
    next();
}

/* ============================================================ */
/*  Image Proxy                                                  */
/* ============================================================ */

const imageCache = new Map();
const MAX_CACHE = 50;

app.get('/api/img', async (req, res) => {
    const url = req.query.url;
    if (!url) return res.status(400).send('Missing url parameter');

    let parsed;
    try { parsed = new URL(url); } catch { return res.status(400).send('Invalid URL'); }

    const { baserowUrl } = getEnvConfig();
    let allowedHost = '';
    try { allowedHost = new URL(baserowUrl).host; } catch { }
    if (parsed.host !== allowedHost) return res.status(403).send('Forbidden');

    if (imageCache.has(url)) {
        const cached = imageCache.get(url);
        res.set('Content-Type', cached.contentType);
        res.set('Cache-Control', 'public, max-age=86400');
        return res.send(cached.buffer);
    }

    try {
        const response = await fetch(url);
        if (!response.ok) return res.status(response.status).send('Image fetch failed');
        const contentType = response.headers.get('content-type') || 'image/png';
        const buffer = Buffer.from(await response.arrayBuffer());
        if (imageCache.size >= MAX_CACHE) {
            const oldest = imageCache.keys().next().value;
            imageCache.delete(oldest);
        }
        imageCache.set(url, { buffer, contentType });
        res.set('Content-Type', contentType);
        res.set('Cache-Control', 'public, max-age=86400');
        res.send(buffer);
    } catch (e) {
        console.error(`[img-proxy] Error:`, e.message);
        res.status(500).send('Proxy error');
    }
});

/* ============================================================ */
/*  Helper: rewrite Baserow image URLs to use our proxy          */
/* ============================================================ */

function rewriteImageUrls(rows) {
    return rows.map((row) => {
        const bild = row['Bild'];
        if (bild && Array.isArray(bild) && bild.length > 0) {
            return {
                ...row,
                Bild: bild.map((img) => ({
                    ...img,
                    url: img.url ? `/api/img?url=${encodeURIComponent(img.url)}` : img.url,
                })),
            };
        }
        return row;
    });
}

/* ============================================================ */
/*  Public API                                                   */
/* ============================================================ */

app.get('/api/sets', (req, res) => {
    const config = readConfig();
    res.json((config.sets || []).map((s) => ({ id: s.id, name: s.name, wettkampfCount: s.wettkampfCount || null, color: s.color || null, wettkampfTimer: s.wettkampfTimer || null })));
});

app.get('/api/sets/:id/questions', async (req, res) => {
    try {
        const config = readConfig();
        const set = config.sets.find((s) => s.id === req.params.id);
        if (!set) return res.status(404).json({ error: 'Fragenset nicht gefunden' });
        let rows = await listRows(set.tableId);
        rows = rewriteImageUrls(rows);
        res.json(rows);
    } catch (e) {
        res.status(500).json({ error: e.message });
    }
});

app.get('/api/sets/:id/count', async (req, res) => {
    try {
        const config = readConfig();
        const set = config.sets.find((s) => s.id === req.params.id);
        if (!set) return res.status(404).json({ error: 'Fragenset nicht gefunden' });
        const rows = await listRows(set.tableId);
        res.json({ count: rows.length });
    } catch (e) {
        res.status(500).json({ error: e.message });
    }
});

/* ---- Solo/Wettkampf: Save single answer immediately ---- */
app.post('/api/results/single', async (req, res) => {
    try {
        const config = readConfig();
        if (!config.answersTableId) return res.status(400).json({ error: 'Antworten-Tabelle nicht konfiguriert' });
        const row = await createRow(config.answersTableId, req.body);
        res.json({ success: true, id: row.id });
    } catch (e) {
        console.error('[api] Single save error:', e.message);
        res.status(500).json({ error: e.message });
    }
});

/* ---- Wettkampf: Update existing answer ---- */
app.put('/api/results/update', async (req, res) => {
    try {
        const config = readConfig();
        if (!config.answersTableId) return res.status(400).json({ error: 'Antworten-Tabelle nicht konfiguriert' });
        const { sessionId, frageId, nutzername, antwort } = req.body;
        if (!sessionId || !frageId || !nutzername) return res.status(400).json({ error: 'sessionId, frageId und nutzername erforderlich' });

        // Find the existing row
        const rows = await listRows(config.answersTableId, {
            Session_ID: sessionId,
            Nutzername: nutzername,
        });
        const existing = rows.find((r) => r['Frage_ID'] === frageId);
        if (!existing) return res.status(404).json({ error: 'Antwort nicht gefunden' });

        await updateRow(config.answersTableId, existing.id, { Antwort: antwort });
        res.json({ success: true });
    } catch (e) {
        console.error('[api] Update answer error:', e.message);
        res.status(500).json({ error: e.message });
    }
});

/* ---- Wettkampf: Finalize scores ---- */
app.put('/api/results/finalize', async (req, res) => {
    try {
        const config = readConfig();
        if (!config.answersTableId) return res.status(400).json({ error: 'Antworten-Tabelle nicht konfiguriert' });
        const { results } = req.body;
        if (!results || !results.length) return res.status(400).json({ error: 'Keine Ergebnisse' });

        // results is array of { rowId, richtig, punkte }
        for (const r of results) {
            await updateRow(config.answersTableId, r.rowId, {
                Richtig: r.richtig,
                Punkte: r.punkte,
            });
        }
        res.json({ success: true });
    } catch (e) {
        console.error('[api] Finalize error:', e.message);
        res.status(500).json({ error: e.message });
    }
});

/* ---- Solo: Check for incomplete session ---- */
app.get('/api/progress/:name/:setId', async (req, res) => {
    try {
        const config = readConfig();
        const set = config.sets.find((s) => s.id === req.params.setId);
        if (!set) return res.status(404).json({ error: 'Set nicht gefunden' });
        if (!config.answersTableId) return res.json({ hasProgress: false });

        // Count total questions
        const questions = await listRows(set.tableId);
        const totalQuestions = questions.length;

        // Get answers for this user + set
        const allAnswers = await listRows(config.answersTableId, {
            Nutzername: req.params.name,
            Fragenset: set.name,
        });

        // Filter to Solo only (Modus may be Single Select object)
        const soloAnswers = allAnswers.filter((a) => {
            return extractFieldValue(a['Modus'], '') === 'Solo';
        });

        // Group by Session_ID
        const bySession = {};
        soloAnswers.forEach((a) => {
            const sid = a['Session_ID'] || '';
            if (!bySession[sid]) bySession[sid] = [];
            bySession[sid].push(a);
        });

        // Find the most recent incomplete session
        let best = null;
        for (const [sid, answers] of Object.entries(bySession)) {
            if (answers.length > 0 && answers.length < totalQuestions) {
                const latest = answers.reduce((max, a) => {
                    const ts = a['Zeitstempel'] || '';
                    return ts > max ? ts : max;
                }, '');
                if (!best || latest > best.latest) {
                    best = { sid, answers, latest };
                }
            }
        }

        if (best) {
            res.json({
                hasProgress: true,
                sessionId: best.sid,
                answers: best.answers.map((a) => ({
                    Frage_ID: a['Frage_ID'],
                    Antwort: a['Antwort'] || '',
                    Richtig: !!a['Richtig'],
                    Punkte: a['Punkte'] || 0,
                })),
                totalAnswered: best.answers.length,
                totalCorrect: best.answers.filter((a) => a['Richtig']).length,
                totalQuestions,
            });
        } else {
            res.json({ hasProgress: false, totalQuestions });
        }
    } catch (e) {
        console.error('[api] Progress error:', e.message);
        res.status(500).json({ error: e.message });
    }
});

/* ---- Wettkampf: Check for incomplete session ---- */
app.get('/api/wettkampf-progress/:name/:setId', async (req, res) => {
    try {
        const config = readConfig();
        const set = config.sets.find((s) => s.id === req.params.setId);
        if (!set) return res.status(404).json({ error: 'Set nicht gefunden' });
        if (!config.answersTableId) return res.json({ hasProgress: false });

        const allAnswers = await listRows(config.answersTableId, {
            Nutzername: req.params.name,
            Fragenset: set.name,
        });

        // Filter to Wettkampf only
        const wkAnswers = allAnswers.filter((a) => {
            return extractFieldValue(a['Modus'], '') === 'Wettkampf';
        });

        // Group by Session_ID
        const bySession = {};
        wkAnswers.forEach((a) => {
            const sid = a['Session_ID'] || '';
            if (!bySession[sid]) bySession[sid] = [];
            bySession[sid].push(a);
        });

        // Find the most recent session where NOT all answers are finalized (Punkte is still 0 for all)
        let best = null;
        for (const [sid, answers] of Object.entries(bySession)) {
            // A session is "incomplete" if all Punkte are 0 and all Richtig are false (not yet confirmed)
            const allUnscored = answers.every((a) => {
                const punkte = Number(a['Punkte']) || 0;
                const richtig = a['Richtig'];
                return punkte === 0 && (richtig === false || richtig === 'false' || !richtig);
            });
            if (allUnscored && answers.length > 0) {
                const latest = answers.reduce((max, a) => {
                    const ts = a['Zeitstempel'] || '';
                    return ts > max ? ts : max;
                }, '');
                if (!best || latest > best.latest) {
                    best = { sid, answers, latest };
                }
            }
        }

        if (best) {
            res.json({
                hasProgress: true,
                sessionId: best.sid,
                answers: best.answers.map((a) => ({
                    rowId: a.id,
                    Frage_ID: a['Frage_ID'],
                    Antwort: a['Antwort'] || '',
                    Zeitstempel: a['Zeitstempel'] || '',
                })),
                totalAnswered: best.answers.length,
            });
        } else {
            res.json({ hasProgress: false });
        }
    } catch (e) {
        console.error('[api] Wettkampf progress error:', e.message);
        res.status(500).json({ error: e.message });
    }
});

/* ---- Batch save (still used by live sessions) ---- */
app.post('/api/results', async (req, res) => {
    try {
        const config = readConfig();
        if (!config.answersTableId) return res.status(400).json({ error: 'Antworten-Tabelle nicht konfiguriert' });
        await batchCreateRows(config.answersTableId, req.body.answers || []);
        res.json({ success: true });
    } catch (e) {
        res.status(500).json({ error: e.message });
    }
});

app.get('/api/results/:name', async (req, res) => {
    try {
        const config = readConfig();
        if (!config.answersTableId) return res.json([]);
        const rows = await listRows(config.answersTableId, { Nutzername: req.params.name });
        res.json(rows);
    } catch (e) {
        res.status(500).json({ error: e.message });
    }
});

/* ============================================================ */
/*  Admin API                                                    */
/* ============================================================ */

app.post('/api/admin/login', (req, res) => {
    if (req.body.password === getEnvConfig().adminPassword) {
        res.json({ success: true, token: getEnvConfig().adminPassword });
    } else {
        res.status(401).json({ error: 'Falsches Passwort' });
    }
});

app.get('/api/admin/config', requireAdmin, (req, res) => res.json(readConfig()));
app.post('/api/admin/config', requireAdmin, (req, res) => {
    try { writeConfig(req.body); res.json({ success: true }); } catch (e) { res.status(500).json({ error: e.message }); }
});
app.get('/api/admin/sessions', requireAdmin, (req, res) => res.json(getSessions()));

app.get('/api/admin/stats', requireAdmin, async (req, res) => {
    try {
        const config = readConfig();
        if (!config.answersTableId) return res.json({ answers: [] });
        const rows = await listRows(config.answersTableId);
        res.json({ answers: rows });
    } catch (e) {
        res.status(500).json({ error: e.message });
    }
});

app.get('/api/admin/debug-baserow', requireAdmin, async (req, res) => {
    try {
        const { baserowUrl, baserowToken } = getEnvConfig();
        const config = readConfig();
        const result = { baserowUrl, tokenLength: baserowToken.length, sets: config.sets, tests: [] };
        for (const set of config.sets) {
            try {
                const rows = await listRows(set.tableId);
                result.tests.push({ set: set.name, tableId: set.tableId, rowCount: rows.length, sampleFields: rows.length > 0 ? Object.keys(rows[0]) : [] });
            } catch (e) {
                result.tests.push({ set: set.name, tableId: set.tableId, error: e.message });
            }
        }
        res.json(result);
    } catch (e) {
        res.status(500).json({ error: e.message });
    }
});

app.get('/admin', (req, res) => res.sendFile(path.join(__dirname, '..', 'public', 'admin.html')));
app.get('/present', (req, res) => res.sendFile(path.join(__dirname, '..', 'public', 'present.html')));
app.get('*', (req, res) => res.sendFile(path.join(__dirname, '..', 'public', 'index.html')));

setupWebSocket(server);
const { port } = getEnvConfig();
server.listen(port, '0.0.0.0', () => {
    console.log(`[quizalarm] Server laeuft auf Port ${port}`);
    console.log(`[quizalarm] Baserow URL: ${getEnvConfig().baserowUrl}`);
});