<script lang="ts">
  import {onMount} from "svelte"
  import type {Readable} from "svelte/store"
  import {readable} from "svelte/store"
  import {page} from "$app/stores"
  import {now, last, formatTimestampAsDate} from "@welshman/lib"
  import type {TrustedEvent} from "@welshman/util"
  import {DELETE, EVENT_TIME, getTagValue} from "@welshman/util"
  import {fly} from "@lib/transition"
  import CalendarMinimalistic from "@assets/icons/calendar-minimalistic.svg?dataurl"
  import CalendarAdd from "@assets/icons/calendar-add.svg?dataurl"
  import Icon from "@lib/components/Icon.svelte"
  import Button from "@lib/components/Button.svelte"
  import Spinner from "@lib/components/Spinner.svelte"
  import PageBar from "@lib/components/PageBar.svelte"
  import PageContent from "@lib/components/PageContent.svelte"
  import Divider from "@lib/components/Divider.svelte"
  import MenuSpaceButton from "@app/components/MenuSpaceButton.svelte"
  import CalendarEventItem from "@app/components/CalendarEventItem.svelte"
  import CalendarEventCreate from "@app/components/CalendarEventCreate.svelte"
  import {pushModal} from "@app/util/modal"
  import {getEventsForUrl, decodeRelay, REACTION_KINDS} from "@app/core/state"
  import {makeCalendarFeed} from "@app/core/requests"
  import {setChecked} from "@app/util/notifications"

  const url = decodeRelay($page.params.relay)

  const makeEvent = () => pushModal(CalendarEventCreate, {url})

  const getStart = (event: TrustedEvent) => parseInt(getTagValue("start", event.tags) || "")

  let element: HTMLElement | undefined = $state()
  let loading = $state(true)
  let cleanup: () => void
  let events: Readable<TrustedEvent[]> = $state(readable([]))

  type Item = {
    event: TrustedEvent
    dateDisplay?: string
    isFirstFutureEvent?: boolean
  }

  const items = $derived.by(() => {
    const todayDateDisplay = formatTimestampAsDate(now())

    let haveISeenTheFuture = false
    let prevDateDisplay: string

    return $events.map<Item>(event => {
      const newDateDisplay = formatTimestampAsDate(getStart(event))
      const dateDisplay = prevDateDisplay === newDateDisplay ? undefined : newDateDisplay
      const isFuture = todayDateDisplay === newDateDisplay || event.created_at > now()
      const isFirstFutureEvent = !haveISeenTheFuture && isFuture

      prevDateDisplay = newDateDisplay
      haveISeenTheFuture = isFuture

      return {event, dateDisplay, isFirstFutureEvent}
    })
  })

  let previousScrollHeight = 0
  let prevFirstEventId = ""
  let initialScrollDone = false

  $effect(() => {
    if (items.length === 0) {
      return
    }

    if (initialScrollDone) {
      // If new events are prepended, adjust the scroll position so that the viewport content remains anchored
      if (prevFirstEventId && items[0].event.id !== prevFirstEventId) {
        const newScrollHeight = element!.scrollHeight
        const delta = newScrollHeight - previousScrollHeight

        if (delta > 0) {
          element!.scrollTop += delta
        }
      }
    } else {
      const {event} = items.find(({event}) => getStart(event) >= now()) || last(items)
      const {offsetTop, clientHeight} = document.querySelector(
        ".calendar-event-" + event.id,
      ) as HTMLElement

      // On initial load, center the scroll container on today's date (or the next available event)
      element!.scrollTop = offsetTop - element!.clientHeight / 2 + clientHeight / 2
      initialScrollDone = true
    }

    previousScrollHeight = element!.scrollHeight
    prevFirstEventId = items[0].event.id
  })

  onMount(() => {
    const feedFilters = [{kinds: [EVENT_TIME]}]
    const subscriptionFilters = [{kinds: [DELETE, EVENT_TIME, ...REACTION_KINDS], since: now()}]

    ;({events, cleanup} = makeCalendarFeed({
      element: element!,
      relays: [url],
      feedFilters,
      subscriptionFilters,
      initialEvents: getEventsForUrl(url, feedFilters),
      onExhausted: () => {
        loading = false
      },
    }))

    return () => {
      setChecked($page.url.pathname)
      cleanup?.()
    }
  })
</script>

<PageBar>
  {#snippet icon()}
    <div class="center">
      <Icon icon={CalendarMinimalistic} />
    </div>
  {/snippet}
  {#snippet title()}
    <strong>Calendar</strong>
  {/snippet}
  {#snippet action()}
    <div class="row-2">
      <Button class="btn btn-primary btn-sm" onclick={makeEvent}>
        <Icon icon={CalendarAdd} />
        Create an Event
      </Button>
      <MenuSpaceButton {url} />
    </div>
  {/snippet}
</PageBar>

<PageContent bind:element class="flex flex-col gap-2 p-2 pt-4">
  {#each items as { event, dateDisplay, isFirstFutureEvent }, i (event.id)}
    <div class={"calendar-event-" + event.id}>
      {#if isFirstFutureEvent}
        <div class="flex items-center gap-2 p-2">
          <div class="h-px flex-grow bg-primary"></div>
          <p class="text-xs uppercase text-primary">Today</p>
          <div class="h-px flex-grow bg-primary"></div>
        </div>
      {/if}
      {#if dateDisplay}
        <Divider>{dateDisplay}</Divider>
      {/if}
      <CalendarEventItem {url} {event} />
    </div>
  {/each}
  {#if loading}
    <p class="flex h-10 items-center justify-center py-20" transition:fly>
      <Spinner {loading}>Looking for events...</Spinner>
    </p>
  {:else if items.length === 0}
    <p class="flex h-10 items-center justify-center py-20" transition:fly>No events found.</p>
  {:else}
    <p class="flex h-10 items-center justify-center py-20" transition:fly>That's all!</p>
  {/if}
</PageContent>
