import { useEffect, useRef, useState } from "react";
import { useFrame } from "@react-three/fiber";
import * as THREE from "three";
import { getSystemPoiPositionAtTime } from "./poiOrbit";
import { RouteLine } from "./RouteLine";
import { ShipMesh } from "./ShipMesh";
import type { GalaxySystem, PoiNavigationStatus, Vec3 } from "./types";

type PoiApproachShipProps = {
  system: GalaxySystem;
  poiId: string;
  scale: number;
  expanded: boolean;
  holdRadius?: number;
  speed?: number;
  onStatusChange?: (status: PoiNavigationStatus) => void;
};

const toVec3 = (value: THREE.Vector3): Vec3 => [value.x, value.y, value.z];

export function PoiApproachShip({
  system,
  poiId,
  scale,
  expanded,
  holdRadius = 1.25 * scale,
  speed = 9 * scale,
  onStatusChange,
}: PoiApproachShipProps) {
  const shipRef = useRef<THREE.Group>(null);
  const shipPosition = useRef(new THREE.Vector3(0, 0.8 * scale, -2.8 * scale));
  const status = useRef<PoiNavigationStatus>("approaching");
  const onStatusChangeRef = useRef(onStatusChange);
  const [linePoints, setLinePoints] = useState<Vec3[]>([toVec3(shipPosition.current), [0, 0, 0]]);
  const [arrived, setArrived] = useState(false);

  useEffect(() => {
    onStatusChangeRef.current = onStatusChange;
  }, [onStatusChange]);

  useEffect(() => {
    shipPosition.current.set(0, 0.8 * scale, -2.8 * scale);
    status.current = "approaching";
    setArrived(false);
    setLinePoints([toVec3(shipPosition.current), [0, 0, 0]]);
    onStatusChangeRef.current?.("approaching");
  }, [poiId, scale, system.id]);

  useFrame(({ clock }, dt) => {
    const target = new THREE.Vector3(...getSystemPoiPositionAtTime({
      systemId: system.id,
      planets: system.planets,
      pointsOfInterest: system.pointsOfInterest,
      poiId,
      elapsedTime: clock.elapsedTime,
      scale,
      expanded,
    }));
    const desired = status.current === "arrived"
      ? target.clone().add(new THREE.Vector3(holdRadius * 0.8, 0.12 * scale, holdRadius * 0.35))
      : target;
    const delta = desired.clone().sub(shipPosition.current);
    const distance = delta.length();

    if (status.current === "approaching" && distance <= holdRadius) {
      status.current = "arrived";
      setArrived(true);
      onStatusChangeRef.current?.("arrived");
    }

    if (status.current === "arrived") {
      shipPosition.current.lerp(desired, Math.min(1, dt * 7));
    } else if (distance > 0.001) {
      const step = Math.min(distance, speed * dt);
      shipPosition.current.add(delta.normalize().multiplyScalar(step));
    }

    if (shipRef.current) {
      shipRef.current.position.copy(shipPosition.current);
      const heading = desired.clone().sub(shipPosition.current);
      if (heading.lengthSq() > 0.0001) {
        shipRef.current.rotation.y = -Math.atan2(heading.z, heading.x);
      }
    }

    setLinePoints([toVec3(shipPosition.current), toVec3(target)]);
  });

  return (
    <group>
      {!arrived && <RouteLine points={linePoints} color="#f0a030" width={1.5} />}
      <group ref={shipRef} position={toVec3(shipPosition.current)}>
        <ShipMesh scale={0.12 * scale} engineActive={!arrived} color="#e2e8f0" emissive="#f0a030" />
      </group>
      <mesh position={linePoints[1]} scale={0.34 * scale}>
        <sphereGeometry args={[1, 16, 16]} />
        <meshBasicMaterial color={arrived ? "#22c55e" : "#f0a030"} transparent opacity={0.72} />
      </mesh>
    </group>
  );
}
