import { Check, ChevronDown, Search } from "lucide-react";
import { useMemo, useState, type ReactNode } from "react";
import { cn } from "../lib/cn";
import { NodeDcDropdown } from "./Dropdown";

export interface NodeDcSelectOption<T extends string> {
  value: T;
  label: string;
  description?: string;
  icon?: ReactNode;
  tone?: string;
  disabled?: boolean;
  hidden?: boolean;
}

interface NodeDcSelectTriggerApi<T extends string> {
  open: boolean;
  selectedOption: NodeDcSelectOption<T>;
  toggle: () => void;
  setTriggerRef: (node: HTMLElement | null) => void;
}

interface NodeDcSelectProps<T extends string> {
  value: T;
  options: Array<NodeDcSelectOption<T>>;
  label: string;
  onChange: (value: T, option: NodeDcSelectOption<T>) => void;
  trigger?: (api: NodeDcSelectTriggerApi<T>) => ReactNode;
  className?: string;
  triggerClassName?: string;
  menuClassName?: string;
  optionClassName?: string;
  searchable?: boolean;
  searchPlaceholder?: string;
  placement?: "bottom-start" | "bottom-end" | "top-start" | "top-end";
  minMenuWidth?: number;
  menuWidth?: number;
  disabled?: boolean;
  renderOptionLabel?: (option: NodeDcSelectOption<T>) => ReactNode;
}

export function NodeDcSelect<T extends string>({
  value,
  options,
  label,
  onChange,
  trigger,
  className,
  triggerClassName,
  menuClassName,
  optionClassName,
  searchable = false,
  searchPlaceholder = "Поиск",
  placement = "bottom-start",
  minMenuWidth = 180,
  menuWidth,
  disabled = false,
  renderOptionLabel,
}: NodeDcSelectProps<T>) {
  const [query, setQuery] = useState("");
  const selectedOption = options.find((option) => option.value === value) ?? options[0];
  const normalizedQuery = query.trim().toLowerCase();
  const menuOptions = useMemo(() => options.filter((option) => !option.hidden), [options]);
  const visibleOptions = useMemo(() => {
    if (!normalizedQuery) return menuOptions;

    return menuOptions.filter((option) => {
      const haystack = `${option.label} ${option.description ?? ""}`.toLowerCase();
      return haystack.includes(normalizedQuery);
    });
  }, [menuOptions, normalizedQuery]);

  return (
    <NodeDcDropdown
      className={className}
      placement={placement}
      minWidth={minMenuWidth}
      width={menuWidth}
      disabled={disabled}
      surfaceClassName={cn("nodedc-ui-select-menu", menuClassName)}
      trigger={({ open, toggle, setTriggerRef }) =>
        trigger ? (
          trigger({ open, selectedOption, toggle, setTriggerRef })
        ) : (
          <button
            ref={setTriggerRef}
            className={cn("nodedc-ui-select-trigger", triggerClassName)}
            type="button"
            aria-label={label}
            aria-expanded={open}
            disabled={disabled}
            onClick={toggle}
          >
            {selectedOption.icon ? <span className="nodedc-ui-select-trigger__icon">{selectedOption.icon}</span> : null}
            <span className="nodedc-ui-select-trigger__text">{selectedOption.label}</span>
            <ChevronDown className="nodedc-ui-select-trigger__chevron" size={14} strokeWidth={1.8} />
          </button>
        )
      }
    >
      {({ close }) => (
        <>
          {searchable ? (
            <label className="nodedc-ui-dropdown-search">
              <Search size={14} strokeWidth={1.8} />
              <input value={query} onChange={(event) => setQuery(event.target.value)} placeholder={searchPlaceholder} />
            </label>
          ) : null}

          <div className="nodedc-ui-option-list">
            {visibleOptions.map((option) => (
              <button
                key={option.value}
                className={cn("nodedc-ui-option nodedc-dropdown-option", optionClassName)}
                data-selected={option.value === value}
                data-tone={option.tone}
                type="button"
                disabled={option.disabled}
                onClick={() => {
                  if (option.disabled) return;
                  onChange(option.value, option);
                  close();
                }}
              >
                {option.icon ? <span className="nodedc-ui-option__icon">{option.icon}</span> : null}
                <span className="nodedc-ui-option__body">
                  <span className="nodedc-ui-option__label">{renderOptionLabel ? renderOptionLabel(option) : option.label}</span>
                  {option.description ? <span className="nodedc-ui-option__description">{option.description}</span> : null}
                </span>
                {option.value === value ? <Check className="nodedc-ui-option__check" size={14} strokeWidth={1.8} /> : null}
              </button>
            ))}
          </div>
        </>
      )}
    </NodeDcDropdown>
  );
}
