import { expect, test } from "bun:test";
import { mkdtempSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { createProductionHandlers, type ProdManifest } from "../src/prod.ts";

const manifest: ProdManifest = {
  pages: [
    {
      raw: "/",
      mod: { default: () => "<main>Production artifact</main>", meta: { title: "Prod" } },
    },
    {
      raw: "/partial",
      staticShell:
        '<main>Build shell<template data-wrn-dynamic-placeholder="wrn-region-0"></template></main>',
      mod: {
        default: () =>
          '<main>Request shell<wrn-dynamic-region data-wrn-dynamic="true"><strong>User 42</strong></wrn-dynamic-region></main>',
        meta: { title: "Partial" },
        __wrnexusRender: "partial-static",
      },
    },
    {
      raw: "/async",
      mod: {
        default: () => "<main>Async page</main>",
        meta: { title: "Async" },
        __wrnexusClientLoad: async () => ({ users: [{ id: 1, name: "Ada" }] }),
      },
    },
  ],
  api: [],
  realtime: [],
  middleware: [],
  components: [],
  layouts: [],
};

const server = { upgrade: () => false } as never;

test("supervised production runtime injects reconnecting DOM-morph support", async () => {
  const handlers = createProductionHandlers(manifest, { developmentRuntime: true });
  const response = await handlers.fetch(new Request("http://localhost/"), server);
  expect(response).toBeInstanceOf(Response);
  expect(await (response as Response).text()).toContain("/__wrnexus/hmr");
});

test("production streams request regions into the build-time static shell", async () => {
  const handlers = createProductionHandlers(manifest, {});
  const response = (await handlers.fetch(
    new Request("http://localhost/partial"),
    server,
  )) as Response;
  const html = await response.text();
  expect(response.headers.get("x-wrnexus-static-shell")).toBe("build");
  expect(html).toContain("Build shell");
  expect(html).not.toContain("Request shell");
  expect(html).toContain("User 42");
});

test("normal production output remains free of development HMR", async () => {
  const handlers = createProductionHandlers(manifest, {});
  const response = await handlers.fetch(new Request("http://localhost/"), server);
  const html = await (response as Response).text();
  expect(html).not.toContain("/__wrnexus/hmr");
  expect(html).toContain('data-wrn-route-params="{}"');
});

test("production client-load endpoint returns only the requested named result", async () => {
  const handlers = createProductionHandlers(manifest, {});
  const response = (await handlers.fetch(
    new Request("http://localhost/__wrnexus/client-load?route=%2Fasync&name=users"),
    server,
  )) as Response;
  expect(response.status).toBe(200);
  expect(response.headers.get("cache-control")).toBe("private, no-store");
  expect(await response.json()).toEqual({ data: [{ id: 1, name: "Ada" }] });
});

test("production prefers fast gzip for dynamic HTML and cached Brotli for immutable assets", async () => {
  const largeManifest: ProdManifest = {
    ...manifest,
    pages: [
      {
        raw: "/large",
        mod: { default: () => `<main>${"fast production ".repeat(500)}</main>` },
      },
    ],
  };
  const directory = mkdtempSync(join(tmpdir(), "wrnexus-prod-assets-"));
  const assetPath = join(directory, "app.js");
  writeFileSync(assetPath, "const production = true;\n".repeat(500));
  const handlers = createProductionHandlers(largeManifest, {
    pluginAssets: {
      "/assets/app.js": {
        path: assetPath,
        contentType: "text/javascript; charset=utf-8",
      },
    },
  });
  const headers = { "accept-encoding": "br, gzip" };
  const page = (await handlers.fetch(
    new Request("http://localhost/large", { headers }),
    server,
  )) as Response;
  expect(page.headers.get("content-encoding")).toBe("gzip");

  const firstAsset = (await handlers.fetch(
    new Request("http://localhost/assets/app.js", { headers }),
    server,
  )) as Response;
  const secondAsset = (await handlers.fetch(
    new Request("http://localhost/assets/app.js", { headers }),
    server,
  )) as Response;
  expect(firstAsset.headers.get("content-encoding")).toBe("br");
  expect(await secondAsset.arrayBuffer()).toEqual(await firstAsset.arrayBuffer());

  const head = (await handlers.fetch(
    new Request("http://localhost/assets/app.js", { method: "HEAD", headers }),
    server,
  )) as Response;
  expect(head.headers.get("content-encoding")).toBeNull();
});

test("client-load preserves the active route query string", async () => {
  const queryManifest: ProdManifest = {
    ...manifest,
    pages: [
      ...manifest.pages,
      {
        raw: "/query",
        mod: {
          default: () => "<main>Query page</main>",
          __wrnexusClientLoad: async (ctx: { url: URL }) => ({
            identity: ctx.url.searchParams.get("as"),
          }),
        },
      },
    ],
  };
  const handlers = createProductionHandlers(queryManifest, {});
  const response = (await handlers.fetch(
    new Request(
      "http://localhost/__wrnexus/client-load?route=%2Fquery&search=%3Fas%3Drohan&name=identity",
    ),
    server,
  )) as Response;
  expect(response.status).toBe(200);
  expect(await response.json()).toEqual({ data: "rohan" });
});

test("production serves generated WRN browser modules from the client directory", async () => {
  const directory = mkdtempSync(join(tmpdir(), "wrnexus-client-modules-"));
  writeFileSync(join(directory, "settings-abc123.mjs"), "export const ready = true;\n");
  const handlers = createProductionHandlers(manifest, { clientModulesDir: directory });
  const response = (await handlers.fetch(
    new Request("http://localhost/__wrnexus/client/settings-abc123.mjs"),
    server,
  )) as Response;
  expect(response.status).toBe(200);
  expect(response.headers.get("content-type")).toContain("text/javascript");
  expect(await response.text()).toContain("ready = true");
});
