import * as Cesium from 'cesium';
import { eciToEcf, gstime, propagate, type SatRec, twoline2satrec } from 'satellite.js';

import type { I卫星 } from '../managers/HCesiumManager.types'; // 保持类型定义位置

const 默认轨道时长秒 = 2 * 60 * 60; // 2小时
const 默认时间步长秒 = 30;

export interface OrbitCalculationResult {
  orbitPositions: Cesium.Cartesian3[]; // 用于绘制完整轨道线
  sampledPositionProperty: Cesium.SampledPositionProperty;
}

export interface OrbitSample {
  position: Cesium.Cartesian3;
  time: Cesium.JulianDate;
}

export class SatelliteCalculator {
  /**
   * 解析 TLE 字符串获取卫星记录对象。
   * @param tle - 包含名称和两行数据的 TLE 字符串。
   * @param satelliteId - 用于错误日志的卫星 ID。
   * @returns 解析成功返回 SatRec 对象，否则返回 null。
   */
  parseTle(tle: string, satelliteId: string): null | SatRec {
    const tleLines = tle.trim().split('\n') as [string, string, string];
    if (tleLines.length < 3) {
      console.error(`无效的 TLE 格式 (ID: ${satelliteId}): TLE 字符串至少需要三行`);
      return null;
    }
    const tle1 = tleLines[1].trim();
    const tle2 = tleLines[2].trim();

    try {
      return twoline2satrec(tle1, tle2);
    } catch (error) {
      console.error(`解析 TLE 失败 (ID: ${satelliteId}):`, error);
      return null;
    }
  }

  /**
   * 计算卫星在给定时间段内的轨道位置。
   * @param satrec - 卫星记录对象。
   * @param startTime - 计算轨道的开始时间。
   * @param options - 包含轨道时长、步长等选项。
   * @param satelliteId - 用于日志记录的卫星 ID。
   * @returns 包含 SampledPositionProperty 和轨道点数组的对象，如果计算失败则返回 null。
   */
  calculateOrbit(
    satrec: SatRec,
    startTime: Cesium.JulianDate,
    options: Pick<I卫星, 'orbitDurationSeconds' | 'showOrbit' | 'timeStepSeconds'>,
    satelliteId: string,
  ): null | OrbitCalculationResult {
    const { orbitDurationSeconds = 默认轨道时长秒, timeStepSeconds = 默认时间步长秒, showOrbit } = options;

    const sampledPositionProperty = new Cesium.SampledPositionProperty();
    const orbitPositions: Cesium.Cartesian3[] = [];
    let hasSamples = false; // 标记是否成功添加了至少一个样本点

    for (let i = 0; i <= orbitDurationSeconds; i += timeStepSeconds) {
      const time = Cesium.JulianDate.addSeconds(startTime, i, new Cesium.JulianDate());
      const jsDate = Cesium.JulianDate.toDate(time);

      try {
        const positionAndVelocity = propagate(satrec, jsDate);
        if (positionAndVelocity === null) {
          console.warn(`卫星 ${satelliteId} 在时间 ${jsDate} 位置计算失败或已衰减。`);
          continue; // 跳过这个时间点
        }

        const gmst = gstime(jsDate);
        const positionEcf = eciToEcf(positionAndVelocity.position, gmst);

        // 转换为 Cesium 坐标（单位：米）
        const cesiumPosition = new Cesium.Cartesian3(positionEcf.x * 1000, positionEcf.y * 1000, positionEcf.z * 1000);

        // 添加位置样本
        sampledPositionProperty.addSample(time, cesiumPosition);
        hasSamples = true; // 标记已添加样本
        if (showOrbit) {
          orbitPositions.push(cesiumPosition);
        }
      } catch (error) {
        console.error(`计算卫星 ${satelliteId} 在时间 ${jsDate} 的位置时出错:`, error);
        // 可以在这里决定是跳过还是中断循环
        continue;
      }
    }

    // 确保至少有一个样本点，否则 SampledPositionProperty 会有问题
    if (!hasSamples) {
      console.warn(`卫星 ${satelliteId} 未能计算出任何有效轨道点。`);
      return null;
    }

    return { sampledPositionProperty, orbitPositions };
  }
}
