import test from "node:test";
import assert from "node:assert/strict";
import { Money } from "../src/shared/money.ts";
import type { CatalogCapabilities } from "../src/modules/marketplace/catalog.ts";
import { activateListing, createListing, submitListing } from "../src/modules/marketplace/listings.ts";
import { InventoryService, type InventoryItem, type InventoryRepository } from "../src/modules/inventory/inventory.ts";
import type { KycSubmission } from "../src/modules/kyc/seller-verification.ts";

const approved: KycSubmission = { id: "kyc-1", sellerId: "seller-1", sequence: 1, status: "approved", challengeDate: "2026-09-13" };
const capability: CatalogCapabilities = { kind: "gift_card", enabled: true, deliveryModes: new Set(["instant_code"]), supportedRegions: new Set(["GLOBAL"]), supportedServers: new Set() };
const input = { id: "listing-1", sellerId: "seller-1", kind: "gift_card" as const, deliveryMode: "instant_code" as const, region: "GLOBAL", title: "Original Crakey gift card listing", unitPrice: Money.ofMinor(5_000n, "USD") };

test("unapproved sellers cannot create listings", () => {
  assert.throws(() => createListing(input, null, capability), { code: "SELLER_KYC_REQUIRED" });
  assert.throws(() => createListing(input, { ...approved, status: "pending" }, capability), { code: "SELLER_KYC_REQUIRED" });
});

test("listing creation validates capability and activation rechecks KYC", () => {
  const draft = createListing(input, approved, capability);
  const pending = submitListing(draft, "seller-1", approved);
  assert.throws(() => activateListing(pending, { ...approved, status: "rejected" }), { code: "SELLER_KYC_REQUIRED" });
  assert.equal(activateListing(pending, approved).status, "active");
});

test("seller cannot submit another seller's listing", () => {
  const draft = createListing(input, approved, capability);
  assert.throws(() => submitListing(draft, "seller-2", approved), { code: "FORBIDDEN" });
});

class AtomicMemoryInventory implements InventoryRepository {
  private chain = Promise.resolve();
  readonly items: InventoryItem[];
  constructor(items: InventoryItem[]) { this.items = items; }
  async findForOrder(orderId: string): Promise<InventoryItem | null> { return this.items.find((item) => item.orderId === orderId) ?? null; }
  async reserveNext(listingId: string, orderId: string, reservedUntil: Date): Promise<InventoryItem | null> {
    let release!: () => void;
    const previous = this.chain;
    this.chain = new Promise<void>((resolve) => { release = resolve; });
    await previous;
    try {
      const existing = this.items.find((item) => item.orderId === orderId);
      if (existing) return existing;
      const item = this.items.find((candidate) => candidate.listingId === listingId && candidate.status === "available");
      if (!item) return null;
      item.status = "reserved"; item.orderId = orderId; item.reservedUntil = reservedUntil;
      return item;
    } finally { release(); }
  }
}

test("last-item race reserves one code only and duplicate order is idempotent", async () => {
  const item: InventoryItem = { id: "item-1", listingId: "listing-1", sellerId: "seller-1", secretCiphertext: new Uint8Array([1, 2, 3]), secretFingerprint: "hash", status: "available" };
  const inventory = new InventoryService(new AtomicMemoryInventory([item]));
  const results = await Promise.allSettled([
    inventory.reserve("listing-1", "order-1", new Date()),
    inventory.reserve("listing-1", "order-2", new Date()),
  ]);
  assert.equal(results.filter((result) => result.status === "fulfilled").length, 1);
  assert.equal(results.filter((result) => result.status === "rejected").length, 1);
  const repeated = await inventory.reserve("listing-1", item.orderId!, new Date());
  assert.equal(repeated.id, "item-1");
});
