import React, { useRef, useEffect, useState } from 'react';
import { Canvas, useFrame } from '@react-three/fiber';
import { OrbitControls, Text } from '@react-three/drei';
import * as THREE from 'three';

interface Body {
  name: string;
  position: [number, number, number];
  velocity: [number, number, number];
  mass: number;
}

interface SimulationUpdate {
  step: number;
  time: number;
  bodies: Body[];
}

interface Props {
  data: SimulationUpdate;
  resetTrails?: boolean; // Signal to reset all trails
}

// Component for rendering a celestial body
function CelestialBody({ body, index, scaleFactor, resetTrails }: { 
  body: Body; 
  index: number; 
  scaleFactor: number;
  resetTrails?: boolean;
}) {
  const meshRef = useRef<THREE.Mesh>(null);
  const trailRef = useRef<THREE.Line>(null);
  const trailPoints = useRef<THREE.Vector3[]>([]);
  const lastPosition = useRef<THREE.Vector3 | null>(null);
  const lastScaleFactor = useRef<number>(scaleFactor);
  
  // Color palette for different bodies
  const colors = [
    '#FFD700', // Gold for Sun
    '#FFA500', // Orange for Mercury
    '#FF6347', // Tomato for Venus
    '#4169E1', // Royal Blue for Earth
    '#FF0000', // Red for Mars
    '#DAA520', // Goldenrod for Jupiter
    '#F4A460', // Sandy Brown for Saturn
    '#40E0D0', // Turquoise for Uranus
    '#0000FF', // Blue for Neptune
    '#DDA0DD', // Plum for other bodies
  ];
  
  const color = colors[index % colors.length];
  
  // Scale positions based on the scale factor
  // Default scale factor of 1 means 1 AU, scale factor of 0.1 means 0.1 AU, etc.
  const AU = 1.496e11; // 1 AU in meters
  const scaledPosition = [
    body.position[0] / (AU * scaleFactor),
    body.position[1] / (AU * scaleFactor), 
    body.position[2] / (AU * scaleFactor)
  ];
  
  // Calculate sphere size with better scaling for visualization
  // Make the Sun larger and planets visible
  let size;
  if (body.name.toLowerCase().includes('sun')) {
    size = 0.2; // Sun gets a fixed, visible size
  } else {
    // Scale planet sizes logarithmically but make them visible
    const earthMass = 5.972e24;
    const massRatio = body.mass / earthMass;
    size = Math.max(0.03, Math.min(Math.pow(massRatio, 1/3) * 0.08, 0.15));
  }
  
  // Update trail with AU-scaled positions
  useEffect(() => {
    const position = new THREE.Vector3(...scaledPosition);
    
    // Check if we need to reset trails
    const shouldReset = resetTrails || 
                       Math.abs(scaleFactor - lastScaleFactor.current) > 0.01 || // Scale factor changed
                       (lastPosition.current && position.distanceTo(lastPosition.current) > 5); // Big position jump
    
    if (shouldReset) {
      // Reset trails for discontinuous changes
      trailPoints.current = [position.clone()];
    } else {
      // Normal trail update
      trailPoints.current.push(position.clone());
      
      // Keep trail length manageable
      if (trailPoints.current.length > 1000) {
        trailPoints.current.shift();
      }
    }
    
    // Update references for next comparison
    lastPosition.current = position.clone();
    lastScaleFactor.current = scaleFactor;
    
    // Update trail geometry
    if (trailRef.current && trailPoints.current.length > 1) {
      const geometry = new THREE.BufferGeometry().setFromPoints(trailPoints.current);
      trailRef.current.geometry.dispose();
      trailRef.current.geometry = geometry;
    }
  }, [body.position, scaleFactor, resetTrails]);
  
  return (
    <group>
      {/* Trail */}
      {trailPoints.current.length > 1 && (
        <primitive
          object={new THREE.Line(
            new THREE.BufferGeometry().setFromPoints(trailPoints.current),
            new THREE.LineBasicMaterial({ color: color, transparent: true, opacity: 0.5 })
          )}
        />
      )}
      
      {/* Body */}
      <mesh
        ref={meshRef}
        position={scaledPosition as [number, number, number]}
      >
        <sphereGeometry args={[size, 16, 16]} />
        <meshStandardMaterial
          color={color}
          emissive={index === 0 ? color : '#000000'} // Sun glows
          emissiveIntensity={index === 0 ? 0.3 : 0}
        />
      </mesh>
      
      {/* Body label */}
      <Text
        position={[
          scaledPosition[0],
          scaledPosition[1] + size + 0.3,
          scaledPosition[2],
        ]}
        fontSize={0.1}
        color="white"
        anchorX="center"
        anchorY="middle"
      >
        {body.name}
      </Text>
    </group>
  );
}

// Component for the 3D scene
function Scene({ data, scaleFactor, resetTrails }: Props & { scaleFactor: number }) {
  return (
    <>
      {/* Lighting */}
      <ambientLight intensity={0.3} />
      <pointLight position={[0, 0, 0]} intensity={2} color="#FFD700" />
      <pointLight position={[100, 100, 100]} intensity={0.5} />
      
      {/* Bodies */}
      {data.bodies.map((body, index) => (
        <CelestialBody 
          key={body.name} 
          body={body} 
          index={index} 
          scaleFactor={scaleFactor} 
          resetTrails={resetTrails}
        />
      ))}
      
      {/* Grid removed for cleaner space view */}
      
      {/* Controls */}
      <OrbitControls
        enablePan={true}
        enableZoom={true}
        enableRotate={true}
        minDistance={0.5}
        maxDistance={50}
      />
    </>
  );
}

const SimulationCanvas: React.FC<Props> = ({ data, resetTrails }) => {
  const [scaleFactor, setScaleFactor] = useState(1.0); // 1.0 = 1 AU
  const [internalResetTrails, setInternalResetTrails] = useState(false);
  const lastStep = useRef<number>(data.step);
  const lastScaleFactor = useRef<number>(1.0);
  
  // Detect big timeline jumps
  useEffect(() => {
    const stepDifference = Math.abs(data.step - lastStep.current);
    const isBigJump = stepDifference > 100; // Consider jumps > 100 steps as "big"
    
    if (isBigJump) {
      setInternalResetTrails(true);
      // Reset the flag after a short delay to trigger the effect
      setTimeout(() => setInternalResetTrails(false), 50);
    }
    
    lastStep.current = data.step;
  }, [data.step]);
  
  // Handle external reset signals and scale changes
  useEffect(() => {
    const scaleChanged = Math.abs(scaleFactor - lastScaleFactor.current) > 0.01;
    
    if (resetTrails || scaleChanged) {
      setInternalResetTrails(true);
      setTimeout(() => setInternalResetTrails(false), 50);
    }
    
    lastScaleFactor.current = scaleFactor;
  }, [resetTrails, scaleFactor]);
  
  const scaleOptions = [
    { value: 0.1, label: '0.1 AU', description: 'Inner planets' },
    { value: 0.5, label: '0.5 AU', description: 'Close view' },
    { value: 1.0, label: '1 AU', description: 'Default' },
    { value: 2.0, label: '2 AU', description: 'Wide view' },
    { value: 5.0, label: '5 AU', description: 'Outer system' },
    { value: 10.0, label: '10 AU', description: 'Very wide' },
  ];
  
  const getCurrentScaleLabel = () => {
    const current = scaleOptions.find(opt => opt.value === scaleFactor);
    return current ? current.label : `${scaleFactor} AU`;
  };
  
  return (
    <div style={{ width: '100%', height: '100%' }}>
      <Canvas
        camera={{
          position: [5, 5, 5],
          fov: 60,
        }}
        style={{ background: '#000' }}
      >
        <Scene data={data} scaleFactor={scaleFactor} resetTrails={resetTrails || internalResetTrails} />
      </Canvas>
      
      {/* Scale control slider */}
      <div style={{
        position: 'absolute',
        top: '1rem',
        right: '1rem',
        background: 'rgba(0, 0, 0, 0.7)',
        padding: '1rem',
        borderRadius: '8px',
        color: 'white',
        fontFamily: 'monospace',
        minWidth: '200px',
      }}>
        <h4 style={{ margin: '0 0 0.5rem 0', fontSize: '0.9rem' }}>View Scale</h4>
        <div style={{ marginBottom: '0.5rem' }}>
          <span style={{ fontSize: '0.8rem', color: '#ccc' }}>Scale: {getCurrentScaleLabel()}</span>
        </div>
        <input
          type="range"
          min="0.1"
          max="10"
          step="0.1"
          value={scaleFactor}
          onChange={(e) => setScaleFactor(parseFloat(e.target.value))}
          style={{
            width: '100%',
            marginBottom: '0.5rem',
          }}
        />
        <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: '0.7rem', color: '#999' }}>
          <span>Close</span>
          <span>Far</span>
        </div>
      </div>
      
      {/* Overlay information */}
      <div style={{
        position: 'absolute',
        top: '1rem',
        left: '1rem',
        background: 'rgba(0, 0, 0, 0.7)',
        padding: '1rem',
        borderRadius: '8px',
        color: 'white',
        fontFamily: 'monospace',
      }}>
        <div>Step: {data.step.toLocaleString()}</div>
        <div>Time: {(data.time / 86400).toFixed(2)} days</div>
        <div>Bodies: {data.bodies.length}</div>
        <div style={{ fontSize: '0.9rem', marginTop: '0.5rem', color: '#ccc' }}>
          Scale: 1 unit = {scaleFactor} AU ({(scaleFactor * 150).toFixed(0)}M km)
        </div>
      </div>
    </div>
  );
};

export default SimulationCanvas;
