<script lang="ts">
  import type {Snippet} from "svelte"
  import {onMount} from "svelte"
  import {goto} from "$app/navigation"
  import {
    ago,
    int,
    ms,
    partition,
    ifLet,
    spec,
    nthEq,
    nthNe,
    MINUTE,
    sortBy,
    remove,
    enumerate,
    formatTimestampAsDate,
  } from "@welshman/lib"
  import type {TrustedEvent, EventTemplate, EventContent} from "@welshman/util"
  import {parse, isLink} from "@welshman/content"
  import {
    makeEvent,
    tagsFromIMeta,
    getTags,
    DIRECT_MESSAGE,
    DIRECT_MESSAGE_FILE,
  } from "@welshman/util"
  import {
    pubkey,
    tagPubkey,
    sendWrapped,
    mergeThunks,
    loadMessagingRelayList,
    messagingRelayListsByPubkey,
  } from "@welshman/app"
  import Danger from "@assets/icons/danger-triangle.svg?dataurl"
  import ArrowLeft from "@assets/icons/arrow-left.svg?dataurl"
  import Icon from "@lib/components/Icon.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 Button from "@lib/components/Button.svelte"
  import ProfileName from "@app/components/ProfileName.svelte"
  import ProfileLink from "@app/components/ProfileLink.svelte"
  import ProfileCircle from "@app/components/ProfileCircle.svelte"
  import ProfileCircles from "@app/components/ProfileCircles.svelte"
  import ProfileDetail from "@app/components/ProfileDetail.svelte"
  import ChatMembers from "@app/components/ChatMembers.svelte"
  import ChatMessage from "@app/components/ChatMessage.svelte"
  import ChatCompose from "@app/components/ChatCompose.svelte"
  import ChatComposeEdit from "@app/components/ChatComposeEdit.svelte"
  import ChatComposeParent from "@app/components/ChatComposeParent.svelte"
  import ThunkToast from "@app/components/ThunkToast.svelte"
  import {userSettingsValues, deriveChat} from "@app/core/state"
  import {pushModal} from "@app/util/modal"
  import {makeDelete, prependParent} from "@app/core/commands"
  import {pushToast} from "@app/util/toast"

  type Props = {
    pubkeys: string[]
    info?: Snippet
  }

  const {pubkeys, info}: Props = $props()

  const chat = deriveChat(pubkeys)
  const others = remove($pubkey!, pubkeys)
  const missingRelayLists = $derived(others.filter(pk => !$messagingRelayListsByPubkey.has(pk)))

  const showMembers = () =>
    others.length === 1
      ? pushModal(ProfileDetail, {pubkey: others[0]})
      : pushModal(ChatMembers, {pubkeys: others})

  const back = () => goto("/chat")

  const replyTo = (event: TrustedEvent) => {
    parent = event
    compose?.focus()
  }

  const clearParent = () => {
    parent = undefined
  }

  const clearEventToEdit = () => {
    eventToEdit = undefined
  }

  const onSubmit = async (params: EventContent) => {
    try {
      const ptags = remove($pubkey!, pubkeys).map(tagPubkey)

      // Remove p tags since they result in forking the conversation
      params.tags = params.tags.filter(nthNe(0, "p"))

      // Add our reply quote to content
      params = prependParent(parent, params)

      if (eventToEdit) {
        if (eventToEdit.content === params.content) {
          return
        }

        await sendWrapped({
          event: makeDelete({event: eventToEdit, protect: false}),
          recipients: pubkeys,
          pow: 16,
        })
      }

      const [imetaTags, tags] = partition(nthEq(0, "imeta"), params.tags)
      const imetas = getTags("imeta", imetaTags).map(tagsFromIMeta)
      const templates: EventTemplate[] = []
      const buffer = []

      const addTemplate = (kind: number, content: string, tags: string[][]) => {
        content = content.trim()

        if (content) {
          templates.push(
            makeEvent(kind, {
              content,
              tags: [...tags, ...ptags],
              created_at: eventToEdit?.created_at,
            }),
          )
        }
      }

      for (const p of parse(params)) {
        const imeta = isLink(p)
          ? imetas.find(tags => tags.find(spec(["url", p.value.url.toString()])))
          : undefined

        if (isLink(p) && imeta) {
          addTemplate(DIRECT_MESSAGE, buffer.splice(0).join(""), tags)
          addTemplate(
            DIRECT_MESSAGE_FILE,
            p.value.url.toString(),
            imeta.slice(1).filter(nthNe(0, "url")),
          )
        } else {
          buffer.push(p.raw)
        }
      }

      addTemplate(DIRECT_MESSAGE, buffer.splice(0).join(""), tags)

      // Split the message into multiple pieces so that we can use kind 15 to send images per nip 17
      // Sleep 1 second between each one to make sure timestamps are distinct
      const thunks = await Promise.all(
        Array.from(enumerate(templates)).map(([i, event]) =>
          sendWrapped({
            event,
            recipients: pubkeys,
            delay: $userSettingsValues.send_delay + ms(i),
            pow: 16,
          }),
        ),
      )

      pushToast({
        timeout: 30_000,
        children: {
          component: ThunkToast,
          props: {thunk: mergeThunks(thunks)},
        },
      })
    } finally {
      clearParent()
      clearEventToEdit()
    }
  }

  const onEscape = () => {
    clearParent()
    clearEventToEdit()
  }

  const canEditEvent = (event: TrustedEvent) =>
    event.pubkey === $pubkey &&
    event.kind === DIRECT_MESSAGE &&
    event.created_at >= ago(500, MINUTE)

  const onEditEvent = (event: TrustedEvent) => {
    clearParent()
    eventToEdit = event
  }

  const onEditPrevious = () => ifLet($chat?.messages.toReversed().find(canEditEvent), onEditEvent)

  let loading = $state(true)
  let compose: ChatCompose | undefined = $state()
  let parent: TrustedEvent | undefined = $state()
  let eventToEdit: TrustedEvent | undefined = $state()

  const elements = $derived.by(() => {
    const elements = []

    let previousDate
    let previousPubkey
    let previousCreatedAt = 0

    for (const event of sortBy(e => e.created_at, $chat?.messages || [])) {
      const {id, pubkey, created_at} = event
      const date = formatTimestampAsDate(created_at)

      if (date !== previousDate) {
        elements.push({type: "date", value: date, id: date, showPubkey: false})
      }

      elements.push({
        id,
        type: "note",
        value: event,
        showPubkey: created_at - previousCreatedAt > int(2, MINUTE) || previousPubkey !== pubkey,
      })

      previousDate = date
      previousPubkey = pubkey
      previousCreatedAt = created_at
    }

    return elements.reverse()
  })

  onMount(() => {
    for (const pubkey of others) {
      loadMessagingRelayList(pubkey)
    }
  })

  setTimeout(() => {
    loading = false
  }, 5000)
</script>

<PageBar>
  <div class="flex">
    <Button onclick={back} class="place-self-start pr-3 md:hidden flex items-center">
      <Icon icon={ArrowLeft} size={7} />
    </Button>
    <div class="flex items-center justify-between gap-4">
      <div class="ellipsize flex items-center gap-4 whitespace-nowrap">
        <Button class="flex flex-col gap-1 sm:flex-row sm:gap-2" onclick={showMembers}>
          {#if others.length === 0}
            <div class="row-2">
              <ProfileCircle pubkey={$pubkey!} size={5} />
              <ProfileName pubkey={$pubkey!} />
            </div>
          {:else if others.length === 1}
            <div class="row-2">
              <ProfileCircle pubkey={others[0]} size={5} />
              <ProfileName pubkey={others[0]} />
            </div>
          {:else}
            <div class="flex items-center gap-2">
              <ProfileCircles pubkeys={others} size={5} />
              <p class="overflow-hidden text-ellipsis whitespace-nowrap">
                <ProfileName pubkey={others[0]} />
                and
                {#if others.length === 2}
                  <ProfileName pubkey={others[1]} />
                {:else}
                  {others.length - 1}
                  {others.length > 2 ? "others" : "other"}
                {/if}
              </p>
            </div>
          {/if}
        </Button>
      </div>
    </div>
  </div>
</PageBar>

<PageContent class="flex flex-col-reverse gap-2 pt-4">
  {#if missingRelayLists.length > 0}
    <div class="py-12">
      <div class="card2 col-2 m-auto max-w-md items-center text-center">
        <p class="row-2 text-lg text-error">
          <Icon icon={Danger} />
          Direct messages are not enabled
        </p>
        <p>
          Ask
          {#each missingRelayLists as pubkey (pubkey)}
            <ProfileLink {pubkey} />
          {/each}
          to enable direct messaging by opening this conversation in their app.
        </p>
      </div>
    </div>
  {/if}
  {#each elements as { type, id, value, showPubkey } (id)}
    {#if type === "date"}
      <Divider>{value}</Divider>
    {:else}
      <ChatMessage
        event={$state.snapshot(value as TrustedEvent)}
        {pubkeys}
        {showPubkey}
        {replyTo}
        canEdit={canEditEvent}
        onEdit={onEditEvent} />
    {/if}
  {/each}
  <p class="m-auto flex h-10 max-w-sm flex-col items-center justify-center gap-4 py-20 text-center">
    <Spinner {loading}>
      {#if loading}
        Looking for messages...
      {:else}
        End of message history
      {/if}
    </Spinner>
    {@render info?.()}
  </p>
  <div class="h-screen"></div>
</PageContent>

<div class="chat__compose bg-base-200">
  <div>
    {#if parent}
      <ChatComposeParent event={parent} clear={clearParent} verb="Replying to" />
    {/if}
    {#if eventToEdit}
      <ChatComposeEdit clear={clearEventToEdit} />
    {/if}
  </div>
  {#key eventToEdit}
    <ChatCompose
      bind:this={compose}
      {onSubmit}
      {onEscape}
      {onEditPrevious}
      content={eventToEdit?.content}
      disabled={Boolean(missingRelayLists.length)} />
  {/key}
</div>
