<template>
  <div class="platine" :class="{ 'loading': isLoadingTrack, 'mounted': isMounted, 'playing': isPlaying }" ref="platine">
    <div v-if="false" class="debug">
      <button @click="Reverse">
        <b v-if="isReversed">reversed</b>
        <b v-else>normal</b>
      </button>
      <button @click="Rewind">
        Rewind
      </button>
      <button @click="Wind">
        Wind
      </button>
      <!-- <div>{{ progressPercentage }}</div> -->
      <div>{{ (currentSpeed).toFixed(2) }}</div>
    </div>
    <div class="disc" ref="discRef" id="disc">
      <div class="bobine" :style="{
        height: progressPercentage + '%',
        width: progressPercentage + '%'
      }"></div>
      <button class="power-button" @click="Power" @touchstart="Power" :disabled="isLoadingTrack">
        <img class="Macaron" src="/favicon.svg" v-if="!isEndOfTrack">
        <div class="spinner" v-if="isLoadingTrack" />
        <img class="Macaron" @click="Rewind" src="/rewind.svg" v-if="isEndOfTrack" />
      </button>
      <div class="turn-point" v-if="!isLoadingTrack">
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import type { Card } from '~~/types/types'
import { ref, onMounted, onUnmounted, watch, computed, nextTick } from 'vue'
import Disc from '~/utils/platine/disc'
import Sampler from '~/utils/platine/sampler'
import { duration } from 'drizzle-orm/gel-core';

const props = defineProps<{ card?: Card, autoplay?: boolean }>()
const autoplay = props.autoplay ?? false

// State
const isLoadingTrack = ref(false)
const isFirstPlay = ref(true)
const isEndOfTrack = ref(false)

const progressPercentage = ref(0)
const currentTurns = ref(0)
const totalTurns = ref(0)// Refs pour les instances

// Refs pour les instances
const disc = ref<Disc | null>(null)
const sampler = ref<Sampler | null>(null)
const discRef = ref<HTMLElement>()
const platine = ref<HTMLElement>()
const isMounted = ref(false)

const isPlaying = computed(() => Math.abs(Math.round(sampler.value?.currentSpeed || 0)) !== 0)
const isReversed = computed(() => disc.value?.isReversed || false)
const currentSpeed = computed(() => sampler.value?.currentSpeed || 0)

// Actions
const initPlatine = (element: HTMLElement) => {
  // console.log('[INIT] Platine')
  discRef.value = element
  disc.value = new Disc(element)
  sampler.value = new Sampler()

  // Configurer les callbacks du disque
  if (disc.value) {
    disc.value.callbacks.onStop = () => {
      // console.log('[DISC] On Stop')
      sampler.value?.pause()
    }

    disc.value.callbacks.onDragStart = () => {
      // Activer le son à chaque fois qu'on glisse, pas seulement au premier play
      if (sampler.value && disc.value) {
        // On joue toujours le son quand on glisse, même après une pause
        sampler.value.play(disc.value.secondsPlayed || 0)
      }
    }

    disc.value.callbacks.onLoop = ({ playbackSpeed, isReversed, secondsPlayed }) => {
      // Ne mettre à jour que si nécessaire et s'assurer que la position est valide
      if (Math.abs((sampler.value?.currentSpeed || 0) - playbackSpeed) > 0.01) {
        const safeSecondsPlayed = Math.max(0, secondsPlayed)
        sampler.value?.updateSpeed(playbackSpeed, isReversed, safeSecondsPlayed)
      }
      updateTurns()
    }
  }
}

const updateTurns = () => {
  if (!disc.value) return

  const newTurns = disc.value.secondsPlayed * 0.75
  const newTotalTurns = (disc.value as any)._duration * 0.75

  // Calcul du pourcentage de progression pour l'affichage visuel (17% à 100%)
  const minPercentage = 17
  const maxPercentage = 100
  const progressRatio = disc.value.secondsPlayed / (disc.value as any)._duration
  const newProgressPercentage = minPercentage + (progressRatio * (maxPercentage - minPercentage))

  // Ne mettre à jour que si les valeurs ont changé de manière significative
  if (
    Math.abs(currentTurns.value - newTurns) > 0.01 ||
    Math.abs(totalTurns.value - newTotalTurns) > 0.01 ||
    Math.abs(progressPercentage.value - newProgressPercentage) > 0.1
  ) {
    currentTurns.value = newTurns
    totalTurns.value = newTotalTurns
    progressPercentage.value = newProgressPercentage
    isEndOfTrack.value = disc.value.secondsPlayed + 0.1 >= (disc.value as any)._duration
  }
}

const loadCard = async (card: Card) => {
  // console.log('[LOAD CARD]', card)
  if (!sampler.value || !card) return

  isLoadingTrack.value = true
  // console.log(disc.value)
  try {
    await sampler.value.loadTrack(card.url_audio)
    if (disc.value) {
      // console.log('[DISC] Set Duration', sampler.value.duration)
      disc.value.setDuration(sampler.value.duration)
      updateTurns()
    }
  } finally {
    isLoadingTrack.value = false
  }
}

const play = (position = 0) => {
  isFirstPlay.value = false
  if (!disc.value || !sampler.value || !props.card) return

  sampler.value.play(position)
  disc.value.powerOn()
}

const pause = () => {
  // console.log('[PAUSE]')
  if (!disc.value || !sampler.value) return

  sampler.value.pause()
  disc.value.powerOff()
}

const togglePlay = () => {
  if (isPlaying.value) {
    pause()
  } else {
    // Reprendre la lecture à la position actuelle
    const currentPosition = disc.value?.secondsPlayed || 0
    play(currentPosition)
  }
}

// Nettoyage
const cleanup = () => {
  // console.log('[CLEANUP] Platine')
  if (disc.value) {
    disc.value.stop()
    disc.value.powerOff()
  }
  if (sampler.value) {
    sampler.value.pause()
  }
}

const Power = (e: MouseEvent) => {
  if (isEndOfTrack.value) {
    Rewind()
  }
  togglePlay()
}

const Reverse = () => {
  if (!disc.value || !sampler.value) return

  // Sauvegarder la position actuelle
  const currentPosition = disc.value.secondsPlayed || 0
  const wasPlaying = !disc.value.isStopped()

  // Inverser la direction du disque et du sampler
  disc.value.reverse()
  sampler.value.reverse(wasPlaying ? currentPosition : 0)
}

const Rewind = async () => {
  if (!disc.value || !sampler.value) return

  // Sauvegarder l'état actuel
  disc.value.isStopped() ? play() : null
  const wasPlaying = !disc.value.isStopped()
  const currentSpeed = disc.value.playbackSpeed

  // Fonction pour l'effet de scratch/pull up
  const scratchEffect = async () => {
    // Ralentir progressivement
    const slowDownDuration = 300 // ms
    const startTime = performance.now()
    const startSpeed = wasPlaying ? currentSpeed : 0.1

    const slowDown = (timestamp: number) => {
      const elapsed = timestamp - startTime
      const progress = Math.min(elapsed / slowDownDuration, 1)

      // Courbe d'accélération pour un effet plus naturel
      const easeOut = 1 - Math.pow(1 - progress, 2)

      // Ralentir progressivement jusqu'à presque l'arrêt
      const newSpeed = startSpeed * (1 - easeOut) + 0.05
      sampler.value?.setPlaybackRate(newSpeed)

      if (progress < 1) {
        requestAnimationFrame(slowDown)
      } else {
        // Une fois ralenti, effectuer le rembobinage
        performRewind()
      }
    }

    requestAnimationFrame(slowDown)
    play()
  }

  const performRewind = () => {
    // Mettre en pause la lecture actuelle
    if (wasPlaying) {
      sampler.value?.pause()
    }

    // Remettre à zéro la position
    sampler.value?.play(0)
    disc.value?.setAngle(0)

    // Effet de scratch rapide
    const scratchDuration = 200 // ms
    const scratchStart = performance.now()

    const scratch = (timestamp: number) => {
      const elapsed = timestamp - scratchStart
      const progress = Math.min(elapsed / scratchDuration, 1)

      // Créer un effet de scratch en variant la vitesse
      if (progress < 0.5) {
        // Phase de scratch vers l'arrière
        const scratchProgress = progress * 2
        const scratchSpeed = 1 - (scratchProgress * 1.8) // Ralenti jusqu'à -0.8
        sampler.value?.setPlaybackRate(scratchSpeed)
      } else {
        // Phase de pull up
        const pullProgress = (progress - 0.5) * 2
        const pullSpeed = -0.8 + (pullProgress * 1.8) // Accélère de -0.8 à 1.0
        sampler.value?.setPlaybackRate(pullSpeed)
      }

      if (progress < 1) {
        requestAnimationFrame(scratch)
      } else {
        // Remettre la vitesse normale à la fin
        sampler.value?.setPlaybackRate(1)

        // Si c'était en lecture avant, relancer la lecture
        if (wasPlaying) {
          setTimeout(() => {
            sampler.value?.play(0)
          }, 50)
        }
      }
    }

    requestAnimationFrame(scratch)
  }

  // Démarrer l'effet
  scratchEffect()
}

const Wind = () => {
  disc.value.secondsPlayed = duration
  sampler.value.secondsPlayed = duration
}

const handleKeyDown = (e: KeyboardEvent) => {
  if (e.code === 'Space' || e.key === ' ' || e.keyCode === 32) {
    e.preventDefault() // Empêcher le défilement de la page
    togglePlay()
  }
}

// Initialisation du lecteur
onMounted(async () => {
  isMounted.value = true
  if (discRef.value) {
    initPlatine(discRef.value)
    await loadCard(props.card!)
    if (autoplay) {
      await nextTick()
      play()
    }
  }

  window.addEventListener('keydown', handleKeyDown)
})

// Nettoyage de l'écouteur d'événement
onUnmounted(() => {
  window.removeEventListener('keydown', handleKeyDown)
  isMounted.value = false
  cleanup()
})

// Surveillance des changements de piste
watch(() => props.card, (newCard) => {
  // console.log('[WATCH] Card', newCard)
  if (newCard) {
    loadCard(newCard)
  }
})

</script>

<style lang="scss">
.platine {
  overflow: hidden;
  position: relative;
  display: flex;
  width: 100%;
  height: 100%;

  .disc {
    pointer-events: auto;
    position: absolute;
    background-color: transparent;
    position: relative;
    aspect-ratio: 1;
    width: 100%;
    overflow: hidden;
    border-radius: 50%;
    cursor: grab;
    background-position: center;
    background-size: cover;
    padding: 14px;

    .loading & {
      box-shadow: none;
    }
  }

  .turn-point {
    @apply absolute top-1/2 right-8 size-1/12 rounded-full bg-esyellow;
  }

  .disc.is-scratching {
    cursor: grabbing;
  }

  .power-button {
    position: absolute;
    z-index: 100;
    cursor: pointer;
    transition: all 0.1s;
    display: flex;
    justify-content: center;
    align-items: center;
    transform-origin: center;
    width: 33%;
    height: 33%;
    border-radius: 100%;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);

    .Macaron {
      position: absolute;
      filter: grayscale(1);
      transition: transform 0.1s, filter 0.8s;
      @apply size-2/3;
    }

    .spinner {
      @apply size-1/2;
    }

    &:active {
      .Macaron {
        transform: scale(0.8);
      }
    }
  }

  &.playing .power-button .Macaron {
    filter: grayscale(0);
  }

  .disc-middle {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 20px;
    height: 20px;
    background: rgb(26, 26, 26);
    border-radius: 50%;
  }

  .spinner {
    width: 40px;
    height: 40px;
    border: 1px solid rgba(255, 255, 255, 0.3);
    border-top-color: #fff;
    border-radius: 50%;
    animation: spin 0.8s linear infinite;
    // @apply md:border-8;
  }

  @keyframes spin {
    to {
      transform: rotate(360deg);
    }
  }

  .bobine {
    width: 17%;
    height: 17%;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    @apply relative bg-black bg-opacity-60 backdrop-blur-sm rounded-full;
  }
}
</style>
