package space.nstart.pcp.complan.service


import org.springframework.beans.factory.annotation.Value
import org.springframework.core.ParameterizedTypeReference
import org.springframework.stereotype.Service
import org.springframework.web.client.RestClient
import org.springframework.web.util.UriComponentsBuilder
import space.nstart.pcp.complan.types.ObjViewRequestDTO
import space.nstart.pcp.pcp_types_lib.dto.ballistics.ExactTimePositionRequestDTO
import space.nstart.pcp.pcp_types_lib.dto.ballistics.FlightLineDTO
import space.nstart.pcp.pcp_types_lib.dto.ballistics.ObjDTO as BallisticsObjDTO
import space.nstart.pcp.pcp_types_lib.dto.ballistics.OrbPointDTO
import space.nstart.pcp.pcp_types_lib.dto.ballistics.SatelliteOrbitAvailabilityDTO
import space.nstart.pcp.pcp_types_lib.dto.ballistics.SquareViewParamDTO
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
import java.time.LocalDateTime

/**Получение данных от сервиса баллистики*/
@Service
class BallTaskServce {


    @Value("\${complex-plan.ballistics-service-url}")
    private lateinit var ballisticsServiceUrl: String

    fun getOrbitAvailability(noradId : Long): SatelliteOrbitAvailabilityDTO =
        RestClient.create()
            .get()
            .uri(buildBallisticsUri("/api/satellites/orbit/availability/$noradId"))
            .retrieve()
            .body(SatelliteOrbitAvailabilityDTO::class.java)
            ?: throw IllegalStateException("Пустой ответ баллистики для доступности ПДЦМ КА $noradId")

    fun getOrbitAvailability(time: LocalDateTime? = null): List<SatelliteOrbitAvailabilityDTO> =
        RestClient.create()
            .get()
            .uri(
                UriComponentsBuilder
                    .fromUriString(ballisticsServiceUrl)
                    .path("/api/satellites/orbit/availability")
                    .apply { time?.let { queryParam("time", UtcDateTimes.format(it)) } }
                    .toUriString()
            )
            .retrieve()
            .body(ORBIT_AVAILABILITY_LIST_TYPE)
            .orEmpty()

    fun getFlightLine(noradId: Long, timeStart : LocalDateTime, timeStop : LocalDateTime): List<FlightLineDTO> =
        RestClient.create()
            .get()
            .uri(
                UriComponentsBuilder
                    .fromUriString(ballisticsServiceUrl)
                    .path("/api/satellites/$noradId/flight-line")
                    .queryParam("time_start", UtcDateTimes.format(timeStart))
                    .queryParam("time_stop", UtcDateTimes.format(timeStop))
                    .toUriString()
            )
            .retrieve()
            .body(FLIGHT_LINE_LIST_TYPE)
            .orEmpty()

    fun getExactTime(noradId: Long, request: ExactTimePositionRequestDTO): List<OrbPointDTO> =
        RestClient.create()
            .post()
            .uri(buildBallisticsUri("/api/satellites/$noradId/extract-time"))
            .body(
                mapOf(
                    "time" to UtcDateTimes.format(request.time),
                    "timeStop" to request.timeStop?.let { UtcDateTimes.format(it) },
                    "stepMs" to request.stepMs
                )
            )
            .retrieve()
            .body(ORB_POINT_LIST_TYPE)
            .orEmpty()

    fun getMplForObjects(objects : ObjViewRequestDTO): List<SquareViewParamDTO> {
        val satellites = objects.satellites?.toList().orEmpty()
        val timeStart = objects.timeStart ?: throw IllegalArgumentException("timeStart must be specified for MPL request")
        val timeStop = objects.timeStop ?: throw IllegalArgumentException("timeStop must be specified for MPL request")

        return objects.objects.flatMap { obj ->
            RestClient.create()
                .post()
                .uri(buildBallisticsUri("/api/obj-view/mpl-square"))
                .body(
                    mapOf(
                        "satellites" to satellites,
                        "timeStart" to UtcDateTimes.format(timeStart),
                        "timeStop" to UtcDateTimes.format(timeStop),
                        "obj" to BallisticsObjDTO(
                            id = obj.id.toString(),
                            position = obj.position,
                            contourWKT = obj.contourWKT
                        ),
                        "sunAngleMin" to objects.sunAngleMin
                    )
                )
                .retrieve()
                .body(SQUARE_VIEW_PARAM_LIST_TYPE)
                .orEmpty()
        }
    }

    private fun buildBallisticsUri(path: String): String =
        UriComponentsBuilder
            .fromUriString(ballisticsServiceUrl)
            .path(path)
            .toUriString()

    private companion object {
        val FLIGHT_LINE_LIST_TYPE = object : ParameterizedTypeReference<List<FlightLineDTO>>() {}
        val ORB_POINT_LIST_TYPE = object : ParameterizedTypeReference<List<OrbPointDTO>>() {}
        val ORBIT_AVAILABILITY_LIST_TYPE = object : ParameterizedTypeReference<List<SatelliteOrbitAvailabilityDTO>>() {}
        val SQUARE_VIEW_PARAM_LIST_TYPE = object : ParameterizedTypeReference<List<SquareViewParamDTO>>() {}
    }
}
