<script lang="ts">
  import { debounce } from "lodash";

  import type { IValue } from "upend/types";
  import { baseSearch, baseSearchOnce, getObjects } from "../../util/search";
  import UpObject from "../display/UpObject.svelte";
  import IconButton from "./IconButton.svelte";
  import Input from "./Input.svelte";

  export let attribute: string | undefined = undefined;
  export let value: IValue | undefined = undefined;
  export let type: "attribute" | "value";

  interface SelectorOption {
    attribute?: string;
    value?: IValue;
  }

  let options: SelectorOption[] = [];
  const updateOptions = debounce(async (query: string) => {
    if (query.length == 0) {
      options = [];
      return;
    }

    switch (type) {
      case "attribute":
        const req = await fetch("/api/all/attributes");
        const allAttributes: string[] = await req.json();
        options = allAttributes
          .filter((attr) => attr.toLowerCase().includes(query.toLowerCase()))
          .map((attribute) => {
            return {
              attribute,
            };
          });
        break;
      case "value":
        const result = await baseSearchOnce(query);
        const objects = await getObjects(result.entries);

        options = [
          {
            value: {
              t: "Value",
              c: query,
            },
          },
          ...objects.slice(0, 25).map(([address, label]) => {
            return {
              value: {
                t: "Address",
                c: address,
              },
            } as SelectorOption;
          }),
        ];
        break;
    }
  }, 200);
  $: updateOptions(inputValue);

  function set(option: SelectorOption) {
    switch (type) {
      case "attribute":
        attribute = option.attribute;
        inputValue = option.attribute;
        break;
      case "value":
        value = option.value;
        inputValue = String(option.value.c);
        break;
    }
    visible = false;
  }

  let inputValue = "";
  if (type == "attribute") {
    inputValue = attribute || "";
  } else {
    inputValue = String(value?.c || "");
  }

  function onInput(ev: CustomEvent<string>) {
    if (type == "attribute") {
      attribute = ev.detail;
    } else {
      value = {
        t: "Value",
        c: ev.detail,
      };
    }
  }

  let inputFocused = false;
  let hover = false;
  $: visible = (inputFocused || hover) && Boolean(options.length);
</script>

<div class="selector">
  {#if value?.t == "Address" && inputValue}
    <div class="input">
      <div class="label">
        <UpObject address={String(value.c)} />
      </div>
      <IconButton name="trash" on:click={() => (inputValue = "")} />
    </div>
  {:else}
    <Input
      bind:value={inputValue}
      on:input={onInput}
      on:focusChange={(ev) => (inputFocused = ev.detail)}
    />
  {/if}
  <ul
    class="options"
    class:visible
    on:mouseenter={() => (hover = true)}
    on:mouseleave={() => (hover = false)}
  >
    {#each options as option}
      <li on:click={() => set(option)}>
        {#if option.attribute}
          {option.attribute}
        {:else if option.value}
          {#if option.value.t == "Address"}
            <UpObject address={String(option.value.c)} />
          {:else}
            {option.value.c}
          {/if}
        {/if}
      </li>
    {/each}
  </ul>
</div>

<style lang="scss">
  .selector {
    position: relative;
  }

  .input {
    display: flex;
    min-width: 0;
    .label {
      flex: 1;
      min-width: 0;
    }
  }

  .options {
    position: absolute;
    list-style: none;
    margin: 0;
    padding: 0;
    border: 1px solid var(--foreground-lighter);
    width: 100%;
    border-radius: 4px;
    margin-top: 2px;
    background: var(--background);
    font-size: smaller;

    visibility: hidden;
    opacity: 0;

    transition: opacity 0.2s;

    z-index: 99;

    &.visible {
      visibility: visible;
      opacity: 1;
    }

    li {
      cursor: pointer;
      padding: 0.5em;

      transition: background-color 0.2s;
      &:hover {
        background-color: var(--background-lighter);
      }
    }
  }
</style>
