import type { Cartesian3 } from 'cesium';

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

class SatelliteEntity {
  private leadTime!: number;
  private name!: string;
  private satrec: SatRec;
  private stepSeconds!: number;
  private tleLine1!: string;
  private tleLine2!: string;
  private totalSeconds!: number;
  private trailTime!: number;

  constructor(tle2LineString = '' /* , options = {} */) {
    const [name, tleLine1, tleLine2] = this._checkTle(tle2LineString);
    const circle = Number(tleLine2.slice(52, 64));

    this.name = name.trim();
    this.tleLine1 = tleLine1.trim();
    this.tleLine2 = tleLine2.trim();
    this.satrec = twoline2satrec(this.tleLine1, this.tleLine2);

    this.totalSeconds = 86_400; // 24小时
    this.stepSeconds = 100;
    this.leadTime = Math.round((24 * 3600) / circle);
    this.trailTime = 0;
  }

  _checkTle(tle2LineString: string) {
    const elements = tle2LineString.split('\n');
    if (elements.length !== 3) throw new Error('tle data error');
    return elements as [string, string, string];
  }

  // 创建PositionProperty
  _getPositionProperty() {
    const start = Cesium.JulianDate.fromIso8601(new Date().toISOString());
    const positionProperty = new Cesium.SampledPositionProperty(Cesium.ReferenceFrame.INERTIAL);

    const now = Date.now();
    for (let index = 0; index < this.totalSeconds / this.stepSeconds; index++) {
      const sateTime = new Date(now + index * this.stepSeconds * 1000);
      const sateCoord = this.getPositionEci(sateTime);
      if (!sateCoord) continue;
      const cesiumTime = Cesium.JulianDate.addSeconds(start, index * this.stepSeconds, new Cesium.JulianDate());
      const cesiumPosition = {
        x: sateCoord.x * 1000,
        y: sateCoord.y * 1000,
        z: sateCoord.z * 1000,
      } as Cartesian3;
      positionProperty.addSample(cesiumTime, cesiumPosition);
    }
    return positionProperty;
  }

  // 创建卫星实例
  createSatelliteEntity() {
    const start = Cesium.JulianDate.fromIso8601(new Date().toISOString());
    const stop = Cesium.JulianDate.addSeconds(start, this.totalSeconds, new Cesium.JulianDate());
    const color = Cesium.Color.fromRandom({ alpha: 1 });
    const satelliteEntity = {
      availability: new Cesium.TimeIntervalCollection([new Cesium.TimeInterval({ start, stop })]),
      description: this.name,
      label: {
        backgroundColor: new Cesium.Color(0.165, 0.165, 0.165, 0.5),
        backgroundPadding: new Cesium.Cartesian2(4, 4),
        fillColor: Cesium.Color.WHITE,
        font: '12px sans-serif',
        horizontalOrigin: Cesium.HorizontalOrigin.LEFT,
        outlineWidth: 1,
        pixelOffset: new Cesium.Cartesian2(0, 5),
        showBackground: true,
        text: this.name,
        verticalOrigin: Cesium.VerticalOrigin.TOP,
        // distanceDisplayCondition: new Cesium.DistanceDisplayCondition(10.0, 50000000),
      },
      name: this.name,
      path: new Cesium.PathGraphics({
        leadTime: this.leadTime,
        material: color,
        show: true,
        trailTime: this.trailTime,
        width: 0.5,
      }),
      point: {
        color,
        pixelSize: 8,
        // scaleByDistance: new Cesium.NearFarScalar(1.5e3, 1, 8.0e8, 0.5),
      },
      position: this._getPositionProperty(),
    };
    return satelliteEntity;
  }

  // 获取地心惯性坐标系坐标
  getPositionEci(date: Date) {
    const result = propagate(this.satrec, date);
    if (result === null) {
      return null;
    }
    return result.position;
  }
}

export default SatelliteEntity;
