import { describe, expect, it } from "vitest";
import bazaar from "../../generated/coinbase-bazaar-v2.json" assert { type: "json" };
import registry from "../../generated/route-registry.json" assert { type: "json" };
import v3PublicationGate from "../../generated/v3-bazaar-publication-gate.json" assert { type: "json" };

type Payment = { network: string; asset: string; payTo: string; scheme: string };
type Resource = {
  resource: string;
  method: "GET" | "POST";
  routeTemplate: string;
  resourceVersion: string;
  protocolVersion: string;
  x402Version: number;
  solanaPaymentStatus: string;
  accepts: Payment[];
  extensions: { bazaar: { info: { method: string; path: string; input: Record<string, unknown>; output: { example: unknown } } } };
};

type SourceEvidence = { path: string; sha256: string };
type Route = {
  id: string;
  method: string;
  path: string;
  free: boolean;
  version: string;
  bazaarEligibility: string;
  solanaPaymentStatus: string;
  inputSchema?: Record<string, unknown>;
  inputSchemaEvidence?: SourceEvidence | { sources: Array<SourceEvidence & { symbol: string }> };
};

const resources = bazaar.resources as Resource[];
const routes = registry.routes as Route[];
const solanaMainnet = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp";

describe("Coinbase Bazaar x402 v2 discovery artifacts", () => {
  it("keeps the V3 publication gate closed without objective listing and deployment evidence", () => {
    expect(v3PublicationGate).toMatchObject({
      schema: "vaultfire.v3.x402.publication-gate.v1",
      schemaVersion: "1.0.0",
      candidateCount: 0,
      publicationEligible: false,
      listingReceiptIds: [],
      deploymentEvidenceIds: [],
      claim: "no-v3-bazaar-listing-or-settlement",
    });
  });

  it("keeps contract protocol, Bazaar resource, and x402 rail versions distinct", () => {
    expect(bazaar.x402Version).toBe(2);
    expect(bazaar.bazaarResourceVersion).toBe("v2");
    expect(bazaar.publicationState).toBe("metadata-candidates-not-live-validated");
    expect(resources.some((resource) => resource.protocolVersion === "v3")).toBe(false);
    for (const resource of resources) {
      expect(resource.x402Version).toBe(2);
      expect(resource.resourceVersion).toBe("v2");
      expect(resource.protocolVersion).toBe("v2");
    }
  });

  it("emits an explicit HTTP method/path and usable input/output metadata for every candidate", () => {
    for (const resource of resources) {
      expect(resource.resource).toBe(`https://theloopbreaker.com${resource.routeTemplate}`);
      const info = resource.extensions.bazaar.info;
      expect(info.method).toBe(resource.method);
      expect(info.path).toBe(resource.routeTemplate);
      expect(info.input).toMatchObject({ type: "http", method: resource.method });
      if (resource.method === "GET") expect(info.input).toHaveProperty(resource.routeTemplate.includes(":") ? "pathParams" : "queryParams");
      if (resource.method === "POST") expect(info.input).toMatchObject({ bodyType: "json" });
      expect(info.output).toHaveProperty("example");
    }
  });

  it("advertises Solana settlement only with a canonical source-backed Solana asset and payTo", () => {
    let solanaCount = 0;
    for (const resource of resources) {
      const solana = resource.accepts.filter((option) => option.network === solanaMainnet);
      if (solana.length) {
        solanaCount += 1;
        expect(resource.solanaPaymentStatus).toBe("metadata-configured-unverified");
        expect(solana).toHaveLength(1);
        expect(solana[0]).toMatchObject({ scheme: "exact" });
        expect(solana[0].asset).toMatch(/^[1-9A-HJ-NP-Za-km-z]{32,44}$/);
        expect(solana[0].payTo).toMatch(/^[1-9A-HJ-NP-Za-km-z]{32,44}$/);
      } else {
        expect(resource.solanaPaymentStatus).toBe("read-only-api-no-solana-settlement");
      }
    }
    expect(solanaCount).toBeGreaterThan(0);
  });

  it("requires source-bound schemas before publishing a paid POST resource", () => {
    const candidates = new Set(resources.map((resource) => `${resource.method} ${resource.routeTemplate}`));
    const blocked = routes.filter((route) => !route.free && route.bazaarEligibility === "blocked-input-schema-unresolved");
    expect(blocked).toEqual([]);
    for (const route of blocked) expect(candidates.has(`${route.method} ${route.path}`)).toBe(false);

    const recoveredV2Actions = routes.filter((route) =>
      route.version === "v2"
      && route.method === "POST"
      && route.path.startsWith("/api/x402/actions/")
      && "inputSchemaEvidence" in route,
    );
    expect(recoveredV2Actions).toHaveLength(24);
    for (const route of recoveredV2Actions) {
      const evidence = route.inputSchemaEvidence as SourceEvidence;
      expect(evidence.path).toMatch(/^repos\/theloopbreaker-site\/app\/api\/x402\/actions\/.+\/route\.ts$/);
      expect(evidence.sha256).toMatch(/^[0-9a-f]{64}$/);
      expect(route.inputSchema?.["x-vaultfire-provenance"]).toMatchObject(evidence);
      expect(candidates.has(`${route.method} ${route.path}`)).toBe(true);
    }

    const v3 = routes.find((route) => route.id === "retired-partnership-write")!;
    expect(v3).toMatchObject({
      free: true,
      contractStatus: "read-only",
      bazaarEligibility: "not-priced",
      responseStatus: "503",
    });
    expect(v3.inputSchema).toBeNull();
    expect(v3.inputSchemaEvidence).toBeUndefined();
    expect(candidates.has(`${v3.method} ${v3.path}`)).toBe(false);
  });
});
