import { isNumberString, ok } from "../../is.js";
import { SERVICE_WORKER_ID, SERVICE_WORKER_URL, SERVICE_WORKER_LISTEN_PORT, SERVICE_WORKER_LISTEN_HOSTNAME } from "../../config/index.js";
import { createServer } from "node:http";
import { createRespondWith } from "../../fetch/index.js";
import { dispatchEvent } from "../../events/index.js";
import { ReadableStream } from "node:stream/web";
import { Readable } from "node:stream";
import { pipeline } from "node:stream/promises";
import { importServiceWorker } from "./import.js";
const DEFAULT_OPTIONS = {
    id: SERVICE_WORKER_ID,
    url: SERVICE_WORKER_URL,
    listen: {
        hostname: SERVICE_WORKER_LISTEN_HOSTNAME,
        port: SERVICE_WORKER_LISTEN_PORT
    }
};
export async function start(options = DEFAULT_OPTIONS) {
    const { url } = options;
    ok(url, "Expected SERVICE_WORKER_URL");
    const registration = await importServiceWorker(url);
    let close = async () => { };
    if (isServiceWorkerListen(options)) {
        close = await listen({
            ...options,
            id: registration.durable.serviceWorkerId,
            listen: {
                origin: registration.durable.origin || registration.durable.baseURL || registration.durable.url,
                ...options.listen
            }
        });
    }
    return close;
}
function isServiceWorkerListen(options = DEFAULT_OPTIONS) {
    // Can be 0, which means, allocate port automatically
    return isNumberString(DEFAULT_OPTIONS.listen.port);
}
export async function listen({ id: serviceWorkerId, listen: { port, hostname, origin } }, dispatch = dispatchEvent) {
    const server = createServer(onServerMessage);
    await new Promise(resolve => {
        server.listen(Number(port), hostname, resolve);
    });
    return () => new Promise(resolve => server.close(() => resolve()));
    // async but local try catch finally
    async function onServerMessage(incomingMessage, serverResponse) {
        try {
            const { respondWith, promise, handled } = createRespondWith();
            const headers = new Headers(Object.entries(incomingMessage.headers)
                .map(([key, value]) => [
                key,
                Array.isArray(value) ? value.join(", ") : value
            ]));
            let body = undefined;
            const method = incomingMessage.method.toLowerCase();
            if (method !== "get" && method !== "head") {
                ok(ReadableStream);
                ok(ReadableStream.from, "Expected node 20.6.0 with ReadableStream.from");
                body = ReadableStream.from(incomingMessage);
            }
            const init = {
                method,
                body,
                duplex: "half",
                headers
            };
            const eventPromise = dispatch({
                type: "fetch",
                request: new Request(new URL(incomingMessage.url, origin || "file://"), init),
                respondWith,
                handled,
                serviceWorkerId,
                virtual: true
            });
            const response = await Promise.race([
                promise,
                // Non resolving, but throwable promise
                eventPromise.then(() => new Promise(() => { }))
            ]);
            const nodeReadable = Readable;
            ok(nodeReadable);
            response.headers.forEach((value, key) => {
                serverResponse.appendHeader(key, value);
            });
            serverResponse.writeHead(response.status, response.statusText);
            if (response.body) {
                const stream = nodeReadable.fromWeb(response.body);
                await pipeline(stream, serverResponse);
            }
            else {
                serverResponse.end();
            }
            await eventPromise;
        }
        catch (error) {
            console.error(error);
            if (!serverResponse.headersSent) {
                serverResponse.writeHead(500);
                serverResponse.end(String(error));
            }
            else {
                // ??
                serverResponse.end();
            }
        }
    }
}
//# sourceMappingURL=start.js.map