import { describe, expect, test } from "bun:test";
import { RPC_ERROR_CODES, isRetryableStatus } from "../src/errors.ts";
import { RPC_IDENTITY_HEADER } from "../src/identity.ts";
import { RPC_INTERNAL_HEADER, httpTransport, rpcPath } from "../src/http.ts";

const target = { app: "billing", service: "billing", procedure: "createInvoice" };

function transportWith(handler: (request: Request) => Response | Promise<Response>) {
  return httpTransport({
    resolveOrigin: () => "http://billing.test",
    fetch: (async (input: RequestInfo | URL, init?: RequestInit) =>
      handler(new Request(input, init))) as typeof fetch,
  });
}

describe("httpTransport", () => {
  test("posts to the private endpoint with identity", async () => {
    const transport = transportWith(async (request) => {
      expect(request.url).toBe("http://billing.test/__wrnexus/rpc/billing/createInvoice");
      expect(request.headers.get(RPC_IDENTITY_HEADER)).toBe("token");
      expect(request.headers.get("x-wrnexus-internal")).toBe("1");
      expect(await request.json()).toEqual({ amountCents: 5 });
      return Response.json({ ok: true, value: { invoiceId: "inv_1" } });
    });
    expect(await transport.call(target, { amountCents: 5 }, { identity: "token" })).toEqual({
      ok: true,
      value: { invoiceId: "inv_1" },
    });
  });

  test("classifies unavailable and malformed responses safely", async () => {
    const unavailable = transportWith(() => new Response("busy", { status: 503 }));
    const failed = await unavailable.call(target, {}, {});
    expect(failed).toMatchObject({ code: RPC_ERROR_CODES.transport, retryable: true });
    const malformed = transportWith(() => Response.json({ hello: "world" }));
    expect(await malformed.call(target, {}, {})).toMatchObject({
      code: RPC_ERROR_CODES.malformed,
      retryable: false,
    });
  });

  test("uses the stable reserved path", () => {
    expect(rpcPath("billing", "createInvoice")).toBe("/__wrnexus/rpc/billing/createInvoice");
  });

  test("omits the identity header entirely for an anonymous call", async () => {
    const transport = transportWith(async (request) => {
      expect(request.headers.has(RPC_IDENTITY_HEADER)).toBe(false);
      return Response.json({ ok: true, value: {} });
    });
    await transport.call(target, {}, {});
  });

  test("sets the internal-marker header", async () => {
    const transport = transportWith(async (request) => {
      expect(request.headers.get(RPC_INTERNAL_HEADER)).toBe("1");
      return Response.json({ ok: true, value: {} });
    });
    await transport.call(target, {}, {});
  });

  test("the full retryable-status sweep matches isRetryableStatus", async () => {
    // 600 is covered directly on isRetryableStatus in errors.test.ts — the
    // Fetch API cannot construct a Response with a status outside 200–599.
    const statuses = [200, 400, 403, 404, 408, 409, 429, 500, 503, 599];
    for (const status of statuses) {
      if (status === 200) continue; // handled by the success-path test above
      const transport = transportWith(() => new Response("x", { status }));
      const result = await transport.call(target, {}, {});
      expect(result).toMatchObject({
        ok: false,
        code: RPC_ERROR_CODES.transport,
        retryable: isRetryableStatus(status),
      });
    }
  });

  test("a network throw yields a structured failure with no host/address surviving", async () => {
    const transport = httpTransport({
      resolveOrigin: () => "http://internal-billing-host.private:4821",
      fetch: (async () => {
        throw new TypeError("fetch failed: connect ECONNREFUSED 10.0.0.7:4821");
      }) as unknown as typeof fetch,
    });
    const result = await transport.call(target, {}, {});
    expect(result).toMatchObject({ ok: false, code: RPC_ERROR_CODES.transport });
    if (!result.ok) {
      expect(result.message).not.toContain("10.0.0.7");
      expect(result.message).not.toContain("internal-billing-host");
    }
  });

  test("a malformed JSON body yields a structured failure with no body content surviving", async () => {
    const transport = transportWith(() => new Response("{not json", { status: 200 }));
    const result = await transport.call(target, {}, {});
    expect(result).toMatchObject({ ok: false, code: RPC_ERROR_CODES.malformed });
    if (!result.ok) expect(result.message).not.toContain("{not json");
  });

  test("an HTML error page yields a structured failure with no page content surviving", async () => {
    const transport = transportWith(
      () =>
        new Response("<html><body>500 Internal Server Error at db-host-42</body></html>", {
          status: 200,
          headers: { "content-type": "text/html" },
        }),
    );
    const result = await transport.call(target, {}, {});
    expect(result).toMatchObject({ ok: false, code: RPC_ERROR_CODES.malformed });
    if (!result.ok) expect(result.message).not.toContain("db-host-42");
  });

  test("the AbortSignal reaches fetch", async () => {
    const controller = new AbortController();
    let seenSignal: AbortSignal | undefined;
    const transport = transportWith(async (request) => {
      seenSignal = request.signal;
      return Response.json({ ok: true, value: {} });
    });
    await transport.call(target, {}, { signal: controller.signal });
    expect(seenSignal).toBeDefined();
  });
});
