import { Check, ListFilter } from 'lucide-react'
import type { ComponentType } from 'react'

import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import {
  Command,
  CommandEmpty,
  CommandGroup,
  CommandInput,
  CommandItem,
  CommandList,
  CommandSeparator,
} from '@/components/ui/command'
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
import { Separator } from '@/components/ui/separator'
import { t } from '@/lib/i18n'
import { cn } from '@/lib/utils'

const LABEL_WIDTHS = {
  default: 'max-w-50',
  wide: 'max-w-[350px]',
} as const

interface FacetedFilterOption {
  label: string
  value: string
  icon?: ComponentType<{ className?: string }>
}

interface FacetedColumn {
  getFacetedUniqueValues?: () => Map<unknown, number>
  getFilterValue: () => unknown
  setFilterValue: (value: unknown) => void
}

interface CommonFacetedFilterProps {
  column?: FacetedColumn
  title: string
  options: FacetedFilterOption[]
  size?: keyof typeof LABEL_WIDTHS
  disabled?: boolean
  triggerClassName?: string
}

interface FacetedFilterBaseProps extends CommonFacetedFilterProps {
  value?: string[]
  onValueChange?: (value: string[]) => void
  selection: 'single' | 'multiple'
}

interface SingleFacetedFilterProps extends CommonFacetedFilterProps {
  value?: string | null
  onValueChange?: (value: string | null) => void
}

interface MultiFacetedFilterProps extends CommonFacetedFilterProps {
  value?: string[]
  onValueChange?: (value: string[]) => void
}

/**
 * Filtro enumerado con búsqueda y selección visible en el disparador.
 *
 * Puede conectarse a una columna de TanStack o recibir el valor controlado por
 * la dirección de una tabla del servidor. Así la vista no guarda una segunda
 * copia del filtro y Atrás, recargar y compartir conservan lo elegido.
 */
function FacetedFilterBase({
  column,
  title,
  options,
  value,
  onValueChange,
  size = 'default',
  disabled = false,
  triggerClassName,
  selection,
}: FacetedFilterBaseProps) {
  const facets = column?.getFacetedUniqueValues?.()
  const isControlled = value !== undefined
  const selectedValues = new Set(
    isControlled ? value : ((column?.getFilterValue() as string[] | undefined) ?? [])
  )

  const commit = (next: string[]) => {
    if (isControlled) onValueChange?.(next)
    else column?.setFilterValue(next.length ? next : undefined)
  }

  return (
    <Popover>
      <PopoverTrigger asChild>
        <Button
          variant="outline"
          size="sm"
          disabled={disabled}
          className={cn('max-w-full gap-1.5', triggerClassName)}
        >
          <ListFilter aria-hidden />
          {title}
          {selectedValues.size > 0 ? (
            <>
              <Separator orientation="vertical" className="mx-1 h-4" />
              <Badge variant="secondary" className="rounded-sm px-1 font-normal lg:hidden">
                {selectedValues.size}
              </Badge>
              <div className="hidden min-w-0 gap-1 lg:flex">
                {selectedValues.size > 2 ? (
                  <Badge variant="secondary" className="rounded-sm px-1 font-normal">
                    {t('table.filterSelected', { count: selectedValues.size })}
                  </Badge>
                ) : (
                  options
                    .filter((option) => selectedValues.has(option.value))
                    .map((option) => (
                      <Badge
                        variant="secondary"
                        key={option.value}
                        className={cn('truncate rounded-sm px-1 font-normal', LABEL_WIDTHS[size])}
                        title={option.label}
                      >
                        {option.label}
                      </Badge>
                    ))
                )}
              </div>
            </>
          ) : null}
        </Button>
      </PopoverTrigger>

      <PopoverContent className="w-64 p-0" align="start">
        <Command>
          <CommandInput placeholder={title} />
          <CommandList>
            <CommandEmpty>{t('table.noMatchingOption')}</CommandEmpty>
            <CommandGroup>
              {options.map((option) => {
                const isSelected = selectedValues.has(option.value)

                return (
                  <CommandItem
                    key={option.value}
                    onSelect={() => {
                      if (selection === 'single') {
                        commit(isSelected ? [] : [option.value])
                        return
                      }

                      const next = new Set(selectedValues)
                      if (isSelected) next.delete(option.value)
                      else next.add(option.value)
                      commit(Array.from(next))
                    }}
                  >
                    <span
                      className={cn(
                        'border-primary flex size-4 shrink-0 items-center justify-center rounded-sm border',
                        isSelected
                          ? 'bg-primary text-primary-foreground'
                          : 'opacity-50 [&_svg]:invisible'
                      )}
                    >
                      <Check aria-hidden />
                    </span>
                    {option.icon ? <option.icon className="text-muted-foreground size-4" /> : null}
                    <span className={cn('truncate', LABEL_WIDTHS[size])} title={option.label}>
                      {option.label}
                    </span>
                    {facets?.get(option.value) ? (
                      <span className="ms-auto font-mono text-xs">{facets.get(option.value)}</span>
                    ) : null}
                  </CommandItem>
                )
              })}
            </CommandGroup>

            {selectedValues.size > 0 ? (
              <>
                <CommandSeparator />
                <CommandGroup>
                  <CommandItem onSelect={() => commit([])} className="justify-center text-center">
                    {t('table.clearFilters')}
                  </CommandItem>
                </CommandGroup>
              </>
            ) : null}
          </CommandList>
        </Command>
      </PopoverContent>
    </Popover>
  )
}

/** Un solo valor vigente; elegir el mismo otra vez lo limpia. */
export function SingleFacetedFilter({
  value,
  onValueChange,
  ...props
}: SingleFacetedFilterProps) {
  return (
    <FacetedFilterBase
      {...props}
      selection="single"
      value={value === undefined ? undefined : value ? [value] : []}
      onValueChange={(next) => onValueChange?.(next[0] ?? null)}
    />
  )
}

/** Varios valores del mismo eje; se combinan como alternativas. */
export function MultiFacetedFilter(props: MultiFacetedFilterProps) {
  return <FacetedFilterBase {...props} selection="multiple" />
}

export const DataTableFacetedFilter = MultiFacetedFilter
