import Bun, { type ServerWebSocket } from 'bun';
import path from 'path';
import fs from 'fs';
import browser from 'browser-detect';
import { buildHTML, buildCSS } from './html';
import { isGame } from './isGame';

// ── CSS Hot Reload via WebSocket ────────────────────────────────────
const srcDir = path.resolve(import.meta.dir, '..', 'src');

interface ClientData {
    game: string;
}

// Map of game name → set of WebSocket connections
const wsClients = new Map<string, Set<ServerWebSocket<ClientData>>>();

// Debounced watcher: on any source change, rebuild CSS only for affected games
const watcher = fs.watch(srcDir, { recursive: true });
let debounce: ReturnType<typeof setTimeout> | null = null;
watcher.on('change', async (_, filename) => {
    if (!filename) return;
    const filenameStr = filename.toString();
    const rel = filenameStr.replace(/\\/g, '/');
    const gameMatch = rel.match(/^games\/([^/]+)\//);
    const changedGame = gameMatch ? gameMatch[1] : null;

    if (debounce) clearTimeout(debounce);
    debounce = setTimeout(async () => {
        if (changedGame && !wsClients.has(changedGame)) return;
        const affectedGames = changedGame
            ? [[changedGame, wsClients.get(changedGame)!] as const]
            : [...wsClients.entries()]; // common/ → all games

        for (const [game, clients] of affectedGames) {
            const css = await buildCSS(game);
            if (css === null) continue;
            const message = JSON.stringify({ type: 'css', css });
            for (const ws of clients) {
                ws.send(message);
            }
        }
    }, 50);
});
// ─────────────────────────────────────────────────────────────────────

Bun.serve<ClientData>({
    websocket: {
        open(ws) {
            const game = ws.data.game;
            if (!wsClients.has(game)) wsClients.set(game, new Set());
            wsClients.get(game)!.add(ws);
            console.log(`[css-hot] ${game} connected (${wsClients.get(game)!.size} clients)`);
        },
        close(ws) {
            const game = ws.data.game;
            const clients = wsClients.get(game);
            if (clients) {
                clients.delete(ws);
                if (clients.size === 0) wsClients.delete(game);
            }
            console.log(`[css-hot] ${game} disconnected (${wsClients.get(game)?.size ?? 0} clients)`);
        },
        message() { },
    },
    async fetch(req, server) {
        const url = new URL(req.url);
        const pathname = path.basename(url.pathname);
        const gameParam = url.searchParams.get('game');
        const game = (gameParam && await isGame(gameParam)) ? gameParam : 'index';

        switch (pathname) {
            case 'css-ws': {
                const upgraded = server.upgrade(req, {
                    data: { game } satisfies ClientData,
                });
                if (upgraded) return undefined;
                return new Response('Upgrade failed', { status: 500 });
            }
            case '':
            case '/':
            case 'index.html':
                try {
                    const detectedBrowser = browser(req.headers.get('user-agent') ?? '');
                    const html = await buildHTML(
                        game,
                        {
                            production: url.searchParams.get('production') === 'true',
                            mobile: detectedBrowser.mobile || url.searchParams.get('mobile') === 'true',
                            local: true,
                        }
                    );
                    if (html) {
                        return new Response(html, {
                            headers: {
                                'Content-Type': 'text/html;charset=utf-8'
                            }
                        });
                    }
                } catch (e) {
                    console.error(e);
                    return new Response(`Error building HTML: ${e}`, { status: 500 });
                }
                return new Response(`Error building HTML`, { status: 500 });
            default:
                console.log(`[Dev Server] requested unknown pathname: ${pathname}`);
                return new Response(null, { status: 404 });
        }
    }
});
