package space.nstart.pcp.pcp_request_service.controller

import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.Parameter
import jakarta.validation.Valid
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController
import space.nstart.pcp.pcp_request_service.configuration.CustomValidationException
import space.nstart.pcp.pcp_request_service.service.SatelliteIcEventService
import space.nstart.pcp.pcp_request_service.service.SatelliteService
import space.nstart.pcp.pcp_request_service.utils.UtcDateTimes
import space.nstart.pcp.pcp_types_lib.dto.ballistics.ExactTimePositionRequestDTO
import space.nstart.pcp.pcp_types_lib.dto.ballistics.SatelliteICDTO
import java.time.LocalDateTime
import java.time.format.DateTimeParseException


@RestController
@RequestMapping("/api/satellites")
class SatelliteController {
    @Autowired
    private lateinit var satelliteService: SatelliteService

    @Autowired
    private lateinit var satelliteIcEventService: SatelliteIcEventService

    @PostMapping("/{satellite_id}/extract-time")
    fun extractTime(
        @PathVariable("satellite_id") satelliteId : Long,
        @Valid @RequestBody body : ExactTimePositionRequestDTO
    ) =
        satelliteService.exactTime(satelliteId, body)

    @PostMapping("/receive-rv")
    fun receiveRv(@RequestBody body: SatelliteICDTO): ResponseEntity<Void> {
        satelliteIcEventService.handlePlacedIcRv(body)
        return ResponseEntity.accepted().build()
    }

    @GetMapping("/{satellite_id}/initial-conditions/current")
    fun currentInitialConditions(
        @PathVariable("satellite_id") satelliteId: Long
    ): ResponseEntity<SatelliteICDTO> =
        satelliteService.currentInitialConditions(satelliteId)
            ?.let { ResponseEntity.ok(it) }
            ?: ResponseEntity.noContent().build()



    @Operation(
        summary = "Получить ПДЦМ",
        description = "Возвраает ПДЦМ для заданного аппарата на заданном интервале времени с шагом 60 с"
    )
    @GetMapping("/{norad_id}/orbit")
    fun getPoints(
        @Parameter(description = "Идентификатор NORAD", example = "56756")
        @PathVariable("norad_id") noradId : Long,
        @Parameter(description = "Время начала (если null, то мин. время для выбранного КА)")
        @RequestParam("time_start", required = false) timeStart : String?,
        @Parameter(description = "Время конца (если null, то макс. время для выбранного КА)")
        @RequestParam("time_stop", required = false) timeStrop : String?
    ) =
        satelliteService.getOrbPoints(noradId, parseOptionalUtcDateTime("time_start", timeStart), parseOptionalUtcDateTime("time_stop", timeStrop))

    @Operation(
        summary = "Получить доступные интервалы прогноза по всем КА",
        description = "Возвращает минимальное и максимальное время ПДЦМ для каждого КА. Если time задан, возвращаются только КА, у которых это время попадает в доступный интервал"
    )
    @GetMapping("/orbit/availability")
    fun getOrbitAvailability(
        @Parameter(description = "Проверяемое время. Если null, возвращаются все КА, для которых есть точки ПДЦМ")
        @RequestParam("time", required = false) time: String?
    ) =
        satelliteService.getOrbitAvailability(parseOptionalUtcDateTime("time", time))


    @GetMapping("/orbit/availability/{id}")
    fun getOrbitAvailabilityById(
        @PathVariable id : Long
    ) =
        satelliteService.getOrbitAvailability(null).filter { it.satelliteId == id }.firstOrNull()?:
        throw CustomValidationException("Спутник с заданным идентификатором не зарегистрирован")


    @Operation(
        summary = "Получить параметры прохождения ВУЗов",
        description = "Возвраает массив ВУЗов для заданного аппарата на заданном интервале времени"
    )
    @GetMapping("/{norad_id}/asc-node")
    fun getAskNodes(
        @Parameter(description = "Идентификатор NORAD", example = "56756")
        @PathVariable("norad_id") noradId : Long,
        @Parameter(description = "Время начала (если null, то мин. время для выбранного КА)")
        @RequestParam("time_start", required = false) timeStart : String?,
        @Parameter(description = "Время конца (если null, то макс. время для выбранного КА)")
        @RequestParam("time_stop", required = false) timeStrop : String?
    ) =
        satelliteService.getAscNodes(noradId, parseOptionalUtcDateTime("time_start", timeStart), parseOptionalUtcDateTime("time_stop", timeStrop))


    @Operation(
        summary = "Получить параметры трассы полета и полосы обзора",
        description = "Возвращает параметры ТП и ПО на заданном интервале времен с шагом 60 с"
    )
    @GetMapping("/{norad_id}/flight-line")
    fun getFL(
        @Parameter(description = "Идентификатор NORAD", example = "56756")
        @PathVariable("norad_id") noradId : Long,
        @Parameter(description = "Время начала (если null, то мин. время для выбранного КА)")
        @RequestParam("time_start", required = false) timeStart : String?,
        @Parameter(description = "Время конца (если null, то макс. время для выбранного КА)")
        @RequestParam("time_stop", required = false) timeStrop : String?
    ) =
        satelliteService.getFL(noradId, parseOptionalUtcDateTime("time_start", timeStart), parseOptionalUtcDateTime("time_stop", timeStrop), 60.0)




    @Operation(
        summary = "Получить параметры ЗРВ",
        description = "Возвращает параметры ЗРВ на заданном интервале времен"
    )
    @GetMapping("/{norad_id}/rva")
    fun getRVA(
        @Parameter(description = "Идентификатор NORAD", example = "56756")
        @PathVariable("norad_id") noradId : Long,
        @Parameter(description = "Время начала (если null, то мин. время для выбранного КА)")
        @RequestParam("time_start") timeStart : String,
        @Parameter(description = "Время конца (если null, то макс. время для выбранного КА)")
        @RequestParam("time_stop") timeStrop : String
    ) =
        satelliteService.getRVA(noradId, parseRequiredUtcDateTime("time_start", timeStart), parseRequiredUtcDateTime("time_stop", timeStrop))


    @PostMapping("/{satellite_id}/clear")
    fun clear(
        @PathVariable("satellite_id") satelliteId : Long
    ) =
        satelliteService.clear(satelliteId)

    private fun parseOptionalUtcDateTime(name: String, value: String?): LocalDateTime? =
        value?.let { parseRequiredUtcDateTime(name, it) }

    private fun parseRequiredUtcDateTime(name: String, value: String): LocalDateTime =
        try {
            UtcDateTimes.parse(value)
        } catch (_: DateTimeParseException) {
            throw CustomValidationException(
                "Некорректный формат параметра '$name': '$value'. Ожидается ISO-8601 UTC/offset, например 2026-06-17T06:28:27Z, или legacy UTC-naive yyyy-MM-dd'T'HH:mm:ss",
            )
        }

}
