import configPromise from '@payload-config'
import { getPayload } from 'payload'
import { Suspense } from 'react'

import type { ShopFacetCounts } from '@/lib/shop/queryProducts'
import { getRelationshipIDs } from '@/utilities/relationships'
import { TaxonomyFilterSection } from './TaxonomyFilterSection.client'

export type ShopFilterSource = 'attribute' | 'category' | 'segment' | 'size' | 'usecase'
export type ShopFilterDisplayStyle =
  | 'auto'
  | 'chips'
  | 'detailList'
  | 'list'
  | 'segmentGrid'
  | 'softChips'
export type ShopZeroCountBehavior = 'disabled' | 'hidden' | 'visible'

export type ShopFilterGroupConfig = {
  attributeGroup?: null | number | string | { id?: null | number | string }
  displayStyle?: null | ShopFilterDisplayStyle
  emptyLabel?: null | string
  maxVisibleItems?: null | number
  showCounts?: boolean | null
  showLessLabel?: null | string
  showMoreLabel?: null | string
  source: ShopFilterSource
  title?: null | string
  zeroCountBehavior?: null | ShopZeroCountBehavior
}

const filterSourceConfig = {
  attribute: {
    mode: 'multi',
    queryKey: 'attribute',
    title: 'Attribute',
  },
  category: {
    mode: 'category',
    queryKey: 'category',
    title: 'Category',
  },
  segment: {
    mode: 'segment',
    queryKey: 'segment',
    title: 'Segment',
  },
  size: {
    mode: 'multi',
    queryKey: 'size',
    title: 'Size',
  },
  usecase: {
    mode: 'multi',
    queryKey: 'usecase',
    title: 'Use Case',
  },
} as const

const defaultFilterGroups: ShopFilterGroupConfig[] = [
  { source: 'segment' },
  { source: 'category' },
  { source: 'attribute' },
  { source: 'usecase' },
  { source: 'size' },
]

type ShopFiltersProps = {
  facetCounts?: ShopFacetCounts
  groups?: null | ShopFilterGroupConfig[]
}

const sortByOrderAndTitle = <
  T extends {
    label?: string | null
    sortOrder?: number | null
    title?: string | null
  },
>(
  docs: T[],
) => {
  return [...docs].sort((left, right) => {
    const leftOrder = left.sortOrder ?? 0
    const rightOrder = right.sortOrder ?? 0

    if (leftOrder !== rightOrder) {
      return leftOrder - rightOrder
    }

    const leftLabel = left.title || left.label || ''
    const rightLabel = right.title || right.label || ''

    return leftLabel.localeCompare(rightLabel, 'id', {
      sensitivity: 'base',
    })
  })
}

const normalizeRelationshipID = (
  value: null | number | string | { id?: null | number | string } | undefined,
): null | string => {
  if (typeof value === 'string' || typeof value === 'number') {
    return String(value)
  }

  if (value && typeof value === 'object') {
    const id = value.id

    if (typeof id === 'string' || typeof id === 'number') {
      return String(id)
    }
  }

  return null
}

async function ShopFiltersContent({ facetCounts, groups }: ShopFiltersProps) {
  const payload = await getPayload({ config: configPromise })
  const resolvedGroups = groups && groups.length > 0 ? groups : defaultFilterGroups
  const getCount = (source: ShopFilterSource, id: number | string): number | undefined => {
    if (!facetCounts) {
      return undefined
    }

    return facetCounts[source]?.[String(id)] || 0
  }

  const [segmentsResult, categoriesResult, attributesResult, useCasesResult, sizesResult] =
    await Promise.all([
      payload.find({
        collection: 'segments',
        depth: 0,
        limit: 0,
        pagination: false,
      }),
      payload.find({
        collection: 'categories',
        depth: 1,
        limit: 0,
        pagination: false,
      }),
      payload.find({
        collection: 'productAttributes',
        depth: 1,
        limit: 0,
        pagination: false,
      }),
      payload.find({
        collection: 'productUseCases',
        depth: 0,
        limit: 0,
        pagination: false,
      }),
      payload.find({
        collection: 'productSizes',
        depth: 0,
        limit: 0,
        pagination: false,
      }),
    ])

  const segments = sortByOrderAndTitle(segmentsResult.docs).map((segment) => ({
    count: getCount('segment', segment.id),
    id: String(segment.id),
    title: segment.title,
  }))

  const categories = sortByOrderAndTitle(categoriesResult.docs).map((category) => ({
    count: getCount('category', category.id),
    id: String(category.id),
    segments: getRelationshipIDs(category.segments).map(String),
    title: category.title,
  }))

  const attributes = sortByOrderAndTitle(attributesResult.docs).map((attribute) => ({
    count: getCount('attribute', attribute.id),
    group: normalizeRelationshipID(attribute.group),
    id: String(attribute.id),
    title: attribute.title,
  }))

  const useCases = sortByOrderAndTitle(useCasesResult.docs).map((useCase) => ({
    count: getCount('usecase', useCase.id),
    id: String(useCase.id),
    title: useCase.title,
  }))

  const sizes = sortByOrderAndTitle(sizesResult.docs).map((size) => ({
    count: getCount('size', size.id),
    description: size.notes,
    id: String(size.id),
    title: size.label,
  }))

  const itemsBySource = {
    attribute: attributes,
    category: categories,
    segment: segments,
    size: sizes,
    usecase: useCases,
  }

  return (
    <div className="flex flex-col gap-5">
      {resolvedGroups.map((group, index) => {
        const sourceConfig = filterSourceConfig[group.source]
        const attributeGroupID =
          group.source === 'attribute' ? normalizeRelationshipID(group.attributeGroup) : null
        const items =
          group.source === 'attribute' && attributeGroupID
            ? attributes.filter((attribute) => attribute.group === attributeGroupID)
            : itemsBySource[group.source]
        const visibleItems =
          group.showCounts === false ? items.map(({ count: _count, ...item }) => item) : items

        return (
          <TaxonomyFilterSection
            displayStyle={group.displayStyle || undefined}
            emptyLabel={group.emptyLabel || undefined}
            items={visibleItems}
            key={`${group.source}-${attributeGroupID || 'all'}-${index}`}
            maxVisibleItems={group.maxVisibleItems}
            mode={sourceConfig.mode}
            queryKey={sourceConfig.queryKey}
            showLessLabel={group.showLessLabel}
            showMoreLabel={group.showMoreLabel}
            title={group.title || sourceConfig.title}
            zeroCountBehavior={group.zeroCountBehavior}
          />
        )
      })}
    </div>
  )
}

export function ShopFilters({ facetCounts, groups }: ShopFiltersProps) {
  return (
    <Suspense fallback={null}>
      <ShopFiltersContent facetCounts={facetCounts} groups={groups} />
    </Suspense>
  )
}
