package space.nstart.pcp.pcp_satellites_service.service

import com.fasterxml.jackson.annotation.JsonIgnoreProperties
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 space.nstart.pcp.pcp_types_lib.dto.requests.CellRequestDOT
import space.nstart.pcp.pcp_types_lib.dto.requests.EarthCellWithRequestsDTO
import java.util.UUID
import kotlin.system.measureTimeMillis


@Service
/**
 * Request/grid reader backed by pcp-request-service /v1 cells API.
 */
class EarthGridService(webClientBuilderProvider: ObjectProvider<WebClient.Builder>) {
    private val webClientBuilder: WebClient.Builder = webClientBuilderProvider.ifAvailable ?: WebClient.builder()
    private val logger = LoggerFactory.getLogger(this::class.java)

    @Value("\${settings.request-service:\${settings.earth-grid-service:pcp-request-service}}")
    val url = ""

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

    fun cells(countLat: Int? = null, countLong: Int? = null): Iterable<EarthCellWithRequestsDTO> {
        var cells = emptyList<EarthCellWithRequestsDTO>()
        val durationMs = measureTimeMillis {
            cells = client()
                .get()
                .uri(cellsWithRequestsUri(countLat, countLong))
                .retrieve()
                .bodyToMono(CellsWithRequestsResponseDto::class.java)
                .block()
                ?.items
                .orEmpty()
                .map { cell -> cell.toEarthCellWithRequestsDto() }
        }
        logger.info(
            "request-service v1 cells with requests loaded: cells={}, durationMs={}",
            cells.size,
            durationMs
        )
        return cells
    }

    private fun cellsWithRequestsUri(countLat: Int?, countLong: Int?): String {
        val params = mutableListOf("minImportance=$REQUEST_GRID_MIN_IMPORTANCE")
        countLat?.let { params += "countLat=$it" }
        countLong?.let { params += "countLong=$it" }
        return "/v1/cells/priority-map?${params.joinToString("&")}"
    }

    private fun CellWithRequestsResponseDto.toEarthCellWithRequestsDto(): EarthCellWithRequestsDTO =
        EarthCellWithRequestsDTO(
            id = cellNum,
            num = cellNum,
            latitude = latitude,
            longitude = longitude,
            importance = importance,
            contour = contour,
            requests = requests.map { request -> request.toCellRequestDto() },
        )

    private fun CellRequestFragmentResponseDto.toCellRequestDto(): CellRequestDOT =
        CellRequestDOT(
            requestId = requestId.toString(),
            covPercent = coveragePercent,
            importance = importance,
            contour = contour,
        )

}

@JsonIgnoreProperties(ignoreUnknown = true)
data class CellsWithRequestsResponseDto(
    val items: List<CellWithRequestsResponseDto> = emptyList(),
)

@JsonIgnoreProperties(ignoreUnknown = true)
data class CellWithRequestsResponseDto(
    val cellNum: Long = 0,
    val latitude: Double = 0.0,
    val longitude: Double = 0.0,
    val importance: Double = 0.0,
    val contour: String = "",
    val requests: List<CellRequestFragmentResponseDto> = emptyList(),
)

@JsonIgnoreProperties(ignoreUnknown = true)
data class CellRequestFragmentResponseDto(
    val requestId: UUID = UUID(0L, 0L),
    val contour: String = "",
    val coveragePercent: Double = 0.0,
    val importance: Double = 0.0,
)

private const val REQUEST_GRID_MIN_IMPORTANCE = "0.001"
