import { DomainError } from "../../shared/errors.ts";
import { Money } from "../../shared/money.ts";
import type { DeliveryMode, ProductKind } from "../marketplace/catalog.ts";
import type { Listing } from "../marketplace/listings.ts";

export type OrderStatus = "awaiting_payment" | "funded" | "delivered" | "disputed" | "completed" | "refunded" | "cancelled";

export interface OrderPriceSnapshot {
  readonly unitPrice: Money;
  readonly quantity: number;
  readonly subtotal: Money;
}

export interface Order {
  readonly id: string;
  readonly publicReference: string;
  readonly buyerId: string;
  readonly sellerId: string;
  readonly listingId: string;
  readonly kind: ProductKind;
  readonly deliveryMode: DeliveryMode;
  readonly titleSnapshot: string;
  readonly regionSnapshot: string;
  readonly price: OrderPriceSnapshot;
  readonly status: OrderStatus;
  readonly version: number;
}

export function createOrderFromListing(listing: Listing, buyerId: string, quantity: number, publicReference: string): Order {
  if (listing.status !== "active") throw new DomainError("LISTING_NOT_ACTIVE", "Listing is not available for purchase.");
  if (listing.sellerId === buyerId) throw new DomainError("SELF_PURCHASE_FORBIDDEN", "A seller cannot purchase their own listing.");
  if (!Number.isSafeInteger(quantity) || quantity < 1 || quantity > 100) throw new DomainError("INVALID_QUANTITY", "Order quantity is invalid.");
  if (!/^[A-Z0-9]{12,24}$/.test(publicReference)) throw new DomainError("INVALID_ORDER_REFERENCE", "Order reference is invalid.");
  const subtotal = Money.ofMinor(listing.unitPrice.minor * BigInt(quantity), listing.unitPrice.currency);
  return {
    id: crypto.randomUUID(), publicReference, buyerId, sellerId: listing.sellerId, listingId: listing.id,
    kind: listing.kind, deliveryMode: listing.deliveryMode, titleSnapshot: listing.title,
    regionSnapshot: listing.region, price: { unitPrice: listing.unitPrice, quantity, subtotal },
    status: "awaiting_payment", version: 1,
  };
}

export function assertBuyerOrderAccess(order: Order, actorUserId: string): void {
  if (order.buyerId !== actorUserId) throw new DomainError("FORBIDDEN", "Order does not belong to this buyer.");
}

export function assertSellerOrderAccess(order: Order, actorUserId: string): void {
  if (order.sellerId !== actorUserId) throw new DomainError("FORBIDDEN", "Order does not belong to this seller.");
}

export function assertCartCompatible(listings: readonly Listing[]): void {
  if (listings.length === 0) throw new DomainError("EMPTY_CART", "Cart is empty.");
  const [first] = listings;
  if (!first) throw new DomainError("EMPTY_CART", "Cart is empty.");
  if (listings.some((listing) => listing.status !== "active")) throw new DomainError("LISTING_NOT_ACTIVE", "Cart contains an unavailable listing.");
  if (listings.some((listing) => listing.unitPrice.currency !== first.unitPrice.currency)) throw new DomainError("CART_CURRENCY_MISMATCH", "Cart items must use the same settlement currency.");
  if (listings.some((listing) => listing.deliveryMode !== "instant_code")) throw new DomainError("CART_DELIVERY_INCOMPATIBLE", "Manual-delivery products must be checked out separately.");
}
