<template>
  <div class="relative w-full aspect-square bg-[#0b0c10] border border-border/40 rounded-3xl overflow-hidden shadow-2xl flex items-center justify-center select-none group">
    <!-- Active Image Frame -->
    <img 
      v-if="isPreloaded && preloadedImages.length > 0"
      :src="preloadedImages[currentFrameIndex].src" 
      class="w-full h-full object-contain pointer-events-none" 
      alt="3D Scan 360 View"
    />

    <!-- Preloading overlay -->
    <div v-if="!isPreloaded" class="absolute inset-0 flex flex-col items-center justify-center bg-[#0b0c10]/95 backdrop-blur-sm z-10 space-y-4">
      <RefreshCw class="w-8 h-8 text-primary animate-spin" />
      <div class="text-center space-y-1">
        <p class="text-xs uppercase font-black tracking-widest text-primary/80">Loading interactive 3D model</p>
        <p class="text-[10px] text-muted-foreground">{{ loadedFramesCount }} / {{ totalFramesCount }} frames ({{ Math.round((loadedFramesCount / totalFramesCount) * 100) }}%)</p>
      </div>
      <div class="w-48 h-1 bg-white/5 rounded-full overflow-hidden border border-white/5">
        <div class="h-full bg-primary transition-all duration-300 shadow-glow" :style="{ width: `${(loadedFramesCount / totalFramesCount) * 100}%` }" />
      </div>
    </div>

    <!-- Drag Rotation Hotspot Overlay -->
    <div 
      v-if="isPreloaded" 
      class="absolute inset-0 cursor-grab active:cursor-grabbing z-20 flex items-center justify-center"
      @mousedown="onMouseDown"
      @touchstart="onTouchStart"
    >
      <!-- Interaction Hint Overlay (fades out after interactive movement) -->
      <div 
        v-if="showHint" 
        class="absolute bottom-6 left-1/2 -translate-x-1/2 bg-black/80 backdrop-blur-md px-4 py-2 rounded-xl border border-white/10 text-[10px] font-bold text-muted-foreground flex items-center gap-2 pointer-events-none transition-opacity duration-1000"
        :class="{ 'opacity-0': isInteracted }"
      >
        <MoveHorizontal class="w-3.5 h-3.5 text-primary" />
        <span>{{ projectMetadata?.view_mode === 'spherical' ? 'Drag in any direction to rotate in 3D' : 'Drag horizontally to rotate 3D scan' }}</span>
      </div>
    </div>

    <!-- Range Scrub Slider Control (Sleek overlay at the bottom) -->
    <div 
      v-if="isPreloaded"
      class="absolute bottom-4 left-4 right-4 z-30 opacity-0 group-hover:opacity-100 focus-within:opacity-100 transition-opacity duration-300 bg-black/60 backdrop-blur-md border border-white/10 rounded-2xl p-4 space-y-2 flex flex-col"
    >
      <div class="flex justify-between text-[9px] font-bold uppercase tracking-wider text-muted-foreground/80">
        <span>Manual Rotate</span>
        <span>Frame {{ currentFrameIndex + 1 }} / {{ totalFramesCount }}</span>
      </div>
      <input 
        type="range" 
        :value="currentFrameIndex"
        @input="onSliderInput"
        :min="0"
        :max="totalFramesCount - 1"
        class="w-full accent-primary bg-white/10 cursor-pointer h-1 rounded-full outline-none"
      />
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, watch } from "vue";
import { RefreshCw, MoveHorizontal } from "lucide-vue-next";

const props = withDefaults(
  defineProps<{
    folderName: string;
    framesCount?: number;
    resourcesBaseUrl: string;
  }>(),
  {
    framesCount: 48
  }
);

const isPreloaded = ref(false);
const loadedFramesCount = ref(0);
const preloadedImages = ref<HTMLImageElement[]>([]);
const currentFrameIndex = ref(0);
const totalFramesCount = ref(props.framesCount);

const showHint = ref(true);
const isInteracted = ref(false);

// Drag & Inertia state
let isDragging = false;
let startX = 0;
let startY = 0;
let lastX = 0;
let lastY = 0;
let velocity = 0; // For 1D turntable velocity
let velocityX = 0; // For spherical horizontal velocity
let velocityY = 0; // For spherical vertical velocity
let animationId: number | null = null;

// 3D Spherical angles
const projectMetadata = ref<any>(null);
let azimuth = 0;
let elevation = 0.38; // Default elevation (approx 21.8 degrees)
const friction = 0.94; // Deceleration factor
const dragSensitivity = 0.08;

// Match current angles to the closest frame vector
const matchSphericalFrame = () => {
  if (!projectMetadata.value || !projectMetadata.value.frames) return;

  const cosEl = Math.cos(elevation);
  const x = cosEl * Math.sin(azimuth);
  const y = -cosEl * Math.cos(azimuth);
  const z = Math.sin(elevation);

  let bestIndex = 0;
  let maxDot = -Infinity;

  for (let i = 0; i < projectMetadata.value.frames.length; i++) {
    const fv = projectMetadata.value.frames[i].vector;
    if (!fv) continue;
    const dot = x * fv[0] + y * fv[1] + z * fv[2];
    if (dot > maxDot) {
      maxDot = dot;
      bestIndex = i;
    }
  }
  currentFrameIndex.value = bestIndex;
};

// Physics loop for smooth inertia
const updatePhysics = () => {
  if (!isDragging) {
    if (projectMetadata.value && projectMetadata.value.view_mode === 'spherical') {
      velocityX *= friction;
      velocityY *= friction;

      if (Math.abs(velocityX) > 0.0001 || Math.abs(velocityY) > 0.0001) {
        azimuth += velocityX;
        elevation += velocityY;
        elevation = Math.max(-1.48, Math.min(1.48, elevation));
        matchSphericalFrame();
      }
    } else {
      velocity *= friction;
      if (Math.abs(velocity) > 0.05) {
        let nextFrame = currentFrameIndex.value - velocity;
        currentFrameIndex.value = Math.floor(((nextFrame % totalFramesCount.value) + totalFramesCount.value) % totalFramesCount.value);
      }
    }
  }
  animationId = requestAnimationFrame(updatePhysics);
};

// Preload Helper
const startPreloading = async () => {
  isPreloaded.value = false;
  loadedFramesCount.value = 0;
  preloadedImages.value = [];
  currentFrameIndex.value = 0;
  projectMetadata.value = null;
  totalFramesCount.value = props.framesCount;

  let meta: any = null;
  const metadataUrl = `${props.resourcesBaseUrl}/uploads/scans/${props.folderName}/metadata.json`;

  try {
    const res = await fetch(metadataUrl);
    if (res.ok) {
      meta = await res.json();
    }
  } catch (e) {
    console.log("No metadata.json found or failed to load. Falling back to default turntable.", e);
  }

  if (meta && meta.frames && meta.frames.length) {
    projectMetadata.value = meta;
    totalFramesCount.value = meta.frames.length;

    // Sync angles from the first frame's vector
    if (meta.frames[0] && meta.frames[0].vector) {
      const vec = meta.frames[0].vector;
      elevation = Math.asin(vec[2]);
      azimuth = Math.atan2(vec[0], -vec[1]);
    } else {
      azimuth = 0;
      elevation = 0.38;
    }

    let loaded = 0;
    const list: HTMLImageElement[] = [];

    meta.frames.forEach((frame: any, index: number) => {
      const img = new Image();
      img.src = `${props.resourcesBaseUrl}/uploads/scans/${props.folderName}/${frame.file}`;

      img.onload = () => {
        loaded++;
        loadedFramesCount.value = loaded;
        if (loaded === totalFramesCount.value) {
          preloadedImages.value = list;
          isPreloaded.value = true;
          startPhysicsLoop();
        }
      };

      img.onerror = () => {
        loaded++;
        loadedFramesCount.value = loaded;
        if (loaded === totalFramesCount.value) {
          preloadedImages.value = list;
          isPreloaded.value = true;
          startPhysicsLoop();
        }
      };

      list.push(img);
    });
  } else {
    // Fallback: 1D horizontal turntable
    if (meta) {
      projectMetadata.value = meta;
      if (meta.frames_count) {
        totalFramesCount.value = meta.frames_count;
      }
    }
    let loaded = 0;
    const list: HTMLImageElement[] = [];

    for (let i = 0; i < totalFramesCount.value; i++) {
      const img = new Image();
      const frameNum = String(i).padStart(3, '0');
      img.src = `${props.resourcesBaseUrl}/uploads/scans/${props.folderName}/${frameNum}.webp`;

      img.onload = () => {
        loaded++;
        loadedFramesCount.value = loaded;
        if (loaded === totalFramesCount.value) {
          preloadedImages.value = list;
          isPreloaded.value = true;
          startPhysicsLoop();
        }
      };

      img.onerror = () => {
        loaded++;
        loadedFramesCount.value = loaded;
        if (loaded === totalFramesCount.value) {
          preloadedImages.value = list;
          isPreloaded.value = true;
          startPhysicsLoop();
        }
      };

      list.push(img);
    }
  }
};

const startPhysicsLoop = () => {
  // Fade hint out after 4 seconds if no interaction occurs
  setTimeout(() => {
    if (!isInteracted.value) {
      isInteracted.value = true;
    }
  }, 4000);

  if (!animationId) {
    updatePhysics();
  }
};

// Drag Interactions
const startDrag = (clientX: number, clientY: number) => {
  isDragging = true;
  isInteracted.value = true;
  showHint.value = false;

  startX = clientX;
  startY = clientY;
  lastX = clientX;
  lastY = clientY;

  velocity = 0;
  velocityX = 0;
  velocityY = 0;
};

const moveDrag = (clientX: number, clientY: number) => {
  if (!isDragging) return;

  if (projectMetadata.value && projectMetadata.value.view_mode === 'spherical' && projectMetadata.value.frames && projectMetadata.value.frames.length > 0 && projectMetadata.value.frames[0].vector) {
    const deltaX = clientX - lastX;
    const deltaY = clientY - lastY;
    lastX = clientX;
    lastY = clientY;

    // Calculate velocity for momentum on release
    velocityX = -deltaX * 0.005;
    velocityY = deltaY * 0.005;

    azimuth += velocityX;
    elevation += velocityY;
    elevation = Math.max(-1.48, Math.min(1.48, elevation)); // Clamp elevation

    matchSphericalFrame();
  } else {
    const deltaX = clientX - lastX;
    lastX = clientX;

    velocity = deltaX * dragSensitivity;

    const frameShift = Math.round(deltaX * 0.2);
    let targetIndex = currentFrameIndex.value - frameShift;
    currentFrameIndex.value = ((targetIndex % totalFramesCount.value) + totalFramesCount.value) % totalFramesCount.value;
  }
};

// Mouse Events
const onMouseDown = (e: MouseEvent) => {
  if (e.button !== 0) return; // Only left click
  startDrag(e.clientX, e.clientY);

  window.addEventListener("mousemove", onMouseMove);
  window.addEventListener("mouseup", onMouseUp);
};

const onMouseMove = (e: MouseEvent) => {
  moveDrag(e.clientX, e.clientY);
};

const onMouseUp = () => {
  isDragging = false;
  window.removeEventListener("mousemove", onMouseMove);
  window.removeEventListener("mouseup", onMouseUp);
};

// Touch Events
const onTouchStart = (e: TouchEvent) => {
  if (e.touches.length !== 1) return;
  startDrag(e.touches[0].clientX, e.touches[0].clientY);

  window.addEventListener("touchmove", onTouchMove, { passive: true });
  window.addEventListener("touchend", onTouchEnd);
};

const onTouchMove = (e: TouchEvent) => {
  if (e.touches.length !== 1) return;
  moveDrag(e.touches[0].clientX, e.touches[0].clientY);
};

const onTouchEnd = () => {
  isDragging = false;
  window.removeEventListener("touchmove", onTouchMove);
  window.removeEventListener("touchend", onTouchEnd);
};

// Slider Manual Control
const onSliderInput = (e: Event) => {
  const target = e.target as HTMLInputElement;
  const frameVal = parseInt(target.value);
  currentFrameIndex.value = frameVal;

  velocity = 0;
  velocityX = 0;
  velocityY = 0;

  // Sync angles
  if (projectMetadata.value && projectMetadata.value.view_mode === 'spherical' && projectMetadata.value.frames && projectMetadata.value.frames[frameVal] && projectMetadata.value.frames[frameVal].vector) {
    const vec = projectMetadata.value.frames[frameVal].vector;
    elevation = Math.asin(vec[2]);
    azimuth = Math.atan2(vec[0], -vec[1]);
  }
};

// Watch for folder change to trigger reload
watch(() => props.folderName, () => {
  startPreloading();
}, { immediate: true });

onBeforeUnmount(() => {
  if (animationId) {
    cancelAnimationFrame(animationId);
  }
  window.removeEventListener("mousemove", onMouseMove);
  window.removeEventListener("mouseup", onMouseUp);
  window.removeEventListener("touchmove", onTouchMove);
  window.removeEventListener("touchend", onTouchEnd);
});
</script>

<style scoped>
input[type="range"] {
  -webkit-appearance: none;
}
input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  height: 12px;
  width: 12px;
  border-radius: 50%;
  background: var(--primary, #66fcf1);
  box-shadow: 0 0 6px rgba(102, 252, 241, 0.8);
  cursor: pointer;
  margin-top: -4px;
}
input[type="range"]::-webkit-slider-runnable-track {
  width: 100%;
  height: 4px;
  background: rgba(255, 255, 255, 0.1);
  border-radius: 2px;
}
</style>
