import { useRef, useMemo, useState, useEffect, Suspense } from 'react';
import { Mesh } from 'three';
import * as THREE from 'three';
import { useGLTF, useTexture } from '@react-three/drei';
import { useFrame } from '@react-three/fiber';
import type { CelestialBody as CelestialBodyType } from '../types';
import { fetchBodyResources } from '../utils/api';
import { getCelestialSize } from '../config/celestialSizes';

interface BodyViewerProps {
  body: CelestialBodyType;
  disableGlow?: boolean; // 禁用光晕效果(用于详情视图)
}

// Reusable component to render just the 3D model/mesh of a celestial body
export function BodyViewer({ body, disableGlow = false }: BodyViewerProps) {
  const meshRef = useRef<Mesh>(null);
  const [texturePath, setTexturePath] = useState<string | null | undefined>(undefined);
  const [modelPath, setModelPath] = useState<string | null | undefined>(undefined);
  const [modelScale, setModelScale] = useState<number>(1.0);
  const [loadError, setLoadError] = useState<boolean>(false);

  // Determine size and appearance - use larger sizes for detail view with a cap
  const appearance = useMemo(() => {
    const baseSize = getCelestialSize(body.name, body.type);

    // Detail view scaling strategy:
    // - Small bodies (< 0.15): scale up 3x for visibility
    // - Medium bodies (0.15-0.3): scale up 2x
    // - Large bodies (> 0.3): use base size with max cap of 1.2
    let finalSize: number;

    if (body.type === 'star') {
      finalSize = 1.0; // Fixed size for stars
    } else if (baseSize < 0.15) {
      // Small bodies: scale up for visibility
      finalSize = baseSize * 3.0;
    } else if (baseSize < 0.3) {
      // Medium bodies: moderate scaling
      finalSize = baseSize * 2.0;
    } else {
      // Large bodies: minimal or no scaling with a cap
      finalSize = Math.min(baseSize * 1.2, 1.2);
    }

    if (body.type === 'star') {
      // Use database color if available (for interstellar stars), otherwise default to yellow
      const starColor = body.starSystemData?.color || '#FDB813';
      return { size: finalSize, emissive: starColor, emissiveIntensity: 1.5 };
    }
    if (body.type === 'comet') {
      return { size: finalSize, emissive: '#000000', emissiveIntensity: 0 };
    }
    if (body.type === 'satellite') {
      return { size: finalSize, emissive: '#888888', emissiveIntensity: 0.4 };
    }
    return { size: finalSize, emissive: '#000000', emissiveIntensity: 0 };
  }, [body.name, body.type]);

  // Fetch resources (texture or model)
  useEffect(() => {
    setLoadError(false);
    setModelPath(undefined);
    setTexturePath(undefined);

    const loadResources = async () => {
      try {
        const response = await fetchBodyResources(body.id, body.type === 'probe' ? 'model' : 'texture');
        if (response.resources.length > 0) {
          const mainResource = response.resources[0];
          const fullPath = `/upload/${mainResource.file_path}`;

          if (body.type === 'probe') {
            useGLTF.preload(fullPath); // Preload GLTF
            setModelPath(fullPath);
            setModelScale(mainResource.extra_data?.scale || 1.0);
          } else {
            setTexturePath(fullPath);
          }
        } else {
          // No resources found
          if (body.type === 'probe') setModelPath(null);
          else setTexturePath(null);
        }
      } catch (err) {
        console.error(`Failed to load resource for ${body.name}:`, err);
        setLoadError(true);
        if (body.type === 'probe') setModelPath(null);
        else setTexturePath(null);
      }
    };
    loadResources();
  }, [body.id, body.name, body.type]);

  // Handle Probe Model rendering
  if (body.type === 'probe' && modelPath !== undefined) {
    if (modelPath === null || loadError) {
      // Fallback sphere for probes if model fails
      return (
        <mesh ref={meshRef}>
          <sphereGeometry args={[0.15, 32, 32]} />
          <meshStandardMaterial color="#ff0000" emissive="#ff0000" emissiveIntensity={0.8} />
        </mesh>
      );
    }
    return <ProbeModelViewer modelPath={modelPath} modelScale={modelScale} />;
  }

  // Handle Celestial Body (Planet, Star, etc.) rendering
  if (texturePath !== undefined) {
    return (
      <PlanetModelViewer
        body={body}
        size={appearance.size}
        emissive={appearance.emissive}
        emissiveIntensity={appearance.emissiveIntensity}
        texturePath={texturePath}
        meshRef={meshRef}
        disableGlow={disableGlow}
      />
    );
  }

  // Show nothing while loading resources
  return null;
}

// Sub-component for Probe models
function ProbeModelViewer({ modelPath, modelScale }: { modelPath: string; modelScale: number }) {
  const groupRef = useRef<Group>(null);
  const gltf = useGLTF(modelPath);
  const scene = gltf.scene;

  const optimalScale = useMemo(() => {
    if (!scene) return 1;
    const box = new THREE.Box3().setFromObject(scene);
    const size = new THREE.Vector3();
    box.getSize(size);
    const maxDimension = Math.max(size.x, size.y, size.z);
    const targetSize = 1.2; // Increased from 0.35 to 1.2 for detail view - larger probes
    const calculatedScale = maxDimension > 0 ? targetSize / maxDimension : 0.8;
    const finalScale = Math.max(0.5, Math.min(3.0, calculatedScale)); // Increased range for larger display
    return finalScale * modelScale;
  }, [scene, modelScale]);

  const configuredScene = useMemo(() => {
    if (!scene) return null;
    const clonedScene = scene.clone();
    clonedScene.traverse((child: any) => {
      if (child.isMesh) {
        if (child.material) {
          if (Array.isArray(child.material)) {
            child.material = child.material.map((mat: any) => {
              const clonedMat = mat.clone();
              clonedMat.depthTest = true;
              clonedMat.depthWrite = true;
              return clonedMat;
            });
          } else {
            child.material = child.material.clone();
            child.material.depthTest = true;
            child.material.depthWrite = true;
          }
        }
      }
    });
    return clonedScene;
  }, [scene]);

  if (!configuredScene) return null;

  return (
    <Suspense fallback={null}>
      <group ref={groupRef}>
        <primitive object={configuredScene} scale={optimalScale} />
      </group>
    </Suspense>
  );
}

// Sub-component for Planet models
function PlanetModelViewer({ body, size, emissive, emissiveIntensity, texturePath, meshRef, disableGlow = false }: {
  body: CelestialBodyType;
  size: number;
  emissive: string;
  emissiveIntensity: number;
  texturePath: string | null;
  meshRef: React.RefObject<Mesh>;
  disableGlow?: boolean;
}) {
  const texture = texturePath ? useTexture(texturePath) : null;

  // Rotation animation
  useFrame((_, delta) => {
    if (meshRef.current) {
      meshRef.current.rotation.y += delta * 0.1;
    }
  });

  // Irregular Comet Nucleus - potato-like shape
  function IrregularNucleus({ size, texture }: { size: number; texture: THREE.Texture | null }) {
    const nucleusRef = useRef<Mesh>(null);

    // Create irregular geometry by deforming a sphere
    const geometry = useMemo(() => {
      const geo = new THREE.SphereGeometry(size, 64, 64); // Higher resolution for detail view
      const positions = geo.attributes.position;

      // Deform vertices to create irregular shape
      for (let i = 0; i < positions.count; i++) {
        const x = positions.getX(i);
        const y = positions.getY(i);
        const z = positions.getZ(i);

        // Create multiple noise frequencies for complex shape
        const noise1 = Math.sin(x * 3.5) * Math.cos(y * 2.7) * Math.sin(z * 4.2);
        const noise2 = Math.cos(x * 5.1) * Math.sin(y * 4.3) * Math.cos(z * 3.8);
        const noise3 = Math.sin(x * 7.2) * Math.sin(y * 6.1) * Math.cos(z * 5.5);

        const deformation = 1 + (noise1 * 0.25 + noise2 * 0.15 + noise3 * 0.1);

        positions.setXYZ(i, x * deformation, y * deformation, z * deformation);
      }

      geo.computeVertexNormals();
      return geo;
    }, [size]);

    // Slow rotation
    useFrame((_, delta) => {
      if (nucleusRef.current) {
        nucleusRef.current.rotation.y += delta * 0.05;
        nucleusRef.current.rotation.z += delta * 0.02;
      }
    });

    return (
      <mesh ref={nucleusRef} geometry={geometry}>
        {texture ? (
          <meshStandardMaterial
            map={texture}
            roughness={0.9}
            metalness={0.1}
            color="#b8b8b8"
          />
        ) : (
          <meshStandardMaterial
            color="#6b6b6b"
            roughness={0.95}
            metalness={0.05}
          />
        )}
      </mesh>
    );
  }

  // Enhanced Coma (gas/dust cloud) around comet nucleus
  function CometComa({ radius }: { radius: number }) {
    const positions = useMemo(() => {
      const count = 300; // More particles for detail view
      const p = new Float32Array(count * 3);
      for (let i = 0; i < count; i++) {
        // Denser near center, spreading outward
        const r = radius * (0.8 + Math.pow(Math.random(), 1.5) * 4); // 0.8x to 4.8x radius
        const theta = Math.random() * Math.PI * 2;
        const phi = Math.acos(2 * Math.random() - 1);

        p[i * 3] = r * Math.sin(phi) * Math.cos(theta);
        p[i * 3 + 1] = r * Math.sin(phi) * Math.sin(theta);
        p[i * 3 + 2] = r * Math.cos(phi);
      }
      return p;
    }, [radius]);

    const pointsRef = useRef<THREE.Points>(null);

    // Animate coma
    useFrame((state, delta) => {
      if (pointsRef.current) {
        pointsRef.current.rotation.y += delta * 0.05;
        // Pulsing effect
        const scale = 1 + Math.sin(state.clock.elapsedTime * 0.5) * 0.1;
        pointsRef.current.scale.setScalar(scale);
      }
    });

    return (
      <>
        {/* Inner bright coma */}
        <points ref={pointsRef}>
          <bufferGeometry>
            <bufferAttribute attach="position" args={[positions, 3]} />
          </bufferGeometry>
          <pointsMaterial
            size={radius * 0.8}
            color="#88ccff"
            transparent
            opacity={0.7}
            sizeAttenuation={true}
            blending={THREE.AdditiveBlending}
            depthWrite={false}
          />
        </points>

        {/* Middle glow layer */}
        <mesh>
          <sphereGeometry args={[radius * 2.5, 32, 32]} />
          <meshBasicMaterial
            color="#66aadd"
            transparent
            opacity={0.15}
            side={THREE.BackSide}
            blending={THREE.AdditiveBlending}
            depthWrite={false}
          />
        </mesh>

        {/* Outer diffuse glow */}
        <mesh>
          <sphereGeometry args={[radius * 4.5, 32, 32]} />
          <meshBasicMaterial
            color="#4488aa"
            transparent
            opacity={0.12}
            side={THREE.BackSide}
            blending={THREE.AdditiveBlending}
            depthWrite={false}
          />
        </mesh>
      </>
    );
  }

  // Saturn Rings component - multiple rings for band effect
  function SaturnRings() {
    return (
      <group rotation={[Math.PI / 2, 0, 0]}>
        {/* Inner bright ring */}
        <mesh>
          <ringGeometry args={[1.4, 1.6, 32]} />
          <meshBasicMaterial color="#D4B896" transparent opacity={0.7} side={THREE.DoubleSide} />
        </mesh>
        {/* Middle darker band */}
        <mesh>
          <ringGeometry args={[1.6, 1.75, 32]} />
          <meshBasicMaterial color="#8B7355" transparent opacity={0.5} side={THREE.DoubleSide} />
        </mesh>
        {/* Outer bright ring */}
        <mesh>
          <ringGeometry args={[1.75, 2.0, 32]} />
          <meshBasicMaterial color="#C4A582" transparent opacity={0.6} side={THREE.DoubleSide} />
        </mesh>
        {/* Cassini Division (gap) */}
        <mesh>
          <ringGeometry args={[2.0, 2.05, 32]} />
          <meshBasicMaterial color="#000000" transparent opacity={0.2} side={THREE.DoubleSide} />
        </mesh>
        {/* A Ring (outer) */}
        <mesh>
          <ringGeometry args={[2.05, 2.2, 32]} />
          <meshBasicMaterial color="#B89968" transparent opacity={0.5} side={THREE.DoubleSide} />
        </mesh>
      </group>
    );
  }

  return (
    <group>
      {/* Use irregular nucleus for comets, regular sphere for others */}
      {body.type === 'comet' ? (
        <>
          <IrregularNucleus size={size} texture={texture} />
          <CometComa radius={size} />
        </>
      ) : (
        <mesh ref={meshRef} position={[0,0,0]}>
          <sphereGeometry args={[size, 64, 64]} /> {/* Higher resolution for detail view */}
          {texture ? (
            <meshStandardMaterial
              map={texture}
              emissive={emissive}
              emissiveIntensity={emissiveIntensity}
              roughness={body.type === 'star' ? 0 : 0.7}
              metalness={0.1}
            />
          ) : (
            <meshStandardMaterial
              color="#888888"
              emissive={emissive}
              emissiveIntensity={emissiveIntensity}
              roughness={0.7}
              metalness={0.1}
            />
          )}
        </mesh>
      )}

      {/* Saturn Rings */}
      {body.id === '699' && <SaturnRings />}

      {/* Sun glow effect - multi-layer scattered light (仅在非禁用光晕模式下显示) */}
      {body.type === 'star' && !disableGlow && (
        <>
          <pointLight intensity={10} distance={400} color="#fff8e7" />

          {/* Inner bright corona */}
          <mesh>
            <sphereGeometry args={[size * 1.3, 32, 32]} />
            <meshBasicMaterial
              color="#FDB813"
              transparent
              opacity={0.6}
              blending={THREE.AdditiveBlending}
              depthWrite={false}
            />
          </mesh>

          {/* Middle glow layer */}
          <mesh>
            <sphereGeometry args={[size * 1.8, 32, 32]} />
            <meshBasicMaterial
              color="#FDB813"
              transparent
              opacity={0.3}
              blending={THREE.AdditiveBlending}
              depthWrite={false}
            />
          </mesh>

          {/* Outer diffuse halo */}
          <mesh>
            <sphereGeometry args={[size * 2.5, 32, 32]} />
            <meshBasicMaterial
              color="#FFD700"
              transparent
              opacity={0.15}
              blending={THREE.AdditiveBlending}
              depthWrite={false}
            />
          </mesh>

          {/* Far scattered light */}
          <mesh>
            <sphereGeometry args={[size * 3.5, 32, 32]} />
            <meshBasicMaterial
              color="#FFA500"
              transparent
              opacity={0.08}
              blending={THREE.AdditiveBlending}
              depthWrite={false}
            />
          </mesh>
        </>
      )}
    </group>
  );
}
