import assert from "node:assert/strict";
import { describe, it } from "node:test";
import {
  fetchEphemeralIceConfiguration,
  IceConfigurationUnavailableError,
} from "../media/iceClient.ts";

const authorization = {
  binding: { officeId: "lumbridge-hq", levelId: "level-1", roomId: "commons", screenId: "wall-display" },
  credential: {
    sessionId: "share-session", participantId: "viewer-opaque", role: "viewer" as const,
    grantToken: "private-viewer-grant",
  },
};

describe("ephemeral ICE configuration client", () => {
  it("posts the exact authenticated request and returns an in-memory RTC configuration", async () => {
    let input: RequestInfo | URL | undefined;
    let init: RequestInit | undefined;
    const result = await fetchEphemeralIceConfiguration({
      authenticatedFetch: async (nextInput, nextInit) => {
        input = nextInput;
        init = nextInit;
        return Response.json({
          type: "ice-config-grant",
          protocolVersion: 1,
          requestId: "ice-test-1",
          issuedAtMs: 100,
          expiresAtMs: 600_000,
          iceServers: [
            { urls: ["stun:relay.example.test:3478"] },
            {
              urls: ["turns:relay.example.test:5349"],
              username: "temporary-user-01",
              credential: "temporary-password-01",
              credentialType: "password",
            },
          ],
        });
      },
      now: () => 200,
      requestId: () => "ice-test-1",
      ...authorization,
    });
    assert.equal(input, "/api/v1/media/ice");
    assert.equal(init?.method, "POST");
    assert.equal(init?.credentials, "same-origin");
    assert.equal(init?.cache, "no-store");
    assert.deepEqual(JSON.parse(String(init?.body)), {
      type: "ice-config-request",
      protocolVersion: 1,
      requestId: "ice-test-1",
      ...authorization,
    });
    assert.deepEqual(result.configuration.iceServers, [
      { urls: ["stun:relay.example.test:3478"] },
      {
        urls: ["turns:relay.example.test:5349"],
        username: "temporary-user-01",
        credential: "temporary-password-01",
        credentialType: "password",
      },
    ]);
    assert.equal(result.expiresAtMs, 600_000);
  });

  it("rejects unavailable, mismatched, expired, and malformed responses", async () => {
    const run = (body: unknown) => fetchEphemeralIceConfiguration({
      authenticatedFetch: async () => Response.json(body),
      now: () => 200,
      requestId: () => "ice-test-1",
      ...authorization,
    });
    await assert.rejects(run({
      type: "ice-config-unavailable",
      protocolVersion: 1,
      requestId: "ice-test-1",
      retryAfterMs: 10_000,
    }), /temporarily unavailable/);
    await assert.rejects(run({
      type: "ice-config-unavailable",
      protocolVersion: 1,
      requestId: "ice-test-2",
      retryAfterMs: 10_000,
    }), /did not match/);
    await assert.rejects(run({
      type: "ice-config-grant",
      protocolVersion: 1,
      requestId: "ice-test-1",
      issuedAtMs: 10,
      expiresAtMs: 100,
      iceServers: [{
        urls: ["turn:relay.example.test:3478"],
        username: "temporary-user-01",
        credential: "temporary-password-01",
        credentialType: "password",
      }],
    }), /not active/);
    await assert.rejects(run({ profile: "must-not-parse" }), /invalid response/);
  });

  it("exposes validated 429 and 503 retry timing without exposing an untrusted body", async () => {
    for (const status of [429, 503]) {
      await assert.rejects(fetchEphemeralIceConfiguration({
        authenticatedFetch: async () => Response.json({
          type: "ice-config-unavailable",
          protocolVersion: 1,
          requestId: "ice-backoff-1",
          retryAfterMs: 12_345,
        }, { status }),
        requestId: () => "ice-backoff-1",
        ...authorization,
      }), (error: unknown) => {
        assert.ok(error instanceof IceConfigurationUnavailableError);
        assert.equal(error.retryAfterMs, 12_345);
        assert.equal(error.status, status);
        return true;
      });
    }

    await assert.rejects(fetchEphemeralIceConfiguration({
      authenticatedFetch: async () => Response.json({
        type: "ice-config-unavailable",
        protocolVersion: 1,
        requestId: "somebody-elses-request",
        retryAfterMs: 12_345,
      }, { status: 503 }),
      requestId: () => "ice-backoff-1",
      ...authorization,
    }), /did not match/);

    await assert.rejects(fetchEphemeralIceConfiguration({
      authenticatedFetch: async () => Response.json({ retryAfterMs: 1 }, { status: 503 }),
      requestId: () => "ice-backoff-1",
      ...authorization,
    }), /failed \(503\)/);
  });
});
