import { Readable } from "node:stream";
import { gunzipSync } from "node:zlib";
import { describe, expect, it, vi } from "vitest";
import { appTestContext, fakeRemoteClient, registerAppTestHooks } from "./app.testSupport.js";

registerAppTestHooks();

describe("browser-facing HTTP compression", () => {
  it("negotiates compression for large local-machine API responses", async () => {
    const marker = "local transcript content ".repeat(256);
    appTestContext.piWebConfig = {
      plugins: { fake: { settings: { marker } } },
    };

    const compressed = await appTestContext.app.inject({
      method: "GET",
      url: "/api/machines/local/config",
      headers: { "accept-encoding": "gzip" },
    });
    const identity = await appTestContext.app.inject({
      method: "GET",
      url: "/api/machines/local/config",
      headers: { "accept-encoding": "identity" },
    });

    expect(compressed.statusCode).toBe(200);
    expect(compressed.headers["content-encoding"]).toBe("gzip");
    expect(compressed.headers["content-length"]).toBeUndefined();
    expect(compressed.headers.vary).toContain("accept-encoding");
    expect(gunzipJson(compressed)).toMatchObject({ effectiveConfig: { plugins: { fake: { settings: { marker } } } } });

    expect(identity.statusCode).toBe(200);
    expect(identity.headers["content-encoding"]).toBeUndefined();
    expect(identity.json()).toMatchObject({ effectiveConfig: { plugins: { fake: { settings: { marker } } } } });
  });

  it("negotiates compression after streaming a remote-machine API response", async () => {
    const addResponse = await appTestContext.app.inject({
      method: "POST",
      url: "/api/machines",
      payload: { name: "Remote", baseUrl: "https://remote.example.test/" },
    });
    const remote = addResponse.json<{ id: string }>();
    const projects = Array.from({ length: 64 }, (_, index) => ({
      id: `p-${String(index)}`,
      name: `Remote project ${String(index)}`,
      path: `/repos/project-${String(index)}`,
      createdAt: "2026-07-11T00:00:00.000Z",
    }));
    const body = JSON.stringify(projects);
    const request = vi.fn(() => Promise.resolve({
      statusCode: 200,
      headers: {
        "content-type": "application/json",
        "content-length": String(Buffer.byteLength(body)),
      },
      body: Readable.from([body]),
    }));
    appTestContext.remoteClient = fakeRemoteClient({ request });
    const url = `/api/machines/${remote.id}/projects`;

    const compressed = await appTestContext.app.inject({
      method: "GET",
      url,
      headers: { "accept-encoding": "gzip" },
    });
    const identity = await appTestContext.app.inject({
      method: "GET",
      url,
      headers: { "accept-encoding": "identity" },
    });

    expect(compressed.statusCode).toBe(200);
    expect(compressed.headers["content-encoding"]).toBe("gzip");
    expect(compressed.headers["content-length"]).toBeUndefined();
    expect(compressed.headers.vary).toContain("accept-encoding");
    expect(gunzipJson(compressed)).toEqual(projects);

    expect(identity.statusCode).toBe(200);
    expect(identity.headers["content-encoding"]).toBeUndefined();
    expect(identity.json()).toEqual(projects);
    expect(request).toHaveBeenNthCalledWith(1, "GET", "/api/projects", undefined);
    expect(request).toHaveBeenNthCalledWith(2, "GET", "/api/projects", undefined);
  });
});

function gunzipJson(response: { rawPayload: Buffer }): unknown {
  const value: unknown = JSON.parse(gunzipSync(response.rawPayload).toString("utf8"));
  return value;
}
