import { describe, expect, it } from "vitest";
import fixtures from "../../../baseline/schemas/v2/route-response-fixtures.json" assert { type: "json" };
import generated from "../../generated/route-registry.json" assert { type: "json" };
import openapi from "../../generated/openapi.json" assert { type: "json" };

type FixtureStatus = "exact" | "unresolved";
type Fixture = { path: string; fixtureStatus: FixtureStatus };
type GeneratedRoute = {
  version: string;
  path: string;
  method: string;
  responseSchema: string | null;
  responseFixtureStatus?: FixtureStatus;
  evidenceKind?: "x402-manifest" | "source-method";
  sourceEvidence?: { file: string; sha256: string };
};
type AlignmentEntry = {
  path: string;
  registryStatus: "operational" | "method-only" | "unresolved";
  registryRouteIds: string[];
  responseFixtureStatus: FixtureStatus;
};
type Alignment = {
  inventory: { routeCount: number };
  registeredRouteCount: number;
  operationalRouteCount: number;
  methodOnlyRouteCount: number;
  unresolvedRouteCount: number;
  registryOnlyRouteCount: number;
  entries: AlignmentEntry[];
  unresolvedRoutes: string[];
};

const fixtureByPath = new Map((fixtures.fixtures as Fixture[]).map((fixture) => [fixture.path, fixture]));
const alignment = generated.v2InventoryAlignment as Alignment;
const generatedV2 = (generated.routes as GeneratedRoute[]).filter((route) => route.version === "v2");

describe("generated V2 registry alignment", () => {
  it("accounts for every inventory route without upgrading source methods into operational metadata", () => {
    expect(alignment.inventory.routeCount).toBe(fixtures.inventory.routeCount);
    expect(alignment.entries).toHaveLength(fixtures.inventory.routeCount);
    expect(alignment.registeredRouteCount + alignment.unresolvedRouteCount).toBe(fixtures.inventory.routeCount);
    expect(alignment.registeredRouteCount).toBe(132);
    expect(alignment.operationalRouteCount).toBe(82);
    expect(alignment.methodOnlyRouteCount).toBe(50);
    expect(alignment.unresolvedRouteCount).toBe(0);
    expect(alignment.registryOnlyRouteCount).toBe(6);

    for (const entry of alignment.entries) {
      const fixture = fixtureByPath.get(entry.path);
      expect(fixture, entry.path).toBeDefined();
      expect(entry.responseFixtureStatus).toBe(fixture!.fixtureStatus);
      expect(entry.registryStatus === "unresolved").toBe(entry.registryRouteIds.length === 0);
    }
  });

  it("keeps x402 metadata and source-method evidence distinct from response schemas", () => {
    const x402Routes = generatedV2.filter((route) => route.evidenceKind === "x402-manifest");
    const sourceMethodRoutes = generatedV2.filter((route) => route.evidenceKind === "source-method");
    expect(x402Routes).toHaveLength(89);
    expect(sourceMethodRoutes).toHaveLength(55);

    for (const route of x402Routes) {
      expect(route.responseSchema).toBeNull();
      expect(route.responseFixtureStatus).toBe("unresolved");
    }

    const contracts = alignment.entries.find((entry) => entry.path === "/api/contracts")!;
    expect(contracts).toMatchObject({ registryStatus: "method-only", responseFixtureStatus: "exact" });
    expect(sourceMethodRoutes.find((route) => route.path === "/api/contracts")).toMatchObject({
      method: "GET",
      responseSchema: null,
      responseFixtureStatus: "exact",
      sourceEvidence: {
        file: "repos/theloopbreaker-site/app/api/contracts/route.ts",
        sha256: "9644887421f6b756a79d905da41529b53556ee923f9572560da744ee6c90be29",
      },
    });

    const webhooks = sourceMethodRoutes.filter((route) => route.path === "/api/agent/webhooks");
    expect(webhooks.map((route) => route.method)).toEqual(["POST", "GET", "DELETE"]);
    for (const route of sourceMethodRoutes) {
      expect(route.sourceEvidence?.sha256).toMatch(/^[0-9a-f]{64}$/);
      expect(route.sourceEvidence?.file).toMatch(/^repos\/theloopbreaker-site\/app\/api\//);
    }

    const operation = openapi.paths["/api/x402/trust/bonds"].get as Record<string, unknown>;
    expect(operation["x-response-fixture-status"]).toBe("unresolved");
    const response = (operation.responses as Record<string, Record<string, unknown>>)["200"];
    expect(response.content).toBeUndefined();
    expect(response.description).toContain("unresolved");

    expect((openapi.paths as Record<string, unknown>)["/api/agent/webhooks"]).toBeUndefined();

    const contractCall = x402Routes.filter((route) => route.path === "/api/x402/contract-call");
    expect(contractCall.map((route) => route.method).sort()).toEqual(["GET", "POST"]);
    expect(openapi.paths["/api/x402/contract-call"].get).toBeDefined();
    expect(openapi.paths["/api/x402/contract-call"].post).toBeDefined();
  });
});
