import { readFileSync } from "node:fs";
import { resolve } from "node:path";
import { afterEach, describe, expect, it } from "vitest";
import { GET as getStatus } from "../../../repos/theloopbreaker-site/app/api/v3/protocol/status/route";
import { GET as getSolvency } from "../../../repos/theloopbreaker-site/app/api/v3/protocol/solvency/route";
import { GET as getDiscovery } from "../../../repos/theloopbreaker-site/app/api/v3/x402/discovery/route";
import { GET as getChallengeStatus } from "../../../repos/theloopbreaker-site/app/api/v3/x402/challenge-status/route";
import { POST as postRetired } from "../../../repos/theloopbreaker-site/app/api/v3/x402/actions/create-partnership-bond/route";
import {
  createProductionReceiptStore,
  RedisX402ReceiptStore,
  UnavailableX402ReceiptStore,
  type X402ReceiptBinding,
  type V3ReceiptRequirement,
} from "../../../repos/theloopbreaker-site/app/api/v3/x402/_lib/receipt-store";
import { createTestReceiptCapabilities, createTestReceiptRedis } from "../../../repos/theloopbreaker-site/app/api/v3/x402/_lib/test-receipt-adapter";
import {
  evaluateV3BazaarPublicationGate,
  evaluateV3CapabilityEvidence,
  parseDuplicateSafeEvidenceJson,
} from "../../../repos/theloopbreaker-site/app/api/v3/x402/_lib/capabilities";
import { createReceiptStatusResponse } from "../../../repos/theloopbreaker-site/app/api/v3/x402/_lib/receipt-status";
import {
  V3_X402_RELEASE_ENV,
  V3_X402_RELEASE_VALUE,
} from "../../../repos/theloopbreaker-site/app/api/v3/x402/_lib/release";
import { validateSchema } from "./json-schema-2020-subset";

const schemaRoot = resolve(import.meta.dirname, "../../schemas/v3-x402");
const schema = (name: string) => JSON.parse(readFileSync(resolve(schemaRoot, name), "utf8"));
const originalFlag = process.env[V3_X402_RELEASE_ENV];

afterEach(() => {
  if (originalFlag === undefined) delete process.env[V3_X402_RELEASE_ENV];
  else process.env[V3_X402_RELEASE_ENV] = originalFlag;
});

describe("V3 x402 durable fail-closed foundation", () => {
  it("conforms status, solvency, discovery, challenge status, and retirement responses to versioned schemas", async () => {
    process.env[V3_X402_RELEASE_ENV] = V3_X402_RELEASE_VALUE;
    const cases: Array<[string, Response]> = [
      ["status.v1.schema.json", getStatus()],
      ["solvency.v1.schema.json", getSolvency()],
      ["discovery.v1.schema.json", getDiscovery()],
      ["challenge-status.v1.schema.json", getChallengeStatus()],
      ["retirement.v1.schema.json", postRetired({} as Request)],
    ];
    for (const [name, response] of cases) {
      const contract = schema(name);
      const body = await response.json();
      expect(validateSchema(contract, body), name).toEqual([]);
      const injected = structuredClone(body);
      const nested = name.startsWith("status") || name.startsWith("solvency") ? injected.claims :
        name.startsWith("discovery") ? injected.settlement :
        name.startsWith("challenge") ? injected.capabilityGate : injected.safety;
      nested.auditorInjected = true;
      expect(validateSchema(contract, injected), `${name} must close nested objects`).not.toEqual([]);
    }
    for (const name of ["receipt-status.v1.schema.json"]) {
      const contract = schema(name);
      expect(contract.$schema).toBe("https://json-schema.org/draft/2020-12/schema");
      expect(contract.required).toContain("auditIntegrity");
    }
  });

  it("does not inspect headers or bodies and never emits 402 on disabled write paths", async () => {
    process.env[V3_X402_RELEASE_ENV] = V3_X402_RELEASE_VALUE;
    const toxic = new Proxy({}, { get: () => { throw new Error("request was inspected"); } }) as Request;
    const response = postRetired(toxic);
    expect(response.status).toBe(503);
    expect(response.status).not.toBe(402);
    expect(response.headers.get("payment-required")).toBeNull();
    expect(await response.json()).toMatchObject({ requestInspected: false, bodyParsed: false });
  });

  it("uses an explicit unavailable production default and an opt-in test adapter", async () => {
    const unavailable = createProductionReceiptStore({}, fetch);
    expect(unavailable).toBeInstanceOf(UnavailableX402ReceiptStore);
    expect(unavailable.available).toBe(false);
    await expect(unavailable.get("request-1")).rejects.toMatchObject({ code: "v3_receipt_store_unavailable" });
    expect(() => createTestReceiptRedis({ NODE_ENV: "production" })).toThrow(/outside NODE_ENV=test/);
    expect(createTestReceiptRedis({ NODE_ENV: "test" })).toBeDefined();
  });

  it("binds exact resource and price and persists a tamper-evident audit chain", async () => {
    const now = 1_800_000_000_000;
    const requirement: V3ReceiptRequirement = {
      id: "mandate-compat",
      route: "/api/v3/mandate/:mandateHash", method: "GET",
      network: "eip155:8453", chainId: 8453,
      asset: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913", amount: "5000", scheme: "exact",
      payee: "0xfa15ee28939b222b0448261a22156070f0a7813c",
      deploymentAddress: "0x3333333333333333333333333333333333333333",
      deploymentCodeDigest: "ab".repeat(32),
      requiredFinality: "safe", minConfirmations: 5,
    };
    const binding: X402ReceiptBinding = {
      schemaVersion: "1.0.0", requestId: "v3r_56789abcdef0123456789abcdef01234", paymentId: "payment-bound",
      authorizationId: "authorization-bound", routeId: "mandate-compat", route: "/api/v3/mandate/:mandateHash",
      resource: `/api/v3/mandate/0x${"12".repeat(32)}`, method: "GET",
      network: "eip155:8453", chainId: 8453, asset: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
      amount: "5000", scheme: "exact", payee: "0xfa15ee28939b222b0448261a22156070f0a7813c",
      nonce: "nonce-bound", validAfter: now - 1, expiresAt: now + 60_000,
      requiredFinality: "safe", minConfirmations: 5,
    };
    const store = new RedisX402ReceiptStore(createTestReceiptRedis({ NODE_ENV: "test" }), {
      now: () => now, requirements: [requirement],
      ...createTestReceiptCapabilities(now, requirement, binding.resource, binding.validAfter, binding.expiresAt),
    });
    const unauthorizedStore = new RedisX402ReceiptStore(createTestReceiptRedis({ NODE_ENV: "test" }), {
      now: () => now, requirements: [requirement],
    });
    await expect(unauthorizedStore.claim(binding)).rejects.toThrow(/authenticated capability evidence/);
    const claimed = await store.claim(binding);
    expect(claimed).toMatchObject({ outcome: "claimed", receipt: { auditIntegrity: { sequence: 1, previousDigest: "0".repeat(64) } } });
    await expect(store.claim({ ...binding, resource: "/api/v3/mandate/changed" }))
      .rejects.toThrow(/registered/);
    await expect(store.claim({ ...binding, resource: `/api/v3/mandate/0x${"56".repeat(32)}` }))
      .rejects.toThrow(/authenticated capability evidence/);
    await expect(store.claim({ ...binding, requestId: "v3r_6789abcdef0123456789abcdef012345", paymentId: "payment-other",
      authorizationId: "authorization-other", nonce: "nonce-other", resource: `/api/v3/mandate/0x${"34".repeat(32)}` }))
      .rejects.toThrow(/authenticated capability evidence/);
    const transitioned = await store.transition(binding.requestId, ["claimed"], "pending_confirmation");
    expect(transitioned).toMatchObject({ outcome: "updated", receipt: { auditIntegrity: { sequence: 2 } } });
    if (claimed.outcome !== "claimed" || transitioned.outcome !== "updated") throw new Error("unexpected");
    expect(transitioned.receipt.auditIntegrity.previousDigest).toBe(claimed.receipt.auditIntegrity.eventDigest);
    const fetchImpl = (async () => Response.json([{ result: JSON.stringify(transitioned.receipt) }])) as typeof fetch;
    const response = await createReceiptStatusResponse(binding.requestId, {
      KV_REST_API_URL: "https://receipt.invalid",
      KV_REST_API_TOKEN: "test-token",
    }, fetchImpl);
    const receiptBody = await response.json();
    expect(response.status).toBe(200);
    expect(validateSchema(schema("receipt-status.v1.schema.json"), receiptBody)).toEqual([]);
    const contradictoryReceipt = structuredClone(receiptBody);
    contradictoryReceipt.state = "confirmed";
    contradictoryReceipt.evidenceLabels.settlementVerified = false;
    expect(validateSchema(schema("receipt-status.v1.schema.json"), contradictoryReceipt)).not.toEqual([]);
    expect(receiptBody).toMatchObject({
      resource: binding.resource,
      paymentRequirement: { amount: binding.amount, scheme: "exact" },
      auditIntegrity: { verified: true, sequence: 2 },
    });
  });

  it("keeps V3 Bazaar candidates at zero and requires objective listing and deployment evidence", () => {
    expect(evaluateV3BazaarPublicationGate()).toMatchObject({
      candidateCount: 0, eligible: false, listingReceiptIds: [], deploymentEvidenceIds: [],
    });
    expect(evaluateV3CapabilityEvidence()).toMatchObject({
      publicationEvidence: false, authenticatedBundleCount: 0, satisfied: false,
    });
    expect(() => parseDuplicateSafeEvidenceJson('{"id":"first","id":"second"}')).toThrow(/duplicate JSON key/);
    expect(() => parseDuplicateSafeEvidenceJson('{"outer":{"id":"first","id":"second"}}')).toThrow(/duplicate JSON key/);
    const now = 1_800_000_000_000;
    const requirement: V3ReceiptRequirement = {
      id: "mandate-compat", route: "/api/v3/mandate/:mandateHash", method: "GET",
      network: "eip155:8453", chainId: 8453, asset: "0x1111111111111111111111111111111111111111",
      payee: "0x2222222222222222222222222222222222222222", amount: "5000", scheme: "exact",
      deploymentAddress: "0x3333333333333333333333333333333333333333", deploymentCodeDigest: "ab".repeat(32),
      requiredFinality: "safe", minConfirmations: 5,
    };
    const resource = `/api/v3/mandate/0x${"12".repeat(32)}`;
    const authenticated = createTestReceiptCapabilities(now, requirement, resource);
    expect(evaluateV3CapabilityEvidence(authenticated.capabilityEvidence, authenticated.evidenceVerifier, now)).toMatchObject({
      authenticatedBundleCount: 1, coherentBundle: true, satisfied: true,
    });
    expect(evaluateV3BazaarPublicationGate(authenticated.capabilityEvidence, authenticated.evidenceVerifier, now)).toMatchObject({
      candidateCount: 0, eligible: false, capabilityEvidenceSatisfied: true,
    });
    const mixed = structuredClone(authenticated.capabilityEvidence);
    if (!mixed.bundle) throw new Error("missing test bundle");
    mixed.bundle.network = "eip155:1";
    expect(evaluateV3CapabilityEvidence(mixed, authenticated.evidenceVerifier, now)).toMatchObject({
      authenticatedBundleCount: 0, coherentBundle: false, satisfied: false,
    });
  });

  it("treats capability expiry as an exclusive upper bound for evaluation and publication", () => {
    const now = 1_800_000_000_000;
    const expiresAt = now + 60_000;
    const requirement: V3ReceiptRequirement = {
      id: "mandate-compat", route: "/api/v3/mandate/:mandateHash", method: "GET",
      network: "eip155:8453", chainId: 8453, asset: "0x1111111111111111111111111111111111111111",
      payee: "0x2222222222222222222222222222222222222222", amount: "5000", scheme: "exact",
      deploymentAddress: "0x3333333333333333333333333333333333333333", deploymentCodeDigest: "ab".repeat(32),
      requiredFinality: "safe", minConfirmations: 5,
    };
    const capabilities = createTestReceiptCapabilities(
      now,
      requirement,
      `/api/v3/mandate/0x${"12".repeat(32)}`,
      now - 1,
      expiresAt,
    );
    expect(evaluateV3CapabilityEvidence(capabilities.capabilityEvidence, capabilities.evidenceVerifier, expiresAt - 1))
      .toMatchObject({ authenticatedBundleCount: 1, satisfied: true });
    for (const instant of [expiresAt, expiresAt + 1]) {
      expect(evaluateV3CapabilityEvidence(capabilities.capabilityEvidence, capabilities.evidenceVerifier, instant))
        .toMatchObject({ authenticatedBundleCount: 0, coherentBundle: false, satisfied: false });
      expect(evaluateV3BazaarPublicationGate(capabilities.capabilityEvidence, capabilities.evidenceVerifier, instant))
        .toMatchObject({
          candidateCount: 0,
          eligible: false,
          capabilityEvidenceSatisfied: false,
          listingReceiptIds: [],
          deploymentEvidenceIds: [],
        });
    }
    expect(evaluateV3BazaarPublicationGate(capabilities.capabilityEvidence, capabilities.evidenceVerifier, expiresAt - 1))
      .toMatchObject({ candidateCount: 0, eligible: false, capabilityEvidenceSatisfied: true });
  });
});
