/**
 * Web server factory module.
 * Exports a function to create and start the web server.
 * This allows the server to be started in-process from the main application.
 */

import { serve, spawn, type Subprocess } from "bun";
import { join, resolve, dirname } from "path";

export interface WebServerConfig {
    port?: number;
    hostname?: string;
}

export interface WebServerInstance {
    server: ReturnType<typeof serve>;
    stop: () => Promise<void>;
    url: string;
}

/**
 * Creates and starts the web server.
 * 
 * Automatically handles building the frontend:
 * - In development: Spawns 'bun run build.ts --watch'
 * - In production: Assumes 'dist' is already built (or builds once)
 */
export async function createWebServer(config: WebServerConfig = {}): Promise<WebServerInstance> {
    const { port = 3000, hostname = "localhost" } = config;

    // Resolve directories
    // server.ts is in web/src/, so we go up one level to get web/
    const currentDir = dirname(new URL(import.meta.url).pathname);
    const webRoot = resolve(currentDir, "..");
    const distDir = join(webRoot, "dist");

    // Manage build process in development
    let buildProcess: Subprocess | undefined;
    const isDev = process.env.NODE_ENV !== "production";

    if (isDev) {
        console.log("🛠️  Starting Web Bundler in Watch Mode...");
        try {
            buildProcess = spawn(["bun", "run", "build.ts", "--watch"], {
                cwd: webRoot,
                stdout: "inherit",
                stderr: "inherit",
            });
        } catch (error) {
            console.error("Failed to start build process:", error);
        }
    }

    const server = serve({
        port,
        hostname,
        async fetch(req) {
            const url = new URL(req.url);

            // API routes
            if (url.pathname === "/api/health") {
                return Response.json({ status: "ok", timestamp: Date.now() });
            }

            // Static File Serving
            let pathName = url.pathname;
            if (pathName === "/") pathName = "/index.html";

            // Construct safe file path
            // Remove leading slash to join correctly
            const safePath = join(distDir, pathName.replace(/^\//, ""));

            // Security check: ensure path is within distDir
            if (!safePath.startsWith(distDir)) {
                return new Response("Forbidden", { status: 403 });
            }

            const fileRef = Bun.file(safePath);
            if (await fileRef.exists()) {
                return new Response(fileRef);
            }

            // SPA Fallback: Serve index.html for unknown non-file routes
            // If the path looks like a file (has extension), return 404
            // Otherwise serve index.html
            const parts = pathName.split("/");
            const lastPart = parts[parts.length - 1];
            if (lastPart?.includes(".")) {
                return new Response("Not Found", { status: 404 });
            }

            return new Response(Bun.file(join(distDir, "index.html")));
        },

        development: isDev,
    });

    const url = `http://${hostname}:${port}`;

    return {
        server,
        url,
        stop: async () => {
            if (buildProcess) {
                buildProcess.kill();
            }
            server.stop(true);
        },
    };
}

/**
 * Starts the web server from the main application root.
 * Kept for backward compatibility, but assumes webProjectPath is handled internally or ignored
 * in favor of relative path resolution from this file.
 */
export async function startWebServerFromRoot(
    webProjectPath: string,
    config: WebServerConfig = {}
): Promise<WebServerInstance> {
    // Current implementation doesn't need CWD switching thanks to absolute path resolution
    return createWebServer(config);
}
