import * as THREE from "three";
import { createNoise3D } from "simplex-noise";

import { CONFIG, CABLE_CONFIG, CABLE_STATE } from "./constants.js";
import { vector3ToLatLon, screenToEarthCoords } from "./utils.js";
import {
  showStatusMessage,
  updateCoordinatesDisplay,
  updateZoomDisplay,
  updateEarthStats,
  setLoading,
  setLoadingMessage,
  showTooltip,
  hideTooltip,
  showError,
  hideError,
  clearUiState,
} from "./ui.js";
import {
  createEarth,
  createClouds,
  createTerrain,
  createStars,
  createGridLines,
  getEarth,
} from "./earth.js";
import {
  loadGeoJSONFromPath,
  loadLandingPoints,
  handleCableClick,
  clearCableSelection,
  getCableLines,
  getCableState,
  setCableState,
  clearAllCableStates,
  applyLandingPointVisualState,
  resetLandingPointVisualState,
  getShowCables,
  clearCableData,
} from "./cables.js";
import {
  createSatellites,
  loadSatellites,
  updateSatellitePositions,
  toggleSatellites,
  getShowSatellites,
  getSatelliteCount,
  selectSatellite,
  getSatellitePoints,
  setSatelliteRingState,
  updateLockedRingPosition,
  updateHoverRingPosition,
  getSatellitePositions,
  showPredictedOrbit,
  hidePredictedOrbit,
  updateBreathingPhase,
  isSatelliteFrontFacing,
  setSatelliteCamera,
  setLockedSatelliteIndex,
  resetSatelliteState,
  clearSatelliteData,
} from "./satellites.js";
import {
  setupControls,
  getAutoRotate,
  getShowTerrain,
  setAutoRotate,
  resetView,
  teardownControls,
} from "./controls.js";
import {
  initInfoCard,
  showInfoCard,
  hideInfoCard,
  setInfoCardNoBorder,
} from "./info-card.js";
import { initLegend, setLegendMode } from "./legend.js";

export let scene;
export let camera;
export let renderer;

let simplex;
let isDragging = false;
let previousMousePosition = { x: 0, y: 0 };
let targetRotation = { x: 0, y: 0 };
let inertialVelocity = { x: 0, y: 0 };
let hoveredCable = null;
let hoveredSatellite = null;
let hoveredSatelliteIndex = null;
let lockedSatellite = null;
let lockedSatelliteIndex = null;
let lockedObject = null;
let lockedObjectType = null;
let dragStartTime = 0;
let isLongDrag = false;
let lastSatClickTime = 0;
let lastSatClickIndex = 0;
let lastSatClickPos = { x: 0, y: 0 };
let earthTexture = null;
let animationFrameId = null;
let initialized = false;
let destroyed = false;
let isDataLoading = false;
let currentLoadToken = 0;

const clock = new THREE.Clock();
const interactionRaycaster = new THREE.Raycaster();
const interactionMouse = new THREE.Vector2();
const scratchCameraToEarth = new THREE.Vector3();
const scratchCableCenter = new THREE.Vector3();
const scratchCableDirection = new THREE.Vector3();

const cleanupFns = [];
const DRAG_ROTATION_FACTOR = 0.005;
const DRAG_SMOOTHING_FACTOR = 0.18;
const INERTIA_DAMPING = 0.92;
const INERTIA_MIN_VELOCITY = 0.00008;

function bindListener(target, eventName, handler, options) {
  if (!target) return;
  target.addEventListener(eventName, handler, options);
  cleanupFns.push(() =>
    target.removeEventListener(eventName, handler, options),
  );
}

function disposeMaterial(material) {
  if (!material) return;
  if (Array.isArray(material)) {
    material.forEach(disposeMaterial);
    return;
  }

  if (material.map) material.map.dispose();
  if (material.alphaMap) material.alphaMap.dispose();
  if (material.aoMap) material.aoMap.dispose();
  if (material.bumpMap) material.bumpMap.dispose();
  if (material.displacementMap) material.displacementMap.dispose();
  if (material.emissiveMap) material.emissiveMap.dispose();
  if (material.envMap) material.envMap.dispose();
  if (material.lightMap) material.lightMap.dispose();
  if (material.metalnessMap) material.metalnessMap.dispose();
  if (material.normalMap) material.normalMap.dispose();
  if (material.roughnessMap) material.roughnessMap.dispose();
  if (material.specularMap) material.specularMap.dispose();
  material.dispose();
}

function disposeSceneObject(object) {
  if (!object) return;

  for (let i = object.children.length - 1; i >= 0; i -= 1) {
    disposeSceneObject(object.children[i]);
  }

  if (object.geometry) {
    object.geometry.dispose();
  }

  if (object.material) {
    disposeMaterial(object.material);
  }

  if (object.parent) {
    object.parent.remove(object);
  }
}

function clearRuntimeSelection() {
  hoveredCable = null;
  hoveredSatellite = null;
  hoveredSatelliteIndex = null;
  lockedObject = null;
  lockedObjectType = null;
  lockedSatellite = null;
  lockedSatelliteIndex = null;
  setLockedSatelliteIndex(null);
}

export function clearLockedObject() {
  hidePredictedOrbit();
  clearAllCableStates();
  clearCableSelection();
  setSatelliteRingState(null, "none", null);
  clearRuntimeSelection();
}

function isSameCable(cable1, cable2) {
  if (!cable1 || !cable2) return false;
  const id1 = cable1.userData?.cableId;
  const id2 = cable2.userData?.cableId;
  if (id1 === undefined || id2 === undefined) return false;
  return id1 === id2;
}

function showCableInfo(cable) {
  setLegendMode("cables");
  showInfoCard("cable", {
    name: cable.userData.name,
    owner: cable.userData.owner,
    status: cable.userData.status,
    length: cable.userData.length,
    coords: cable.userData.coords,
    rfs: cable.userData.rfs,
  });
}

function showSatelliteInfo(props) {
  const meanMotion = props?.mean_motion || 0;
  const period = meanMotion > 0 ? (1440 / meanMotion).toFixed(1) : "-";
  const ecc = props?.eccentricity || 0;
  const perigee = (6371 * (1 - ecc)).toFixed(0);
  const apogee = (6371 * (1 + ecc)).toFixed(0);

  setLegendMode("satellites");
  showInfoCard("satellite", {
    name: props?.name || "-",
    norad_id: props?.norad_cat_id,
    inclination: props?.inclination ? props.inclination.toFixed(2) : "-",
    period,
    perigee,
    apogee,
  });
}

function applyCableVisualState() {
  const allCables = getCableLines();
  const pulse = (Math.sin(Date.now() * CABLE_CONFIG.pulseSpeed) + 1) * 0.5;

  allCables.forEach((cable) => {
    const cableId = cable.userData.cableId;
    const state = getCableState(cableId);

    switch (state) {
      case CABLE_STATE.LOCKED:
        cable.material.opacity =
          CABLE_CONFIG.lockedOpacityMin +
          pulse *
            (CABLE_CONFIG.lockedOpacityMax - CABLE_CONFIG.lockedOpacityMin);
        cable.material.color.setRGB(1, 1, 1);
        break;
      case CABLE_STATE.HOVERED:
        cable.material.opacity = 1;
        cable.material.color.setRGB(1, 1, 1);
        break;
      case CABLE_STATE.NORMAL:
      default:
        if (
          (lockedObjectType === "cable" && lockedObject) ||
          (lockedObjectType === "satellite" && lockedSatellite)
        ) {
          cable.material.opacity = CABLE_CONFIG.otherOpacity;
          const origColor = cable.userData.originalColor;
          const brightness = CABLE_CONFIG.otherBrightness;
          cable.material.color.setRGB(
            (((origColor >> 16) & 255) / 255) * brightness,
            (((origColor >> 8) & 255) / 255) * brightness,
            ((origColor & 255) / 255) * brightness,
          );
        } else {
          cable.material.opacity = 1;
          cable.material.color.setHex(cable.userData.originalColor);
        }
    }
  });
}

function updatePointerFromEvent(event) {
  interactionMouse.set(
    (event.clientX / window.innerWidth) * 2 - 1,
    -(event.clientY / window.innerHeight) * 2 + 1,
  );
  interactionRaycaster.setFromCamera(interactionMouse, camera);
}

function buildLoadErrorMessage(errors) {
  if (errors.length === 0) return "";
  return errors
    .map(
      ({ label, reason }) =>
        `${label}加载失败: ${reason?.message || String(reason)}`,
    )
    .join("；");
}

function updateStatsSummary() {
  updateEarthStats({
    cableCount: getCableLines().length,
    landingPointCount:
      document.getElementById("landing-point-count")?.textContent || 0,
    terrainOn: getShowTerrain(),
    textureQuality: "8K 卫星图",
  });
}

window.addEventListener("error", (event) => {
  console.error("全局错误:", event.error);
});

window.addEventListener("unhandledrejection", (event) => {
  console.error("未处理的 Promise 错误:", event.reason);
});

export function init() {
  if (initialized && !destroyed) return;

  destroyed = false;
  initialized = true;
  simplex = createNoise3D();

  scene = new THREE.Scene();
  camera = new THREE.PerspectiveCamera(
    75,
    window.innerWidth / window.innerHeight,
    0.1,
    1000,
  );
  camera.position.z = CONFIG.defaultCameraZ;
  setSatelliteCamera(camera);

  renderer = new THREE.WebGLRenderer({
    antialias: true,
    alpha: false,
    powerPreference: "high-performance",
  });
  renderer.setSize(window.innerWidth, window.innerHeight);
  renderer.setClearColor(0x0a0a1a, 1);
  renderer.setPixelRatio(window.devicePixelRatio);

  const container = document.getElementById("container");
  if (container) {
    container.querySelector("canvas")?.remove();
    container.appendChild(renderer.domElement);
  }

  addLights();
  initInfoCard();
  initLegend();
  const earthObj = createEarth(scene);
  targetRotation = {
    x: earthObj.rotation.x,
    y: earthObj.rotation.y,
  };
  inertialVelocity = { x: 0, y: 0 };
  createClouds(scene, earthObj);
  createTerrain(scene, earthObj, simplex);
  createStars(scene);
  createGridLines(scene, earthObj);
  createSatellites(scene, earthObj);

  setupControls(camera, renderer, scene, earthObj);
  resetView(camera);
  setupEventListeners();

  clock.start();
  loadData();
  animate();
  registerGlobalApi();
}

function registerGlobalApi() {
  window.__planetEarth = {
    reloadData,
    clearSelection: () => {
      hideInfoCard();
      clearLockedObject();
    },
    destroy,
    init,
  };
}

function addLights() {
  scene.add(new THREE.AmbientLight(0x404060));

  const directionalLight = new THREE.DirectionalLight(0xffffff, 1.2);
  directionalLight.position.set(5, 3, 5);
  scene.add(directionalLight);

  const backLight = new THREE.DirectionalLight(0x446688, 0.3);
  backLight.position.set(-5, 0, -5);
  scene.add(backLight);

  const pointLight = new THREE.PointLight(0xffffff, 0.4);
  pointLight.position.set(10, 10, 10);
  scene.add(pointLight);
}

async function loadData(showWhiteSphere = false) {
  if (!scene || !camera || !renderer) return;
  if (isDataLoading) return;

  const earth = getEarth();
  if (!earth) return;

  const loadToken = ++currentLoadToken;
  isDataLoading = true;
  hideError();
  setLoadingMessage(
    showWhiteSphere ? "正在刷新全球态势数据..." : "正在初始化全球态势数据...",
    showWhiteSphere
      ? "重新同步卫星、海底光缆与登陆点数据"
      : "同步卫星、海底光缆与登陆点数据",
  );
  setLoading(true);
  clearLockedObject();
  hideInfoCard();

  if (showWhiteSphere && earth.material) {
    earthTexture = earth.material.map;
    earth.material.map = null;
    earth.material.color.setHex(0xffffff);
    earth.material.needsUpdate = true;
  }

  const results = await Promise.allSettled([
    loadGeoJSONFromPath(scene, earth),
    loadLandingPoints(scene, earth),
    (async () => {
      clearSatelliteData();
      const satelliteCount = await loadSatellites();
      const satelliteCountEl = document.getElementById("satellite-count");
      if (satelliteCountEl) {
        satelliteCountEl.textContent = `${satelliteCount} 颗`;
      }
      updateSatellitePositions(POSITION_UPDATE_FORCE_DELTA, true);
      toggleSatellites(true);
      const satBtn = document.getElementById("toggle-satellites");
      if (satBtn) {
        satBtn.classList.add("active");
        const tooltip = satBtn.querySelector(".tooltip");
        if (tooltip) tooltip.textContent = "隐藏卫星";
      }
      return satelliteCount;
    })(),
  ]);

  if (loadToken !== currentLoadToken) {
    isDataLoading = false;
    return;
  }

  const errors = [];
  if (results[0].status === "rejected") {
    errors.push({ label: "电缆", reason: results[0].reason });
  }
  if (results[1].status === "rejected") {
    errors.push({ label: "登陆点", reason: results[1].reason });
  }
  if (results[2].status === "rejected") {
    errors.push({ label: "卫星", reason: results[2].reason });
  }

  if (errors.length > 0) {
    const errorMessage = buildLoadErrorMessage(errors);
    showError(errorMessage);
    showStatusMessage(errorMessage, "error");
  } else {
    hideError();
    showStatusMessage("数据已重新加载", "success");
  }

  updateStatsSummary();
  setLoading(false);
  isDataLoading = false;

  if (showWhiteSphere && earth.material) {
    earth.material.map = earthTexture;
    earth.material.color.setHex(0xffffff);
    earth.material.needsUpdate = true;
  }
}

const POSITION_UPDATE_FORCE_DELTA = 250;

export async function reloadData() {
  await loadData(true);
}

function setupEventListeners() {
  const handleResize = () => onWindowResize();
  const handleMouseMove = (event) => onMouseMove(event);
  const handleMouseDown = (event) => onMouseDown(event);
  const handleMouseUp = () => onMouseUp();
  const handleMouseLeave = () => onMouseLeave();
  const handleClick = (event) => onClick(event);
  const handlePageHide = () => destroy();

  bindListener(window, "resize", handleResize);
  bindListener(window, "pagehide", handlePageHide);
  bindListener(window, "beforeunload", handlePageHide);
  bindListener(window, "mousemove", handleMouseMove);
  bindListener(renderer.domElement, "mousedown", handleMouseDown);
  bindListener(window, "mouseup", handleMouseUp);
  bindListener(renderer.domElement, "mouseleave", handleMouseLeave);
  bindListener(renderer.domElement, "click", handleClick);
}

function onWindowResize() {
  if (!camera || !renderer) return;
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
}

function getFrontFacingCables(cableLines) {
  const earth = getEarth();
  if (!earth) return cableLines;

  scratchCameraToEarth.subVectors(camera.position, earth.position).normalize();

  return cableLines.filter((cable) => {
    if (!cable.userData.localCenter) {
      return true;
    }

    scratchCableCenter.copy(cable.userData.localCenter);
    cable.localToWorld(scratchCableCenter);
    scratchCableDirection
      .subVectors(scratchCableCenter, earth.position)
      .normalize();
    return scratchCameraToEarth.dot(scratchCableDirection) > 0;
  });
}

function onMouseMove(event) {
  const earth = getEarth();
  if (!earth) return;

  if (isDragging) {
    if (Date.now() - dragStartTime > 500) {
      isLongDrag = true;
    }

    const deltaX = event.clientX - previousMousePosition.x;
    const deltaY = event.clientY - previousMousePosition.y;
    const rotationDeltaY = deltaX * DRAG_ROTATION_FACTOR;
    const rotationDeltaX = deltaY * DRAG_ROTATION_FACTOR;

    targetRotation.y += rotationDeltaY;
    targetRotation.x += rotationDeltaX;
    inertialVelocity.y = rotationDeltaY;
    inertialVelocity.x = rotationDeltaX;
    previousMousePosition = { x: event.clientX, y: event.clientY };
    hideTooltip();
    return;
  }

  updatePointerFromEvent(event);

  const frontCables = getFrontFacingCables(getCableLines());
  const cableIntersects = interactionRaycaster.intersectObjects(frontCables);

  let hoveredSat = null;
  let hoveredSatIndexFromIntersect = null;
  if (getShowSatellites()) {
    const satPoints = getSatellitePoints();
    if (satPoints) {
      const satIntersects = interactionRaycaster.intersectObject(satPoints);
      if (satIntersects.length > 0) {
        const satIndex = satIntersects[0].index;
        if (isSatelliteFrontFacing(satIndex, camera)) {
          hoveredSatIndexFromIntersect = satIndex;
          hoveredSat = selectSatellite(satIndex);
        }
      }
    }
  }

  if (
    hoveredCable &&
    (!cableIntersects.length ||
      !isSameCable(cableIntersects[0]?.object, hoveredCable))
  ) {
    if (!isSameCable(hoveredCable, lockedObject)) {
      setCableState(hoveredCable.userData.cableId, CABLE_STATE.NORMAL);
    }
    hoveredCable = null;
  }

  if (
    hoveredSatelliteIndex !== null &&
    hoveredSatelliteIndex !== hoveredSatIndexFromIntersect
  ) {
    if (hoveredSatelliteIndex !== lockedSatelliteIndex) {
      setSatelliteRingState(hoveredSatelliteIndex, "none", null);
    }
    hoveredSatelliteIndex = null;
  }

  if (cableIntersects.length > 0 && getShowCables()) {
    const cable = cableIntersects[0].object;
    hoveredCable = cable;
    if (!isSameCable(cable, lockedObject)) {
      setCableState(cable.userData.cableId, CABLE_STATE.HOVERED);
    }
    showCableInfo(cable);
    setInfoCardNoBorder(true);
    hideTooltip();
  } else if (hoveredSat?.properties) {
    hoveredSatellite = hoveredSat;
    hoveredSatelliteIndex = hoveredSatIndexFromIntersect;
    if (hoveredSatelliteIndex !== lockedSatelliteIndex) {
      const satPositions = getSatellitePositions();
      if (satPositions && satPositions[hoveredSatelliteIndex]) {
        setSatelliteRingState(
          hoveredSatelliteIndex,
          "hover",
          satPositions[hoveredSatelliteIndex].current,
        );
      }
    }
    showSatelliteInfo(hoveredSat.properties);
    setInfoCardNoBorder(true);
  } else if (lockedObjectType === "cable" && lockedObject) {
    showCableInfo(lockedObject);
  } else if (lockedObjectType === "satellite" && lockedSatellite) {
    const satPositions = getSatellitePositions();
    if (lockedSatelliteIndex !== null && satPositions?.[lockedSatelliteIndex]) {
      setSatelliteRingState(
        lockedSatelliteIndex,
        "locked",
        satPositions[lockedSatelliteIndex].current,
      );
    }
    showSatelliteInfo(lockedSatellite.properties);
  } else {
    hideInfoCard();
  }

  const earthPoint = screenToEarthCoords(
    event.clientX,
    event.clientY,
    camera,
    earth,
    document.body,
    interactionRaycaster,
    interactionMouse,
  );
  if (earthPoint) {
    const coords = vector3ToLatLon(earthPoint);
    updateCoordinatesDisplay(coords.lat, coords.lon, coords.alt);
    showTooltip(
      event.clientX + 10,
      event.clientY + 10,
      `纬度: ${coords.lat}°<br>经度: ${coords.lon}°<br>海拔: ${coords.alt.toFixed(1)} km`,
    );
  } else {
    hideTooltip();
  }
}

function onMouseDown(event) {
  const earth = getEarth();
  isDragging = true;
  dragStartTime = Date.now();
  isLongDrag = false;
  previousMousePosition = { x: event.clientX, y: event.clientY };
  inertialVelocity = { x: 0, y: 0 };
  if (earth) {
    targetRotation = {
      x: earth.rotation.x,
      y: earth.rotation.y,
    };
  }
  document.getElementById("container")?.classList.add("dragging");
  hideTooltip();
}

function onMouseUp() {
  isDragging = false;
  document.getElementById("container")?.classList.remove("dragging");
}

function onMouseLeave() {
  hideTooltip();
}

function onClick(event) {
  const earth = getEarth();
  if (!earth) return;

  updatePointerFromEvent(event);

  const cableIntersects = interactionRaycaster.intersectObjects(
    getFrontFacingCables(getCableLines()),
  );
  const satIntersects = getShowSatellites()
    ? interactionRaycaster.intersectObject(getSatellitePoints())
    : [];

  if (cableIntersects.length > 0 && getShowCables()) {
    clearLockedObject();

    const clickedCable = cableIntersects[0].object;
    const cableId = clickedCable.userData.cableId;
    setCableState(cableId, CABLE_STATE.LOCKED);

    lockedObject = clickedCable;
    lockedObjectType = "cable";
    setAutoRotate(false);
    handleCableClick(clickedCable);
    return;
  }

  if (satIntersects.length > 0) {
    const now = Date.now();
    const clickX = event.clientX;
    const clickY = event.clientY;

    const frontFacingSats = satIntersects.filter((sat) =>
      isSatelliteFrontFacing(sat.index, camera),
    );
    if (frontFacingSats.length === 0) return;

    let selectedIndex = frontFacingSats[0].index;
    if (
      frontFacingSats.length > 1 &&
      now - lastSatClickTime < 500 &&
      Math.abs(clickX - lastSatClickPos.x) < 30 &&
      Math.abs(clickY - lastSatClickPos.y) < 30
    ) {
      const currentIdx = frontFacingSats.findIndex(
        (sat) => sat.index === lastSatClickIndex,
      );
      selectedIndex =
        frontFacingSats[(currentIdx + 1) % frontFacingSats.length].index;
    }

    lastSatClickTime = now;
    lastSatClickIndex = selectedIndex;
    lastSatClickPos = { x: clickX, y: clickY };

    const sat = selectSatellite(selectedIndex);
    if (!sat?.properties) return;

    clearLockedObject();

    lockedObject = sat;
    lockedObjectType = "satellite";
    lockedSatellite = sat;
    lockedSatelliteIndex = selectedIndex;
    setLockedSatelliteIndex(selectedIndex);
    showPredictedOrbit(sat);
    setAutoRotate(false);

    const satPositions = getSatellitePositions();
    if (satPositions?.[selectedIndex]) {
      setSatelliteRingState(
        selectedIndex,
        "locked",
        satPositions[selectedIndex].current,
      );
    }

    showSatelliteInfo(sat.properties);
    showStatusMessage("已选择: " + sat.properties.name, "info");
    return;
  }

  if (!isLongDrag) {
    clearLockedObject();
    setAutoRotate(true);
  }
}

function animate() {
  if (destroyed) return;

  animationFrameId = requestAnimationFrame(animate);

  const earth = getEarth();
  const deltaTime = clock.getDelta() * 1000;
  const hasInertia =
    Math.abs(inertialVelocity.x) > INERTIA_MIN_VELOCITY ||
    Math.abs(inertialVelocity.y) > INERTIA_MIN_VELOCITY;

  if (getAutoRotate() && earth) {
    earth.rotation.y += CONFIG.rotationSpeed * (deltaTime / 16);

    // Keep the drag target aligned with autorotation only when the user is not
    // actively dragging and there is no residual inertial motion to preserve.
    if (!isDragging && !hasInertia) {
      targetRotation.y = earth.rotation.y;
      targetRotation.x = earth.rotation.x;
    }
  }

  if (earth) {
    if (isDragging) {
      // Smoothly follow the drag target to match the legacy interaction feel.
      earth.rotation.x +=
        (targetRotation.x - earth.rotation.x) * DRAG_SMOOTHING_FACTOR;
      earth.rotation.y +=
        (targetRotation.y - earth.rotation.y) * DRAG_SMOOTHING_FACTOR;
    } else if (
      Math.abs(inertialVelocity.x) > INERTIA_MIN_VELOCITY ||
      Math.abs(inertialVelocity.y) > INERTIA_MIN_VELOCITY
    ) {
      // Continue rotating after release and gradually decay the motion.
      targetRotation.x += inertialVelocity.x * (deltaTime / 16);
      targetRotation.y += inertialVelocity.y * (deltaTime / 16);
      earth.rotation.x +=
        (targetRotation.x - earth.rotation.x) * DRAG_SMOOTHING_FACTOR;
      earth.rotation.y +=
        (targetRotation.y - earth.rotation.y) * DRAG_SMOOTHING_FACTOR;
      inertialVelocity.x *= Math.pow(INERTIA_DAMPING, deltaTime / 16);
      inertialVelocity.y *= Math.pow(INERTIA_DAMPING, deltaTime / 16);
    } else {
      inertialVelocity.x = 0;
      inertialVelocity.y = 0;
      targetRotation.x = earth.rotation.x;
      targetRotation.y = earth.rotation.y;
    }
  }

  applyCableVisualState();

  if (lockedObjectType === "cable" && lockedObject) {
    applyLandingPointVisualState(lockedObject.userData.name, false);
  } else if (lockedObjectType === "satellite" && lockedSatellite) {
    applyLandingPointVisualState(null, true);
  } else {
    resetLandingPointVisualState();
  }

  updateSatellitePositions(deltaTime);
  updateBreathingPhase(deltaTime);

  const satPositions = getSatellitePositions();
  if (
    lockedObjectType === "satellite" &&
    lockedSatelliteIndex !== null &&
    satPositions?.[lockedSatelliteIndex]
  ) {
    updateLockedRingPosition(satPositions[lockedSatelliteIndex].current);
  } else if (
    hoveredSatelliteIndex !== null &&
    satPositions?.[hoveredSatelliteIndex]
  ) {
    updateHoverRingPosition(satPositions[hoveredSatelliteIndex].current);
  }

  renderer.render(scene, camera);
}

export function destroy() {
  if (destroyed) return;
  destroyed = true;
  currentLoadToken += 1;
  isDataLoading = false;

  if (animationFrameId) {
    cancelAnimationFrame(animationFrameId);
    animationFrameId = null;
  }

  teardownControls();
  while (cleanupFns.length) {
    const cleanup = cleanupFns.pop();
    cleanup?.();
  }

  clearLockedObject();
  clearCableData(getEarth());
  resetSatelliteState();
  clearUiState();

  if (scene) {
    disposeSceneObject(scene);
  }

  if (renderer) {
    renderer.dispose();
    if (typeof renderer.forceContextLoss === "function") {
      renderer.forceContextLoss();
    }
    renderer.domElement?.remove();
  }

  scene = null;
  camera = null;
  renderer = null;
  initialized = false;

  delete window.__planetEarth;
}

document.addEventListener("DOMContentLoaded", init);
