import { execFile } from "node:child_process";
import { createServer } from "node:http";
import { readFile, stat } from "node:fs/promises";
import { dirname, extname, relative, resolve } from "node:path";
import { fileURLToPath } from "node:url";

const root = resolve(dirname(fileURLToPath(import.meta.url)), "web", "dist");
const mimeTypes = {
    ".css": "text/css; charset=utf-8",
    ".html": "text/html; charset=utf-8",
    ".js": "text/javascript; charset=utf-8",
    ".json": "application/json; charset=utf-8",
    ".svg": "image/svg+xml",
    ".png": "image/png",
    ".jpg": "image/jpeg",
    ".jpeg": "image/jpeg",
    ".webp": "image/webp",
    ".ico": "image/x-icon",
    ".woff2": "font/woff2",
};

function openBrowser(url) {
    execFile("cmd.exe", ["/c", "start", "", url], { windowsHide: true }, () => {});
}

async function responseFile(pathname) {
    const requested = pathname === "/" ? "index.html" : pathname.replace(/^\/+/, "");
    const filePath = resolve(root, requested);
    if (relative(root, filePath).startsWith("..")) return null;
    try {
        const info = await stat(filePath);
        return info.isFile() ? filePath : null;
    } catch {
        return null;
    }
}

const server = createServer(async (request, response) => {
    try {
        const url = new URL(request.url || "/", "http://127.0.0.1");
        let filePath = await responseFile(decodeURIComponent(url.pathname));
        if (!filePath && !extname(url.pathname)) filePath = resolve(root, "index.html");
        if (!filePath) {
            response.writeHead(404).end("Not found");
            return;
        }
        const body = await readFile(filePath);
        response.writeHead(200, { "Content-Type": mimeTypes[extname(filePath)] || "application/octet-stream", "Cache-Control": "no-cache" });
        response.end(request.method === "HEAD" ? undefined : body);
    } catch {
        response.writeHead(500).end("Unable to load the application.");
    }
});

function listen(port) {
    const onListening = () => {
        server.off("error", onError);
        const url = `http://127.0.0.1:${port}`;
        console.log(`Infinite Canvas is ready: ${url}`);
        console.log("Keep this window open while using Infinite Canvas. Press Ctrl+C to stop.");
        if (process.env.INFINITE_CANVAS_NO_BROWSER !== "1") openBrowser(url);
    };
    const onError = (error) => {
        server.off("listening", onListening);
        if (error.code === "EADDRINUSE" && port < 3010) listen(port + 1);
        else throw error;
    };
    server.once("listening", onListening);
    server.once("error", onError);
    server.listen(port, "127.0.0.1");
}

listen(3000);
