import { Suspense, useRef, useState, useEffect } from 'react'
import { Canvas, useThree, useFrame } from '@react-three/fiber'
import { 
  OrbitControls, 
  Environment, 
  Grid, 
  GizmoHelper, 
  GizmoViewport,
  useGLTF,
  useProgress,
  Html,
  PerspectiveCamera,
  Center
} from '@react-three/drei'
import { useStore, Model3D } from '../store/useStore'
import { ViewerControls } from './ViewerControls'
import { ViewerSidebar } from './ViewerSidebar'
import { motion, AnimatePresence } from 'framer-motion'
import * as THREE from 'three'
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader'
import { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader'
import { PLYLoader } from 'three/examples/jsm/loaders/PLYLoader'

function LoadingScreen() {
  const { progress } = useProgress()
  
  return (
    <Html center>
      <div className="flex flex-col items-center gap-4">
        <div className="relative w-20 h-20">
          <svg className="w-20 h-20 transform -rotate-90">
            <circle
              cx="40"
              cy="40"
              r="36"
              stroke="#e2e8f0"
              strokeWidth="8"
              fill="none"
            />
            <circle
              cx="40"
              cy="40"
              r="36"
              stroke="#0ea5e9"
              strokeWidth="8"
              fill="none"
              strokeDasharray={226}
              strokeDashoffset={226 - (226 * progress) / 100}
              strokeLinecap="round"
            />
          </svg>
          <div className="absolute inset-0 flex items-center justify-center">
            <span className="text-sm font-semibold text-surface-700">{Math.round(progress)}%</span>
          </div>
        </div>
        <p className="text-surface-500 text-sm font-medium">Loading model...</p>
      </div>
    </Html>
  )
}

function GLTFModel({ url }: { url: string }) {
  const { scene } = useGLTF(url)
  const modelRef = useRef<THREE.Group>(null)
  const { settings } = useStore()
  
  useEffect(() => {
    if (scene) {
      // Center and scale the model
      const box = new THREE.Box3().setFromObject(scene)
      const size = box.getSize(new THREE.Vector3())
      const maxDim = Math.max(size.x, size.y, size.z)
      const scale = 2 / maxDim
      scene.scale.setScalar(scale)
      
      const center = box.getCenter(new THREE.Vector3())
      scene.position.sub(center.multiplyScalar(scale))
    }
  }, [scene])

  useFrame((_, delta) => {
    if (modelRef.current && settings.autoRotate) {
      modelRef.current.rotation.y += delta * 0.3
    }
  })

  return (
    <group ref={modelRef}>
      <primitive object={scene.clone()} />
    </group>
  )
}

function OBJModel({ url }: { url: string }) {
  const [model, setModel] = useState<THREE.Group | null>(null)
  const { setIsLoading, setLoadingProgress, settings } = useStore()
  const modelRef = useRef<THREE.Group>(null)

  useEffect(() => {
    setIsLoading(true)
    const loader = new OBJLoader()
    
    loader.load(
      url,
      (obj) => {
        // Center and scale
        const box = new THREE.Box3().setFromObject(obj)
        const size = box.getSize(new THREE.Vector3())
        const maxDim = Math.max(size.x, size.y, size.z)
        const scale = 2 / maxDim
        obj.scale.setScalar(scale)
        
        const center = box.getCenter(new THREE.Vector3())
        obj.position.sub(center.multiplyScalar(scale))
        
        // Add default material if none
        obj.traverse((child) => {
          if (child instanceof THREE.Mesh && !child.material) {
            child.material = new THREE.MeshStandardMaterial({ color: '#888888' })
          }
        })
        
        setModel(obj)
        setIsLoading(false)
      },
      (progress) => {
        if (progress.total > 0) {
          setLoadingProgress((progress.loaded / progress.total) * 100)
        }
      },
      (error) => {
        console.error('Error loading OBJ:', error)
        setIsLoading(false)
      }
    )
  }, [url, setIsLoading, setLoadingProgress])

  useFrame((_, delta) => {
    if (modelRef.current && settings.autoRotate) {
      modelRef.current.rotation.y += delta * 0.3
    }
  })

  if (!model) return null

  return (
    <group ref={modelRef}>
      <primitive object={model} />
    </group>
  )
}

function FBXModel({ url }: { url: string }) {
  const [model, setModel] = useState<THREE.Group | null>(null)
  const { setIsLoading, setLoadingProgress, settings } = useStore()
  const modelRef = useRef<THREE.Group>(null)

  useEffect(() => {
    setIsLoading(true)
    const loader = new FBXLoader()
    
    loader.load(
      url,
      (fbx) => {
        // Center and scale
        const box = new THREE.Box3().setFromObject(fbx)
        const size = box.getSize(new THREE.Vector3())
        const maxDim = Math.max(size.x, size.y, size.z)
        const scale = 2 / maxDim
        fbx.scale.setScalar(scale)
        
        const center = box.getCenter(new THREE.Vector3())
        fbx.position.sub(center.multiplyScalar(scale))
        
        setModel(fbx)
        setIsLoading(false)
      },
      (progress) => {
        if (progress.total > 0) {
          setLoadingProgress((progress.loaded / progress.total) * 100)
        }
      },
      (error) => {
        console.error('Error loading FBX:', error)
        setIsLoading(false)
      }
    )
  }, [url, setIsLoading, setLoadingProgress])

  useFrame((_, delta) => {
    if (modelRef.current && settings.autoRotate) {
      modelRef.current.rotation.y += delta * 0.3
    }
  })

  if (!model) return null

  return (
    <group ref={modelRef}>
      <primitive object={model} />
    </group>
  )
}

function PointCloudModel({ url }: { url: string }) {
  const [points, setPoints] = useState<THREE.Points | null>(null)
  const { setIsLoading, setLoadingProgress, settings } = useStore()
  const modelRef = useRef<THREE.Points>(null)

  useEffect(() => {
    setIsLoading(true)
    const loader = new PLYLoader()
    
    loader.load(
      url,
      (geometry) => {
        // Center the geometry
        geometry.computeBoundingBox()
        const box = geometry.boundingBox!
        const size = box.getSize(new THREE.Vector3())
        const maxDim = Math.max(size.x, size.y, size.z)
        const scale = 2 / maxDim
        
        const center = box.getCenter(new THREE.Vector3())
        geometry.translate(-center.x, -center.y, -center.z)
        geometry.scale(scale, scale, scale)
        
        // Create material
        const material = new THREE.PointsMaterial({
          size: settings.pointSize * 0.01,
          vertexColors: geometry.hasAttribute('color'),
          color: geometry.hasAttribute('color') ? undefined : '#0ea5e9'
        })
        
        const pointCloud = new THREE.Points(geometry, material)
        setPoints(pointCloud)
        setIsLoading(false)
      },
      (progress) => {
        if (progress.total > 0) {
          setLoadingProgress((progress.loaded / progress.total) * 100)
        }
      },
      (error) => {
        console.error('Error loading PLY:', error)
        setIsLoading(false)
      }
    )
  }, [url, setIsLoading, setLoadingProgress, settings.pointSize])

  useFrame((_, delta) => {
    if (modelRef.current && settings.autoRotate) {
      modelRef.current.rotation.y += delta * 0.3
    }
  })

  if (!points) return null

  return <primitive ref={modelRef} object={points} />
}

function GaussianSplatModel({ url }: { url: string }) {
  const { setIsLoading } = useStore()
  
  useEffect(() => {
    setIsLoading(true)
    // Gaussian splat loading would require a specialized library
    // For now, we'll show a placeholder
    setTimeout(() => setIsLoading(false), 1000)
  }, [url, setIsLoading])

  return (
    <Center>
      <mesh>
        <sphereGeometry args={[1, 32, 32]} />
        <meshStandardMaterial color="#0ea5e9" wireframe />
      </mesh>
      <Html center position={[0, -1.5, 0]}>
        <div className="text-center text-sm text-surface-500 whitespace-nowrap">
          Gaussian Splat Viewer<br />
          <span className="text-xs">(Specialized renderer required)</span>
        </div>
      </Html>
    </Center>
  )
}

function ModelRenderer({ model }: { model: Model3D }) {
  const format = model.format.toLowerCase()

  switch (format) {
    case 'gltf':
    case 'glb':
      return <GLTFModel url={model.url} />
    case 'obj':
      return <OBJModel url={model.url} />
    case 'fbx':
      return <FBXModel url={model.url} />
    case 'ply':
    case 'las':
    case 'laz':
      return <PointCloudModel url={model.url} />
    case 'splat':
      return <GaussianSplatModel url={model.url} />
    default:
      return (
        <Html center>
          <div className="text-red-500">Unsupported format: {format}</div>
        </Html>
      )
  }
}

function Scene({ model }: { model: Model3D }) {
  const { settings } = useStore()

  return (
    <>
      <PerspectiveCamera makeDefault position={[3, 2, 3]} fov={50} />
      
      <ambientLight intensity={settings.ambientIntensity} />
      <directionalLight position={[10, 10, 5]} intensity={0.8} castShadow />
      <directionalLight position={[-10, -10, -5]} intensity={0.3} />
      
      <Suspense fallback={<LoadingScreen />}>
        <ModelRenderer model={model} />
        <Environment preset="city" />
      </Suspense>

      {settings.showGrid && (
        <Grid
          position={[0, -1.5, 0]}
          args={[20, 20]}
          cellSize={0.5}
          cellThickness={0.5}
          cellColor="#e2e8f0"
          sectionSize={2}
          sectionThickness={1}
          sectionColor="#cbd5e1"
          fadeDistance={25}
          fadeStrength={1}
          followCamera={false}
        />
      )}

      {settings.showAxes && (
        <GizmoHelper alignment="bottom-right" margin={[80, 80]}>
          <GizmoViewport axisColors={['#ef4444', '#22c55e', '#3b82f6']} labelColor="white" />
        </GizmoHelper>
      )}

      <OrbitControls 
        makeDefault 
        enableDamping 
        dampingFactor={0.05}
        minDistance={0.5}
        maxDistance={20}
      />
    </>
  )
}

export function Viewer({ model }: { model: Model3D }) {
  const { settings, sidebarOpen, isLoading } = useStore()

  return (
    <div className="h-[calc(100vh-64px)] flex relative">
      {/* Main viewer */}
      <div className="flex-1 relative">
        <Canvas
          gl={{ antialias: true, preserveDrawingBuffer: true }}
          shadows
          style={{ background: settings.backgroundColor }}
        >
          <Scene model={model} />
        </Canvas>

        {/* Loading overlay */}
        <AnimatePresence>
          {isLoading && (
            <motion.div
              initial={{ opacity: 0 }}
              animate={{ opacity: 1 }}
              exit={{ opacity: 0 }}
              className="absolute inset-0 bg-white/80 backdrop-blur-sm flex items-center justify-center z-10"
            >
              <div className="flex flex-col items-center gap-4">
                <div className="w-12 h-12 border-4 border-primary-200 border-t-primary-500 rounded-full animate-spin" />
                <p className="text-surface-600 font-medium">Loading model...</p>
              </div>
            </motion.div>
          )}
        </AnimatePresence>

        {/* Controls */}
        <ViewerControls />
      </div>

      {/* Sidebar */}
      <AnimatePresence>
        {sidebarOpen && (
          <motion.div
            initial={{ x: 300, opacity: 0 }}
            animate={{ x: 0, opacity: 1 }}
            exit={{ x: 300, opacity: 0 }}
            transition={{ type: 'spring', damping: 25, stiffness: 200 }}
            className="w-80 border-l border-surface-200 bg-white overflow-y-auto hidden lg:block"
          >
            <ViewerSidebar model={model} />
          </motion.div>
        )}
      </AnimatePresence>
    </div>
  )
}


