<script lang="ts">
  // types
  interface Props {
    id?: string;
    value?: string | null;
    label?: string;
    readonly?: boolean;
    required?: boolean;
    mustMatch?: boolean;

    requestSuggestions: (query: string, limit: number) => Promise<string[]>;

    onSubmit?: (value: string | null) => void;
  }

  // html bindings
  let container: HTMLDivElement;

  // inputs
  let { id, value = $bindable(), label, readonly, required, mustMatch, requestSuggestions, onSubmit }: Props = $props();

  // states
  let inputValue = $derived(value);
  let suggestions = $state<string[]>([]);
  let matched = $state(false);

  // callbacks
  async function onBodyMouseDown(e: MouseEvent) {
    if (!container.contains(e.target as Node)) suggestions = [];
  }

  async function onSearchInput() {
    if (readonly) return;

    suggestions = await requestSuggestions(inputValue ?? '', 5);

    let suggestion = suggestions.find((s) => s === inputValue);
    if (suggestion != null) {
      inputValue = value = suggestion;
      matched = true;
      onSubmit?.(suggestion);
    } else if (!mustMatch) {
      value = inputValue;
      matched = false;
    } else {
      value = null;
      matched = false;
      onSubmit?.(null);
    }
  }

  function onSuggestionClick(suggestion: string) {
    inputValue = value = suggestion;
    suggestions = [];
    onSubmit?.(suggestion);
  }
</script>

<svelte:body onmousedown={onBodyMouseDown} />

<fieldset class="fieldset">
  <legend class="fieldset-legend">
    <span>
      {label}
      {#if required}
        <span class="text-red-700">*</span>
      {/if}
    </span>
  </legend>
  <div class="relative" bind:this={container}>
    <input
      {id}
      {readonly}
      type="search"
      autocomplete="off"
      class="input"
      bind:value={inputValue}
      oninput={() => onSearchInput()}
      onfocusin={() => onSearchInput()}
      pattern={mustMatch && matched ? `^(${suggestions.join('|')})$` : undefined}
    />
    {#if suggestions.length > 0}
      <ul class="absolute bg-base-200 w-full z-20 menu menu-sm rounded-box">
        {#each suggestions as suggestion (suggestion)}
          <li class="w-full text-left">
            <button
              class="block w-full overflow-hidden text-ellipsis whitespace-nowrap"
              title={suggestion}
              onclick={() => onSuggestionClick(suggestion)}>{suggestion}</button
            >
          </li>
        {/each}
      </ul>
    {/if}
  </div>
  <p class="fieldset-label"></p>
</fieldset>
