<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, onMounted } from 'vue'
import { useFocusTrap } from '@vueuse/integrations/useFocusTrap'
import { useRouter } from 'vue-router'
import { useI18n } from 'vue-i18n'
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 TrackFavoriteIcon from '~/components/favorites/TrackFavoriteIcon.vue'
import TrackPlaylistIcon from '~/components/playlists/TrackPlaylistIcon.vue'
import PlayerControls from '~/components/audio/PlayerControls.vue'
import MilkDrop from '~/components/audio/visualizer/MilkDrop.vue'

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

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

const {
  hasNext,
  currentTrack,
  currentIndex,
  queue,
  dequeue,
  playTrack,
  reorder,
  endsIn: timeLeft,
  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.queue'),
  populating: t('components.Queue.queue.populatingRadio'),
  duration: t('components.Queue.duration'),
  addArtistContentFilter: t('components.Queue.addArtistContentFilter'),
  restart: t('components.Queue.restart'),
  previous: t('components.Queue.previous'),
  next: t('components.Queue.next'),
  pause: t('components.Queue.pause'),
  play: t('components.Queue.play'),
  fullscreen: t('components.Queue.enterFullscreen'),
  exitFullscreen: t('components.Queue.exitFullscreen'),
  showCoverArt: t('components.Queue.showCoverArt'),
  showVisualizer: t('components.Queue.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 })

const scrollLoop = () => {
  const visible = [...(list.value?.scroller.$_views.values() ?? [])].map(item => item.nr.index)
  if (!visible.includes(currentIndex.value)) {
    list.value?.scrollToIndex(currentIndex.value)
    requestAnimationFrame(scrollLoop)
  }
}

onMounted(scrollLoop)

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.remove'),
    selectTrack: t('components.Queue.selectTrack'),
    favorite: t('components.Queue.favorite')
  },
  duration: time.durationFormatted(track.uploads[0]?.duration ?? 0) ?? ''
}) 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) {
    return store.dispatch('moderation/hide', {
      type: 'artist',
      target: {
        id: currentTrack.value.artistId,
        name: currentTrack.value.artistName
      }
    })
  }
}

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
}

const coverType = useStorage('queue:cover-type', CoverType.COVER_ART)
</script>

<template>
  <section
    class="main with-background 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"
                >
                  <button
                    v-if="coverType === CoverType.COVER_ART"
                    class="ui secondary button"
                    :aria-label="labels.showVisualizer"
                    :title="labels.showVisualizer"
                    @click="coverType = CoverType.MILK_DROP"
                  >
                    <i class="icon signal" />
                  </button>
                  <button
                    v-else-if="coverType === CoverType.MILK_DROP"
                    class="ui secondary button"
                    :aria-label="labels.showCoverArt"
                    :title="labels.showCoverArt"
                    @click="coverType = CoverType.COVER_ART"
                  >
                    <i class="icon image outline" />
                  </button>

                  <button
                    v-if="!fullscreen"
                    class="ui secondary button"
                    :aria-label="labels.fullscreen"
                    :title="labels.fullscreen"
                    @click="enter"
                  >
                    <i class="icon expand" />
                  </button>
                  <button
                    v-else
                    class="ui secondary button"
                    :aria-label="labels.exitFullscreen"
                    :title="labels.exitFullscreen"
                    @click="exit"
                  >
                    <i class="icon compress" />
                  </button>
                </div>
              </Transition>
              <Transition name="queue">
                <div
                  v-if="fullscreen && (!idle || showTrackInfo)"
                  class="track-info"
                  @click="loadRandomPreset()"
                >
                  <h1>{{ currentTrack.title }}</h1>
                  <h2>{{ currentTrack.artistName }} &mdash; {{ currentTrack.albumTitle }}</h2>
                </div>
              </Transition>
            </div>
          </div>
          <h1 class="ui header">
            <div class="content ellipsis">
              <router-link
                class="small header discrete link track"
                :to="{name: 'library.tracks.detail', params: {id: currentTrack.id }}"
              >
                {{ currentTrack.title }}
              </router-link>
              <div class="sub header ellipsis">
                <router-link
                  class="discrete link artist"
                  :to="{name: 'library.artists.detail', params: {id: currentTrack.artistId }}"
                >
                  {{ currentTrack.artistName }}
                </router-link>
                <template v-if="currentTrack.albumId !== -1">
                  <span class="middle slash symbol" />
                  <router-link
                    class="discrete link album"
                    :to="{name: 'library.albums.detail', params: {id: currentTrack.albumId }}"
                  >
                    {{ currentTrack.albumTitle }}
                  </router-link>
                </template>
              </div>
            </div>
          </h1>
          <div
            v-if="currentTrack && errored"
            class="ui small warning message"
          >
            <h3 class="header">
              {{ $t('components.Queue.trackLoadFailure') }}
            </h3>
            <p v-if="hasNext && isPlaying">
              {{ $t('components.Queue.automaticPlay') }}
              <i class="loading spinner icon" />
            </p>
            <p>
              {{ $t('components.Queue.connectivityWarning') }}
            </p>
          </div>
          <div
            v-else-if="currentSound && !currentSound.playable"
            class="ui small warning message"
          >
            <h3 class="header">
              <translate translate-context="Sidebar/Player/No sources">
                The track has no available sources
              </translate>
            </h3>
            <p v-if="hasNext && isPlaying">
              <translate translate-context="Sidebar/Player/Error message.Paragraph">
                The next track will play automatically in a few seconds…
              </translate>
              <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"
            />
            <track-playlist-icon
              v-if="$store.state.auth.authenticated"
              :track="currentTrack"
            />
            <button
              v-if="$store.state.auth.authenticated"
              :class="['ui', 'really', 'basic', 'circular', 'icon', 'button']"
              :aria-label="labels.addArtistContentFilter"
              :title="labels.addArtistContentFilter"
              @click="hideArtist"
            >
              <i :class="['eye slash outline', 'basic', 'icon']" />
            </button>
          </div>
          <div class="progress-wrapper">
            <div
              v-if="currentTrack && !errored"
              class="progress-area"
            >
              <div
                ref="progressBar"
                :class="['ui', 'small', 'vibrant', {'indicating': isLoadingAudio}, 'progress']"
                @click="touchProgress"
              >
                <div
                  class="buffer bar"
                  :style="{ 'transform': `translateX(${bufferProgress - 100}%)` }"
                />
                <div
                  class="position bar"
                  :style="{ 'transform': `translateX(calc(${progress}% - 100%)` }"
                />
              </div>
            </div>
            <div
              v-else
              class="progress-area"
            >
              <div
                ref="progress"
                :class="['ui', 'small', 'vibrant', 'progress']"
              >
                <div class="buffer bar" />
                <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">{{ durationFormatted(0) }}</span>
                <span class="right floated timer">{{ durationFormatted(0) }}</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
                class="ui right floated basic button"
                @click="$store.commit('ui/queueFocused', null)"
              >
                {{ $t('components.Queue.closeButton') }}
              </button>
              <button
                class="ui right floated basic button danger"
                @click="clear"
              >
                {{ $t('components.Queue.clearButton') }}
              </button>
              {{ labels.queue }}
              <div class="sub header">
                <div>
                  {{ $t('components.Queue.queuePosition', {index: currentIndex +1, length: queue.length}) }}
                  <template v-if="!$store.state.radios.running">
                    <span class="middle ellipses symbol" />
                    <span :title="labels.duration">
                      {{ timeLeft }}
                    </span>
                  </template>
                </div>
              </div>
            </div>
          </h2>
        </div>
        <virtual-list
          ref="list"
          :list="queueItems"
          :component="QueueItem"
          :size="50"
          @reorder="reorderTracks"
          @visible="scrollToCurrent('auto')"
          @hidden="scrollLoop"
        >
          <template #default="{ index, item, classlist }">
            <queue-item
              :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="feed icon" />
                  {{ $t('components.Queue.radioPlaying') }}
                </h3>
                <p>
                  {{ $t('components.Queue.appendTracks') }}
                </p>
                <button
                  class="ui basic primary button"
                  @click="$store.dispatch('radios/stop')"
                >
                  {{ $t('components.Queue.stopRadio') }}
                </button>
              </div>
            </div>
          </template>
        </virtual-list>
      </div>
    </div>
  </section>
</template>
