import { CompilerOptions, transpile, TranspileOptions } from "jsr:@deno/emit";
import denoJson from "./deno.json" with { type: "json" };

const contentTypes = {
    "js": "application/javascript; charset=utf-8",

    // https://github.com/denoland/deno_ast/blob/ea1ccec37e1aa8e5e1e70f983a7ed1472d0e132a/src/media_type.rs#L117
    "ts": "text/typescript; charset=utf-8",
    "jsx": "text/jsx; charset=utf-8",
    "tsx": "text/tsx; charset=utf-8",
} as const;

const transpileOptions = (
    extension: keyof typeof contentTypes,
    tsCode: string,
    targetUrlStr: string,
): TranspileOptions => ({
    importMap: {
        imports: denoJson.imports,
    },
    compilerOptions: denoJson.compilerOptions as CompilerOptions,
    load(specifier) {
        const correctContent = specifier === targetUrlStr;
        return Promise.resolve({
            kind: "module",
            specifier,
            content: correctContent ? tsCode : "",
            headers: { "content-type": contentTypes[correctContent ? extension : "js"] },
        });
    },
});

export default async function transpileResponse(
    response: Response,
    requestUrl: string,
    filepath?: string,
): Promise<Response> {
    const url = new URL(`ts-serve:///${requestUrl}`);
    const pathname = filepath !== undefined ? filepath : url.pathname;
    const extension = pathname.split(".").at(-1) ?? "";
    if (response.status === 200 && (extension === "ts" || extension === "tsx" || extension === "jsx")) {
        const tsCode = await response.text();
        const targetUrlStr = url.toString();
        try {
            const result = await transpile(url, transpileOptions(extension, tsCode, targetUrlStr));
            const jsCode = result.get(targetUrlStr);
            response.headers.delete("content-length");
            response.headers.set("content-type", contentTypes.js);
            return new Response(jsCode, {
                status: response.status,
                statusText: response.statusText,
                headers: response.headers,
            });
        } catch (e) {
            console.error("[transpileResponse]: Error transpiling!\n", e);
            if (typeof e === "string") {
                return new Response("Internal Server Error", {
                    status: 500,
                    headers: response.headers,
                });
            }
        }
    }
    return response;
}
