<template>
  <article class="box box-scene z-10" ref="scene">
    <div ref="domBox" class="box-object" :class="{ 'is-draggable': isDraggable }">
      <div class="face front relative" ref="frontFace">
        <img v-if="isCompilation" class="cover absolute" :src="`/${box.id}/${box.activeSide}/cover.jpg`" alt="" />
        <div v-else class="size-full flex flex-col justify-center items-center text-7xl text-black"
          v-html="box.description" />
        <CinemaScreen />
      </div>
      <div class="face back flex flex-row flex-wrap items-start p-4 overflow-hidden"
        :class="{ 'overflow-y-scroll': !isCompilation }" ref="backFace">
        <!-- <li class="list-none text-xxs w-1/2 flex flex-row"
          v-for="track in dataStore.getTracksByboxId(box.id, box.activeSide)" :key="track.id" :track="track">
          <span class="text-slate-700" v-if="isNotManifesto"> {{ track.order }}. </span>
          <p class="text-left text-slate-700">
            <i class="text-slate-950">
              {{ track.title }}
            </i>
            <br />
            {{ track.artist.name }}
          </p>
        </li> -->
      </div>
      <div class="face right" ref="rightFace" />
      <div class="face left" ref="leftFace" />
      <div class="face top" ref="topFace">
        <template v-if="isCompilation">
          <img class="logo h-full p-3" src="/logo.svg" alt="" />
          <img class="absolute block h-10" style="left: 5%" :src="`/${box.id}/${box.activeSide}/title.svg`" alt="" />
        </template>
        <template v-if="box.type === 'playlist'">
          <span class="absolute block h-1/2 right-6 text-black"> ♠♦♣♥</span>
          <span class="absolute block h-1/2 text-black" style="left: 5%">
            {{ box.name }}
          </span>
        </template>
      </div>
      <div class="face bottom" ref="bottomFace" />
    </div>
    <slot></slot>
  </article>
</template>

<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, watch, computed } from 'vue'
import type { Box } from '~~/types'
import { useDataStore } from '~/store/data'

interface Props {
  box: Box
}

const props = withDefaults(defineProps<Props>(), {})

const { $isMobile } = useNuxtApp()

const dataStore = useDataStore()
const isDraggable = computed(() => !['box-list', 'box-hidden'].includes(props.box.state))
const isNotManifesto = computed(() => !props.box.id.startsWith('ES00'))
const isCompilation = computed(() => props.box.type === 'compilation')

// --- Réfs ---
const scene = ref<HTMLElement>()
const domBox = ref<HTMLElement>()
const frontFace = ref<HTMLElement>()
const backFace = ref<HTMLElement>()
const rightFace = ref<HTMLElement>()
const leftFace = ref<HTMLElement>()
const topFace = ref<HTMLElement>()
const bottomFace = ref<HTMLElement>()

// --- Angles ---
const rotateX = ref(0)
const rotateY = ref(0)
const rotateZ = ref(0)

// --- Drag + inertie ---
let dragging = false
let lastPointer = { x: 0, y: 0, time: 0 }
let velocity = { x: 0, y: 0 }
let raf: number | null = null

const sensitivity = $isMobile ? 0.5 : 0.15
const friction = 0.95
const minVelocity = 0.02
const enableInertia = true

// --- Transformations ---
function applyTransform(duration = 0.5) {
  if (!domBox.value) return
  rotateX.value = Math.round(rotateX.value)
  rotateY.value = Math.round(rotateY.value)
  rotateZ.value = Math.round(rotateZ.value)

  domBox.value.style.transition = `transform ${duration}s ease`
  domBox.value.style.transform = `rotateX(${rotateX.value}deg) rotateY(${rotateY.value}deg) rotateZ(${rotateZ.value}deg)`
}

// --- Gestion BoxState ---
function applyBoxState() {
  switch (props.box.state) {
    case 'box-list':
      rotateX.value = 76
      rotateY.value = 0
      rotateZ.value = 150
      break
    case 'box-selected':
      rotateX.value = -20
      rotateY.value = 20
      rotateZ.value = 0
      break
    case 'box-hidden':
      rotateX.value = 76
      rotateY.value = 0
      rotateZ.value = 150
      break
  }
  applyTransform(0.8) // transition fluide
}

// --- Couleurs ---
function applyColor() {
  if (!frontFace.value || !backFace.value || !leftFace.value || !topFace.value || !bottomFace.value)
    return

  frontFace.value.style.background = props.box.color2
  backFace.value.style.background = `linear-gradient(to top, ${props.box.color1}, ${props.box.color2})`
  leftFace.value.style.background = `linear-gradient(to top, ${props.box.color1}, ${props.box.color2})`
  rightFace.value.style.background = `linear-gradient(to top, ${props.box.color1}, ${props.box.color2})`
  topFace.value.style.background = `linear-gradient(to top, ${props.box.color2}, ${props.box.color2})`
  bottomFace.value.style.background = props.box.color1
}

// --- Rotation complète ---
function rotateBox() {
  if (!domBox.value) return

  rotateX.value = -20


  rotateY.value = rotateY.value === 20 ? 380 : 20

  applyTransform(0.8)
}

// --- Inertie ---
function tickInertia() {
  if (!enableInertia) return

  velocity.x *= friction
  velocity.y *= friction

  rotateX.value += velocity.y
  rotateY.value += velocity.x
  rotateX.value = Math.max(-80, Math.min(80, rotateX.value))

  applyTransform(0.05) // court duration pour inertie fluide

  if (Math.abs(velocity.x) > minVelocity || Math.abs(velocity.y) > minVelocity) {
    raf = requestAnimationFrame(tickInertia)
  } else {
    raf = null
  }
}

// --- Pointer events ---
let listenersAttached = false

const down = (ev: PointerEvent) => {
  ev.preventDefault()
  dragging = true
  domBox.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
  }
}

const move = (ev: PointerEvent) => {
  if (!dragging) return
  ev.preventDefault()
  const now = performance.now()
  const dx = ev.clientX - lastPointer.x
  const dy = ev.clientY - lastPointer.y
  const dt = Math.max(1, now - lastPointer.time)

  rotateY.value += dx * sensitivity
  rotateX.value -= dy * sensitivity
  rotateX.value = Math.max(-80, Math.min(80, rotateX.value))

  velocity.x = (dx / dt) * 16 * sensitivity
  velocity.y = (-dy / dt) * 16 * sensitivity

  lastPointer = { x: ev.clientX, y: ev.clientY, time: now }
  applyTransform(0) // immédiat pendant drag
}

const end = (ev: PointerEvent) => {
  if (!dragging) return
  dragging = false
  try {
    domBox.value?.releasePointerCapture(ev.pointerId)
  } catch { }
  if (enableInertia && (Math.abs(velocity.x) > minVelocity || Math.abs(velocity.y) > minVelocity)) {
    if (!raf) raf = requestAnimationFrame(tickInertia)
  }
}

function addListeners() {
  if (!domBox.value || listenersAttached) return
  domBox.value.addEventListener('pointerdown', down)
  domBox.value.addEventListener('pointermove', move)
  domBox.value.addEventListener('pointerup', end)
  domBox.value.addEventListener('pointercancel', end)
  domBox.value.addEventListener('pointerleave', end)
  listenersAttached = true
}

function removeListeners() {
  if (!domBox.value || !listenersAttached) return
  domBox.value.removeEventListener('pointerdown', down)
  domBox.value.removeEventListener('pointermove', move)
  domBox.value.removeEventListener('pointerup', end)
  domBox.value.removeEventListener('pointercancel', end)
  domBox.value.removeEventListener('pointerleave', end)
  listenersAttached = false
}

onMounted(() => {
  applyColor()
  applyBoxState()
  if (isDraggable.value) addListeners()
})

onBeforeUnmount(() => {
  cancelAnimationFrame(raf!)
  removeListeners()
})

// --- Watchers ---
watch(
  () => props.box.activeSide,
  () => rotateBox()
)
watch(
  () => props.box.state,
  () => applyBoxState()
)
watch(
  () => props.box,
  () => applyColor(),
  { deep: true }
)
watch(
  isDraggable,
  (enabled) => (enabled ? addListeners() : removeListeners())
)
</script>

<style lang="scss" scoped>
.box {
  --size: 7px;
  --height: calc(var(--size) * (100 / 3));
  --width: calc(var(--size) * 50);
  --depth: calc(var(--size) * 10);
  transition:
    height 0.5s ease,
    opacity 0.5s ease;

  &.box-list {
    height: calc(var(--size) * 20);
    @apply hover:scale-105 hover:z-20 focus-visible:scale-105 focus-visible:z-20 focus-visible:outline-none;
    transition: all 0.5s ease;
    will-change: transform;
  }

  &.box-selected {
    height: calc(var(--size) * 34);
    padding-top: 80px;
  }

  &-scene {
    perspective: 2000px;
  }

  &.box-hidden {
    height: 0;
    opacity: 0;
    z-index: 0;
  }

  &-object {
    width: var(--width);
    height: var(--height);
    position: relative;
    transform-style: preserve-3d;
    margin: auto;
    user-select: none;

    .box-list & {
      cursor: pointer;
    }

    .box-selected & {
      cursor: grab;
    }

    &:active {
      cursor: grabbing;
    }
  }

  .face {
    position: absolute;
    color: white;
    font-weight: 600;
    backface-visibility: hidden;
    box-sizing: border-box;
    // border: 1px solid black;
  }

  .front,
  .back {
    width: 100%;
    height: 100%;
    filter: brightness(1.1);
  }

  .face.top,
  .face.bottom {
    width: var(--width);
    height: var(--depth);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 20px;
  }

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

  .face.front {
    transform: translateX(0) translateY(0) translateZ(var(--depth));
  }

  .face.back {
    transform: rotateY(180deg) translateX(0) translateY(0) translateZ(0);
  }

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

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

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

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

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

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

  /* Deck fade in/out purely in CSS */
  .box-page {
    opacity: 0;
    transition: opacity 0.25s ease;
    pointer-events: none;
  }

  &.box-selected .box-page {
    opacity: 1;
    pointer-events: auto;
  }

  :deep(.indice) {
    @apply text-xl p-2 relative bg-black/50 rounded-full backdrop-blur-md;
  }
}
</style>
