package space.nstart.pcp.slots_service.service

import org.springframework.stereotype.Service
import space.nstart.pcp.pcp_types_lib.dto.ballistics.SatelliteOrbitAvailabilityDTO
import space.nstart.pcp.slots_service.configuration.CustomErrorException
import space.nstart.pcp.slots_service.dto.pdcm.SatellitePdcmAvailabilityDTO
import space.nstart.pcp.slots_service.dto.pdcm.SatellitePdcmDetailsDTO
import space.nstart.pcp.slots_service.dto.pdcm.SatellitePdcmSummaryDTO
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
import java.time.LocalDateTime

@Service
class SatellitePdcmService(
    private val satelliteCatalogService: SatelliteCatalogService,
    private val ballisticsService: BallisticsService
) {

    fun satelliteSummaries(): List<SatellitePdcmSummaryDTO> = satelliteSummaries(UtcDateTimes.now())

    fun satelliteSummaries(referenceTime: LocalDateTime): List<SatellitePdcmSummaryDTO> {
        val satellites = satelliteCatalogService.allSatellites()
        val availabilityBySatelliteId = ballisticsService.orbitAvailability().associateBy { it.satelliteId }

        return satellites
            .map { satellite ->
                val availability = availabilityBySatelliteId[satellite.id]
                SatellitePdcmSummaryDTO(
                    satelliteId = satellite.id,
                    code = satellite.code,
                    name = satellite.name,
                    activeInterval = availability.isActive(referenceTime),
                    availableInterval = availability?.toDto()
                )
            }
            .sortedWith(
                compareByDescending<SatellitePdcmSummaryDTO> { it.activeInterval }
                    .thenBy { it.name.lowercase() }
                    .thenBy { it.satelliteId }
            )
    }

    fun satelliteDetails(satelliteId: Long): SatellitePdcmDetailsDTO =
        satelliteDetails(satelliteId, UtcDateTimes.now())

    fun satelliteDetails(satelliteId: Long, referenceTime: LocalDateTime): SatellitePdcmDetailsDTO {
        val satellites = satelliteCatalogService.allSatellites()
        val satellite = satellites.firstOrNull { it.id == satelliteId }
            ?: throw CustomErrorException("Спутник $satelliteId не найден")
        val availability = ballisticsService.orbitAvailability().firstOrNull { it.satelliteId == satelliteId }
        val ascNodes = availability?.let {
            // Для страницы ПДЦМ используется ID спутника из каталога, без перехода к NORAD ID.
            ballisticsService.ascNodes(satellite.id, it.timeStart, it.timeStop).collectList().block()
        } ?: emptyList()

        return SatellitePdcmDetailsDTO(
            satelliteId = satellite.id,
            code = satellite.code,
            name = satellite.name,
            activeInterval = availability.isActive(referenceTime),
            availableInterval = availability?.toDto(),
            ascNodes = ascNodes ?: emptyList()
        )
    }

    private fun SatelliteOrbitAvailabilityDTO?.isActive(referenceTime: LocalDateTime): Boolean {
        if (this == null) {
            return false
        }
        return !referenceTime.isBefore(timeStart) && !referenceTime.isAfter(timeStop)
    }

    private fun SatelliteOrbitAvailabilityDTO.toDto() = SatellitePdcmAvailabilityDTO(
        timeStart = timeStart,
        timeStop = timeStop
    )
}
