import {
  RedisX402ReceiptStore,
  type ReceiptRedisExecutor,
  type X402ReceiptBinding,
  type V3ReceiptRequirement,
} from "../../../repos/theloopbreaker-site/app/api/v3/x402/_lib/receipt-store";
import { createTestReceiptCapabilities } from "../../../repos/theloopbreaker-site/app/api/v3/x402/_lib/test-receipt-adapter";
import { request as httpRequest } from "node:http";

/**
 * Test-only local REST transport.
 *
 * The atomicity test is about independent OS processes reaching one serialized
 * Redis EVAL boundary.  It must not inherit the production adapter's short
 * upstream abort budget, which is intentionally retained and tested
 * separately.  This transport has its own bounded, local-only request budget
 * so a stalled test server still fails rather than hanging a worker.
 */
class LocalReceiptRedis implements ReceiptRedisExecutor {
  constructor(private readonly url: URL) {}

  async eval(script: string, keys: string[], args: string[]): Promise<unknown> {
    return this.pipeline([["EVAL", script, String(keys.length), ...keys, ...args]]);
  }

  async get(key: string): Promise<string | null> {
    const reply = await this.pipeline([["GET", key]]);
    const item = Array.isArray(reply) ? reply[0] : reply;
    const value = item && typeof item === "object" && "result" in item
      ? (item as { result: unknown }).result
      : item;
    return typeof value === "string" ? value : null;
  }

  private pipeline(commands: string[][]): Promise<unknown> {
    return new Promise((resolve, reject) => {
      const request = httpRequest(this.url, {
        method: "POST",
        headers: { "content-type": "application/json" },
      }, (response) => {
        const chunks: Buffer[] = [];
        response.on("data", (chunk: Buffer | string) => chunks.push(Buffer.from(chunk)));
        response.once("error", reject);
        response.once("end", () => {
          if (response.statusCode !== 200) {
            reject(new Error(`local receipt test server returned HTTP ${response.statusCode}`));
            return;
          }
          try {
            resolve(JSON.parse(Buffer.concat(chunks).toString("utf8")));
          } catch {
            reject(new Error("local receipt test server returned invalid JSON"));
          }
        });
      });
      request.setTimeout(10_000, () => request.destroy(new Error("local receipt test transport timed out")));
      request.once("error", reject);
      request.end(JSON.stringify(commands));
    });
  }
}

const now = 1_800_000_000_000;
const binding: X402ReceiptBinding = {
  schemaVersion: "1.0.0",
  requestId: "v3r_456789abcdef0123456789abcdef0123",
  paymentId: "multiprocess-payment",
  authorizationId: "multiprocess-authorization",
  routeId: "retired-partnership-write",
  route: "/api/v3/x402/actions/create-partnership-bond",
  resource: "/api/v3/x402/actions/create-partnership-bond",
  method: "POST",
  network: "eip155:8453",
  chainId: 8453,
  asset: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
  amount: "20000",
  scheme: "exact",
  payee: "0xfa15ee28939b222b0448261a22156070f0a7813c",
  nonce: "multiprocess-nonce",
  validAfter: now - 1,
  expiresAt: now + 10_000,
  requiredFinality: "safe",
  minConfirmations: 5,
};
const requirement: V3ReceiptRequirement = {
  id: "retired-partnership-write",
  route: binding.route,
  method: "POST",
  network: binding.network as `eip155:${number}`,
  chainId: binding.chainId,
  asset: binding.asset as `0x${string}`,
  amount: binding.amount,
  scheme: "exact",
  payee: binding.payee as `0x${string}`,
  deploymentAddress: "0x3333333333333333333333333333333333333333",
  deploymentCodeDigest: "ab".repeat(32),
  requiredFinality: binding.requiredFinality,
  minConfirmations: binding.minConfirmations,
};

const url = process.env.RECEIPT_TEST_REDIS_URL;
if (!url) throw new Error("RECEIPT_TEST_REDIS_URL is required");

const store = new RedisX402ReceiptStore(
  new LocalReceiptRedis(new URL("/pipeline", url)),
  { now: () => now, requirements: [requirement], ...createTestReceiptCapabilities(now, requirement, binding.resource, binding.validAfter, binding.expiresAt) },
);
const result = await store.claim(binding);
process.stdout.write(`${JSON.stringify(result)}\n`);
