package space.nstart.pcp.pcp_mission_planing_service.service

import org.springframework.beans.factory.ObjectProvider
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Service
import org.springframework.web.reactive.function.client.WebClient
import org.springframework.web.reactive.function.client.bodyToFlux
import org.springframework.web.reactive.function.client.bodyToMono
import reactor.core.publisher.Mono
import space.nstart.pcp.pcp_types_lib.configuration.CustomErrorException
import space.nstart.pcp.pcp_types_lib.dto.satellite.mission.SatelliteModeResponseDTO
import space.nstart.pcp.pcp_types_lib.dto.satellite.mission.SatelliteModeSnapshotResponseDTO
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
import tools.jackson.databind.ObjectMapper
import java.time.LocalDateTime

@Service
/**
 * Stage 5 primary reader for persisted stage 4 survey modes.
 * The source of truth is pcp-complex-mission-service, not slots-service.
 */
class ComplexMissionService(webClientBuilderProvider: ObjectProvider<WebClient.Builder>) {

    private val webClientBuilder: WebClient.Builder = webClientBuilderProvider.ifAvailable ?: WebClient.builder()

    @Value("\${pcp.services.complex-mission-service:pcp-complex-mission-service}")
    val url = ""

    private fun client() = webClientBuilder.baseUrl(url).build()

    fun activeSnapshot(
        satelliteId: Long
    ): SatelliteModeSnapshotResponseDTO? =
        client()
            .get()
            .uri(
                "/api/satellites/{satelliteId}/snapshots/active",
                satelliteId
            )
            .retrieve()
            .onStatus({ status -> status.isError }) { response ->
                response.bodyToMono<String>()
                    .defaultIfEmpty("error")
                    .flatMap { body -> Mono.error(CustomErrorException(extractErrorMessage(body))) }
            }
            .bodyToMono(SatelliteModeSnapshotResponseDTO::class.java)
            .blockOptional()
            .orElse(null)

    fun snapshot(snapshotId: Long): SatelliteModeSnapshotResponseDTO =
        client()
            .get()
            .uri("/api/satellites/snapshots/{snapshotId}", snapshotId)
            .retrieve()
            .onStatus({ status -> status.isError }) { response ->
                response.bodyToMono<String>()
                    .defaultIfEmpty("error")
                    .flatMap { body -> Mono.error(CustomErrorException(extractErrorMessage(body))) }
            }
            .bodyToMono(SatelliteModeSnapshotResponseDTO::class.java)
            .block()
            ?: throw CustomErrorException("Snapshot $snapshotId not found")

    fun snapshotModes(
        snapshotId: Long,
        intervalStart: LocalDateTime,
        intervalEnd: LocalDateTime
    ): List<SatelliteModeResponseDTO> =
        client()
            .get()
            .uri(
                "/api/satellites/snapshots/{snapshotId}/modes?intervalStart={intervalStart}&intervalEnd={intervalEnd}",
                snapshotId,
                UtcDateTimes.format(intervalStart),
                UtcDateTimes.format(intervalEnd)
            )
            .retrieve()
            .onStatus({ status -> status.isError }) { response ->
                response.bodyToMono<String>()
                    .defaultIfEmpty("error")
                    .flatMap { body -> Mono.error(CustomErrorException(extractErrorMessage(body))) }
            }
            .bodyToFlux<SatelliteModeResponseDTO>()
            .collectList()
            .block()
            .orEmpty()

    private fun extractErrorMessage(body: String): String {
        val text = body.trim()
        if (text.isEmpty()) return "error"

        return runCatching {
            val node = ObjectMapper().readTree(text)
            when {
                node.hasNonNull("error") -> node["error"].asText()
                node.hasNonNull("message") -> node["message"].asText()
                else -> text
            }
        }.getOrDefault(text)
    }
}
