'use client';

import { cn } from '@lib/utils';
import { CaretDownIcon, MagnifyingGlassIcon } from '@phosphor-icons/react';
import type { ReactNode } from 'react';
import { useCallback, useEffect, useRef, useState } from 'react';
import AvatarUser, { AvatarUserType } from '../avatar/avatar-user';
import { Button } from './button';
import {
  Item,
  ItemContent,
  ItemDescription,
  ItemMedia,
  ItemTitle,
} from './item';

export type SelectGenericProps<T> = {
  value: string;
  onValueChange: (value: string) => void;
  values: T[];
  getValue: (item: T) => string;
  getItemLabel: (item: T) => string;
  renderOption?: (item: T) => ReactNode;
  filterOption?: (item: T, query: string) => boolean;
  placeholder?: string;
  keyword?: string;
  onKeywordChange?: (value: string) => void;
  searchPlaceholder?: string;
  name?: string;
  id?: string;
  'aria-invalid'?: boolean;
  disabled?: boolean;
  className?: string;
};

const defaultFilter =
  <T,>(getItemLabel: (item: T) => string) =>
  (item: T, query: string) => {
    const q = query.trim().toLowerCase();
    return q === '' || getItemLabel(item).toLowerCase().includes(q);
  };

export function SelectGeneric<T>({
  value,
  onValueChange,
  values,
  getValue,
  getItemLabel,
  renderOption,
  filterOption,
  placeholder,
  keyword,
  onKeywordChange,
  searchPlaceholder = 'Tìm kiếm...',
  name,
  id,
  'aria-invalid': ariaInvalid,
  disabled = false,
  className,
}: SelectGenericProps<T>) {
  const [open, setOpen] = useState(false);
  const [localQuery, setLocalQuery] = useState('');
  const wrapperRef = useRef<HTMLDivElement>(null);
  const searchInputRef = useRef<HTMLInputElement>(null);

  const useServerSearch = keyword !== undefined && onKeywordChange != null;
  const searchValue = useServerSearch ? keyword : localQuery;
  const setSearchValue = useServerSearch ? onKeywordChange! : setLocalQuery;

  const selectedItem =
    value != null && value !== ''
      ? values.find((item) => getValue(item) === value)
      : null;
  const displayValue = selectedItem ? getItemLabel(selectedItem) : '';

  const filterFn = filterOption ?? defaultFilter(getItemLabel);
  const filtered = useServerSearch
    ? values
    : values.filter((item) => filterFn(item, localQuery));

  const close = useCallback(() => {
    setOpen(false);
    if (!useServerSearch) setLocalQuery('');
  }, [useServerSearch]);

  useEffect(() => {
    if (!open) return;
    searchInputRef.current?.focus();
  }, [open]);

  useEffect(() => {
    if (!open) return;
    const onMouseDown = (e: MouseEvent) => {
      if (
        wrapperRef.current &&
        !wrapperRef.current.contains(e.target as Node)
      ) {
        close();
      }
    };
    document.addEventListener('mousedown', onMouseDown);
    return () => document.removeEventListener('mousedown', onMouseDown);
  }, [open, close]);

  const handleSelect = (itemValue: string) => {
    onValueChange(itemValue);
    close();
  };

  const controlId = id ?? name;
  const listboxId = controlId ? `${controlId}-listbox` : undefined;

  return (
    <div ref={wrapperRef} className={cn('relative', className)}>
      {name != null && (
        <input type="hidden" name={name} value={value} readOnly />
      )}
      <div
        role="combobox"
        aria-expanded={open}
        aria-haspopup="listbox"
        aria-invalid={ariaInvalid}
        aria-controls={open ? listboxId : undefined}
        aria-disabled={disabled}
        id={controlId}
        className={cn(
          'border-input bg-input/20 dark:bg-input/30 flex h-7 w-full cursor-pointer items-center gap-1.5 rounded-md border px-2 py-0.5 text-xs/relaxed outline-none transition-colors focus-visible:ring-2 focus-visible:ring-ring/30 aria-invalid:border-destructive aria-invalid:ring-destructive/20 disabled:cursor-not-allowed disabled:opacity-50',
        )}
        onClick={() => !disabled && setOpen((o) => !o)}
      >
        <span className="min-w-0 flex-1 truncate text-left">
          {displayValue || (
            <span className="text-muted-foreground">{placeholder}</span>
          )}
        </span>
        <CaretDownIcon
          className={cn(
            'size-3.5 shrink-0 text-muted-foreground transition-transform',
            open && 'rotate-180',
          )}
        />
      </div>
      {open && (
        <div
          id={listboxId}
          role="listbox"
          className="border-input bg-popover text-popover-foreground absolute top-full z-50 mt-1 max-h-60 w-full min-w-(--radix-popper-anchor-width) overflow-hidden rounded-lg border shadow-md"
        >
          <div className="border-input flex items-center gap-1 border-b px-2 py-1">
            <MagnifyingGlassIcon className="text-muted-foreground size-3.5 shrink-0" />
            <input
              ref={searchInputRef}
              type="search"
              autoComplete="off"
              placeholder={searchPlaceholder}
              value={searchValue}
              onChange={(e) => setSearchValue(e.target.value)}
              onKeyDown={(e) => e.stopPropagation()}
              className="min-w-0 flex-1 bg-transparent py-1 text-xs outline-none placeholder:text-muted-foreground"
            />
          </div>
          <div className="max-h-52 overflow-y-auto p-1">
            {filtered.length === 0 ? (
              <div className="text-muted-foreground py-4 text-center text-xs">
                Không có kết quả
              </div>
            ) : (
              filtered.map((item) => {
                const itemValue = getValue(item);
                return (
                  <Button
                    key={itemValue}
                    variant="ghost"
                    role="option"
                    aria-selected={value === itemValue}
                    className={cn(
                      'flex w-full justify-start cursor-pointer h-auto px-2 py-2',
                      {
                        'bg-accent text-accent-foreground': value === itemValue,
                        'p-0': renderOption,
                      },
                    )}
                    onClick={(e) => {
                      e.preventDefault();
                      e.stopPropagation();
                      handleSelect(itemValue);
                    }}
                  >
                    {renderOption ? renderOption(item) : getItemLabel(item)}
                  </Button>
                );
              })
            )}
          </div>
        </div>
      )}
    </div>
  );
}

export type SelectUserItem = {
  id: string;
  name: string;
  email: string;
  image: string | null;
  role: string | null;
};

const userLabel = (u: SelectUserItem) => `${u.name} - ${u.email}`;

type SelectUserProps = Omit<
  SelectGenericProps<SelectUserItem>,
  'getValue' | 'getItemLabel'
> & {
  values: SelectUserItem[];
  selectKey?: 'id' | 'email';
};

/** Select dành cho User, tương thích code cũ. Dùng SelectGeneric cho đối tượng khác. */
export function SelectUser({
  selectKey = 'id',
  searchPlaceholder = 'Tìm theo tên hoặc email...',
  ...rest
}: SelectUserProps) {
  return (
    <SelectGeneric<SelectUserItem>
      {...rest}
      getValue={(u) => u[selectKey]}
      getItemLabel={userLabel}
      renderOption={(item) => (
        <Item key={item.id}>
          <ItemMedia>
            <AvatarUser className="h-8 w-8" user={item as AvatarUserType} />
          </ItemMedia>
          <ItemContent>
            <ItemTitle>{item.name}</ItemTitle>
            <ItemDescription>{item.email}</ItemDescription>
          </ItemContent>
        </Item>
      )}
      searchPlaceholder={searchPlaceholder}
    />
  );
}

export type SelectHouseItem = {
  id: string;
  name: string;
};

type SelectHouseProps = Omit<
  SelectGenericProps<SelectHouseItem>,
  'getValue' | 'getItemLabel'
> & {
  values: SelectHouseItem[];
};

export function SelectHouse({
  searchPlaceholder = 'Tìm theo tên hoặc email...',
  ...rest
}: SelectHouseProps) {
  return (
    <SelectGeneric<SelectHouseItem>
      {...rest}
      getValue={(h) => h.id}
      getItemLabel={(h: SelectHouseItem) => h.name}
      searchPlaceholder={searchPlaceholder}
    />
  );
}
