<script setup lang="ts">
import type { QueueItemSource } from '~/types'

import { whenever, watchDebounced, useCurrentElement, useScrollLock, useFullscreen, useIdle, refAutoReset, useStorage } from '@vueuse/core'
import { nextTick, ref, computed, watchEffect, defineAsyncComponent } from 'vue'
import { useFocusTrap } from '@vueuse/integrations/useFocusTrap'
import { useRouter } from 'vue-router'
import { useStore } from '~/store'

import { usePlayer } from '~/composables/audio/player'
import { useTracks } from '~/composables/audio/tracks'
import { useQueue } from '~/composables/audio/queue'

import time from '~/utils/time'

import { useI18n } from 'vue-i18n'

import TrackFavoriteIcon from '~/components/favorites/TrackFavoriteIcon.vue'
import TrackPlaylistIcon from '~/components/playlists/TrackPlaylistIcon.vue'
import PlayerControls from '~/components/audio/PlayerControls.vue'

import VirtualList from '~/components/vui/list/VirtualList.vue'
import QueueItem from '~/components/QueueItem.vue'

import Button from '~/components/ui/Button.vue'
import Link from '~/components/ui/Link.vue'
import Pill from '~/components/ui/Pill.vue'

const MilkDrop = defineAsyncComponent(() => import('~/components/audio/visualizer/MilkDrop.vue'))

const {
  isPlaying,
  currentTime,
  duration,
  bufferProgress,
  seekTo,
  loading: isLoadingAudio,
  errored
} = usePlayer()

const {
  hasNext,
  currentTrack,
  currentIndex,
  queue,
  dequeue,
  playTrack,
  reorder,
  endsIn,
  clear
} = useQueue()

const { currentSound } = useTracks()

const queueModal = ref()
const { activate, deactivate } = useFocusTrap(queueModal, { allowOutsideClick: true, preventScroll: true })

const { t } = useI18n()
const scrollLock = useScrollLock(document.body)
const store = useStore()

const labels = computed(() => ({
  queue: t('components.Queue.label.queue'),
  populating: t('components.Queue.label.populatingRadio'),
  duration: t('components.Queue.label.duration'),
  addArtistContentFilter: t('components.Queue.label.addArtistContentFilter'),
  restart: t('components.Queue.label.restart'),
  previous: t('components.Queue.label.previous'),
  next: t('components.Queue.label.next'),
  pause: t('components.Queue.label.pause'),
  play: t('components.Queue.label.play'),
  fullscreen: t('components.Queue.label.enterFullscreen'),
  exitFullscreen: t('components.Queue.label.exitFullscreen'),
  showCoverArt: t('components.Queue.label.showCoverArt'),
  showVisualizer: t('components.Queue.label.showVisualizer')
}))

watchEffect(async () => {
  scrollLock.value = !!store.state.ui.queueFocused
  if (store.state.ui.queueFocused) {
    await nextTick()
    activate()
  } else {
    deactivate()
  }
})

const list = ref()
const el = useCurrentElement()
const scrollToCurrent = (behavior: ScrollBehavior = 'smooth') => {
  const item = el.value?.querySelector('.queue-item.active')
  item?.scrollIntoView({
    behavior,
    block: 'center'
  })
}

watchDebounced(currentTrack, () => scrollToCurrent(), { debounce: 100 })

whenever(
  () => queue.value.length === 0,
  () => store.commit('ui/queueFocused', null),
  { immediate: true }
)

const router = useRouter()
router.beforeEach(() => store.commit('ui/queueFocused', null))

const progressBar = ref()
const touchProgress = (event: MouseEvent) => {
  const time = ((event.clientX - ((event.target as Element).closest('.progress')?.getBoundingClientRect().left ?? 0)) / progressBar.value.offsetWidth) * duration.value
  seekTo(time)
}

const play = async (index: number) => {
  isPlaying.value = true
  return playTrack(index)
}

const queueItems = computed(() => queue.value.map((track, index) => ({
  ...track,
  key: `${index}-${track.id}`,
  labels: {
    remove: t('components.Queue.label.remove'),
    selectTrack: t('components.Queue.label.selectTrack'),
    favorite: t('components.Queue.label.favorite')
  }
}) as QueueItemSource))

const reorderTracks = async (from: number, to: number) => {
  reorder(from, to)

  await nextTick()
  if (to === currentIndex.value) {
    scrollToCurrent()
  }
}
const hideArtist = () => {
  if (currentTrack.value.artistId !== -1 && currentTrack.value.artistCredit) {
    return store.dispatch('moderation/hide', {
      type: 'artist',
      target: {
        id: currentTrack.value.artistCredit[0].artist.id,
        name: currentTrack.value.artistCredit[0].artist.name
      }
    })
  }
}

const cover = ref()
const { isFullscreen: fullscreen, enter, exit } = useFullscreen(cover)
const { idle } = useIdle(2000)

const showTrackInfo = refAutoReset(false, 5000)
whenever(currentTrack, () => (showTrackInfo.value = true))

const milkdrop = ref()
const loadRandomPreset = () => {
  milkdrop.value?.loadRandomPreset()
}

enum CoverType {
  COVER_ART,
  MILK_DROP
}

let isWebGLSupported = false
try {
  const canvas = document.createElement('canvas')
  isWebGLSupported = !!canvas.getContext('webgl2')
} catch (error) {}

const coverType = useStorage('queue:cover-type', CoverType.COVER_ART)
if (!isWebGLSupported) {
  coverType.value = CoverType.COVER_ART
}
</script>

<template>
  <section
    class="main opaque component-queue"
    :aria-label="labels.queue"
  >
    <div
      id="queue-grid"
      :class="store.state.ui.queueFocused && `show-${store.state.ui.queueFocused}`"
    >
      <div
        id="player"
        class="ui basic segment"
      >
        <template v-if="currentTrack">
          <div
            ref="cover"
            :class="['cover-container', { idle, fullscreen }]"
          >
            <div class="cover">
              <template v-if="coverType === CoverType.COVER_ART">
                <img
                  v-if="fullscreen"
                  class="cover-shadow"
                  :src="store.getters['instance/absoluteUrl'](currentTrack.coverUrl)"
                >
                <img
                  ref="cover"
                  alt=""
                  :src="store.getters['instance/absoluteUrl'](currentTrack.coverUrl)"
                >
              </template>
              <milk-drop
                v-else-if="coverType === CoverType.MILK_DROP"
                ref="milkdrop"
              />

              <Transition name="queue">
                <div
                  v-if="!fullscreen || !idle"
                  class="cover-buttons"
                >
                  <tooltip :content="!isWebGLSupported && t('components.Queue.message.webglUnsupported')">
                    <Button
                      v-if="coverType === CoverType.COVER_ART"
                      :aria-label="labels.showVisualizer"
                      :title="labels.showVisualizer"
                      :disabled="!isWebGLSupported"
                      icon="bi-display"
                      @click="coverType = CoverType.MILK_DROP"
                    />
                    <Button
                      v-else-if="coverType === CoverType.MILK_DROP"
                      :aria-label="labels.showCoverArt"
                      :title="labels.showCoverArt"
                      :disabled="!isWebGLSupported"
                      icon="bi-image-fill"
                      @click="coverType = CoverType.COVER_ART"
                    />
                  </tooltip>

                  <Button
                    v-if="!fullscreen"
                    :aria-label="labels.fullscreen"
                    :title="labels.fullscreen"
                    icon="bi-arrows-fullscreen"
                    @click="enter"
                  />
                  <Button
                    secondary
                    v-else
                    :aria-label="labels.exitFullscreen"
                    :title="labels.exitFullscreen"
                    icon="bi-fullscreen-exit"
                    @click="exit"
                  />
                </div>
              </Transition>
              <Transition name="queue">
                <div
                  v-if="fullscreen && (!idle || showTrackInfo)"
                  class="track-info"
                  @click="loadRandomPreset()"
                >
                  <h1>{{ currentTrack.title }}</h1>
                  <h2>
                    <div
                      v-for="ac in currentTrack.artistCredit"
                      :key="ac.artist.id"
                    >
                      {{ ac.credit ?? t('components.Queue.meta.unknownArtist') }}
                      <span>{{ ac.joinphrase }}</span>
                    </div>
                    <span class="symbol hyphen middle" />
                    {{ currentTrack.albumTitle ?? t('components.Queue.meta.unknownAlbum') }}
                  </h2>
                </div>
              </Transition>
            </div>
          </div>
          <h1 class="ui header">
            <Link
              class="track"
              :to="{name: 'library.tracks.detail', params: {id: currentTrack.id }}"
            >
              {{ currentTrack.title }}
            </Link>
          </h1>
          <h2>
            <template v-if="currentTrack.albumId !== -1">
              <Link
                class="album"
                :to="{name: 'library.albums.detail', params: {id: currentTrack.albumId }}"
              >
                {{ currentTrack.albumTitle ?? t('components.Queue.meta.unknownAlbum') }}
              </Link>
            </template>
          </h2>
          <span>
            <template
              v-for="ac in currentTrack.artistCredit"
              :key="ac.artist.id"
            >
              <router-link
                :to="{name: 'library.artists.detail', params: {id: ac.artist.id }}"
                @click.stop.prevent=""
              >
                <Pill>
                  <template #image v-if="ac.artist.attachment_cover">
                    <img v-lazy="ac.artist.attachment_cover?.urls.medium_square_crop">
                  </template>
                  {{ ac.credit ?? t('components.Queue.meta.unknownArtist') }}
                </Pill>
              </router-link>
              <span>{{ ac.joinphrase }}</span>
            </template>
          </span>
          <div
            v-if="currentTrack && errored"
            class="ui small warning message"
          >
            <h3 class="header">
              {{ t('components.Queue.header.failure') }}
            </h3>
            <p v-if="hasNext && isPlaying">
              {{ t('components.Queue.message.automaticPlay') }}
              <i class="loading spinner icon" />
            </p>
            <p>
              {{ t('components.Queue.warning.connectivity') }}
            </p>
          </div>
          <div
            v-else-if="currentSound && !currentSound.playable"
            class="ui small warning message"
          >
            <h3 class="header">
              {{ t('components.Queue.header.noSources') }}
            </h3>
            <p v-if="hasNext && isPlaying">
              {{ t('components.Queue.message.automaticPlay') }}
              <i class="loading spinner icon" />
            </p>
          </div>
          <div class="additional-controls desktop-and-below">
            <track-favorite-icon
              v-if="store.state.auth.authenticated"
              :track="currentTrack"
              ghost
            />
            <track-playlist-icon
              v-if="store.state.auth.authenticated"
              :track="currentTrack"
              ghost
            />
            <Button
              v-if="store.state.auth.authenticated"
              ghost
              icon="bi-eye-slash"
              :aria-label="labels.addArtistContentFilter"
              :title="labels.addArtistContentFilter"
              @click="hideArtist"
            />
          </div>
          <div class="progress-wrapper">
            <div class="progress-area">
              <div
                ref="progressBar"
                :class="['ui', 'small', 'vibrant', {'indicating': isLoadingAudio && !errored}, 'progress']"
                @click="touchProgress"
              >
                <div
                  class="buffer bar"
                  :style="{ 'transform': `translate3d(${bufferProgress - 100}%, 0, 0)` }"
                />
                <div class="position bar" />
              </div>
            </div>
            <div class="progress">
              <template v-if="!isLoadingAudio">
                <a
                  href=""
                  :aria-label="labels.restart"
                  class="left floated timer discrete start"
                  @click.prevent="currentTime = 0"
                >
                  {{ time.parse(Math.round(currentTime)) }}
                </a>
                <span class="right floated timer total">{{ time.parse(Math.round(duration)) }}</span>
              </template>
              <template v-else>
                <span class="left floated timer">{{ t('components.Queue.meta.startTime') }}</span>
                <span class="right floated timer">{{ t('components.Queue.meta.startTime') }}</span>
              </template>
            </div>
          </div>
          <player-controls class="desktop-and-below" />
        </template>
      </div>
      <div id="queue">
        <div class="ui basic clearing segment">
          <h2 class="ui header">
            <div class="content">
              <Button
                ghost
                icon="bi-chevron-down"
                style="float: right; margin-right: 24px;"
                @click="store.commit('ui/queueFocused', null)"
              />
              <Button
                red
                outline
                icon="bi-trash-fill"
                style="float: right; margin-right: 16px;"
                @click="clear"
              >
                {{ t('components.Queue.button.clear') }}
              </Button>
              {{ labels.queue }}
              <div class="sub header">
                <div>
                  <i18n-t keypath="components.Queue.meta.queuePosition">
                    <template #index>
                      {{ currentIndex + 1 }}
                    </template>
                    <template #length>
                      {{ queue.length }}
                    </template>
                  </i18n-t>
                  <span class="middle pipe symbol" />
                  <span style="margin-right: 8px;" v-t="'components.Queue.meta.end'" />
                  <span :title="labels.duration">
                    {{ endsIn }}
                  </span>
                </div>
              </div>
            </div>
          </h2>
        </div>
        <virtual-list
          v-if="queueItems.length !== 0"
          ref="list"
          :list="queueItems"
          :component="QueueItem"
          :size="50"
          @reorder="reorderTracks"
          @visible="list.scrollToIndex(currentIndex, 'center')"
        >
          <template #default="{ index, item, classlist }">
            <queue-item
              v-if="index !== undefined"
              :data-index="index"
              :index="index"
              :source="item"
              :class="[...classlist, currentIndex === index && 'active']"
              @play="play"
              @remove="dequeue"
            />
          </template>
          <template #footer>
            <div
              v-if="store.state.radios.populating"
              class="radio-populating"
            >
              <i class="loading spinner icon" />
              {{ labels.populating }}
            </div>
            <div
              v-if="store.state.radios.running"
              class="ui info message radio-message"
            >
              <div class="content">
                <h3 class="header">
                  <i class="bi bi-boombox-fill" />
                  {{ t('components.Queue.header.radio') }}
                </h3>
                <p>
                  {{ t('components.Queue.message.radio') }}
                </p>
                <Button
                  primary
                  icon="bi-stop-fill"
                  @click="store.dispatch('radios/stop')"
                >
                  {{ t('components.Queue.button.stopRadio') }}
                </Button>
              </div>
            </div>
          </template>
        </virtual-list>
      </div>
    </div>
  </section>
</template>
