import {
  splitProps,
  mergeProps,
  createSignal,
  createEffect,
  onCleanup,
  For,
  Show,
  JSX,
  createUniqueId,
} from 'solid-js';
import { Portal } from 'solid-js/web';
import { useFloating } from 'solid-floating-ui';
import { offset, flip, shift, autoUpdate } from '@floating-ui/dom';
import clsx from 'clsx';

export interface DropdownOption {
  value: string;
  label: string;
  disabled?: boolean;
}

export interface DropdownProps {
  options: DropdownOption[];
  value?: string;
  defaultValue?: string;
  onChange?: (value: string) => void;
  placeholder?: string;
  size?: 'sm' | 'md' | 'lg';
  color?: 'success' | 'error' | 'warning' | 'info';
  accent?: string;
  disabled?: boolean;
  class?: string;
}

/**
 * Dropdown -- WAI-ARIA combobox/listbox with overlay popup via Portal + solid-floating-ui.
 *
 * The popup renders outside parent clip-path via Portal (Pitfall 4).
 * Positioned with useFloating + offset/flip/shift middleware.
 * Full keyboard navigation: ArrowDown/Up to navigate, Enter/Space to select, Escape to close.
 * Type-ahead: typing a character jumps to the first matching option.
 */
export function Dropdown(rawProps: DropdownProps) {
  const merged = mergeProps(
    { placeholder: 'Select...', size: 'md' as const },
    rawProps,
  );
  const [local] = splitProps(merged, [
    'options',
    'value',
    'defaultValue',
    'onChange',
    'placeholder',
    'size',
    'color',
    'accent',
    'disabled',
    'class',
  ]);

  const uid = createUniqueId();
  const listboxId = `hh-dropdown-listbox-${uid}`;
  const optionIdPrefix = `hh-dropdown-option-${uid}`;

  // State
  const [open, setOpen] = createSignal(false);
  const [activeIndex, setActiveIndex] = createSignal(-1);
  const [internalValue, setInternalValue] = createSignal(local.defaultValue ?? '');

  // Element refs
  let triggerRef: HTMLButtonElement | undefined;
  const [reference, setReference] = createSignal<HTMLButtonElement | undefined>();
  const [floating, setFloating] = createSignal<HTMLDivElement | undefined>();

  // Floating UI positioning
  const position = useFloating(reference, floating, {
    placement: 'bottom-start',
    middleware: [offset(4), flip(), shift({ padding: 8 })],
    whileElementsMounted: autoUpdate,
  });

  // Resolve value: controlled vs uncontrolled
  const currentValue = () => {
    if (local.value !== undefined) return local.value;
    return internalValue();
  };

  // Get selected option label
  const selectedLabel = () => {
    const val = currentValue();
    if (!val) return '';
    const opt = local.options.find((o) => o.value === val);
    return opt?.label ?? '';
  };

  // Get non-disabled option indices
  const enabledIndices = () =>
    local.options
      .map((opt, i) => ({ opt, i }))
      .filter(({ opt }) => !opt.disabled)
      .map(({ i }) => i);

  // Find next/previous enabled index
  const findNextEnabled = (from: number, direction: 1 | -1): number => {
    const enabled = enabledIndices();
    if (enabled.length === 0) return -1;

    if (from < 0) {
      return direction === 1 ? enabled[0] : enabled[enabled.length - 1];
    }

    const currentPos = enabled.indexOf(from);
    if (currentPos === -1) {
      // Current index is disabled; find nearest in direction
      if (direction === 1) {
        return enabled.find((i) => i > from) ?? enabled[0];
      } else {
        return [...enabled].reverse().find((i) => i < from) ?? enabled[enabled.length - 1];
      }
    }

    const nextPos = currentPos + direction;
    if (nextPos < 0) return enabled[enabled.length - 1];
    if (nextPos >= enabled.length) return enabled[0];
    return enabled[nextPos];
  };

  // Select a value
  const selectValue = (value: string) => {
    if (local.value === undefined) {
      setInternalValue(value);
    }
    local.onChange?.(value);
    closeDropdown();
  };

  // Open/close
  const openDropdown = () => {
    if (local.disabled) return;
    setOpen(true);
    // Set active index to currently selected option, or first enabled
    const val = currentValue();
    const selectedIdx = val ? local.options.findIndex((o) => o.value === val) : -1;
    if (selectedIdx >= 0 && !local.options[selectedIdx].disabled) {
      setActiveIndex(selectedIdx);
    } else {
      const first = enabledIndices()[0];
      setActiveIndex(first ?? -1);
    }
  };

  const closeDropdown = () => {
    setOpen(false);
    setActiveIndex(-1);
    triggerRef?.focus();
  };

  const toggleDropdown = () => {
    if (open()) {
      closeDropdown();
    } else {
      openDropdown();
    }
  };

  // Click outside handler
  createEffect(() => {
    if (!open()) return;

    const handleMouseDown = (e: MouseEvent) => {
      const target = e.target as Node;
      const floatingEl = floating();
      if (
        triggerRef &&
        !triggerRef.contains(target) &&
        (!floatingEl || !floatingEl.contains(target))
      ) {
        closeDropdown();
      }
    };

    document.addEventListener('mousedown', handleMouseDown);
    onCleanup(() => document.removeEventListener('mousedown', handleMouseDown));
  });

  // Keyboard handler on trigger
  const handleTriggerKeyDown = (e: KeyboardEvent) => {
    if (local.disabled) return;

    switch (e.key) {
      case 'Enter':
      case ' ':
        e.preventDefault();
        if (open() && activeIndex() >= 0) {
          const opt = local.options[activeIndex()];
          if (opt && !opt.disabled) selectValue(opt.value);
        } else {
          toggleDropdown();
        }
        break;
      case 'ArrowDown':
        e.preventDefault();
        if (!open()) {
          openDropdown();
        } else {
          setActiveIndex(findNextEnabled(activeIndex(), 1));
        }
        break;
      case 'ArrowUp':
        e.preventDefault();
        if (!open()) {
          openDropdown();
        } else {
          setActiveIndex(findNextEnabled(activeIndex(), -1));
        }
        break;
      case 'Home':
        if (open()) {
          e.preventDefault();
          const first = enabledIndices()[0];
          if (first !== undefined) setActiveIndex(first);
        }
        break;
      case 'End':
        if (open()) {
          e.preventDefault();
          const enabled = enabledIndices();
          const last = enabled[enabled.length - 1];
          if (last !== undefined) setActiveIndex(last);
        }
        break;
      case 'Escape':
        if (open()) {
          e.preventDefault();
          closeDropdown();
        }
        break;
      default:
        // Type-ahead: jump to first option starting with typed character
        if (open() && e.key.length === 1 && !e.ctrlKey && !e.metaKey) {
          const char = e.key.toLowerCase();
          const idx = local.options.findIndex(
            (opt) => !opt.disabled && opt.label.toLowerCase().startsWith(char),
          );
          if (idx >= 0) setActiveIndex(idx);
        }
    }
  };

  // Handle option click
  const handleOptionClick = (opt: DropdownOption, e: MouseEvent) => {
    e.preventDefault();
    e.stopPropagation();
    if (opt.disabled) return;
    selectValue(opt.value);
  };

  // Accent style
  const accentStyle = (): JSX.CSSProperties | undefined => {
    if (local.accent) {
      return { '--hh-comp-accent': local.accent } as JSX.CSSProperties;
    }
    return undefined;
  };

  // Trigger classes
  const triggerClasses = () =>
    clsx(
      'hh-dropdown-trigger',
      'hh-btn',
      'hh-hover-gradient',
      'hh-focus-pulse',
      'hh-active-flood',
      'hh-disabled',
      `hh-size-${local.size}`,
      local.color && `hh-color-${local.color}`,
      open() && 'hh-dropdown-trigger--open',
      local.class,
    );

  return (
    <div class="hh-dropdown" style={accentStyle()}>
      <button
        ref={(el) => {
          triggerRef = el;
          setReference(el);
        }}
        type="button"
        role="combobox"
        aria-expanded={open()}
        aria-haspopup="listbox"
        aria-controls={listboxId}
        aria-activedescendant={
          open() && activeIndex() >= 0 ? `${optionIdPrefix}-${activeIndex()}` : undefined
        }
        disabled={local.disabled}
        class={triggerClasses()}
        onClick={toggleDropdown}
        onKeyDown={handleTriggerKeyDown}
      >
        <span class="hh-dropdown-trigger__label">
          {selectedLabel() || local.placeholder}
        </span>
        <span
          class={clsx('hh-dropdown-trigger__chevron', open() && 'hh-dropdown-trigger__chevron--open')}
          aria-hidden="true"
        >
          {'\u25B8'}
        </span>
      </button>

      <Show when={open()}>
        <Portal>
          <div
            ref={setFloating}
            id={listboxId}
            role="listbox"
            class={clsx(
              'hh-dropdown-popup',
              `hh-size-${local.size}`,
            )}
            style={{
              position: position.strategy ?? 'absolute',
              top: `${position.y ?? 0}px`,
              left: `${position.x ?? 0}px`,
              '--hh-comp-accent': local.accent || undefined,
            } as JSX.CSSProperties}
          >
            <For each={local.options}>
              {(opt, i) => (
                <div
                  id={`${optionIdPrefix}-${i()}`}
                  role="option"
                  aria-selected={opt.value === currentValue()}
                  aria-disabled={opt.disabled ? 'true' : undefined}
                  class={clsx(
                    'hh-dropdown-option',
                    opt.value === currentValue() && 'hh-dropdown-option--selected',
                    opt.disabled && 'hh-dropdown-option--disabled',
                    i() === activeIndex() && 'hh-dropdown-option--active',
                  )}
                  onClick={(e) => handleOptionClick(opt, e)}
                  onMouseEnter={() => {
                    if (!opt.disabled) setActiveIndex(i());
                  }}
                >
                  <span class="hh-dropdown-option__indicator" />
                  <span class="hh-dropdown-option__label">{opt.label}</span>
                </div>
              )}
            </For>
          </div>
        </Portal>
      </Show>
    </div>
  );
}
