import React, { useState, useRef, useEffect, useMemo, useCallback, useLayoutEffect } from 'react';
import { createPortal } from 'react-dom';
import { Search, ChevronDown, X } from 'lucide-react';

export interface SearchableSelectOption {
    value: string;
    label: string;
    /** Secondary text shown below label (e.g. type) */
    detail?: string;
}

interface SearchableSelectProps {
    options: SearchableSelectOption[];
    value: string;
    onChange: (value: string) => void;
    placeholder?: string;
    disabled?: boolean;
    /** Show error styling */
    error?: boolean;
    /** Error message below the input */
    errorMessage?: string;
    /** How to render the selected value: 'label' shows friendly label, 'value' shows raw value */
    displayMode?: 'label' | 'value';
    className?: string;
    searchInputAutoComplete?: string;
    /**
     * DOM id for the trigger control, so a caller's <label htmlFor> associates
     * with it. When `error` is set, the trigger is also marked aria-invalid and
     * linked (aria-describedby) to the caller's `${id}-error` message element.
     */
    id?: string;
}

export function SearchableSelect({
    options,
    value,
    onChange,
    placeholder = 'Select...',
    disabled = false,
    error = false,
    errorMessage,
    displayMode = 'label',
    className = '',
    searchInputAutoComplete,
    id,
}: SearchableSelectProps) {
    const [isOpen, setIsOpen] = useState(false);
    const [search, setSearch] = useState('');
    const [dropdownRect, setDropdownRect] = useState<{ top: number; left: number; width: number; openUp: boolean } | null>(null);
    const containerRef = useRef<HTMLDivElement>(null);
    const dropdownRef = useRef<HTMLDivElement>(null);
    const inputRef = useRef<HTMLInputElement>(null);

    // Close dropdown when clicking outside (considers both trigger and portal dropdown)
    useEffect(() => {
        const handler = (e: MouseEvent) => {
            const target = e.target as Node;
            const inTrigger = containerRef.current?.contains(target);
            const inDropdown = dropdownRef.current?.contains(target);
            if (!inTrigger && !inDropdown) {
                setIsOpen(false);
                setSearch('');
            }
        };
        document.addEventListener('mousedown', handler);
        return () => document.removeEventListener('mousedown', handler);
    }, []);

    // Position the portal dropdown relative to the trigger; recompute on scroll/resize
    useLayoutEffect(() => {
        if (!isOpen) {
            setDropdownRect(null);
            return;
        }
        const update = () => {
            const el = containerRef.current;
            if (!el) return;
            const rect = el.getBoundingClientRect();
            const estimatedHeight = 320; // max-h-60 list + search + footer
            const spaceBelow = window.innerHeight - rect.bottom;
            const openUp = spaceBelow < estimatedHeight && rect.top > spaceBelow;
            setDropdownRect({
                top: openUp ? rect.top : rect.bottom,
                left: rect.left,
                width: rect.width,
                openUp,
            });
        };
        update();
        window.addEventListener('scroll', update, true);
        window.addEventListener('resize', update);
        return () => {
            window.removeEventListener('scroll', update, true);
            window.removeEventListener('resize', update);
        };
    }, [isOpen]);

    // Focus search input when dropdown opens
    useEffect(() => {
        if (isOpen && inputRef.current) {
            inputRef.current.focus();
        }
    }, [isOpen]);

    const filteredOptions = useMemo(() => {
        if (!search) return options;
        const q = search.toLowerCase();
        return options.filter(o =>
            o.label.toLowerCase().includes(q) ||
            o.value.toLowerCase().includes(q) ||
            (o.detail && o.detail.toLowerCase().includes(q))
        );
    }, [options, search]);

    const selectedOption = useMemo(
        () => options.find(o => o.value === value),
        [options, value]
    );

    const handleSelect = useCallback((val: string) => {
        onChange(val);
        setIsOpen(false);
        setSearch('');
    }, [onChange]);

    const handleClear = useCallback((e: React.MouseEvent) => {
        e.stopPropagation();
        onChange('');
        setIsOpen(false);
        setSearch('');
    }, [onChange]);

    const displayText = selectedOption
        ? (displayMode === 'label' ? selectedOption.label : selectedOption.value)
        : value || '';

    const borderColor = error
        ? 'border-red-400'
        : isOpen ? 'border-blue-500 ring-1 ring-blue-500' : 'border-gray-300 dark:border-gray-700';

    return (
        <div ref={containerRef} className={`relative ${className}`}>
            {/* Trigger button */}
            <button
                id={id}
                type="button"
                disabled={disabled}
                onClick={() => { if (!disabled) setIsOpen(!isOpen); }}
                aria-invalid={error || undefined}
                aria-describedby={error && id ? `${id}-error` : undefined}
                className={`w-full flex items-center justify-between gap-2 bg-white dark:bg-gray-900 border ${borderColor} rounded-md px-3 py-2 text-sm text-left disabled:bg-gray-100 disabled:text-gray-500 dark:disabled:bg-gray-800 dark:disabled:text-gray-500 transition-colors`}
                title={selectedOption ? `${selectedOption.value}${selectedOption.detail ? ` (${selectedOption.detail})` : ''}` : undefined}
            >
                <span className={`truncate ${displayText ? 'text-gray-900 dark:text-white' : 'text-gray-400'}`}>
                    {displayText || placeholder}
                </span>
                <div className="flex items-center gap-1 flex-shrink-0">
                    {value && !disabled && (
                        <span
                            role="button"
                            tabIndex={-1}
                            onClick={handleClear}
                            className="p-0.5 hover:bg-gray-200 dark:hover:bg-gray-700 rounded"
                        >
                            <X className="w-3.5 h-3.5 text-gray-400" />
                        </span>
                    )}
                    <ChevronDown className={`w-4 h-4 text-gray-400 transition-transform ${isOpen ? 'rotate-180' : ''}`} />
                </div>
            </button>

            {errorMessage && <p className="text-xs text-red-500 mt-1">{errorMessage}</p>}

            {/* Dropdown — portaled to body with fixed positioning to escape overflow-hidden / overflow-auto ancestors */}
            {isOpen && dropdownRect && createPortal(
                <div
                    ref={dropdownRef}
                    style={{
                        position: 'fixed',
                        top: dropdownRect.openUp ? undefined : dropdownRect.top + 4,
                        bottom: dropdownRect.openUp ? window.innerHeight - dropdownRect.top + 4 : undefined,
                        left: dropdownRect.left,
                        width: dropdownRect.width,
                    }}
                    className="z-[1000] bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg overflow-hidden"
                >
                    {/* Search input */}
                    <div className="p-2 border-b border-gray-100 dark:border-gray-700">
                        <div className="relative">
                            <Search className="absolute left-2.5 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-400" />
                            <input
                                ref={inputRef}
                                type="text"
                                value={search}
                                onChange={e => setSearch(e.target.value)}
                                placeholder="Search..."
                                className="w-full pl-8 pr-3 py-1.5 bg-gray-50 dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-md text-sm text-gray-900 dark:text-white placeholder-gray-400 focus:outline-none focus:border-blue-500"
                                autoComplete={searchInputAutoComplete}
                            />
                        </div>
                    </div>

                    {/* Options list */}
                    <div className="max-h-60 overflow-y-auto">
                        {filteredOptions.length === 0 ? (
                            <div className="px-3 py-4 text-center text-sm text-gray-400">No matches found</div>
                        ) : (
                            filteredOptions.map(option => (
                                <button
                                    key={option.value}
                                    type="button"
                                    onClick={() => handleSelect(option.value)}
                                    className={`w-full text-left px-3 py-2 hover:bg-blue-50 dark:hover:bg-blue-900/30 transition-colors ${option.value === value ? 'bg-blue-50 dark:bg-blue-900/20' : ''
                                        }`}
                                    title={option.value}
                                >
                                    <div className="text-sm font-medium text-gray-900 dark:text-white truncate">
                                        {option.label}
                                    </div>
                                    {option.detail && (
                                        <div className="text-xs text-gray-500 dark:text-gray-400 truncate">
                                            {option.detail}
                                        </div>
                                    )}
                                </button>
                            ))
                        )}
                    </div>

                    {/* Count footer */}
                    {options.length > 10 && (
                        <div className="px-3 py-1.5 border-t border-gray-100 dark:border-gray-700 text-xs text-gray-400 text-right">
                            {filteredOptions.length} of {options.length}
                        </div>
                    )}
                </div>,
                document.body
            )}
        </div>
    );
}
