'use client';

import { useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
import { Check, ChevronDown, Search, X } from 'lucide-react';
import { cn } from '@/lib/utils';
import { matchesExpandedSearch } from '@ehandyhire/shared';

export type SearchableSelectOption = {
  value: string;
  label: string;
  group?: string;
  /** Extra text included in search matching */
  keywords?: string;
};

interface SearchableSelectProps {
  value: string;
  onChange: (value: string) => void;
  options: SearchableSelectOption[];
  /** Label shown when value is empty */
  emptyLabel?: string;
  placeholder?: string;
  searchPlaceholder?: string;
  className?: string;
  disabled?: boolean;
}

function matchesOption(option: SearchableSelectOption, query: string) {
  if (!query) return true;
  const haystack = [option.label, option.group, option.keywords, option.value.replace(/-/g, ' ')]
    .filter(Boolean)
    .join(' ');
  return matchesExpandedSearch(haystack, query);
}

export function SearchableSelect({
  value,
  onChange,
  options,
  emptyLabel = 'All',
  placeholder = 'Select…',
  searchPlaceholder = 'Search…',
  className,
  disabled,
}: SearchableSelectProps) {
  const [open, setOpen] = useState(false);
  const [query, setQuery] = useState('');
  const [mounted, setMounted] = useState(false);
  const [menuStyle, setMenuStyle] = useState<React.CSSProperties>({});
  const rootRef = useRef<HTMLDivElement>(null);
  const menuRef = useRef<HTMLDivElement>(null);
  const searchRef = useRef<HTMLInputElement>(null);
  const listboxId = useId();

  const selected = options.find((o) => o.value === value);
  const displayLabel = value ? selected?.label ?? placeholder : emptyLabel;

  const filtered = useMemo(() => {
    return options.filter((o) => matchesOption(o, query.trim()));
  }, [options, query]);

  const grouped = useMemo(() => {
    const map = new Map<string, SearchableSelectOption[]>();
    for (const option of filtered) {
      const key = option.group ?? '';
      const list = map.get(key) ?? [];
      list.push(option);
      map.set(key, list);
    }
    return Array.from(map.entries());
  }, [filtered]);

  useEffect(() => {
    setMounted(true);
  }, []);

  useLayoutEffect(() => {
    if (!open || !rootRef.current) return;

    const updatePosition = () => {
      const rect = rootRef.current?.getBoundingClientRect();
      if (!rect) return;

      const menuHeight = 280;
      const gap = 6;
      const spaceBelow = window.innerHeight - rect.bottom;
      const openUp = spaceBelow < menuHeight && rect.top > spaceBelow;

      setMenuStyle({
        position: 'fixed',
        left: rect.left,
        width: Math.max(rect.width, 220),
        zIndex: 10050,
        ...(openUp
          ? { bottom: window.innerHeight - rect.top + gap, maxHeight: Math.min(menuHeight, rect.top - 16) }
          : { top: rect.bottom + gap, maxHeight: Math.min(menuHeight, spaceBelow - 16) }),
      });
    };

    updatePosition();
    window.addEventListener('resize', updatePosition);
    window.addEventListener('scroll', updatePosition, true);
    return () => {
      window.removeEventListener('resize', updatePosition);
      window.removeEventListener('scroll', updatePosition, true);
    };
  }, [open]);

  useEffect(() => {
    if (!open) return;
    setQuery('');
    const timer = window.setTimeout(() => searchRef.current?.focus(), 0);

    const onPointerDown = (e: MouseEvent | TouchEvent) => {
      const target = e.target as Node;
      if (
        rootRef.current?.contains(target) ||
        menuRef.current?.contains(target)
      ) {
        return;
      }
      setOpen(false);
    };
    const onKeyDown = (e: KeyboardEvent) => {
      if (e.key === 'Escape') {
        e.stopPropagation();
        setOpen(false);
      }
    };

    document.addEventListener('mousedown', onPointerDown);
    document.addEventListener('touchstart', onPointerDown);
    document.addEventListener('keydown', onKeyDown, true);
    return () => {
      window.clearTimeout(timer);
      document.removeEventListener('mousedown', onPointerDown);
      document.removeEventListener('touchstart', onPointerDown);
      document.removeEventListener('keydown', onKeyDown, true);
    };
  }, [open]);

  const pick = (next: string) => {
    onChange(next);
    setOpen(false);
  };

  return (
    <div ref={rootRef} className={cn('relative w-full', className)}>
      <button
        type="button"
        disabled={disabled}
        aria-haspopup="listbox"
        aria-expanded={open}
        aria-controls={listboxId}
        onClick={() => setOpen((prev) => !prev)}
        className={cn(
          'form-select mt-0 flex h-11 w-full items-center justify-between gap-2 text-left',
          !value && 'text-slate-500',
          open && 'ring-2 ring-primary/20 border-primary'
        )}
      >
        <span className="min-w-0 truncate">{displayLabel}</span>
        <ChevronDown
          className={cn('h-4 w-4 shrink-0 text-slate-400 transition-transform', open && 'rotate-180')}
        />
      </button>

      {mounted &&
        open &&
        createPortal(
          <div
            ref={menuRef}
            style={menuStyle}
            className="overflow-hidden rounded-xl border border-border bg-white shadow-xl"
          >
            <div className="border-b border-border p-2">
              <div className="relative">
                <Search className="pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-slate-400" />
                <input
                  ref={searchRef}
                  type="search"
                  value={query}
                  onChange={(e) => setQuery(e.target.value)}
                  placeholder={searchPlaceholder}
                  className="h-10 w-full rounded-lg border border-border bg-slate-50 pl-9 pr-9 text-sm text-slate-900 placeholder:text-slate-400 focus:border-primary focus:bg-white focus:outline-none focus:ring-2 focus:ring-primary/20"
                  autoComplete="off"
                />
                {query ? (
                  <button
                    type="button"
                    aria-label="Clear search"
                    onClick={() => {
                      setQuery('');
                      searchRef.current?.focus();
                    }}
                    className="absolute right-2 top-1/2 -translate-y-1/2 rounded p-1 text-slate-400 hover:bg-slate-100 hover:text-slate-600"
                  >
                    <X className="h-3.5 w-3.5" />
                  </button>
                ) : null}
              </div>
            </div>

            <div
              id={listboxId}
              role="listbox"
              className="overflow-y-auto overscroll-contain p-1.5"
              style={{ maxHeight: 'calc(100% - 3.5rem)' }}
            >
              <button
                type="button"
                role="option"
                aria-selected={!value}
                onClick={() => pick('')}
                className={cn(
                  'flex w-full items-center gap-2 rounded-lg px-3 py-2.5 text-left text-sm transition-colors',
                  !value
                    ? 'bg-primary/10 font-semibold text-primary-dark'
                    : 'text-slate-700 hover:bg-slate-50'
                )}
              >
                <span
                  className={cn(
                    'flex h-4 w-4 shrink-0 items-center justify-center rounded-full border',
                    !value ? 'border-primary bg-primary text-white' : 'border-slate-200'
                  )}
                >
                  {!value ? <Check className="h-2.5 w-2.5" strokeWidth={3} /> : null}
                </span>
                <span className="truncate">{emptyLabel}</span>
              </button>

              {filtered.length === 0 ? (
                <p className="px-3 py-6 text-center text-sm text-muted">No matches found</p>
              ) : (
                grouped.map(([group, items]) => (
                  <div key={group || '__ungrouped'} className="mt-1">
                    {group ? (
                      <p className="px-3 py-1.5 text-[10px] font-semibold uppercase tracking-wide text-muted">
                        {group}
                      </p>
                    ) : null}
                    {items.map((option) => {
                      const isSelected = option.value === value;
                      return (
                        <button
                          key={option.value}
                          type="button"
                          role="option"
                          aria-selected={isSelected}
                          onClick={() => pick(option.value)}
                          className={cn(
                            'flex w-full items-center gap-2 rounded-lg px-3 py-2.5 text-left text-sm transition-colors',
                            isSelected
                              ? 'bg-primary/10 font-semibold text-primary-dark'
                              : 'text-slate-700 hover:bg-slate-50'
                          )}
                        >
                          <span
                            className={cn(
                              'flex h-4 w-4 shrink-0 items-center justify-center rounded-full border',
                              isSelected
                                ? 'border-primary bg-primary text-white'
                                : 'border-slate-200'
                            )}
                          >
                            {isSelected ? <Check className="h-2.5 w-2.5" strokeWidth={3} /> : null}
                          </span>
                          <span className="truncate">{option.label}</span>
                        </button>
                      );
                    })}
                  </div>
                ))
              )}
            </div>
          </div>,
          document.body
        )}
    </div>
  );
}
