/**
 * CameraController - handles camera movement and focus
 */
import { useEffect, useRef } from 'react';
import { useFrame, useThree } from '@react-three/fiber';
import { Vector3 } from 'three';
import type { CelestialBody } from '../types';
import { calculateRenderPosition, findParentPlanet } from '../utils/renderPosition';

interface CameraControllerProps {
  focusTarget: CelestialBody | null;
  allBodies: CelestialBody[];
  onAnimationComplete?: () => void;
}

export function CameraController({ focusTarget, allBodies, onAnimationComplete }: CameraControllerProps) {
  const { camera } = useThree();
  const targetPosition = useRef(new Vector3());
  const isAnimating = useRef(false);
  const animationProgress = useRef(0);
  const startPosition = useRef(new Vector3());
  
  // Track previous focus to detect changes vs updates
  const lastFocusId = useRef<string | null>(null);
  const lastTargetPos = useRef<Vector3 | null>(null);

  useEffect(() => {
    if (focusTarget) {
      // Focus on target - use smart rendered position
      const renderPos = calculateRenderPosition(focusTarget, allBodies);
      const currentTargetPos = new Vector3(renderPos.x, renderPos.z, renderPos.y); // Note: Y/Z swap in rendering

      // Check if we are just updating positions for the same body
      if (lastFocusId.current === focusTarget.id && lastTargetPos.current) {
        // Same body, position changed (timeline playing)
        // Move camera by the same delta to maintain relative view
        const delta = new Vector3().subVectors(currentTargetPos, lastTargetPos.current);
        camera.position.add(delta);
        
        // Update tracking ref
        lastTargetPos.current.copy(currentTargetPos);
        return; // Skip animation and reset logic
      }

      // New target or first load: Calculate ideal camera position
      const pos = focusTarget.positions[0];
      const distance = Math.sqrt(pos.x ** 2 + pos.y ** 2 + pos.z ** 2);
      const parentInfo = findParentPlanet(focusTarget, allBodies);

      // Calculate camera position based on target type and context
      let offset: number;
      let heightMultiplier = 1; // For adjusting vertical position
      let sideMultiplier = 1; // For adjusting horizontal offset

      if (focusTarget.type === 'planet') {
        // For planets, use closer view from above
        offset = 4;
        heightMultiplier = 1.5;
        sideMultiplier = 1;
      } else if (focusTarget.type === 'probe') {
        // For probes, determine view based on context
        if (parentInfo) {
          // Probe near a planet - use closer view to see both probe and planet
          offset = 3; // Closer view (was 5)
          heightMultiplier = 0.8;
          sideMultiplier = 1.2;
        } else if (distance < 10) {
          // Inner solar system probe (not near a planet)
          offset = 5; // Closer view (was 8)
          heightMultiplier = 0.6;
          sideMultiplier = 1.5;
        } else if (distance > 50) {
          // Far probes (Voyagers, New Horizons) - need even closer view since they're so far
          offset = 4; // Much closer (was 12)
          heightMultiplier = 0.8;
          sideMultiplier = 1;
        } else {
          // Medium distance probes
          offset = 6; // Closer view (was 10)
          heightMultiplier = 0.8;
          sideMultiplier = 1.2;
        }
      } else {
        offset = 10;
        heightMultiplier = 1;
        sideMultiplier = 1;
      }

      targetPosition.current.set(
        currentTargetPos.x + (offset * sideMultiplier),
        currentTargetPos.y + (offset * heightMultiplier), // already swapped in currentTargetPos? No, currentTargetPos is X, Z, Y world coords. 
        // Wait, let's look at the original code logic carefully.
        // Original: scaledPos.x + offset, scaledPos.z + offset, scaledPos.y + offset
        // currentTargetPos is created as (renderPos.x, renderPos.z, renderPos.y). 
        // In Three.js scene: x=x, y=z(up), z=y(depth) usually? 
        // Let's verify Scene coordinate system. 
        // Scene.tsx: controlsTarget = [scaledPos.x, scaledPos.z, scaledPos.y]
        // So Y is up in Threejs, but Z is Up in data?
        // renderPos (from calculateRenderPosition) usually returns data coords.
        // let's stick to the exact mapping used in the original code.
        // Original: 
        // targetPosition.current.set(
        //   scaledPos.x + (offset * sideMultiplier),
        //   scaledPos.z + (offset * heightMultiplier),
        //   scaledPos.y + offset
        // );
        // So: X_cam = X_data + off
        //     Y_cam = Z_data + off  <-- Y is up in ThreeJS, so Z_data is mapped to Y
        //     Z_cam = Y_data + off
        currentTargetPos.y + (offset * heightMultiplier), // Z_data is in .y of currentTargetPos because we did new Vector3(x, z, y)
        currentTargetPos.z + offset                   // Y_data is in .z of currentTargetPos
      );
      // Wait, my `currentTargetPos` construction `new Vector3(renderPos.x, renderPos.z, renderPos.y)` 
      // maps Data X->X, Data Z->Y, Data Y->Z.
      // So currentTargetPos.x = Data X
      // currentTargetPos.y = Data Z (Up)
      // currentTargetPos.z = Data Y (Depth)
      // This matches the Scene.tsx controlsTarget `[scaledPos.x, scaledPos.z, scaledPos.y]`.
      
      // So the calculation should be relative to currentTargetPos:
      targetPosition.current.set(
        currentTargetPos.x + (offset * sideMultiplier),
        currentTargetPos.y + (offset * heightMultiplier),
        currentTargetPos.z + offset
      );

      // Update tracking refs
      lastFocusId.current = focusTarget.id;
      if (!lastTargetPos.current) {
        lastTargetPos.current = new Vector3();
      }
      lastTargetPos.current.copy(currentTargetPos);

      // Start animation
      startPosition.current.copy(camera.position);
      isAnimating.current = true;
      animationProgress.current = 0;
    } else {
      // No target - stop any ongoing animation and reset tracking refs.
      // Do NOT force camera to return to solar system overview.
      // OrbitControls will maintain the current camera position.
      if (isAnimating.current) { // If an animation was in progress, stop it.
        isAnimating.current = false;
        animationProgress.current = 0;
      }
      lastFocusId.current = null;
      lastTargetPos.current = null;
    }
  }, [focusTarget, allBodies, camera]);

  useFrame((_, delta) => {
    if (isAnimating.current) {
      // Smooth animation using easing
      animationProgress.current += delta * 0.8; // Animation speed

      if (animationProgress.current >= 1) {
        animationProgress.current = 1;
        isAnimating.current = false;
        if (onAnimationComplete) onAnimationComplete();
      }

      // Easing function (ease-in-out)
      const t = animationProgress.current;
      const eased = t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;

      // Interpolate camera position
      camera.position.lerpVectors(startPosition.current, targetPosition.current, eased);

      // Look at target - use smart rendered position (only during animation)
      if (focusTarget) {
        const renderPos = calculateRenderPosition(focusTarget, allBodies);
        camera.lookAt(renderPos.x, renderPos.z, renderPos.y);
      } else {
        camera.lookAt(0, 0, 0);
      }
    }
    // After animation completes, OrbitControls will take over
  });

  return null;
}
