import * as http from "http";
import * as https from "https";
import httpProxy from "http-proxy";
import devcert from "devcert";
const HTTP_PORT = 80;
const HTTPS_PORT = 443;
const DOMAIN_SUFFIX = ".local.spcf.app";
export async function startHttpProxy(services) {
    // Build service lookup map: name -> port
    const serviceMap = new Map();
    const domains = [];
    for (const svc of services) {
        serviceMap.set(svc.name, svc.port);
        domains.push(`${svc.name}${DOMAIN_SUFFIX}`);
    }
    // Get TLS certificate from devcert for all service domains
    // Use skipCertutil to avoid sudo prompts - CA was already installed during `specific init`
    let key;
    let cert;
    try {
        const result = await devcert.certificateFor(domains, { skipCertutil: true });
        key = result.key;
        cert = result.cert;
    }
    catch (err) {
        const message = err instanceof Error ? err.message : String(err);
        // Check if this is a CA installation issue
        if (message.includes("sudo") || message.includes("password") || message.includes("CA")) {
            throw new Error(`TLS certificate authority not installed. Run 'specific init' first to set up TLS certificates.`);
        }
        throw err;
    }
    // Create the proxy server
    const proxy = httpProxy.createProxyServer({});
    // Handle proxy errors
    proxy.on("error", (err, req, res) => {
        if (res instanceof http.ServerResponse && !res.headersSent) {
            res.writeHead(502, { "Content-Type": "text/html" });
            res.end(`<h1>Bad Gateway</h1><p>Service unavailable: ${err.message}</p>`);
        }
    });
    // Shared request handler
    const handleRequest = (req, res) => {
        const host = req.headers.host || "";
        const serviceName = extractServiceName(host);
        if (!serviceName) {
            return sendNotFound(res, null, services);
        }
        const targetPort = serviceMap.get(serviceName);
        if (!targetPort) {
            return sendNotFound(res, serviceName, services);
        }
        proxy.web(req, res, { target: `http://127.0.0.1:${targetPort}` });
    };
    // Shared WebSocket upgrade handler
    const handleUpgrade = (req, socket, head) => {
        const host = req.headers.host || "";
        const serviceName = extractServiceName(host);
        if (!serviceName) {
            socket.end("HTTP/1.1 404 Not Found\r\n\r\n");
            return;
        }
        const targetPort = serviceMap.get(serviceName);
        if (!targetPort) {
            socket.end("HTTP/1.1 404 Not Found\r\n\r\n");
            return;
        }
        proxy.ws(req, socket, head, { target: `http://127.0.0.1:${targetPort}` });
    };
    // Create HTTP server (port 80)
    const httpServer = http.createServer(handleRequest);
    httpServer.on("upgrade", handleUpgrade);
    // Create HTTPS server (port 443)
    const httpsServer = https.createServer({ key, cert }, handleRequest);
    httpsServer.on("upgrade", handleUpgrade);
    // Start both servers
    return new Promise((resolve, reject) => {
        let httpStarted = false;
        let httpsStarted = false;
        let failed = false;
        const checkReady = () => {
            if (failed)
                return;
            if (httpStarted && httpsStarted) {
                resolve({
                    httpPort: HTTP_PORT,
                    httpsPort: HTTPS_PORT,
                    async stop() {
                        return new Promise((resolve) => {
                            let closed = 0;
                            const onClose = () => {
                                closed++;
                                if (closed === 2)
                                    resolve();
                            };
                            httpServer.close(onClose);
                            httpsServer.close(onClose);
                            // Force close after timeout
                            setTimeout(() => {
                                httpServer.closeAllConnections?.();
                                httpsServer.closeAllConnections?.();
                                resolve();
                            }, 3000);
                        });
                    },
                });
            }
        };
        const handleError = (port) => (err) => {
            if (failed)
                return;
            failed = true;
            if (err.code === "EACCES") {
                reject(new Error(`Cannot bind to port ${port}. ` +
                    `Try running with sudo: sudo specific dev`));
            }
            else if (err.code === "EADDRINUSE") {
                reject(new Error(`Port ${port} is already in use`));
            }
            else {
                reject(err);
            }
        };
        httpServer.on("error", handleError(HTTP_PORT));
        httpsServer.on("error", handleError(HTTPS_PORT));
        httpServer.listen(HTTP_PORT, () => {
            httpStarted = true;
            checkReady();
        });
        httpsServer.listen(HTTPS_PORT, () => {
            httpsStarted = true;
            checkReady();
        });
    });
}
function extractServiceName(host) {
    // Remove port if present
    const hostname = host.split(":")[0];
    if (!hostname || !hostname.endsWith(DOMAIN_SUFFIX)) {
        return null;
    }
    return hostname.slice(0, -DOMAIN_SUFFIX.length);
}
function sendNotFound(res, requestedService, availableServices) {
    const serviceList = availableServices
        .map((s) => `<li>${s.name}.local.spcf.app</li>`)
        .join("\n");
    const message = requestedService
        ? `No service named "${requestedService}" is running.`
        : "Invalid host header.";
    res.writeHead(404, { "Content-Type": "text/html" });
    res.end(`<!DOCTYPE html>
<html>
<head><title>Service Not Found</title></head>
<body>
  <h1>Service Not Found</h1>
  <p>${message}</p>
  ${availableServices.length > 0
        ? `<p>Available services:</p>
  <ul>${serviceList}</ul>`
        : "<p>No services are currently exposed.</p>"}
</body>
</html>`);
}
//# sourceMappingURL=http-proxy.js.map