import * as React from 'react'
import { Check } from 'lucide-react'

import { cn } from '@/lib/utils'
import {
  Command,
  CommandEmpty,
  CommandGroup,
  CommandInput,
  CommandItem,
  CommandList,
  CommandSeparator,
} from './command'

export interface MultiSelectOption {
  value: string
  label: string
  /** Optional right-hand meta (count, badge, etc.). */
  meta?: React.ReactNode
  /** Optional keywords added to the cmdk search index. */
  keywords?: string[]
}

interface MultiSelectProps {
  options: MultiSelectOption[]
  values: string[]
  onChange: (next: string[]) => void
  /** Show the search input. Defaults to true when options.length > 5. */
  searchable?: boolean
  searchPlaceholder?: string
  emptyMessage?: string
  /** When true, selected items pin to the top with a separator. */
  pinSelected?: boolean
  className?: string
}

/**
 * Multi-select list built on cmdk/Command. Meant to live inside an existing
 * popover/pill/dialog container — it does NOT render its own trigger.
 * Clicking a row toggles it; selected rows show a checkmark.
 */
export function MultiSelect({
  options,
  values,
  onChange,
  searchable,
  searchPlaceholder = 'Search…',
  emptyMessage = 'No results.',
  pinSelected = true,
  className,
}: MultiSelectProps) {
  const showSearch = searchable ?? options.length > 5
  const selectedSet = React.useMemo(() => new Set(values), [values])

  const { selected, unselected } = React.useMemo(() => {
    if (!pinSelected) {
      return { selected: [] as MultiSelectOption[], unselected: options }
    }
    const sel: MultiSelectOption[] = []
    const unsel: MultiSelectOption[] = []
    for (const o of options) {
      ;(selectedSet.has(o.value) ? sel : unsel).push(o)
    }
    return { selected: sel, unselected: unsel }
  }, [options, selectedSet, pinSelected])

  const toggle = (v: string) => {
    const next = selectedSet.has(v)
      ? values.filter((x) => x !== v)
      : [...values, v]
    onChange(next)
  }

  return (
    <Command className={cn('bg-transparent', className)}>
      {showSearch && (
        <CommandInput placeholder={searchPlaceholder} className="h-8 text-xs" />
      )}
      <CommandList className="max-h-64">
        <CommandEmpty>{emptyMessage}</CommandEmpty>
        {pinSelected && selected.length > 0 && (
          <>
            <CommandGroup className="p-0">
              {selected.map((opt) => (
                <Row key={opt.value} option={opt} selected onToggle={toggle} />
              ))}
            </CommandGroup>
            {unselected.length > 0 && <CommandSeparator />}
          </>
        )}
        {unselected.length > 0 && (
          <CommandGroup className="p-0">
            {unselected.map((opt) => (
              <Row
                key={opt.value}
                option={opt}
                selected={selectedSet.has(opt.value)}
                onToggle={toggle}
              />
            ))}
          </CommandGroup>
        )}
      </CommandList>
    </Command>
  )
}

function Row({
  option,
  selected,
  onToggle,
}: {
  option: MultiSelectOption
  selected: boolean
  onToggle: (v: string) => void
}) {
  return (
    <CommandItem
      value={option.value}
      keywords={[option.label, ...(option.keywords ?? [])]}
      onSelect={() => onToggle(option.value)}
      className={cn(
        'cursor-pointer gap-1.5 px-2 py-1 text-xs',
        selected && 'bg-primary/10 data-[selected=true]:bg-primary/20'
      )}
    >
      <span className="min-w-0 flex-1 truncate">{option.label}</span>
      {option.meta != null && (
        <span className="shrink-0 text-[10px] tabular-nums text-text-muted">
          {option.meta}
        </span>
      )}
      <Check
        className={cn(
          'h-3 w-3 shrink-0 text-primary',
          selected ? 'opacity-100' : 'opacity-0'
        )}
      />
    </CommandItem>
  )
}
