<script lang="ts">
  import { visible } from "$lib/actions/visible";
  import { downloadMedia } from "$lib/api/download";
  import { fetchMedia } from "$lib/api/endpoints";
  import {
    type InlineMedia,
    isPreviewable,
    loadInlineMedia,
    visualKind,
  } from "$lib/api/media";
  import type { MessageView } from "$lib/api/types";
  import AudioFile from "$lib/components/media/AudioFile.svelte";
  import FileChip from "$lib/components/media/FileChip.svelte";
  import TgsSticker from "$lib/components/media/TgsSticker.svelte";
  import VideoNote from "$lib/components/media/VideoNote.svelte";
  import VoiceMessage from "$lib/components/media/VoiceMessage.svelte";
  import ContextMenu from "$lib/components/ui/ContextMenu.svelte";
  import ContextMenuItem from "$lib/components/ui/ContextMenuItem.svelte";
  import Icon from "$lib/components/ui/Icon.svelte";
  import Spinner from "$lib/components/ui/Spinner.svelte";
  import { formatBytes, mediaKindLabel } from "$lib/format/media";
  import { poster } from "$lib/media/poster";
  import { shareUi } from "$lib/stores/shares.svelte";
  import { toasts } from "$lib/stores/toasts.svelte";
  import { ui } from "$lib/stores/ui.svelte";

  interface Props {
    message: MessageView;
    onopen: () => void;
    own: boolean;
  }

  let { message, onopen, own }: Props = $props();

  const POLL_TRIES = 5;
  const POLL_DELAY = 3000;

  let loaded = $state(false);
  let media = $state<InlineMedia | null>(null);
  let queuing = $state(false);
  let saving = $state(false);

  const ref = $derived(message.media[0] ?? null);
  const asFile = $derived(
    ref !== null && ref.id !== null && !isPreviewable(ref.kind, ref.mime)
  );

  const ready = $derived(media?.state === "ready" ? media : null);
  const kind = $derived(ready?.kind ?? ref?.kind ?? "");
  const mime = $derived(ready?.mime ?? "");
  const isImage = $derived(kind === "photo");
  const isStaticSticker = $derived(
    kind === "sticker" && mime.startsWith("image/")
  );
  const isVideoSticker = $derived(
    kind === "sticker" && mime.startsWith("video/")
  );
  const isTgsSticker = $derived(
    kind === "sticker" && mime === "application/x-tgsticker"
  );
  const isAnimation = $derived(kind === "animation" || kind === "gif");
  const isThumbVideo = $derived(kind === "video");

  const vk = $derived(
    media && media.state !== "missing" ? visualKind(media.kind) : "other"
  );
  const label = $derived(
    media && media.state !== "missing" ? media.kind : "media"
  );
  const storedId = $derived(
    ready?.mediaId ?? (asFile ? ref?.id : null) ?? null
  );
  const fileName = $derived(ref?.file_name ?? mediaKindLabel(kind) ?? "Файл");

  function delay(ms: number): Promise<void> {
    return new Promise((resolve) => {
      setTimeout(resolve, ms);
    });
  }

  async function start() {
    if (asFile) {
      loaded = true;
      return;
    }
    media = await loadInlineMedia(message.chat_id, message.message_id);
    loaded = true;
  }

  async function poll() {
    for (let i = 0; i < POLL_TRIES; i++) {
      await delay(POLL_DELAY);
      const next = await loadInlineMedia(message.chat_id, message.message_id);
      if (next.state === "ready") {
        media = next;
        return;
      }
    }
  }

  async function queue() {
    if (queuing) {
      return;
    }
    queuing = true;
    try {
      await fetchMedia(message.chat_id, message.message_id);
      toasts.success("Скачивание поставлено в очередь");
      poll();
    } catch {
      toasts.error("Не удалось поставить в очередь");
    } finally {
      queuing = false;
    }
  }

  async function save() {
    if (storedId === null || saving) {
      return;
    }
    saving = true;
    try {
      await downloadMedia(storedId);
    } catch {
      toasts.error("Не удалось скачать файл");
    } finally {
      saving = false;
    }
  }

  function share() {
    if (storedId !== null) {
      shareUi.share({ kind: "media", mediaId: storedId }, fileName);
    }
  }
</script>

<ContextMenu>
  {#snippet children({ props })}
    <div {...props} class="message-media" use:visible={start}>
      {#if message.is_self_destruct}
        <button class="media-chip self-destruct" onclick={onopen} type="button">
          <Icon name="timer" size="1.25rem" />
          <span>Самоуничтожающееся медиа</span>
        </button>
      {:else if asFile && ref}
        <FileChip media={ref} {own} />
      {:else if !loaded}
        <div class="media-skeleton"><Spinner /></div>
      {:else if ready && kind === "voice"}
        <VoiceMessage
          url={ready.url}
          transcript={ready.transcript}
          chatId={message.chat_id}
          messageId={message.message_id}
          {own}
        />
      {:else if ready && kind === "video_note"}
        <VideoNote url={ready.url} transcript={ready.transcript} />
      {:else if ready && kind === "audio"}
        <AudioFile url={ready.url} title={fileName} {own} />
      {:else if ready && isImage}
        <button class="media-thumb" onclick={onopen} type="button">
          <img src={ready.url} alt="attachment">
        </button>
      {:else if ready && isStaticSticker}
        <button class="media-sticker" onclick={onopen} type="button">
          <img src={ready.url} alt="sticker">
        </button>
      {:else if ready && isVideoSticker}
        <video
          class="media-sticker-video"
          src={ready.url}
          autoplay
          loop
          muted
          playsinline
        ></video>
      {:else if ready && isTgsSticker}
        <TgsSticker url={ready.url} />
      {:else if ready && isAnimation}
        <button class="media-thumb" onclick={onopen} type="button">
          <video src={ready.url} autoplay loop muted playsinline></video>
          <span class="gif-badge">GIF</span>
        </button>
      {:else if ready && isThumbVideo}
        <button class="media-thumb" onclick={onopen} type="button">
          <video
            src={ready.url}
            muted
            playsinline
            preload="metadata"
            use:poster
          ></video>
          <span class="play"><Icon name="large-play" size="2.5rem" /></span>
        </button>
      {:else if ready}
        <button class="media-chip" onclick={onopen} type="button">
          <Icon name="document" size="1.25rem" />
          <span>{label}</span>
        </button>
      {:else if media?.state === "not-downloaded" && vk !== "other"}
        <button class="media-placeholder" onclick={queue} type="button">
          <Icon name={queuing ? "timer" : "download"} size="1.5rem" />
          <span>{vk === "video" ? "Видео" : "Фото"}</span>
          <small>{queuing ? "В очереди" : "Нажмите, чтобы скачать"}</small>
        </button>
      {:else if media?.state === "not-downloaded"}
        <button class="media-chip" onclick={queue} type="button">
          <Icon name={queuing ? "timer" : "download"} size="1.25rem" />
          <span>{queuing ? "В очереди" : `Скачать ${label}`}</span>
        </button>
      {:else}
        <button class="media-chip" onclick={onopen} type="button">
          <Icon name="photo" size="1.25rem" />
          <span>Медиа</span>
        </button>
      {/if}
    </div>
  {/snippet}
  {#snippet menu()}
    <ContextMenuItem icon="open-in-new-tab" onselect={onopen}
      >Открыть на весь экран</ContextMenuItem
    >
    <ContextMenuItem
      icon="recent"
      onselect={() => ui.openMessagePanel("versions", message.message_id)}
      >Версии медиа</ContextMenuItem
    >
    {#if storedId === null}
      <ContextMenuItem icon="cloud-download" onselect={queue}>
        Скачать в архив
      </ContextMenuItem>
    {:else}
      <ContextMenuItem icon="download" onselect={save}>
        Скачать файл
      </ContextMenuItem>
      <ContextMenuItem icon="allow-share" onselect={share}>
        Доступ по ссылке
      </ContextMenuItem>
    {/if}
  {/snippet}
</ContextMenu>

<style lang="scss">
  .message-media {
    margin-bottom: 0.25rem;
  }

  .media-thumb {
    cursor: pointer;
    position: relative;

    display: block;
    overflow: hidden;

    max-width: 100%;
    padding: 0;
    border: 0;
    border-radius: var(--border-radius-default-small);

    background-color: var(--color-default-shadow);

    img,
    video {
      display: block;

      width: 100%;
      max-width: 20rem;
      max-height: 20rem;

      object-fit: cover;
    }
  }

  .media-sticker {
    cursor: pointer;
    display: block;

    padding: 0;
    border: 0;

    background: transparent;

    img {
      display: block;
      width: 12rem;
      height: 12rem;
      object-fit: contain;
    }
  }

  .media-sticker-video {
    display: block;
    width: 12rem;
    height: 12rem;
    object-fit: contain;
  }

  .gif-badge {
    position: absolute;
    top: 0.375rem;
    left: 0.375rem;

    padding: 0.0625rem 0.3125rem;
    border-radius: 0.5rem;

    font-size: 0.6875rem;
    font-weight: var(--font-weight-medium);
    color: var(--color-white);

    background-color: rgba(0, 0, 0, 0.45);
  }

  .play {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);

    display: flex;
    color: var(--color-white);

    filter: drop-shadow(0 1px 4px rgba(0, 0, 0, 0.5));
  }

  .media-skeleton {
    display: flex;
    align-items: center;
    justify-content: center;

    width: 12rem;
    height: 9rem;
    border-radius: var(--border-radius-default-small);

    background-color: var(--color-default-shadow);
  }

  .media-placeholder {
    cursor: pointer;

    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.25rem;

    width: 12rem;
    height: 9rem;
    border: 0;
    border-radius: var(--border-radius-default-small);

    color: var(--color-primary);
    text-align: center;

    background-color: var(--color-primary-tint);

    small {
      font-size: 0.75rem;
      color: var(--color-text-secondary);
    }
  }

  .media-chip {
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: 0.5rem;

    width: 100%;
    padding: 0.5rem 0.625rem;
    border: 0;
    border-radius: var(--border-radius-default-small);

    font-size: 0.9375rem;
    color: var(--color-primary);
    text-align: start;

    background-color: var(--color-primary-tint);

    &.self-destruct {
      color: var(--color-orange);
      background-color: var(--color-light-coral);
    }
  }
</style>
