package space.nstart.pcp.slots_service.service

import org.springframework.beans.factory.ObjectProvider
import org.springframework.beans.factory.annotation.Value
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Service
import org.springframework.web.reactive.function.client.WebClient
import reactor.core.publisher.Mono
import space.nstart.pcp.pcp_types_lib.dto.ballistics.StationDTO
import space.nstart.pcp.slots_service.configuration.CustomErrorException
import tools.jackson.databind.ObjectMapper

@Service
class StationService(webClientBuilderProvider: ObjectProvider<WebClient.Builder>) {
    private val log = LoggerFactory.getLogger(StationService::class.java)
    private val webClientBuilder: WebClient.Builder = webClientBuilderProvider.ifAvailable ?: WebClient.builder()

    @Value("\${settings.stations-service:stations-service}")
    val url = ""

    fun stations() =
        webClientBuilder.build()
            .get()
            .uri("$url/api/stations")
            .retrieve()
            .onStatus({ status -> status.isError }, ::mapError)
            .bodyToFlux(StationDTO::class.java)


    fun station(id : String) =
        webClientBuilder.build()
            .get()
            .uri("$url/api/stations/$id")
            .retrieve()
            .onStatus({ status -> status.isError }, ::mapError)
            .bodyToMono(StationDTO::class.java)

    fun allStations(): List<StationDTO> =
        stations().collectList().block() ?: emptyList()

    fun allStationsOrEmpty(): List<StationDTO> =
        runCatching { allStations() }
            .onFailure { error ->
                log.warn("Stations service is unavailable. Map page will be opened without stations.", error)
            }
            .getOrDefault(emptyList())

    fun saveStation(request: StationDTO): StationDTO =
        webClientBuilder.build()
            .post()
            .uri("$url/api/stations")
            .bodyValue(request)
            .retrieve()
            .onStatus({ status -> status.isError }, ::mapError)
            .bodyToMono(StationDTO::class.java)
            .block() ?: throw CustomErrorException("Не удалось сохранить станцию: пустой ответ")

    fun deleteStation(id: String) {
        webClientBuilder.build()
            .delete()
            .uri("$url/api/stations/$id")
            .retrieve()
            .onStatus({ status -> status.isError }, ::mapError)
            .toBodilessEntity()
            .block()
    }

    private fun mapError(response: org.springframework.web.reactive.function.client.ClientResponse): Mono<Throwable> =
        response.bodyToMono(String::class.java)
            .defaultIfEmpty("error")
            .flatMap { body -> Mono.error(CustomErrorException(extractErrorMessage(body))) }

    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)
    }
}
