<template>
  <!-- scène 3D -->
  <div ref="scene" class="scene z-10">
    <div ref="box" class="box">
      <div class="face front relative" ref="frontFace">
        <img class="cover absolute" :src="`/${compilation.id}/cover.jpg`" alt="">
      </div>
      <div class="face back" ref="backFace" />
      <div class="face right" ref="rightFace" />
      <div class="face left" ref="leftFace" />
      <div class="face top" ref="topFace">
        <img class="logo h-full p-1" src="/logo.svg" alt="">
        <img class="absolute block h-1/2" style="left:5%;" :src="`/${compilation.id}/title.svg`" alt="">
      </div>
      <div class="face bottom" ref="bottomFace" />
    </div>
  </div>
</template>

<script setup lang="ts">
import type { Compilation, BoxPosition } from '~~/types/types';

const props = withDefaults(
  defineProps<{
    compilation: Compilation
    position?: BoxPosition
    size?: number
  }>(),
  {
    position: () => ({ x: 0, y: 0, z: 0 }),
    size: 6,
  }
)

// States

const angleX = ref(props.position.x)
const angleY = ref(props.position.y)
const angleZ = ref(props.position.z)

const frontFace = ref()
const backFace = ref()
const rightFace = ref()
const leftFace = ref()
const topFace = ref()
const bottomFace = ref()

const box = ref()
const scene = ref()

/*
  ÉTATS POUR LE DRAG + INERTIE
*/
let dragging = false
let lastPointer = { x: 0, y: 0, time: 0 } // position précédente du pointeur
let velocity = { x: 0, y: 0 }              // vitesse calculée pour inertie
let raf = null                              // id du requestAnimationFrame

/*
  PARAMÈTRES DE RÉGLAGE
*/
const sensitivity = 0.3    // combien de degrés par pixel de mouvement
const friction = 0.95      // inertie : 1 = sans perte, plus bas = ralentit vite
const minVelocity = 0.02   // seuil sous lequel on arrête l’inertie
const enableInertia = true // true = inertie activée, false = rotation immédiate sans suite

/*
  Applique la transformation CSS à la box
*/
function applyTransform() {
  angleX.value = Math.round(angleX.value)
  angleY.value = Math.round(angleY.value)
  angleZ.value = Math.round(angleZ.value)

  box.value.style.transform = `rotateX(${angleX.value}deg) rotateY(${angleY.value}deg) rotateZ(${angleZ.value}deg)`
}

function applySize() {
  updateCssVar('--height', `${props.size * (100 / 3)}px`, scene.value)
  updateCssVar('--width', `${props.size * 50}px`, scene.value)
  updateCssVar('--depth', `${props.size * 10}px`, scene.value)
}
function applyColor() {
  frontFace.value.style.setProperty('background', `${props.compilation.color2}`)
  backFace.value.style.setProperty('background', `linear-gradient(to top, ${props.compilation.color1}, ${props.compilation.color2})`)
  rightFace.value.style.setProperty('background', `linear-gradient(to top, ${props.compilation.color1}, ${props.compilation.color2})`)
  leftFace.value.style.setProperty('background', `linear-gradient(to top, ${props.compilation.color1}, ${props.compilation.color2})`)
  topFace.value.style.setProperty('background', `linear-gradient(to top, ${props.compilation.color2}, ${props.compilation.color2})`)
  bottomFace.value.style.setProperty('background', `${props.compilation.color1}`)
}

/*
  Fonction utilitaire : place la box directement à une rotation donnée
*/
function applyRotation() {
  angleX.value = props.position.x
  angleY.value = props.position.y
  angleZ.value = props.position.z
  applyTransform()

  box.value.style.setProperty('transition', 'transform 800ms ease-in-out')
  setTimeout(() => {
    box.value.style.setProperty('transition', 'transform 120ms linear')
  }, 120)
}

/*
  Boucle d’inertie après un drag
  - reprend la vitesse calculée à la release
  - diminue petit à petit avec friction
  - stoppe quand vitesse < minVelocity
*/
function tickInertia() {
  if (!enableInertia) return

  // appliquer la friction
  velocity.x *= friction
  velocity.y *= friction

  // appliquer au box
  angleX.value += velocity.y
  angleY.value += velocity.x

  // clamp angleX pour éviter de retourner la box trop loin
  angleX.value = Math.max(-80, Math.min(80, angleX.value))

  applyTransform()

  // continuer tant qu’il reste du mouvement
  if (Math.abs(velocity.x) > minVelocity || Math.abs(velocity.y) > minVelocity) {
    raf = requestAnimationFrame(tickInertia)
  } else {
    raf = null
  }
}

/*
  Mise en place des listeners une fois le composant monté
*/
onMounted(() => {
  applySize()
  applyColor()
  applyTransform()

  // pointerdown = début du drag
  const down = (ev) => {
    ev.preventDefault()
    dragging = true
    scene.value.setPointerCapture(ev.pointerId)
    lastPointer = { x: ev.clientX, y: ev.clientY, time: performance.now() }
    velocity = { x: 0, y: 0 }
    if (raf) { cancelAnimationFrame(raf); raf = null }
  }

  // pointermove = on bouge la souris ou le doigt
  const move = (ev) => {
    if (!dragging) return
    ev.preventDefault()
    const now = performance.now()
    const dx = ev.clientX - lastPointer.x
    const dy = ev.clientY - lastPointer.y

    // mise à jour des angles
    angleY.value += dx * sensitivity
    angleX.value -= dy * sensitivity
    angleX.value = Math.max(-80, Math.min(80, angleX.value))

    // calcul vitesse pour inertie
    const dt = Math.max(1, now - lastPointer.time)
    velocity.x = (dx / dt) * 16 * sensitivity
    velocity.y = (-dy / dt) * 16 * sensitivity

    lastPointer = { x: ev.clientX, y: ev.clientY, time: now }

    applyTransform()
  }

  // pointerup = fin du drag
  const end = (ev) => {
    if (!dragging) return
    dragging = false
    try { scene.value.releasePointerCapture(ev.pointerId) } catch { }
    if (enableInertia && (Math.abs(velocity.x) > minVelocity || Math.abs(velocity.y) > minVelocity)) {
      if (!raf) raf = requestAnimationFrame(tickInertia)
    }
  }

  // attach des events
  scene.value.addEventListener('pointerdown', down)
  scene.value.addEventListener('pointermove', move)
  scene.value.addEventListener('pointerup', end)
  scene.value.addEventListener('pointercancel', end)
  scene.value.addEventListener('pointerleave', end)

  // cleanup au démontage
  onBeforeUnmount(() => {
    cancelAnimationFrame(raf)
  })
})

watch(() => props.position, () => {
  applyRotation()
}, { deep: true })

watch(() => props.compilation, () => {
  applyColor()
}, { deep: true })

watch(() => props.size, () => {
  applySize()
})
</script>


<style>
html,
body {
  height: 100%;
  margin: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: system-ui, Segoe UI, Roboto, Helvetica, Arial;
  -webkit-font-smoothing: antialiased;
}

/* scène avec perspective */
.scene {
  height: var(--height);
  width: var(--width);
  perspective: 1000px;
  touch-action: none;
  /* essentiel pour empêcher le scroll pendant le drag */
  /* height: 20px; */
  transition: all .5s;
}

/* l'objet 3D (box simple) */
.box {
  width: var(--width);
  height: var(--height);
  position: relative;
  transform-style: preserve-3d;
  transition: transform 120ms linear;
  transition: height 120ms linear;
  /* légère smoothing quand on lâche */
  margin: auto;
  user-select: none;
  cursor: grab;
}

.box:active {
  cursor: grabbing;
}

/* faces du box */
.face {
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 20px;
  color: white;
  font-weight: 600;
  backface-visibility: hidden;
  /* border: 2px solid rgba(255, 255, 255, 0.06); */
  box-sizing: border-box;
  transform-origin: top right;
}

.front,
.back {
  width: 100%;
  height: 100%;
}

.face.top,
.face.bottom {
  width: var(--width);
  height: var(--depth);
}

.face.left,
.face.right {
  width: var(--depth);
  height: var(--height);
}

.face.front {
  transform: translateX(0px) translateY(0px) translateZ(0px);
}

.face.back {
  transform: rotateY(180deg) translateX(var(--width)) translateY(0px) translateZ(var(--depth));
}

.face.right {
  transform: rotateY(90deg) translateX(0px) translateY(0px) translateZ(var(--width));
  transform-origin: top left;
}

.face.left {
  transform: rotateY(-90deg) translateX(0) translateY(0px) translateZ(var(--depth));
}

.face.top {
  transform: rotateX(90deg) translateX(0px) translateY(calc(var(--depth) * -1)) translateZ(0px);
}

.face.top>* {
  @apply rotate-180;
}

.face.bottom {
  transform: rotateX(-90deg) translateX(0) translateY(0px) translateZ(calc(var(--height)));
}

.cover {
  height: 100%;
  width: 100%;
  object-fit: cover;
}

.logo {
  filter: drop-shadow(2px 2px 0 rgb(0 0 0 / 0.8));
}
</style>