import { execFileSync } from "node:child_process";
import { createHash } from "node:crypto";
import { readFileSync } from "node:fs";
import { resolve } from "node:path";
import { describe, expect, it } from "vitest";
import matrix from "../../compat/v2-v3-route-disposition-matrix.json" assert { type: "json" };
import recovered from "../../compat/recovered-97-public-x402-identities.json" assert { type: "json" };
import sourceEvidence from "../../../baseline/schemas/v2/route-source-method-evidence.json" assert { type: "json" };
import x402ActionEvidence from "../../../baseline/schemas/v2/x402-action-input-schema-evidence.json" assert { type: "json" };

const root = resolve(import.meta.dirname, "../../..");
const sourcePaths = readFileSync(resolve(root, "baseline/extracted/v2_api_routes.txt"), "utf8")
  .split(/\r?\n/).map((path) => path.trim()).filter(Boolean);
const statuses = new Set([
  "implemented-additive-handler",
  "safe-compatibility-wrapper",
  "blocked-pending-v3-dependency-evidence",
  "retired-for-safety",
]);

describe("recovered 97-identity V3 x402 disposition matrix", () => {
  it("is deterministic and generated from the additive recovered inventory", () => {
    expect(() => execFileSync("python3", ["pr7/compat/build-v2-v3-route-disposition-matrix.py", "--check"], {
      cwd: root, stdio: "pipe",
    })).not.toThrow();
  });

  it("covers all and only the 97 recovered method/path identities", () => {
    expect(recovered.counts).toEqual({ total: 97, coreX402: 86, solana: 8, crossChain: 1, wellKnown: 2 });
    expect(matrix.counts).toMatchObject({ publicIdentities: 97, coreX402: 86, solana: 8, crossChain: 1, wellKnown: 2 });
    const rows = matrix.publicIdentityDispositions;
    expect(rows).toHaveLength(97);
    expect(rows.map((row) => row.identity)).toEqual(recovered.identities.map((row) => row.identity));
    expect(new Set(rows.map((row) => row.identity))).toHaveLength(97);
    expect(rows.filter((row) => row.group === "core-x402")).toHaveLength(86);
    expect(rows.filter((row) => row.group === "solana")).toHaveLength(8);
    expect(rows.find((row) => row.identity === "GET /api/agent/bridge")?.group).toBe("cross-chain");
    expect(rows.filter((row) => row.group === "well-known")).toHaveLength(2);
    for (const row of rows) {
      expect(statuses.has(row.v3.status)).toBe(true);
      expect(row.v3.reason).toBeTruthy();
      expect(row.v3.activationEvidence).toBeTruthy();
      expect(row.v3.v2Fallback).toBe("forbidden");
      expect(row.v3.paymentSettlement).toContain("not-enabled");
      expect(row.v3.bazaar).toContain("no-listing-id");
    }
  });

  it("separately preserves every V2 source-route disposition", () => {
    expect(sourcePaths).toHaveLength(132);
    expect(matrix.v2SourceRouteDispositions).toHaveLength(132);
    expect(matrix.v2SourceRouteDispositions.map((row) => row.v2SourcePath)).toEqual(sourcePaths);
    for (const row of matrix.v2SourceRouteDispositions) {
      expect(row.preserveV2Unchanged).toBe(true);
      expect(row.v2Disposition).toBe("preserve-unchanged");
      expect(row.v3Relationship).toContain("separate additive V3 namespace");
    }
  });

  it("retire/fail-closes mission-sensitive routes and only materializes safe V3 status/wrappers", () => {
    const rows = matrix.publicIdentityDispositions;
    for (const identity of [
      "POST /api/x402/actions/attest-belief",
      "POST /api/x402/actions/submit-accountability-metrics",
      "POST /api/x402/actions/slash-reputation",
      "GET /api/x402/trust/risk-score",
      "GET /api/agent/bridge",
    ]) {
      const row = rows.find((item) => item.identity === identity);
      expect(row?.v3.status).toBe("retired-for-safety");
      expect(row?.v3.reason).toMatch(/retired:/);
    }
    expect(rows.find((row) => row.identity === "GET /api/solana/health")?.v3).toMatchObject({
      status: "implemented-additive-handler", route: "/api/v3/solana/health",
    });
    expect(rows.find((row) => row.identity === "GET /api/x402/discovery")?.v3).toMatchObject({
      status: "safe-compatibility-wrapper", route: "/api/v3/x402/discovery",
    });
    expect(rows.find((row) => row.identity === "POST /api/solana/webhook")?.v3.status)
      .toBe("blocked-pending-v3-dependency-evidence");
  });

  it("leaves protected V2 x402 handler bytes and V2 release artifacts untouched", () => {
    const v2X402Evidence = x402ActionEvidence.entries;
    expect(v2X402Evidence).toHaveLength(24);
    for (const entry of v2X402Evidence) {
      const bytes = readFileSync(resolve(root, entry.source));
      expect(createHash("sha256").update(bytes).digest("hex"), entry.source).toBe(entry.sha256);
    }
    expect(sourceEvidence.inventory.sha256).toBe(matrix.sources.v2SourceInventory.sha256);
    expect(() => execFileSync("git", ["diff", "--exit-code", "--",
      "repos/theloopbreaker-site/app/api/x402",
      "legacy/v2",
      "baseline/x402-route-registry.json",
      "ci/manifests/v2-bytecode-manifest.json",
    ], { cwd: root, stdio: "pipe" })).not.toThrow();
  });
});
