import { useEffect, useLayoutEffect, useRef, useState, type ReactNode } from 'react';
import { createPortal } from 'react-dom';
import { ChevronDown, X } from 'lucide-react';
import { cn } from '../../lib/cn';

export interface PickerShellProps<T> {
  label?: string;
  required?: boolean;
  hint?: string;
  /** Class for the outer label wrapper (width etc.). */
  className?: string;
  /** Text shown in the trigger for the current selection ('' = nothing picked). */
  displayLabel: string;
  placeholder?: string;
  /** Rows shown in the open panel (already filtered/fetched by the caller). */
  items: T[];
  renderItem: (item: T) => ReactNode;
  itemKey: (item: T, i: number) => string | number;
  onPick: (item: T) => void;
  /** When provided and something is selected, a clear (✕) button shows. */
  onClear?: () => void;
  /** Show the search box (default true). */
  searchable?: boolean;
  search: string;
  onSearch: (s: string) => void;
  loading?: boolean;
  emptyText?: string;
  /** Notifies the caller so it can fetch on open / reset on close. */
  onOpenChange?: (open: boolean) => void;
  disabled?: boolean;
}

/** Shared searchable-picker chrome: trigger pill + portal dropdown with an
 *  optional search box and a scrollable option list. Both Select (static
 *  options) and LookupField (remote rows) render through this so the two are
 *  visually identical — one picker UI everywhere. The panel lives in a portal
 *  so a Card's `overflow-hidden` can't clip it. */
export function PickerShell<T>({
  label,
  required,
  hint,
  className,
  displayLabel,
  placeholder = 'Select…',
  items,
  renderItem,
  itemKey,
  onPick,
  onClear,
  searchable = true,
  search,
  onSearch,
  loading,
  emptyText = 'No matches.',
  onOpenChange,
  disabled,
}: PickerShellProps<T>) {
  const [open, setOpen] = useState(false);
  const triggerRef = useRef<HTMLDivElement>(null);
  const panelRef = useRef<HTMLDivElement>(null);
  const [pos, setPos] = useState<{ top: number; left: number; width: number } | null>(null);

  const toggle = (next: boolean) => {
    if (disabled) return;
    setOpen(next);
    onOpenChange?.(next);
  };

  // Anchor the portal panel under the trigger; track scroll/resize while open.
  useLayoutEffect(() => {
    if (!open) return;
    const place = () => {
      const el = triggerRef.current;
      if (!el) return;
      const r = el.getBoundingClientRect();
      setPos({ top: r.bottom + 4, left: r.left, width: r.width });
    };
    place();
    window.addEventListener('scroll', place, true);
    window.addEventListener('resize', place);
    return () => {
      window.removeEventListener('scroll', place, true);
      window.removeEventListener('resize', place);
    };
  }, [open]);

  // Close on outside click — ignore clicks inside the trigger or the portal panel.
  useEffect(() => {
    if (!open) return;
    const onDoc = (e: MouseEvent) => {
      const t = e.target as Node;
      if (triggerRef.current?.contains(t) || panelRef.current?.contains(t)) return;
      toggle(false);
    };
    document.addEventListener('mousedown', onDoc);
    return () => document.removeEventListener('mousedown', onDoc);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [open]);

  return (
    <label className={cn('flex flex-col gap-1.5 font-sans', className)}>
      {label && (
        <span className="text-sm font-medium text-muted">
          {label}
          {required && <span className="text-ruby-600"> *</span>}
        </span>
      )}
      <div
        ref={triggerRef}
        className={cn(
          'flex items-center gap-2 bg-card rounded-md px-3 h-[42px] border border-border-default focus-ring',
          disabled ? 'opacity-60 cursor-not-allowed' : 'cursor-pointer',
        )}
        onClick={() => toggle(!open)}
      >
        <span className={cn('flex-1 truncate text-base', displayLabel ? 'text-strong' : 'text-faint')}>
          {displayLabel || placeholder}
        </span>
        {onClear && displayLabel ? (
          <button
            type="button"
            onClick={(e) => {
              e.stopPropagation();
              onClear();
            }}
            className="text-faint hover:text-strong"
          >
            <X size={15} />
          </button>
        ) : (
          <ChevronDown size={15} className="text-faint" />
        )}
      </div>

      {hint && <span className="text-xs text-faint">{hint}</span>}

      {open &&
        pos &&
        createPortal(
          <div
            ref={panelRef}
            style={{ position: 'fixed', top: pos.top, left: pos.left, width: pos.width, zIndex: 10050 }}
            className="bg-card border border-border-default rounded-md shadow-lg overflow-hidden"
          >
            {searchable && (
              <div className="p-2 border-b border-border-subtle">
                <input
                  autoFocus
                  value={search}
                  onChange={(e) => onSearch(e.target.value)}
                  placeholder="Search…"
                  className="w-full h-9 px-2.5 border border-border-default rounded-md outline-none text-sm focus:border-sunrise-500"
                />
              </div>
            )}
            <div className="max-h-56 overflow-auto">
              {loading ? (
                <div className="px-3 py-3 text-sm text-faint">Searching…</div>
              ) : items.length ? (
                items.map((item, i) => (
                  <button
                    type="button"
                    key={itemKey(item, i)}
                    onClick={() => {
                      onPick(item);
                      toggle(false);
                    }}
                    className="w-full text-left px-3 py-2.5 text-sm text-body hover:bg-slate-50 border-none bg-transparent cursor-pointer"
                  >
                    {renderItem(item)}
                  </button>
                ))
              ) : (
                <div className="px-3 py-3 text-sm text-faint">{emptyText}</div>
              )}
            </div>
          </div>,
          document.body,
        )}
    </label>
  );
}
