/**
 * 3D Brace Model Viewer with Auto-Rotation
 * Displays STL/GLB brace models with spinning animation
 */
import { useEffect, useRef, useState } from 'react';

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

type BraceModelViewerProps = {
  stlUrl?: string | null;
  glbUrl?: string | null;
  autoRotate?: boolean;
  rotationSpeed?: number;
};

export default function BraceModelViewer({
  stlUrl,
  glbUrl,
  autoRotate = true,
  rotationSpeed = 0.005,
}: BraceModelViewerProps) {
  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]);

  // Determine which URL to use (prefer GLB over STL)
  const modelUrl = glbUrl || stlUrl;
  const modelType = glbUrl ? 'glb' : (stlUrl ? 'stl' : null);

  // 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 { 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 with gradient background
    const scene = new THREE.Scene();
    scene.background = new THREE.Color(0x1e1e2e);
    sceneRef.current = scene;
    
    // Camera - positioned to view upright brace from front
    const camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 10000);
    camera.position.set(0, 0, 400);
    camera.lookAt(0, 0, 0);
    cameraRef.current = camera;
    
    // Renderer
    const renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setSize(width, height);
    renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
    renderer.shadowMap.enabled = true;
    renderer.shadowMap.type = THREE.PCFSoftShadowMap;
    renderer.toneMapping = THREE.ACESFilmicToneMapping;
    renderer.toneMappingExposure = 1.0;
    container.appendChild(renderer.domElement);
    rendererRef.current = renderer;
    
    // Improved lighting for skin-like appearance with shadows
    // Soft ambient for base illumination
    scene.add(new THREE.AmbientLight(0xffeedd, 0.4));
    
    // Hemisphere light for natural sky/ground coloring
    const hemiLight = new THREE.HemisphereLight(0xffffff, 0x8d7c6d, 0.5);
    hemiLight.position.set(0, 200, 0);
    scene.add(hemiLight);
    
    // Key light (main light with shadows)
    const keyLight = new THREE.DirectionalLight(0xfff5e8, 1.0);
    keyLight.position.set(150, 200, 300);
    keyLight.castShadow = true;
    keyLight.shadow.mapSize.width = 1024;
    keyLight.shadow.mapSize.height = 1024;
    keyLight.shadow.camera.near = 50;
    keyLight.shadow.camera.far = 1000;
    keyLight.shadow.camera.left = -200;
    keyLight.shadow.camera.right = 200;
    keyLight.shadow.camera.top = 200;
    keyLight.shadow.camera.bottom = -200;
    keyLight.shadow.bias = -0.0005;
    scene.add(keyLight);
    
    // Fill light (softer, opposite side)
    const fillLight = new THREE.DirectionalLight(0xe8f0ff, 0.5);
    fillLight.position.set(-200, 100, -100);
    scene.add(fillLight);
    
    // Rim light (back light for edge definition)
    const rimLight = new THREE.DirectionalLight(0xffffff, 0.4);
    rimLight.position.set(0, 50, -300);
    scene.add(rimLight);
    
    // Bottom fill (subtle, prevents too dark shadows underneath)
    const bottomFill = new THREE.DirectionalLight(0xffe8d0, 0.2);
    bottomFill.position.set(0, -200, 100);
    scene.add(bottomFill);
    
    // Animation loop - rotate around Y axis (lazy susan for Y-up brace)
    const animate = () => {
      animationFrameRef.current = requestAnimationFrame(animate);
      
      if (meshRef.current && autoRotateRef.current) {
        meshRef.current.rotation.y += 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 model when URL changes
  useEffect(() => {
    if (!threeLoaded || !modelUrl || !sceneRef.current) return;
    
    setLoading(true);
    setError(null);
    
    // Clear previous mesh
    if (meshRef.current) {
      sceneRef.current.remove(meshRef.current);
      meshRef.current = null;
    }
    
    const onLoadSTL = (geometry: any) => {
      // Center the geometry
      geometry.center();
      geometry.computeVertexNormals();
      
      // Skin-like material for natural appearance
      const material = new THREE.MeshStandardMaterial({
        color: 0xf5d0c5,  // Warm skin tone (light peach/beige)
        roughness: 0.7,    // Slightly smooth for skin-like appearance
        metalness: 0.0,
        side: THREE.DoubleSide,
        flatShading: false,
      });
      
      const mesh = new THREE.Mesh(geometry, material);
      mesh.castShadow = true;
      mesh.receiveShadow = true;
      
      // Brace template is Y-up, no rotation needed
      
      // Scale to fit
      const box = new THREE.Box3().setFromObject(mesh);
      const size = box.getSize(new THREE.Vector3());
      const maxDim = Math.max(size.x, size.y, size.z);
      const scale = 200 / maxDim;
      mesh.scale.multiplyScalar(scale);
      
      mesh.position.set(0, 0, 0);
      
      sceneRef.current.add(mesh);
      meshRef.current = mesh;
      
      // Position camera - view from front at torso level
      cameraRef.current.position.set(0, 0, 350);
      cameraRef.current.lookAt(0, 0, 0);
      
      setLoading(false);
    };
    
    const onLoadGLB = (gltf: any) => {
      const mesh = gltf.scene;
      
      // Apply skin-like material to all meshes
      mesh.traverse((child: any) => {
        if (child.isMesh) {
          child.material = new THREE.MeshStandardMaterial({
            color: 0xf5d0c5,  // Warm skin tone (light peach/beige)
            roughness: 0.7,    // Slightly smooth for skin-like appearance
            metalness: 0.0,
            side: THREE.DoubleSide,
            flatShading: false,
          });
          child.castShadow = true;
          child.receiveShadow = true;
        }
      });
      
      // Center and scale
      const box = new THREE.Box3().setFromObject(mesh);
      const center = box.getCenter(new THREE.Vector3());
      const size = box.getSize(new THREE.Vector3());
      
      mesh.position.sub(center);
      
      const maxDim = Math.max(size.x, size.y, size.z);
      const scale = 200 / maxDim;
      mesh.scale.multiplyScalar(scale);
      
      mesh.position.set(0, 0, 0);
      
      sceneRef.current.add(mesh);
      meshRef.current = mesh;
      
      // Position camera - view from front at torso level
      cameraRef.current.position.set(0, 0, 350);
      cameraRef.current.lookAt(0, 0, 0);
      
      setLoading(false);
    };
    
    const onError = (err: any) => {
      console.error('Failed to load model:', err);
      setError('Failed to load 3D model');
      setLoading(false);
    };
    
    if (modelType === 'stl') {
      const loader = new STLLoader();
      loader.load(modelUrl, onLoadSTL, undefined, onError);
    } else if (modelType === 'glb') {
      const loader = new GLTFLoader();
      loader.load(modelUrl, onLoadGLB, undefined, onError);
    }
  }, [threeLoaded, modelUrl, modelType]);

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

  return (
    <div className="brace-model-viewer-container">
      <div 
        ref={containerRef} 
        className="brace-model-viewer-canvas"
      />
      
      {loading && (
        <div className="brace-model-viewer-overlay">
          <div className="spinner"></div>
          <p>Loading brace model...</p>
        </div>
      )}
      
      {error && (
        <div className="brace-model-viewer-overlay error">
          <p>{error}</p>
        </div>
      )}
      
      {!modelUrl && !loading && (
        <div className="brace-model-viewer-overlay placeholder">
          <div className="placeholder-icon">🦾</div>
          <p>Generate a brace to see 3D preview</p>
        </div>
      )}
    </div>
  );
}
