'use client';

import React, { useEffect, useRef, useState, useId } from 'react';
import Select, { ActionMeta, MultiValue, SingleValue } from 'react-select';
import CreatableSelect from 'react-select/creatable';

export interface Option {
  value: string | number;
  label: string;
}

interface SearchableSelectProps {
  options?: Option[];
  value?: any; // Controlled value (raw value or Option object/array)
  defaultValue?: any; // Uncontrolled default value
  onChange?: (value: any) => void;
  onSearchChange?: (query: string) => void; // For async searches
  loading?: boolean;
  disabled?: boolean;
  isMulti?: boolean;
  placeholder?: string;
  isClearable?: boolean;
  className?: string;
  noOptionsMessage?: string;
  isCreatable?: boolean;
  wrap?: boolean;
  controlBgClass?: string;
  menuPlacement?: 'auto' | 'top' | 'bottom';
  portalMenu?: boolean; // Portal the dropdown to <body> so parent overflow never clips it
}

export default function SearchableSelect({
  options = [],
  value,
  defaultValue,
  onChange,
  onSearchChange,
  loading = false,
  disabled = false,
  isMulti = false,
  placeholder = 'Select option...',
  isClearable = true,
  className = '',
  noOptionsMessage = 'No options found',
  isCreatable = false,
  wrap = false,
  controlBgClass = 'bg-slate-50 dark:bg-slate-950',
  menuPlacement = 'auto',
  portalMenu = true,
}: SearchableSelectProps) {
  // Use React's useId hook to avoid hydration mismatches for select element IDs
  const instanceId = useId();

  // Controlled vs Uncontrolled state
  const isControlled = value !== undefined;
  const [internalValue, setInternalValue] = useState<any>(
    isControlled ? value : defaultValue !== undefined ? defaultValue : isMulti ? [] : ''
  );

  // Sync internal state if controlled value changes
  useEffect(() => {
    if (isControlled) {
      setInternalValue(value);
    }
  }, [value, isControlled]);

  // Debounced search query handler if onSearchChange is provided
  const [inputValue, setInputValue] = useState('');
  const debounceTimer = useRef<NodeJS.Timeout | null>(null);

  const handleInputChange = (newInputValue: string) => {
    setInputValue(newInputValue);
    if (!onSearchChange) return;

    if (debounceTimer.current) {
      clearTimeout(debounceTimer.current);
    }

    debounceTimer.current = setTimeout(() => {
      onSearchChange(newInputValue);
    }, 300);
  };

  // Clean up debounce timer on unmount
  useEffect(() => {
    return () => {
      if (debounceTimer.current) {
        clearTimeout(debounceTimer.current);
      }
    };
  }, []);

  // Map raw values to option objects
  const getSelectedOptions = () => {
    const activeValue = isControlled ? value : internalValue;

    if (activeValue === undefined || activeValue === null) {
      return isMulti ? [] : null;
    }

    if (isMulti) {
      if (Array.isArray(activeValue)) {
        return activeValue.map((val) => {
          if (val && typeof val === 'object' && 'value' in val) {
            return val as Option;
          }
          return options.find((o) => o.value === val) || { value: val, label: String(val) };
        });
      }
      return [];
    } else {
      if (activeValue && typeof activeValue === 'object' && 'value' in activeValue) {
        return activeValue as Option;
      }
      return (
        options.find((o) => o.value === activeValue) ||
        (activeValue !== '' ? { value: activeValue, label: String(activeValue) } : null)
      );
    }
  };

  const handleSelectChange = (
    newValue: MultiValue<Option> | SingleValue<Option>,
    actionMeta: ActionMeta<Option>
  ) => {
    void actionMeta;
    let resolvedValue: any;

    if (isMulti) {
      const selectedList = (newValue as MultiValue<Option>) || [];
      resolvedValue = selectedList.map((o) => o.value);
    } else {
      const selectedItem = newValue as SingleValue<Option>;
      resolvedValue = selectedItem ? selectedItem.value : '';
    }

    if (!isControlled) {
      setInternalValue(resolvedValue);
    }

    if (onChange) {
      onChange(resolvedValue);
    }
  };

  const selectedOptions = getSelectedOptions();
  const SelectComponent = isCreatable ? CreatableSelect : Select;

  return (
    <div className={`min-w-0 max-w-full ${className || 'w-full'}`}>
      <SelectComponent
        instanceId={instanceId}
        unstyled
        options={options}
        value={selectedOptions}
        onChange={handleSelectChange}
        onInputChange={handleInputChange}
        inputValue={inputValue}
        isLoading={loading}
        isDisabled={disabled}
        isMulti={isMulti}
        isClearable={isClearable}
        placeholder={placeholder}
        menuPlacement={menuPlacement}
        noOptionsMessage={() => noOptionsMessage}
        loadingMessage={() => 'Searching options...'}
        filterOption={onSearchChange ? () => true : undefined} // Skip client-side filtering if async
        // Portal the menu to <body> so parent overflow/height never clips it,
        // flipping up or down automatically based on viewport space.
        menuPortalTarget={portalMenu && typeof document !== 'undefined' ? document.body : undefined}
        menuPosition={portalMenu ? 'fixed' : undefined}
        // A fixed-position portal menu doesn't track its control while scrolling,
        // so close it when anything other than the menu list itself scrolls.
        closeMenuOnScroll={portalMenu ? (e: Event) => {
          const t = e.target as Node | null;
          return !(t instanceof HTMLElement && t.closest('.searchable-select-menu'));
        } : undefined}
        styles={{
          valueContainer: (base) => ({
            ...base,
            flexWrap: wrap ? 'wrap' : 'nowrap',
          }),
          menuPortal: (base) => ({
            ...base,
            zIndex: 9999,
          }),
        }}
        classNames={{
          control: ({ isFocused, isDisabled }) =>
            `flex items-center justify-between border rounded-none ${controlBgClass} px-3 ${wrap ? 'py-1' : 'py-0.5'} text-xs transition-all duration-200 cursor-pointer min-h-[34px] ${wrap ? '' : 'h-[34px]'} w-full ${isFocused
              ? 'border-[#405189] dark:border-indigo-500 ring-2 ring-[#405189]/30 dark:ring-indigo-500/40 shadow-sm'
              : 'border-slate-200 dark:border-slate-800 hover:border-[#405189]/50 dark:hover:border-indigo-500/50'
            } ${isDisabled ? 'opacity-60 cursor-not-allowed' : ''}`,
          valueContainer: () =>
            `flex items-center gap-1 text-slate-800 dark:text-slate-250 py-0 flex-1 min-w-0 ${wrap ? 'flex-wrap' : '!flex-nowrap !overflow-x-auto scrollbar-none'}`,
          placeholder: () => 'text-slate-400 dark:text-slate-500 text-xs whitespace-nowrap overflow-hidden text-ellipsis',
          singleValue: () => 'text-slate-800 dark:text-white text-xs whitespace-nowrap overflow-hidden text-ellipsis w-full block',
          input: () => 'text-slate-800 dark:text-white text-xs outline-none border-none p-0 m-0 min-w-[2px]',
          indicatorsContainer: () => 'flex items-center gap-0.5 text-slate-400 shrink-0',
          clearIndicator: () => 'text-slate-400 hover:text-rose-500 dark:hover:text-rose-450 cursor-pointer p-0.5 transition-colors',
          dropdownIndicator: () => 'text-slate-400 hover:text-slate-600 dark:hover:text-slate-200 cursor-pointer p-0.5 transition-colors',
          menu: (state) =>
            `searchable-select-menu bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-800 rounded-none shadow-lg z-50 overflow-hidden ${portalMenu ? '' : 'absolute left-0 right-0 w-full'} ${state.placement === 'top' ? 'bottom-full mb-1' : 'top-full mt-1'}`,
          // divide-y draws a subtle separator between options only — never
          // after the last one — and follows the theme's border colors.
          menuList: () => 'max-h-60 overflow-y-auto custom-scrollbar p-1 divide-y divide-slate-100 dark:divide-slate-800',
          option: ({ isFocused }) =>
            `px-3 py-2 text-xs cursor-pointer transition-colors duration-150 whitespace-nowrap overflow-hidden text-ellipsis text-slate-700 dark:text-slate-350 ${isFocused ? 'bg-slate-100 dark:bg-slate-800/40' : ''} hover:bg-slate-50 dark:hover:bg-slate-800/40`,
          multiValue: () => 'bg-indigo-50/70 dark:bg-indigo-950/30 border border-indigo-100/60 dark:border-indigo-900/30 rounded-none flex items-center px-1.5 py-0.5 text-xs text-indigo-700 dark:text-indigo-400 whitespace-nowrap overflow-hidden text-ellipsis max-w-full shrink-0',
          multiValueLabel: () => 'mr-1 overflow-hidden text-ellipsis text-[11px] font-normal leading-none',
          multiValueRemove: () => 'text-indigo-400 hover:text-rose-600 dark:hover:text-rose-400 cursor-pointer p-0.5 rounded-none hover:bg-rose-50 dark:hover:bg-rose-950/30 shrink-0 transition-colors duration-150',
          noOptionsMessage: () => 'text-slate-400 p-4 text-center text-xs',
          loadingMessage: () => 'text-slate-400 p-4 text-center text-xs',
        }}
      />
    </div>
  );
}
