import test from "node:test";
import assert from "node:assert/strict";
import { Money } from "../src/shared/money.ts";
import { moderateListing, setCatalogAvailability } from "../src/modules/admin/marketplace-moderation.ts";
import { Permissions, type StaffPrincipal } from "../src/modules/identity/permissions.ts";
import type { KycSubmission } from "../src/modules/kyc/seller-verification.ts";
import type { Listing } from "../src/modules/marketplace/listings.ts";
import { publicListingDetail, searchActiveListings } from "../src/modules/marketplace/search.ts";

const listing = (id: string, status: Listing["status"], price: bigint, title = "Steam global game key"): Listing => ({ id, sellerId: "seller-1", kind: "game_key", deliveryMode: "instant_code", region: "GLOBAL", title, unitPrice: Money.ofMinor(price, "USD"), status, version: 1 });
const approvedKyc: KycSubmission = { id: "kyc", sellerId: "seller-1", sequence: 1, status: "approved", challengeDate: "2026-09-13" };
const moderator: StaffPrincipal = { staffId: "admin-1", active: true, permissions: new Set([Permissions.ListingsModerate]) };

test("public search returns only active listings and applies bounded filters", () => {
  const result = searchActiveListings([
    listing("active-1", "active", 2_500n),
    listing("draft-1", "draft", 100n, "Secret draft"),
    listing("active-2", "active", 1_500n, "Steam deluxe game key"),
  ], { query: "steam", minPriceMinor: 1_000n, maxPriceMinor: 2_000n, currency: "usd", sort: "price_asc" });
  assert.deepEqual(result.items.map((item) => item.id), ["active-2"]);
  assert.equal(result.total, 1);
  assert.throws(() => searchActiveListings([], { limit: 101 }), { code: "INVALID_PAGINATION" });
});

test("non-active listing detail fails as not found", () => {
  assert.throws(() => publicListingDetail(listing("pending", "pending_review", 100n)), { code: "NOT_FOUND" });
});

test("only authorized staff can approve and approval rechecks current KYC", () => {
  const pending = listing("pending", "pending_review", 100n);
  assert.throws(() => moderateListing(pending, "approved", { ...moderator, permissions: new Set() }, approvedKyc, new Date()), { code: "FORBIDDEN" });
  assert.throws(() => moderateListing(pending, "approved", moderator, { ...approvedKyc, status: "rejected" }, new Date()), { code: "SELLER_KYC_REQUIRED" });
  const result = moderateListing(pending, "approved", moderator, approvedKyc, new Date("2026-09-13T12:00:00Z"));
  assert.equal(result.listing.status, "active");
  assert.equal(result.event.actorStaffId, "admin-1");
});

test("listing rejection requires reason and catalog control requires configuration permission", () => {
  const pending = listing("pending", "pending_review", 100n);
  assert.throws(() => moderateListing(pending, "rejected", moderator, approvedKyc, new Date()), { code: "REJECTION_REASON_REQUIRED" });
  assert.throws(() => setCatalogAvailability(moderator, false), { code: "FORBIDDEN" });
  const owner: StaffPrincipal = { staffId: "owner", active: true, permissions: new Set([Permissions.ConfigurationManage]) };
  assert.equal(setCatalogAvailability(owner, false), false);
});
