<script lang="ts">
  import { onMount } from 'svelte';

  interface CommandPaletteItem {
    name: string;
    url: string;
    onClick?: () => void;
    preventDefault?: boolean;
  }

  export let items: CommandPaletteItem[] = [];
  export let open = false;

  let search = '';
  let filtered: CommandPaletteItem[] = [];
  let selectedIndex = -1;
  let inputRef: HTMLInputElement;

  $: filtered = items
    .filter((item) => item.name.toLowerCase().includes(search.toLowerCase()))
    .slice(0, 10);

  $: if (selectedIndex >= filtered.length) selectedIndex = filtered.length - 1;
  $: if (selectedIndex < 0 && filtered.length > 0) selectedIndex = 0;

  const executeItem = (item: CommandPaletteItem) => {
    if (item.onClick) item.onClick();
    if (!item.preventDefault) window.location.href = item.url;

    open = false;
  };

  const handleKey = (e: KeyboardEvent) => {
    if (e.key === 'ArrowDown') {
      e.preventDefault();

      selectedIndex = (selectedIndex + 1) % filtered.length;
    } else if (e.key === 'ArrowUp') {
      e.preventDefault();

      selectedIndex = (selectedIndex - 1 + filtered.length) % filtered.length;
    } else if (e.key === 'Enter') {
      if (filtered.length === 1) {
        executeItem(filtered[0]);
      } else if (filtered.length > 1 && selectedIndex >= 0) {
        executeItem(filtered[selectedIndex]);
      }
    } else if (e.key === 'Escape') {
      open = false;
    }
  };

  onMount(() => {
    window.addEventListener('keydown', handleGlobalKey);

    return () => window.removeEventListener('keydown', handleGlobalKey);
  });

  const handleClickOutside = (event: MouseEvent) => {
    const target = event.target as HTMLElement;

    if (target.classList.contains('command-palette-overlay')) {
      open = false;
    } else if (!target.closest('.dropdown')) {
      open = false;
    }
  };

  const handleGlobalKey = (e: KeyboardEvent) => {
    if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
      e.preventDefault();

      open = !open;

      if (open) requestAnimationFrame(() => inputRef?.focus());
    }
  };
</script>

<svelte:window on:click={handleClickOutside} />

{#if open}
  <div
    class="command-palette-overlay"
    on:click={() => (open = false)}
    on:keydown={(e) => e.key === 'Escape' && (open = false)}
  />

  <div class="dropdown">
    <div class="dropdown-content card card-small">
      <input
        bind:this={inputRef}
        bind:value={search}
        class="command-input"
        placeholder="Search"
        on:keydown={handleKey}
      />

      {#each filtered as item, i}
        <a
          href={item.url}
          class="header-item {selectedIndex === i ? 'selected' : ''}"
          on:click={(e) => {
            if (item.preventDefault) e.preventDefault();
            if (item.onClick) item.onClick();

            open = false;
          }}
        >
          {item.name}
        </a>
      {/each}
    </div>
  </div>
{/if}

<style lang="scss">
  a {
    color: var(--base06);
  }

  .header-item {
    margin: 0 0.5rem;
  }

  .header-item:hover {
    text-decoration: none;
  }

  .header-item:active {
    outline: none;
  }

  .command-palette-overlay {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-color: rgba(0, 0, 0, 0.6);
    z-index: 100;
    backdrop-filter: blur(3px);
    animation: fadeIn 0.15s ease-in-out;
    cursor: pointer;
  }

  @keyframes fadeIn {
    from {
      opacity: 0;
      backdrop-filter: blur(0px);
    }

    to {
      opacity: 1;
      backdrop-filter: blur(3px);
    }
  }

  .dropdown {
    position: fixed;
    top: 15%;
    left: 50%;
    transform: translateX(-50%);
    z-index: 101;
    width: min(600px, 90%);
    font-size: 1.05em;
  }

  .command-input {
    width: calc(100% - 2rem);
    padding: 0.5em 0.75em;
    margin: 0.5rem 1rem;
    margin-bottom: 0.75rem;
    outline: none;
  }

  .dropdown-content {
    display: block;
    position: relative;
    min-width: max-content;
    padding: 0.5em 0;
  }

  .dropdown-content a {
    padding: 0.5em 0.75em;
    text-decoration: none;
    display: block;
    border-radius: 8px;
    margin: 0.5em 0.75em;
    backdrop-filter: blur(0px);
    font-weight: 450;
  }

  .dropdown-content a:hover,
  .dropdown-content a.selected {
    background-color: var(--base01);
  }
</style>
