<script lang="ts">
  import {tick} from "svelte"
  import {debounce} from "throttle-debounce"
  import {request} from "@welshman/net"
  import {formatTimestampAsDate, groupBy, now, MINUTE, HOUR, DAY, WEEK} from "@welshman/lib"
  import type {TrustedEvent, Filter} from "@welshman/util"
  import {sortEventsDesc} from "@welshman/util"
  import CloseCircle from "@assets/icons/close-circle.svg?dataurl"
  import Magnifier from "@assets/icons/magnifier.svg?dataurl"
  import {fly} from "@lib/transition"
  import Button from "@lib/components/Button.svelte"
  import Icon from "@lib/components/Icon.svelte"
  import {CONTENT_KINDS} from "@app/core/state"
  import {goToEvent} from "@app/util/routes"

  type Props = {
    url: string
    h?: string
  }

  const {url, h}: Props = $props()

  let term = $state("")
  let show = $state(false)
  let results = $state<TrustedEvent[]>([])
  let loading = $state(false)
  let input: HTMLInputElement | undefined = $state()
  let controller: AbortController | undefined

  const relayStatus = $derived(
    h ? `Searching this room on relay: ${url}.` : `Searching this space on relay: ${url}.`,
  )

  const open = () => {
    show = true
    tick().then(() => input?.focus())
  }

  const close = () => {
    show = false
  }

  const clear = () => {
    term = ""
    show = false
    loading = false
    results = []
    controller?.abort()
    controller = undefined
  }

  const getRelayUrls = () => [url]

  const getFilter = (searchTerm: string): Filter =>
    h
      ? {kinds: CONTENT_KINDS, "#h": [h], search: searchTerm}
      : {kinds: CONTENT_KINDS, search: searchTerm}

  const search = debounce(300, async (searchTerm: string) => {
    controller?.abort()

    if (!searchTerm.trim()) {
      loading = false
      results = []
      return
    }

    controller = new AbortController()
    loading = true

    try {
      const events = await request({
        relays: getRelayUrls(),
        autoClose: true,
        signal: controller.signal,
        filters: [getFilter(searchTerm.trim())],
      })

      results = sortEventsDesc(events)
    } catch (error) {
      if (!(error instanceof DOMException && error.name === "AbortError")) {
        results = []
      }
    } finally {
      loading = false
    }
  })

  const onInput = () => {
    void search(term)
  }

  const eventsByAge = $derived(groupBy(e => getAgeSection(e.created_at), results))

  const getAgeSection = (createdAt: number) => {
    const age = now() - createdAt

    if (age <= DAY) {
      return "day"
    }

    if (age <= WEEK) {
      return "week"
    }

    return "older"
  }

  const getAgeLabel = (createdAt: number) => {
    const age = now() - createdAt

    if (age < MINUTE) {
      return "Just now"
    }

    if (age < HOUR) {
      return `${Math.floor(age / MINUTE)}m ago`
    }

    if (age < DAY) {
      return `${Math.floor(age / HOUR)}h ago`
    }

    return `${Math.floor(age / DAY)}d ago`
  }

  const onRoomSearchResultClick = (event: TrustedEvent) => {
    close()
    goToEvent(event, {keepFocus: true})
  }
</script>

<button class="btn btn-neutral btn-sm btn-square" aria-label="Search" onclick={open}>
  <Icon size={4} icon={Magnifier} />
</button>
{#if show}
  <button class="fixed inset-0 z-feature" aria-label="Close search" onclick={close}></button>
  <div class="fixed top-sai right-sai left-content z-feature p-2">
    <div
      class="card2 card2-sm bg-alt flex flex-col gap-2 shadow-md"
      transition:fly={{y: -40, duration: 150}}>
      <div class="flex justify-between">
        <strong>Search</strong>
        <Button onclick={clear}>
          <Icon icon={CloseCircle} />
        </Button>
      </div>
      <label class="input input-sm input-bordered flex w-full items-center gap-2">
        <Icon size={4} icon={Magnifier} />
        <input
          bind:this={input}
          bind:value={term}
          class="min-w-0 grow"
          type="text"
          placeholder={h ? "Search this room..." : "Search this space..."}
          oninput={onInput} />
      </label>
      <div class="max-h-[65vh] overflow-y-auto">
        <p class="mb-2 text-xs opacity-70">{relayStatus}</p>
        {#if !term}
          <p class="text-sm opacity-70">
            {h ? "Search for content in this room." : "Search for content in this space."}
          </p>
        {:else if loading}
          <p class="text-sm opacity-70">Searching...</p>
        {:else if eventsByAge.size === 0}
          <p class="text-sm opacity-70">No results found.</p>
        {:else}
          <div class="col-2">
            {#each eventsByAge as [key, events] (key)}
              <div class="col-2">
                <p class="text-xs uppercase tracking-wide opacity-60">
                  {#if key === "day"}
                    Last 24 Hours
                  {:else if key === "week"}
                    Last 7 Days
                  {:else}
                    Older
                  {/if}
                </p>
                <div class="col-2">
                  {#each events as event (event.id)}
                    <button
                      class="card2 bg-alt card2-sm col-2 transition-colors hover:bg-base-200 text-left"
                      onclick={() => onRoomSearchResultClick(event)}>
                      <p class="line-clamp-2 text-sm">
                        {event.content.trim() || "(No text content)"}
                      </p>
                      <div class="row-2 text-xs opacity-70">
                        <span>{getAgeLabel(event.created_at)}</span>
                        <span>{formatTimestampAsDate(event.created_at)}</span>
                      </div>
                    </button>
                  {/each}
                </div>
              </div>
            {/each}
          </div>
        {/if}
      </div>
    </div>
  </div>
{/if}
