import * as THREE from "three";
import { ACTIVE_SYSTEM_DETAIL_SCALE, PROJECTED_GALAXY_RADIUS } from "./viewerConstants";
import { computeMoonLocalPosition, computePlanetLocalPosition, currentWorldTimeSeconds, scaleLocalVector } from "./viewerMath";
import type { PlanetVisual, ShipVisual, SystemVisual, WorldState } from "./viewerTypes";
import { rawObject } from "./viewerScenePrimitives";

export const MIN_ICON_PIXELS = 25;
export const MAX_ICON_PIXELS = 50;

export function iconWorldScale(distToCamera: number, camera: THREE.PerspectiveCamera, pixels: number): number {
  return pixels * distToCamera * 2 * Math.tan((camera.fov * Math.PI / 180) / 2) / window.innerHeight;
}

export function getAnimatedShipLocalPosition(visual: ShipVisual, now = performance.now()) {
  const elapsedMs = now - visual.receivedAtMs;
  const blendT = THREE.MathUtils.clamp(elapsedMs / visual.blendDurationMs, 0, 1);
  return new THREE.Vector3().lerpVectors(visual.startPosition, visual.authoritativePosition, blendT);
}

export function resolveShipHeading(visual: ShipVisual, worldPosition: THREE.Vector3, orbitYaw: number) {
  const desiredHeading = visual.targetPosition.clone().sub(worldPosition);
  if (desiredHeading.lengthSq() > 0.01) {
    return desiredHeading;
  }

  if (visual.velocity.lengthSq() > 0.01) {
    return visual.velocity.clone();
  }

  return new THREE.Vector3(Math.cos(orbitYaw), 0, Math.sin(orbitYaw));
}

export function updatePlanetPresentation(
  world: WorldState | undefined,
  worldTimeSyncMs: number,
  planetVisuals: PlanetVisual[],
  systemCamera: THREE.PerspectiveCamera,
) {
  const nowSeconds = currentWorldTimeSeconds(world, worldTimeSyncMs);
  // In systemScene all positions use scaleLocalVector * ACTIVE_SYSTEM_DETAIL_SCALE.
  // Star is always at origin (0,0,0); orbits are centered there.
  for (const visual of planetVisuals) {
    const position = scaleLocalVector(computePlanetLocalPosition(visual.planet, nowSeconds))
      .multiplyScalar(ACTIVE_SYSTEM_DETAIL_SCALE);

    visual.mesh.setPosition(position);
    visual.icon.setPosition(position);
    const iconWorldPos = visual.icon.getWorldPosition(new THREE.Vector3());
    const distToIcon = systemCamera.position.distanceTo(iconWorldPos);
    const t = THREE.MathUtils.clamp(distToIcon / 300, 0, 1);
    const rawScale = visual.iconBaseScale * t * Math.sqrt(t);
    const planetIconScale = THREE.MathUtils.clamp(rawScale, iconWorldScale(distToIcon, systemCamera, MIN_ICON_PIXELS), iconWorldScale(distToIcon, systemCamera, MAX_ICON_PIXELS));
    visual.icon.setScaleScalar(planetIconScale);
    if (visual.ring) {
      visual.ring.setPosition(position);
    }

    const distToPlanet = systemCamera.position.distanceTo(position);
    const moonOrbitOpacity = THREE.MathUtils.clamp(1 - distToPlanet / 500, 0, 1) * 0.18;

    const clusterVisible = distToPlanet < 300;
    for (const [moonIndex, moon] of visual.moons.entries()) {
      const moonPos = position.clone().add(
        scaleLocalVector(computeMoonLocalPosition(visual.planet.moons[moonIndex], nowSeconds))
          .multiplyScalar(ACTIVE_SYSTEM_DETAIL_SCALE),
      );
      moon.mesh.setPosition(moonPos);
      moon.mesh.setVisible(clusterVisible);
      moon.icon.setPosition(moonPos);
      moon.icon.setVisible(clusterVisible);
      if (clusterVisible) {
        const iconWorldPos = moon.icon.getWorldPosition(new THREE.Vector3());
        const moonDist = systemCamera.position.distanceTo(iconWorldPos);
        const t = THREE.MathUtils.clamp(moonDist / 120, 0, 1);
        const rawMoonScale = moon.iconBaseScale * t * Math.sqrt(t);
        const moonIconScale = THREE.MathUtils.clamp(rawMoonScale, iconWorldScale(moonDist, systemCamera, MIN_ICON_PIXELS), iconWorldScale(moonDist, systemCamera, MAX_ICON_PIXELS));
        moon.icon.setScaleScalar(moonIconScale);
      }
      moon.orbit.setPosition(position);
      const orbitObj = rawObject(moon.orbit);
      if (orbitObj instanceof THREE.LineLoop) {
        (orbitObj.material as THREE.LineBasicMaterial).opacity = moonOrbitOpacity;
      }
    }
  }
}


export function updateSystemStarPresentation(
  systemVisuals: Map<string, SystemVisual>,
  activeSystemId: string | undefined,
  galaxyCamera: THREE.PerspectiveCamera,
  setShellReticleOpacity: (sprite: SystemVisual["shellReticle"], opacity: number) => void,
) {
  const activeSystem = activeSystemId ? systemVisuals.get(activeSystemId) : undefined;

  for (const [systemId, visual] of systemVisuals.entries()) {
    // galaxyRoot is always at the galaxy position of this system
    visual.galaxyRoot.setPosition(visual.galaxyPosition);
    visual.shellReticle.setScaleScalar(visual.shellReticleBaseScale);

    if (!activeSystem) {
      // Galaxy view: show star dot, hide shell reticle
      visual.icon.setPosition(new THREE.Vector3(0, 0, 0));
      visual.icon.setVisible(true);
      visual.shellReticle.setPosition(new THREE.Vector3(0, 0, 0));
      visual.shellReticle.setVisible(false);
      setShellReticleOpacity(visual.shellReticle, 0);
      const dotWorldPos = visual.icon.getWorldPosition(new THREE.Vector3());
      visual.icon.setScaleScalar(galaxyCamera.position.distanceTo(dotWorldPos) * 0.01);
      continue;
    }

    if (systemId !== activeSystemId) {
      // Other systems in galaxy view while a system is active: show shell reticle projected to edge
      visual.icon.setPosition(new THREE.Vector3(0, 0, 0));
      visual.icon.setVisible(false);
      visual.shellReticle.setPosition(new THREE.Vector3(0, 0, 0));
      visual.shellReticle.setVisible(true);
      setShellReticleOpacity(visual.shellReticle, 1);
      const direction = visual.galaxyPosition.clone().sub(activeSystem.galaxyPosition);
      if (direction.lengthSq() > 0.0001) {
        visual.galaxyRoot.setPosition(
          activeSystem.galaxyPosition.clone().add(direction.normalize().multiplyScalar(PROJECTED_GALAXY_RADIUS)),
        );
      }
      const reticleWorldPosition = visual.galaxyRoot.getWorldPosition(new THREE.Vector3());
      const reticleDistance = galaxyCamera.position.distanceTo(reticleWorldPosition);
      const reticleScale = Math.max(900, reticleDistance * 0.032);
      visual.shellReticle.setScaleScalar(reticleScale);
      continue;
    }

    // Active system in galaxy view: show star dot, hide shell reticle
    visual.icon.setPosition(new THREE.Vector3(0, 0, 0));
    visual.icon.setVisible(true);
    visual.shellReticle.setVisible(false);
    setShellReticleOpacity(visual.shellReticle, 0);
  }
}
