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 space.nstart.pcp.pcp_types_lib.dto.requests.slots.SlotHolderDTO
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): Iterable<EarthCellDTO> {
        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 =
        // TODO: restore countLat/countLong forwarding if pcp-request-service adds aggregation to GET /v1/cells.
        "$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)
    }

    /**
     * Имя/важность заявок-держателей по идентификаторам (M3 — обзор конфликтов BUSY-слотов).
     * Один батч-проход по списку заявок (summary уже несёт name+importance — без N+1 на деталь).
     * Деградация: при недоступности request-service возвращаем пустую карту — бейдж покажет
     * только идентификатор заявки, без падения request-cover.
     */
    fun requestHolders(ids: Set<String>): Map<String, SlotHolderDTO> {
        if (ids.isEmpty()) return emptyMap()
        return runCatching {
            val firstPage = requestsPage(page = 0)
            val items = firstPage.items.toMutableList()
            for (page in 1 until firstPage.totalPages) {
                items += requestsPage(page).items
            }
            items.asSequence()
                .filter { ids.contains(it.id.toString()) }
                .associate { summary ->
                    summary.id.toString() to SlotHolderDTO(
                        requestId = summary.id.toString(),
                        name = summary.name.takeIf { it.isNotBlank() },
                        importance = summary.importance,
                    )
                }
        }.getOrElse { emptyMap() }
    }

    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)

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

@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,
)

@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? = null,
    val currentPercent: Double = 0.0,
)

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

@JsonIgnoreProperties(ignoreUnknown = true)
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 = "",
)
