/**
 * 3D Body Scan Viewer with Auto-Rotation
 * Displays STL/OBJ/GLB body scans with spinning animation
 */
import { useEffect, useRef, useState } from 'react';

// Three.js is loaded dynamically
let THREE: any = null;
let STLLoader: any = null;
let OBJLoader: any = null;
let GLTFLoader: any = null;

type BodyScanViewerProps = {
  scanUrl: string | null;
  autoRotate?: boolean;
  rotationSpeed?: number;
};

export default function BodyScanViewer({
  scanUrl,
  autoRotate = true,
  rotationSpeed = 0.01,
}: BodyScanViewerProps) {
  const containerRef = useRef<HTMLDivElement>(null);
  const rendererRef = useRef<any>(null);
  const sceneRef = useRef<any>(null);
  const cameraRef = useRef<any>(null);
  const meshRef = useRef<any>(null);
  const animationFrameRef = useRef<number>(0);
  const autoRotateRef = useRef(autoRotate);
  const rotationSpeedRef = useRef(rotationSpeed);
  
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [threeLoaded, setThreeLoaded] = useState(false);
  
  // Keep refs in sync with props
  useEffect(() => {
    autoRotateRef.current = autoRotate;
    rotationSpeedRef.current = rotationSpeed;
  }, [autoRotate, rotationSpeed]);

  // Load Three.js dynamically
  useEffect(() => {
    const loadThree = async () => {
      try {
        const threeModule = await import('three');
        THREE = threeModule;
        
        const { STLLoader: STL } = await import('three/examples/jsm/loaders/STLLoader.js');
        STLLoader = STL;
        
        const { OBJLoader: OBJ } = await import('three/examples/jsm/loaders/OBJLoader.js');
        OBJLoader = OBJ;
        
        const { GLTFLoader: GLTF } = await import('three/examples/jsm/loaders/GLTFLoader.js');
        GLTFLoader = GLTF;
        
        setThreeLoaded(true);
      } catch (e) {
        console.error('Failed to load Three.js:', e);
        setError('Failed to load 3D viewer');
      }
    };
    
    loadThree();
    
    return () => {
      if (animationFrameRef.current) {
        cancelAnimationFrame(animationFrameRef.current);
      }
      if (rendererRef.current) {
        rendererRef.current.dispose();
      }
    };
  }, []);

  // Initialize scene when Three.js is loaded
  useEffect(() => {
    if (!threeLoaded || !containerRef.current || rendererRef.current) return;
    
    const container = containerRef.current;
    const width = container.clientWidth;
    const height = container.clientHeight;
    
    // Scene
    const scene = new THREE.Scene();
    scene.background = new THREE.Color(0x1a1a2e);
    sceneRef.current = scene;
    
    // Camera
    const camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 10000);
    camera.position.set(0, 50, 500);
    camera.lookAt(0, 0, 0);
    cameraRef.current = camera;
    
    // Renderer - fills container
    const renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setSize(width, height);
    renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
    container.appendChild(renderer.domElement);
    rendererRef.current = renderer;
    
    // Lighting - multiple lights for good coverage during rotation
    const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
    scene.add(ambientLight);
    
    const frontLight = new THREE.DirectionalLight(0xffffff, 0.7);
    frontLight.position.set(0, 100, 400);
    scene.add(frontLight);
    
    const backLight = new THREE.DirectionalLight(0x88ccff, 0.5);
    backLight.position.set(0, 100, -400);
    scene.add(backLight);
    
    const topLight = new THREE.DirectionalLight(0xffffff, 0.4);
    topLight.position.set(0, 400, 0);
    scene.add(topLight);
    
    const sideLight1 = new THREE.DirectionalLight(0xffffff, 0.3);
    sideLight1.position.set(400, 100, 0);
    scene.add(sideLight1);
    
    const sideLight2 = new THREE.DirectionalLight(0xffffff, 0.3);
    sideLight2.position.set(-400, 100, 0);
    scene.add(sideLight2);
    
    // Animation loop - rotate the mesh around its own Y-axis (self-rotation)
    const animate = () => {
      animationFrameRef.current = requestAnimationFrame(animate);
      
      // Self-rotation: rotate around Z axis (the original vertical axis of STL files)
      if (meshRef.current && autoRotateRef.current) {
        meshRef.current.rotation.z += rotationSpeedRef.current;
      }
      
      renderer.render(scene, camera);
    };
    animate();
    
    // Handle resize
    const handleResize = () => {
      if (!container || !renderer || !camera) return;
      const newWidth = container.clientWidth;
      const newHeight = container.clientHeight;
      camera.aspect = newWidth / newHeight;
      camera.updateProjectionMatrix();
      renderer.setSize(newWidth, newHeight);
    };
    
    const resizeObserver = new ResizeObserver(handleResize);
    resizeObserver.observe(container);
    
    return () => {
      resizeObserver.disconnect();
    };
  }, [threeLoaded]);

  // Load mesh when URL changes
  useEffect(() => {
    if (!threeLoaded || !scanUrl || !sceneRef.current) return;
    
    setLoading(true);
    setError(null);
    
    // Clear previous mesh
    if (meshRef.current) {
      sceneRef.current.remove(meshRef.current);
      meshRef.current = null;
    }
    
    // Determine loader based on file extension
    const ext = scanUrl.toLowerCase().split('.').pop() || '';
    
    const onLoad = (result: any) => {
      let mesh: any;
      
      if (ext === 'stl') {
        // STL returns geometry directly - center the geometry itself
        const geometry = result;
        geometry.center(); // This centers the geometry at origin
        
        const material = new THREE.MeshPhongMaterial({
          color: 0xccaa88,
          specular: 0x222222,
          shininess: 50,
          side: THREE.DoubleSide,
        });
        mesh = new THREE.Mesh(geometry, material);
        
        // STL files are typically Z-up, rotate to Y-up
        mesh.rotation.x = -Math.PI / 2;
        
      } else if (ext === 'obj') {
        mesh = result;
        mesh.traverse((child: any) => {
          if (child.isMesh) {
            child.material = new THREE.MeshPhongMaterial({
              color: 0xccaa88,
              specular: 0x222222,
              shininess: 50,
              side: THREE.DoubleSide,
            });
          }
        });
      } else if (ext === 'glb' || ext === 'gltf') {
        mesh = result.scene;
      }
      
      if (mesh) {
        // Get bounding box to scale appropriately
        const box = new THREE.Box3().setFromObject(mesh);
        const size = box.getSize(new THREE.Vector3());
        const center = box.getCenter(new THREE.Vector3());
        
        // For non-STL, center the mesh position
        if (ext !== 'stl') {
          mesh.position.sub(center);
        }
        
        // Scale to fit in view (smaller = further away appearance)
        const maxDim = Math.max(size.x, size.y, size.z);
        const targetSize = 250;
        const scale = targetSize / maxDim;
        mesh.scale.multiplyScalar(scale);
        
        // Position at scene center
        mesh.position.set(0, 0, 0);
        
        sceneRef.current.add(mesh);
        meshRef.current = mesh;
        
        // Position camera to see the whole model
        cameraRef.current.position.set(0, 80, 400);
        cameraRef.current.lookAt(0, 0, 0);
      }
      
      setLoading(false);
    };
    
    const onError = (err: any) => {
      console.error('Failed to load mesh:', err);
      setError('Failed to load 3D model');
      setLoading(false);
    };
    
    // Load based on extension
    if (ext === 'stl') {
      const loader = new STLLoader();
      loader.load(scanUrl, onLoad, undefined, onError);
    } else if (ext === 'obj') {
      const loader = new OBJLoader();
      loader.load(scanUrl, onLoad, undefined, onError);
    } else if (ext === 'glb' || ext === 'gltf') {
      const loader = new GLTFLoader();
      loader.load(scanUrl, onLoad, undefined, onError);
    } else if (ext === 'ply') {
      // PLY not directly supported, show message
      setError('PLY format preview not supported');
      setLoading(false);
    } else {
      setError(`Unsupported format: ${ext}`);
      setLoading(false);
    }
  }, [threeLoaded, scanUrl]);

  if (!threeLoaded) {
    return (
      <div className="body-scan-viewer-loading">
        <div className="spinner"></div>
        <p>Loading 3D viewer...</p>
      </div>
    );
  }

  return (
    <div className="body-scan-viewer-container">
      <div 
        ref={containerRef} 
        className="body-scan-viewer-canvas"
      />
      
      {loading && (
        <div className="body-scan-viewer-overlay">
          <div className="spinner"></div>
          <p>Loading model...</p>
        </div>
      )}
      
      {error && (
        <div className="body-scan-viewer-overlay error">
          <p>{error}</p>
        </div>
      )}
      
      {!scanUrl && !loading && (
        <div className="body-scan-viewer-overlay placeholder">
          <p>No scan loaded</p>
        </div>
      )}
    </div>
  );
}
