import type { ListingCard, ListingFilterFacets, ListingFilterState } from "~/types/listing"

export const normalizeListingValue = (value?: string | null) =>
  value
    ?.normalize("NFD")
    .replace(/\p{Diacritic}/gu, "")
    .toLowerCase()
    .trim() ?? ""

export const sortListingStrings = (values: Array<string | undefined>) =>
  [...new Set(values.filter((value): value is string => Boolean(value && value.trim())))]
    .sort((left, right) => left.localeCompare(right, "es", { sensitivity: "base" }))

export const medianListingPrice = (values: number[]) => {
  if (!values.length) {
    return 0
  }

  const sorted = [...values].sort((left, right) => left - right)
  const middle = Math.floor(sorted.length / 2)

  return sorted.length % 2 === 0
    ? Math.round((sorted[middle - 1] + sorted[middle]) / 2)
    : sorted[middle]
}

export const createListingFilterState = (overrides: Partial<ListingFilterState> = {}): ListingFilterState => ({
  q: "",
  source: "",
  make: "",
  transmission: "",
  fuel: "",
  minYear: "",
  maxYear: "",
  maxPrice: null,
  ...overrides
})

const matchesKeyword = (item: ListingCard, keyword?: string) => {
  if (!keyword) {
    return true
  }

  const haystack = [
    item.title,
    item.make,
    item.model,
    item.version,
    item.seller,
    item.source,
    item.normalizedLabel
  ]
    .map(normalizeListingValue)
    .join(" ")

  return haystack.includes(normalizeListingValue(keyword))
}

const matchesExactValue = (value: string | undefined, expected: string | undefined) => {
  if (!expected) {
    return true
  }

  return normalizeListingValue(value) === normalizeListingValue(expected)
}

export const buildListingFilterFacets = (items: ListingCard[]): ListingFilterFacets => ({
  sources: sortListingStrings(items.map((item) => item.source)) as ListingFilterFacets["sources"],
  makes: sortListingStrings(items.map((item) => item.make)),
  transmissions: sortListingStrings(items.map((item) => item.transmission)),
  fuels: sortListingStrings(items.map((item) => item.fuel)),
  years: [...new Set(items.map((item) => item.year))].sort((left, right) => right - left),
  maxPrice: Math.max(...items.map((item) => item.priceClp), 0)
})

export const filterListingItems = (items: ListingCard[], filters: ListingFilterState) =>
  items
    .filter((item) => matchesKeyword(item, filters.q))
    .filter((item) => matchesExactValue(item.source, filters.source))
    .filter((item) => matchesExactValue(item.make, filters.make))
    .filter((item) => matchesExactValue(item.transmission, filters.transmission))
    .filter((item) => matchesExactValue(item.fuel, filters.fuel))
    .filter((item) => (filters.minYear ? item.year >= Number(filters.minYear) : true))
    .filter((item) => (filters.maxYear ? item.year <= Number(filters.maxYear) : true))
    .filter((item) => (filters.maxPrice ? item.priceClp <= filters.maxPrice : true))
    .sort((left, right) => left.priceClp - right.priceClp || right.year - left.year || left.title.localeCompare(right.title, "es"))
