package space.nstart.pcp.slots_service.controller

import org.springframework.web.bind.annotation.GetMapping
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_types_lib.dto.BookedSlotDetailsDTO
import space.nstart.pcp.pcp_types_lib.dto.satellite.SatelliteSummaryDTO
import space.nstart.pcp.slots_service.dto.bookings.BookedSlotViewDTO
import space.nstart.pcp.slots_service.dto.bookings.BookingFilterOptionsDTO
import space.nstart.pcp.slots_service.dto.bookings.BookingRequestOptionDTO
import space.nstart.pcp.slots_service.service.EarthService
import space.nstart.pcp.slots_service.service.SatelliteCatalogService
import space.nstart.pcp.slots_service.service.SlotService
import java.time.LocalDateTime

@RestController
@RequestMapping("/api/bookings")
class BookingsController(
    private val slotService: SlotService,
    private val satelliteCatalogService: SatelliteCatalogService,
    private val earthService: EarthService,
) {

    @GetMapping("/options")
    fun options(): BookingFilterOptionsDTO {
        val satellites = satelliteCatalogService.allSatellites()
        val requests = runCatching {
            earthService.reqs()
                .collectList()
                .block()
                .orEmpty()
                .mapNotNull { request ->
                    val id = request.requestId?.toString() ?: return@mapNotNull null
                    BookingRequestOptionDTO(id = id, name = request.name)
                }
                .sortedWith(compareBy<BookingRequestOptionDTO> { it.name.ifBlank { it.id } }.thenBy { it.id })
        }.getOrDefault(emptyList())

        return BookingFilterOptionsDTO(
            satellites = satellites,
            requests = requests,
        )
    }

    @GetMapping
    fun bookedSlots(
        @RequestParam(required = false) timeStart: LocalDateTime?,
        @RequestParam(required = false) timeStop: LocalDateTime?,
        @RequestParam(required = false) requestId: String?,
        @RequestParam(required = false) satelliteIds: List<Long>?,
    ): List<BookedSlotViewDTO> {
        val satellitesById = satelliteCatalogService.allSatellites().associateBy { it.id }
        return slotService.searchBooked(
            timeStart = timeStart,
            timeStop = timeStop,
            requestId = requestId,
            satelliteIds = satelliteIds,
        )
            .map { booking -> booking.toView(satellitesById[booking.satelliteId]) }
            .sortedWith(
                compareBy<BookedSlotViewDTO> { it.timeStart ?: LocalDateTime.MAX }
                    .thenBy { it.satelliteId }
                    .thenBy { it.slotNum }
                    .thenBy { it.cycle }
                    .thenBy { it.bookedSlotId }
            )
    }

    private fun BookedSlotDetailsDTO.toView(satellite: SatelliteSummaryDTO?): BookedSlotViewDTO = BookedSlotViewDTO(
        bookedSlotId = bookedSlotId,
        satelliteId = satelliteId,
        satelliteCode = satellite?.code,
        satelliteName = satellite?.name,
        satelliteTypeCode = satellite?.typeCode,
        slotNum = slotNum,
        cycle = cycle,
        timeStart = timeStart,
        timeStop = timeStop,
        durationSeconds = durationSeconds,
        roll = roll,
        revolution = revolution,
        revolutionSign = revolutionSign,
        status = status,
        requestIds = requestIds,
    )
}
