<script lang="ts">
  import { onDestroy, onMount, tick } from 'svelte';
  import { browser } from '$app/environment';

  type OptionRecord = Record<string, unknown>;

  export let options: OptionRecord[] = [];
  export let selected: string[] = [];
  export let labelKey: keyof OptionRecord = 'name';
  export let valueKey: keyof OptionRecord = 'id';
  export let placeholder = 'Select...';
  export let disabled = false;
  export let maxDisplay = 3;
  /** When true, trigger shows a count summary instead of inline badges */
  export let compactTrigger = false;

  let open = false;
  let searchTerm = '';
  let highlightedIndex = 0;
  let container: HTMLDivElement | null = null;
  let searchInput: HTMLInputElement | null = null;
  let triggerButton: HTMLButtonElement | null = null;
  let portalRoot: HTMLDivElement | null = null;
  let dropdownTop = 0;
  let dropdownLeft = 0;
  let dropdownWidth = 0;
  let clickHandler: ((event: MouseEvent) => void) | null = null;

  const getValue = (option: OptionRecord) => {
    const candidate = option[valueKey];
    if (candidate === undefined || candidate === null) {
      return '';
    }
    return String(candidate);
  };

  const getLabel = (option: OptionRecord) => {
    const candidate = option[labelKey];
    if (candidate === undefined || candidate === null) {
      return getValue(option);
    }
    return String(candidate);
  };

  $: filteredOptions = searchTerm
    ? options.filter(option => getLabel(option).toLowerCase().includes(searchTerm.toLowerCase()))
    : options;

  $: if (highlightedIndex >= filteredOptions.length) {
    highlightedIndex = Math.max(filteredOptions.length - 1, 0);
  }

  $: visibleBadges = selected.slice(0, maxDisplay);

  function updateSelection(values: string[]) {
    selected = values;
  }

  function toggleValue(value: string) {
    const next = selected.includes(value) ? selected.filter(item => item !== value) : [...selected, value];
    updateSelection(next);
  }

  function removeValue(value: string) {
    updateSelection(selected.filter(item => item !== value));
  }

  function selectAllVisible() {
    const values = filteredOptions.map(option => getValue(option)).filter(Boolean);
    if (!values.length) return;
    const union = Array.from(new Set([...selected, ...values]));
    updateSelection(union);
  }

  function clearAll() {
    updateSelection([]);
  }

  function handleOutsideClick(event: MouseEvent) {
    const target = event.target as Node;
    if (container?.contains(target) || portalRoot?.contains(target)) return;
    open = false;
  }

  function handleKeyDown(event: KeyboardEvent) {
    if (disabled) return;

    if (!open) {
      if (event.key === 'ArrowDown' || event.key === 'Enter') {
        open = true;
        event.preventDefault();
      }
      return;
    }

    if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
      const delta = event.key === 'ArrowDown' ? 1 : -1;
      const total = filteredOptions.length; 
      if (total === 0) return;
      highlightedIndex = (highlightedIndex + delta + total) % total;
      event.preventDefault();
      return;
    }

    if (event.key === 'Escape') {
      open = false;
      event.preventDefault();
      return;
    }

    if (event.key === 'Enter' && filteredOptions[highlightedIndex]) {
      toggleValue(getValue(filteredOptions[highlightedIndex]));
      event.preventDefault();
    }
  }

  function toggleOpen() {
    if (disabled) return;
    open = !open;
  }

  function updateDropdownPosition() {
    if (!triggerButton || typeof window === 'undefined') return;
    const rect = triggerButton.getBoundingClientRect();
    dropdownTop = rect.bottom + window.scrollY;
    dropdownLeft = rect.left + window.scrollX;
    dropdownWidth = rect.width;
  }

  $: if (open) {
    tick().then(() => {
      updateDropdownPosition();
      searchInput?.focus();
    });
  }

  function handleViewportChange() {
    if (open) updateDropdownPosition();
  }

  function portal(node: HTMLElement) {
    if (typeof document === 'undefined') return;
    const host = document.createElement('div');
    document.body.appendChild(host);
    host.appendChild(node);
    return {
      destroy() {
        if (host.parentNode) host.parentNode.removeChild(host);
      }
    };
  }

  onMount(() => {
    if (typeof window === 'undefined') return;
    clickHandler = handleOutsideClick;
    document.addEventListener('click', clickHandler);
    window.addEventListener('resize', handleViewportChange);
    window.addEventListener('scroll', handleViewportChange, true);
  });

  onDestroy(() => {
    if (typeof window === 'undefined') return;
    if (clickHandler) {
      document.removeEventListener('click', clickHandler);
    }
    window.removeEventListener('resize', handleViewportChange);
    window.removeEventListener('scroll', handleViewportChange, true);
  });

  const getBadgeLabel = (value: string) => {
    const matched = options.find(option => getValue(option) === value);
    if (!matched) return value;
    return getLabel(matched);
  };
</script>

<div class="dropdown dropdown-end" class:dropdown-open={open} bind:this={container} onkeydown={handleKeyDown}>
  <button
    class="btn btn-outline btn-sm w-full justify-between"
    bind:this={triggerButton}
    onclick={toggleOpen}
    type="button"
    {disabled}
  >
    <span class="truncate">
      {#if selected.length === 0}
        {placeholder}
      {:else if compactTrigger}
        <span class="text-sm">{placeholder}</span>
        <span class="badge badge-sm badge-primary ml-1">{selected.length}</span>
      {:else}
        {#each visibleBadges as value (value)}
          {@const option = options.find(o => getValue(o) === value)}
          {#if option}
            <span class="badge badge-sm badge-primary mr-1">
              {getLabel(option)}
              <button
                class="ml-1 hover:text-error"
                onclick={(e) => { e.stopPropagation(); removeValue(value); }}
                type="button"
              >
                ×
              </button>
            </span>
          {/if}
        {/each}
        {#if selected.length > maxDisplay}
          <span class="badge badge-sm badge-ghost">+{selected.length - maxDisplay} more</span>
        {/if}
      {/if}
    </span>
    <span class="text-xs ml-1">▼</span>
  </button>

  {#if open && browser}
    <div
      use:portal
      class="dropdown-content mt-2 rounded-xl border border-base-200 bg-base-100 p-3 shadow-lg"
      bind:this={portalRoot}
      style={`z-index: 9999; position: absolute; top: ${dropdownTop}px; left: ${dropdownLeft}px; width: ${Math.max(dropdownWidth, 256)}px;`}
      onclick={(e) => e.stopPropagation()}
    >
        <input
          class="input input-sm input-bordered w-full"
          type="search"
          placeholder="Search..."
          bind:value={searchTerm}
          bind:this={searchInput}
          oninput={() => (highlightedIndex = 0)}
        />

      <div class="mt-2 flex items-center justify-between text-xs">
        <button type="button" class="btn btn-ghost btn-xs" onclick={selectAllVisible}>
          ☑ Select All
        </button>
        <button type="button" class="btn btn-ghost btn-xs" onclick={clearAll}>
          ☐ Clear All
        </button>
      </div>

      <div class="divider my-2"></div>

      <div class="max-h-64 overflow-auto space-y-1">
        {#if filteredOptions.length === 0}
          <p class="text-sm text-base-content/60">No results</p>
        {:else}
          {#each filteredOptions as option, index (getValue(option))}
            <label
              class={`flex items-center gap-3 rounded-md px-2 py-1 text-sm transition ${highlightedIndex === index ? 'bg-base-200' : 'hover:bg-base-200/60'}`}
              onmouseenter={() => (highlightedIndex = index)}
            >
              <input
                type="checkbox"
                class="checkbox checkbox-sm"
                checked={selected.includes(getValue(option))}
                onchange={() => toggleValue(getValue(option))}
              />
              <span class="truncate">{getLabel(option)}</span>
            </label>
          {/each}
        {/if}
      </div>
    </div>
  {/if}
</div>
