<script lang="ts">
  import type { EmojiReaction, Status } from '$lib/api/types'
  import { useAppServices } from '$lib/app-services'
  import { safeCustomEmojiUrl } from '$lib/util/html'

  interface Props {
    status: Status
    onupdate?: (status: Status) => void
  }

  let { status, onupdate }: Props = $props()

  const { endpoints, session } = useAppServices()
  const reactions = $derived(
    (status.pleroma?.emoji_reactions ?? []).filter(
      (reaction) => reaction.name.trim().length > 0 && reaction.count > 0,
    ),
  )

  let busyEmoji = $state<string | null>(null)
  let error = $state<string | null>(null)

  function customLabel(reaction: EmojiReaction): string {
    return `:${reaction.name}:`
  }

  function accessibleLabel(reaction: EmojiReaction): string {
    const action = reaction.me ? 'Remove' : 'Add'
    const emoji = reaction.url ? customLabel(reaction) : reaction.name
    const countLabel = reaction.count === 1 ? '1 reaction' : `${reaction.count} reactions`
    return `${action} ${emoji} reaction, ${countLabel}`
  }

  function withReactions(base: Status, next: EmojiReaction[]): Status {
    return {
      ...base,
      pleroma: {
        ...(base.pleroma ?? {}),
        emoji_reactions: next,
      },
    }
  }

  function optimisticReactions(
    source: EmojiReaction[],
    selected: EmojiReaction,
    on: boolean,
  ): EmojiReaction[] {
    return source
      .map((reaction) => {
        if (reaction.name !== selected.name) return reaction
        return {
          ...reaction,
          count: Math.max(0, reaction.count + (on ? 1 : -1)),
          me: on,
        }
      })
      .filter((reaction) => reaction.count > 0)
  }

  async function toggle(reaction: EmojiReaction): Promise<void> {
    if (!session.signedIn || busyEmoji) return

    const before = status
    const on = !reaction.me
    busyEmoji = reaction.name
    error = null
    onupdate?.(
      withReactions(
        before,
        optimisticReactions(before.pleroma?.emoji_reactions ?? [], reaction, on),
      ),
    )

    try {
      const updated = await endpoints.setEmojiReaction(
        session.api,
        status.id,
        reaction.name,
        on,
      )
      onupdate?.(updated)
    } catch (cause) {
      onupdate?.(before)
      error =
        cause instanceof Error
          ? cause.message
          : 'Could not save that emoji reaction.'
    } finally {
      busyEmoji = null
    }
  }
</script>

{#if reactions.length > 0}
  <div class="emoji-reaction-bar" aria-label="Emoji reactions">
    {#each reactions as reaction (reaction.url ?? reaction.name)}
      {@const imageUrl = reaction.url ? safeCustomEmojiUrl(reaction.url) : null}
      <button
        type="button"
        class="emoji-reaction"
        class:emoji-reaction--mine={reaction.me}
        data-custom={imageUrl ? 'true' : 'false'}
        aria-label={accessibleLabel(reaction)}
        aria-pressed={reaction.me ? 'true' : 'false'}
        title={session.signedIn
          ? accessibleLabel(reaction)
          : 'Sign in to react with this emoji'}
        disabled={!session.signedIn || busyEmoji !== null}
        onclick={() => void toggle(reaction)}
      >
        {#if imageUrl}
          <img
            class="emoji-reaction-image"
            src={imageUrl}
            alt={customLabel(reaction)}
            loading="lazy"
            decoding="async"
          />
        {:else}
          <span class="emoji-reaction-glyph" aria-hidden="true">{reaction.name}</span>
        {/if}
        <span class="emoji-reaction-count">{reaction.count}</span>
      </button>
    {/each}
  </div>
{/if}

{#if error}
  <p class="error-note emoji-reaction-error" role="alert">{error}</p>
{/if}
