package space.nstart.pcp.slots_service.service

import com.fasterxml.jackson.annotation.JsonIgnoreProperties
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 reactor.core.publisher.Flux
import space.nstart.pcp.pcp_types_lib.dto.requests.EarthCellDTO
import space.nstart.pcp.pcp_types_lib.dto.requests.RequestDTO
import space.nstart.pcp.pcp_types_lib.dto.requests.RequestWithCellsDTO
import java.util.UUID


@Service
/**
 * Request/grid reader backed by pcp-request-service.
 */
class EarthService(
    webClientBuilderProvider: ObjectProvider<WebClient.Builder>,
    @param:Value("\${settings.request-service:\${settings.earth-grid-service:pcp-request-service}}")
    private val url: String = "pcp-request-service",
) {
    private val webClientBuilder: WebClient.Builder = webClientBuilderProvider.ifAvailable ?: WebClient.builder()

    fun cells(importance: Double, countLat: Int? = null, countLong: Int? = null): Iterable<EarthCellDTO> {
        /**
         * countLat/countLong remain in the UI contract for compatibility.
         * TODO: forward them when pcp-request-service restores aggregation for GET /v1/cells.
         */
        val firstPage = cellsPage(importance, page = 0)
        val items = firstPage.items.toMutableList()

        for (page in 1 until firstPage.totalPages) {
            items += cellsPage(importance, page).items
        }

        return items.map { cell -> cell.toEarthCellDto() }
    }

    private fun cellsPage(importance: Double, page: Int): CellsListResponseDto =
        webClientBuilder.build()
            .get()
            .uri(cellsUri(importance, page))
            .retrieve()
            .bodyToMono(CellsListResponseDto::class.java)
            .block() ?: CellsListResponseDto()

    private fun cellsUri(importance: Double, page: Int): String =
        "$url/v1/cells?minImportance=$importance&page=$page&size=$CELLS_PAGE_SIZE"

    fun reqcells(id : String): RequestWithCellsDTO? =
        webClientBuilder.build()
            .get()
            .uri("$url/v1/requests/$id/cells")
            .retrieve()
            .bodyToMono(RequestWithCellsResponseDto::class.java)
            .block()
            ?.toRequestWithCellsDto()

    fun reqs(): Flux<RequestDTO> {
        val firstPage = requestsPage(page = 0)
        val summaries = firstPage.items.toMutableList()

        for (page in 1 until firstPage.totalPages) {
            summaries += requestsPage(page).items
        }

        val requests = summaries.map { summary -> requestDetails(summary.id).toRequestDto() }
        return Flux.fromIterable(requests)
    }

    private fun requestsPage(page: Int): RequestsListResponseDto =
        webClientBuilder.build()
            .get()
            .uri("$url/v1/requests?page=$page&size=$REQUESTS_PAGE_SIZE")
            .retrieve()
            .bodyToMono(RequestsListResponseDto::class.java)
            .block() ?: RequestsListResponseDto()

    private fun requestDetails(id: UUID): RequestDetailsResponseDto =
        webClientBuilder.build()
            .get()
            .uri("$url/v1/requests/$id")
            .retrieve()
            .bodyToMono(RequestDetailsResponseDto::class.java)
            .block() ?: RequestDetailsResponseDto(id = id)

    fun addRequest(request: RequestDTO): RequestDTO? =
        webClientBuilder.build()
            .post()
            .uri("$url/v1/requests")
            .bodyValue(request.toCreateRequestRequestDto())
            .retrieve()
            .bodyToMono(CreateRequestResponseDto::class.java)
            .block()
            ?.toRequestDto(request)

    private fun CellSummaryResponseDto.toEarthCellDto(): EarthCellDTO =
        EarthCellDTO(
            id = cellNum,
            num = cellNum,
            latitude = latitude,
            longitude = longitude,
            importance = importance,
            contour = contour,
        )

    private fun RequestDetailsResponseDto.toRequestDto(): RequestDTO =
        RequestDTO(
            requestId = id,
            name = name,
            importance = importance,
            contour = geometry,
        )

    private fun RequestWithCellsResponseDto.toRequestWithCellsDto(): RequestWithCellsDTO =
        RequestWithCellsDTO(
            request = request.toRequestDto(),
            cells = cells.map { cell -> cell.toEarthCellDto() },
        )

    private fun RequestResponseDto.toRequestDto(): RequestDTO =
        RequestDTO(
            requestId = id,
            name = name,
            importance = importance,
            contour = geometry,
        )

    private fun RequestDTO.toCreateRequestRequestDto(): CreateRequestRequestDto =
        CreateRequestRequestDto(
            id = UUID.randomUUID(),
            name = name,
            geometry = contour,
            importance = importance,
            beginDateTime = DEFAULT_BEGIN_DATE_TIME,
            endDateTime = DEFAULT_END_DATE_TIME,
            kpp = emptyList(),
            highPriorityTransmit = false,
            optics = DEFAULT_OPTICS,
            rsa = null,
        )

    private fun CreateRequestResponseDto.toRequestDto(source: RequestDTO): RequestDTO =
        RequestDTO(
            requestId = id,
            importance = importance,
            contour = source.contour,
            name = name,
        )

    private fun RequestCellResponseDto.toEarthCellDto(): EarthCellDTO =
        EarthCellDTO(
            id = cellNum,
            num = cellNum,
            importance = importance,
            contour = contour,
        )

    private companion object {
        const val CELLS_PAGE_SIZE = 500
        const val REQUESTS_PAGE_SIZE = 500
        const val DEFAULT_BEGIN_DATE_TIME = "2026-01-01T00:00:00Z"
        const val DEFAULT_END_DATE_TIME = "2026-12-31T23:59:59Z"
        val DEFAULT_OPTICS = OpticsParamsDto(
            resultType = "PANCHROMATIC",
            resolution = 5.0,
            sunAngleMin = 10.0,
            sunAngleMax = 90.0,
            clouds = 100.0,
        )
    }
}

data class CreateRequestRequestDto(
    val id: UUID,
    val name: String,
    val geometry: String,
    val importance: Double,
    val beginDateTime: String,
    val endDateTime: String,
    val kpp: List<Int>,
    val highPriorityTransmit: Boolean,
    val optics: OpticsParamsDto?,
    val rsa: RsaParamsDto?,
)

data class OpticsParamsDto(
    val resultType: String,
    val resolution: Double,
    val sunAngleMin: Double? = null,
    val sunAngleMax: Double? = null,
    val clouds: Double? = null,
)

data class RsaParamsDto(
    val resultType: String,
    val interferometry: Boolean? = null,
    val polarisation: String,
    val resolution: Double,
)

@JsonIgnoreProperties(ignoreUnknown = true)
data class CreateRequestResponseDto(
    val id: UUID = UUID(0L, 0L),
    val name: String = "",
    val status: String = "",
    val surveyType: String = "",
    val importance: Double = 0.0,
    val message: String = "",
)

@JsonIgnoreProperties(ignoreUnknown = true)
data class RequestWithCellsResponseDto(
    val request: RequestResponseDto = RequestResponseDto(),
    val cells: List<RequestCellResponseDto> = emptyList(),
)

@JsonIgnoreProperties(ignoreUnknown = true)
data class RequestResponseDto(
    val id: UUID = UUID(0L, 0L),
    val name: String = "",
    val status: String = "",
    val surveyType: String = "",
    val geometry: String = "",
    val importance: Double = 0.0,
    val beginDateTime: String = "",
    val endDateTime: String = "",
    val kpp: List<Int> = emptyList(),
    val highPriorityTransmit: Boolean = false,
    val optics: Any? = null,
    val rsa: Any? = null,
    val coverage: CoverageStateDto = CoverageStateDto(),
    val createdAt: String = "",
    val updatedAt: String = "",
    val deletedAt: String? = null,
)

@JsonIgnoreProperties(ignoreUnknown = true)
data class RequestCellResponseDto(
    val cellNum: Long = 0,
    val coveragePercent: Double = 0.0,
    val importance: Double = 0.0,
    val contour: String = "",
)

@JsonIgnoreProperties(ignoreUnknown = true)
data class CoverageStateDto(
    val requiredPercent: Double = 0.0,
    val currentPercent: Double = 0.0,
)

@JsonIgnoreProperties(ignoreUnknown = true)
data class RequestsListResponseDto(
    val items: List<RequestSummaryResponseDto> = emptyList(),
    val page: Int = 0,
    val size: Int = 0,
    val totalItems: Long = 0,
    val totalPages: Int = 0,
)

@JsonIgnoreProperties(ignoreUnknown = true)
data class RequestSummaryResponseDto(
    val id: UUID,
    val name: String = "",
    val status: String = "",
    val surveyType: String = "",
    val importance: Double = 0.0,
    val beginDateTime: String = "",
    val endDateTime: String = "",
    val kpp: List<Int> = emptyList(),
    val highPriorityTransmit: Boolean = false,
)

@JsonIgnoreProperties(ignoreUnknown = true)
data class RequestDetailsResponseDto(
    val id: UUID,
    val name: String = "",
    val geometry: String = "",
    val importance: Double = 0.0,
)

data class CellsListResponseDto(
    val items: List<CellSummaryResponseDto> = emptyList(),
    val page: Int = 0,
    val size: Int = 0,
    val totalItems: Long = 0,
    val totalPages: Int = 0,
)

data class CellSummaryResponseDto(
    val cellNum: Long = 0,
    val latitude: Double = 0.0,
    val longitude: Double = 0.0,
    val importance: Double = 0.0,
    val contour: String = "",
)
