import { DomainError } from "../../shared/errors.ts";
import type { ProductKind } from "./catalog.ts";
import type { Listing } from "./listings.ts";

export interface ListingSearch {
  readonly query?: string;
  readonly kind?: ProductKind;
  readonly region?: string;
  readonly server?: string;
  readonly currency?: string;
  readonly minPriceMinor?: bigint;
  readonly maxPriceMinor?: bigint;
  readonly sort?: "newest" | "price_asc" | "price_desc";
  readonly limit?: number;
  readonly offset?: number;
}

export interface SearchResult {
  readonly items: readonly Listing[];
  readonly total: number;
  readonly limit: number;
  readonly offset: number;
}

export function searchActiveListings(all: readonly Listing[], filter: ListingSearch): SearchResult {
  const limit = filter.limit ?? 24;
  const offset = filter.offset ?? 0;
  if (!Number.isSafeInteger(limit) || limit < 1 || limit > 100 || !Number.isSafeInteger(offset) || offset < 0) {
    throw new DomainError("INVALID_PAGINATION", "Pagination is invalid.");
  }
  if (filter.minPriceMinor !== undefined && filter.minPriceMinor < 0n) throw new DomainError("INVALID_PRICE_FILTER", "Minimum price is invalid.");
  if (filter.maxPriceMinor !== undefined && filter.maxPriceMinor < 0n) throw new DomainError("INVALID_PRICE_FILTER", "Maximum price is invalid.");
  if (filter.minPriceMinor !== undefined && filter.maxPriceMinor !== undefined && filter.minPriceMinor > filter.maxPriceMinor) throw new DomainError("INVALID_PRICE_FILTER", "Price range is invalid.");
  const query = filter.query?.trim().toLocaleLowerCase().slice(0, 120);
  let items = all.filter((listing) => listing.status === "active")
    .filter((listing) => !filter.kind || listing.kind === filter.kind)
    .filter((listing) => !filter.region || listing.region === filter.region)
    .filter((listing) => !filter.server || listing.server === filter.server)
    .filter((listing) => !filter.currency || listing.unitPrice.currency === filter.currency.toUpperCase())
    .filter((listing) => filter.minPriceMinor === undefined || listing.unitPrice.minor >= filter.minPriceMinor)
    .filter((listing) => filter.maxPriceMinor === undefined || listing.unitPrice.minor <= filter.maxPriceMinor)
    .filter((listing) => !query || listing.title.toLocaleLowerCase().includes(query));
  const direction = filter.sort ?? "newest";
  const compareMinor = (left: bigint, right: bigint): number => left < right ? -1 : left > right ? 1 : 0;
  items = [...items].sort((a, b) => direction === "price_asc"
    ? compareMinor(a.unitPrice.minor, b.unitPrice.minor)
    : direction === "price_desc" ? compareMinor(b.unitPrice.minor, a.unitPrice.minor) : b.version - a.version);
  return { items: items.slice(offset, offset + limit), total: items.length, limit, offset };
}

export function publicListingDetail(listing: Listing): Listing {
  if (listing.status !== "active") throw new DomainError("NOT_FOUND", "Listing was not found.");
  return listing;
}
