import { useContext, useMemo, useState, type KeyboardEvent, type ReactNode } from "react";
import type { FloatingPlacement } from "@nodedc/ui-core";
import { Dropdown } from "./Dropdown.js";
import { InspectorSelectPolicyContext } from "./InspectorContext.js";
import { cn } from "./cn.js";

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

export type SelectVariant = "integrated" | "split";

export interface SelectProps<T extends string> {
  value: T;
  options: Array<SelectOption<T>>;
  label: string;
  onChange: (value: T, option: SelectOption<T>) => void;
  searchable?: boolean;
  searchPlaceholder?: string;
  emptyLabel?: string;
  placement?: FloatingPlacement;
  minMenuWidth?: number;
  menuWidth?: number | "anchor";
  disabled?: boolean;
  variant?: SelectVariant;
  className?: string;
  triggerClassName?: string;
  menuClassName?: string;
}

export function Select<T extends string>({
  value,
  options,
  label,
  onChange,
  searchable = false,
  searchPlaceholder = "Поиск",
  emptyLabel = "Ничего не найдено",
  placement = "bottom-start",
  minMenuWidth = 180,
  menuWidth = "anchor",
  disabled = false,
  variant = "integrated",
  className,
  triggerClassName,
  menuClassName,
}: SelectProps<T>) {
  const inspectorSplitRequired = useContext(InspectorSelectPolicyContext);
  const resolvedVariant: SelectVariant = inspectorSplitRequired ? "split" : variant;
  const [query, setQuery] = useState("");
  const selected = options.find((option) => option.value === value) ?? options[0];
  const visibleOptions = useMemo(() => {
    const normalized = query.trim().toLocaleLowerCase();
    if (!normalized) return options;
    return options.filter((option) => `${option.label} ${option.description ?? ""}`.toLocaleLowerCase().includes(normalized));
  }, [options, query]);

  return (
    <Dropdown
      className={cn("nodedc-select-anchor", className)}
      placement={placement}
      minWidth={minMenuWidth}
      width={menuWidth}
      disabled={disabled}
      surfaceRole="listbox"
      surfaceClassName={cn(resolvedVariant === "split" && "nodedc-select__menu", menuClassName)}
      trigger={({ open, toggle, setAnchorRef, setTriggerRef, surfaceId }) => {
        const handleKeyDown = (event: KeyboardEvent<HTMLButtonElement>) => {
          if (event.key === "ArrowDown" || event.key === "Enter" || event.key === " ") {
            event.preventDefault();
            if (!open) toggle();
          }
        };
        if (resolvedVariant === "split") {
          return (
            <div className={cn("nodedc-select", triggerClassName)} data-disabled={disabled ? "true" : undefined}>
              <div ref={setAnchorRef} className="nodedc-select__control">
                <div className="nodedc-select__value">
                  {selected?.icon ? <span className="nodedc-select__value-icon">{selected.icon}</span> : null}
                  <span>{selected?.label ?? "—"}</span>
                </div>
                <button
                  ref={setTriggerRef}
                  type="button"
                  className="nodedc-select__toggle"
                  aria-label={label}
                  aria-haspopup="listbox"
                  aria-controls={surfaceId}
                  aria-expanded={open}
                  disabled={disabled}
                  onClick={toggle}
                  onKeyDown={handleKeyDown}
                >
                  <span className="nodedc-select-trigger__chevron" aria-hidden="true" />
                </button>
              </div>
            </div>
          );
        }

        return (
          <button
            ref={setTriggerRef}
            type="button"
            className={cn("nodedc-select-trigger", triggerClassName)}
            aria-label={label}
            aria-haspopup="listbox"
            aria-controls={surfaceId}
            aria-expanded={open}
            disabled={disabled}
            onClick={toggle}
            onKeyDown={handleKeyDown}
          >
            {selected?.icon ? <span className="nodedc-select-trigger__icon">{selected.icon}</span> : <span className="nodedc-select-trigger__icon" aria-hidden="true" />}
            <span className="nodedc-select-trigger__label">{selected?.label ?? "—"}</span>
            <span className="nodedc-select-trigger__chevron" aria-hidden="true" />
          </button>
        );
      }}
    >
      {({ close }) => (
        <>
          {searchable ? (
            <label className="nodedc-dropdown-search">
              <span aria-hidden="true">⌕</span>
              <input value={query} onChange={(event) => setQuery(event.target.value)} placeholder={searchPlaceholder} />
            </label>
          ) : null}
          {visibleOptions.map((option) => (
            <button
              key={option.value}
              type="button"
              className="nodedc-dropdown-option"
              role="option"
              aria-selected={option.value === value}
              data-selected={option.value === value ? "true" : undefined}
              disabled={option.disabled}
              onClick={() => {
                if (option.disabled) return;
                onChange(option.value, option);
                setQuery("");
                close();
              }}
            >
              {option.icon ? <span className="nodedc-dropdown-option__icon">{option.icon}</span> : <span />}
              <span className="nodedc-dropdown-option__body">
                <span className="nodedc-dropdown-option__label">{option.label}</span>
                {option.description ? <span className="nodedc-dropdown-option__description">{option.description}</span> : null}
              </span>
              {option.value === value ? <span className="nodedc-dropdown-option__check" aria-hidden="true">•</span> : null}
            </button>
          ))}
          {visibleOptions.length === 0 ? <div className="nodedc-empty-state">{emptyLabel}</div> : null}
        </>
      )}
    </Dropdown>
  );
}
