package space.nstart.pcp.pcp_request_service.entity

import space.nstart.pcp.pcp_request_service.utils.UtcDateTimes
import ballistics.types.KeplerParams
import ballistics.types.RevolutionParameter
import ballistics.utils.fromDateTime
import ballistics.utils.toDateTime
import jakarta.persistence.Column
import jakarta.persistence.Entity
import jakarta.persistence.GeneratedValue
import jakarta.persistence.GenerationType
import jakarta.persistence.Id
import jakarta.persistence.Table
import space.nstart.pcp.pcp_types_lib.dto.ballistics.AscNodeDTO
import space.nstart.pcp.pcp_types_lib.dto.ballistics.KeplersDTO
import java.time.LocalDateTime
import kotlin.math.PI


@Entity
@Table(name = "asc_node")
class AscNodeEntity(
    @Id
    @Column(name = "asc_node_id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    val id : Long? = null,
    @Column(nullable = false)
    val satelliteId : Long = 0,
    @Column(name = "t", nullable = false)
    val time : LocalDateTime = UtcDateTimes.now(),
    @Column(nullable = false)
    val revolution : Long = 0,
    val longitude : Double = 0.0,
    val height : Double = 0.0,
    val x: Double = 0.0,
    val y: Double = 0.0,
    val z: Double = 0.0,
    val vx: Double = 0.0,
    val vy: Double = 0.0,
    val vz: Double = 0.0,
) {
    constructor(an : RevolutionParameter, sat : Long) : this(
        null,
        sat,
        toDateTime(an.vuz.t),
        an.vuz.vit.toLong(),
        an.lVuz * 180 / PI,
        an.hVuz,
        an.vuz.r.x,
        an.vuz.r.y,
        an.vuz.r.z,
        an.vuz.v.x,
        an.vuz.v.y,
        an.vuz.v.z,
    )

    private companion object {
        const val SECONDS_IN_DAY = 86_400.0
        const val NANOS_IN_DAY = 86_400_000_000_000L
    }

    private fun positiveModulo(value: Double, modulo: Double): Double {
        val remainder = value % modulo
        return if (remainder < 0) remainder + modulo else remainder
    }

    /**
     * Местное среднее солнечное время на долготе longitudeRad.
     * Используется та же идея, что в tmest(t, Lvuz): UTC-время суток плюс поправка по долготе.
     */
    private fun localMeanSolarSeconds(epochSeconds: Double, longitudeRad: Double): Double {
        val utcSecondsOfDay = positiveModulo(epochSeconds, SECONDS_IN_DAY)
        val longitudeShiftSeconds = longitudeRad / (2.0 * PI) * SECONDS_IN_DAY
        return positiveModulo(utcSecondsOfDay + longitudeShiftSeconds, SECONDS_IN_DAY)
    }


    fun tmest(t : LocalDateTime, l : Double) : LocalDateTime
    {
        var dl = l;
        if (dl > PI)
            dl = dl - 2 * PI;
        var tm = t.plusSeconds(-60*60*3);
        tm = tm.plusNanos((dl/2.0/PI * 86400.0*1e9).toLong());
        return tm;

    }

    fun toDTO(keps : KeplerParams, rekv : Double) = AscNodeDTO(
        time = this.time,
        revolution = this.revolution.toInt(),
        long = this.longitude,
        height = this.height,
        x = this.x,
        y = this.y,
        z = this.z,
        vx = this.vx,
        vy = this.vy,
        vz = this.vz,
        keps = KeplersDTO(
            (keps.ael - rekv) / 1000.0,
            keps.e,
            keps.nakl * 180 / PI,
            keps.omegab * 180 / PI,
            keps.omegam * 180 / PI
        ),
        localMeanTime = tmest(this.time, this.longitude * PI / 180.0)
    )
}


