package space.nstart.pcp.pcp_request_service.message

import org.slf4j.LoggerFactory
import org.springframework.beans.factory.ObjectProvider
import org.springframework.kafka.annotation.KafkaListener
import org.springframework.stereotype.Component
import space.nstart.pcp.pcp_request_service.service.SatelliteIcEventService
import space.nstart.pcp.pcp_types_lib.dto.ballistics.InitialConditionsDTO
import space.nstart.pcp.pcp_types_lib.dto.ballistics.MovementModel
import space.nstart.pcp.pcp_types_lib.dto.ballistics.SatelliteICDTO
import tools.jackson.databind.ObjectMapper
import tools.jackson.databind.node.ObjectNode

@Component
class SatelliteIcRvKafkaListener(
    objectMapperProvider: ObjectProvider<ObjectMapper>,
    private val satelliteIcEventService: SatelliteIcEventService
) {

    private val logger = LoggerFactory.getLogger(this::class.java)
    private val objectMapper = objectMapperProvider.ifAvailable
        ?: throw IllegalStateException("ObjectMapper bean is required for SatelliteIcRvKafkaListener")

    @KafkaListener(
        id = "placedIcRvFilter",
        topics = ["\${spring.kafka.template.default-topic}"],
        filter = "placedIcRvFilter"
    )
    fun listener(data: String?) {
        consume(data)
    }

    fun consume(data: String?) {
        if (data.isNullOrBlank()) {
            logger.warn("Received empty satellite initial conditions Kafka message")
            return
        }

        try {
            logger.info("Received satellite initial conditions Kafka message body: {}", data)
            val rootNode = objectMapper.readTree(data)
            val payloadNode = rootNode["data"] ?: throw IllegalArgumentException("В сообщении отсутствует поле data")
            val payload = objectMapper.treeToValue(normalizePayload(payloadNode), SatelliteICDTO::class.java)
            satelliteIcEventService.handlePlacedIcRv(payload)
        } catch (exception: Exception) {
            logger.error("Ошибка обработки сообщения о начальных условиях КА: {}", exception.message, exception)
            throw exception
        }
    }

    /**
     * Keeps consumer backward compatible with already published messages where optional IC coefficients were null.
     */
    private fun normalizePayload(payloadNode: tools.jackson.databind.JsonNode): tools.jackson.databind.JsonNode {
        val normalizedPayload = payloadNode.deepCopy()
        if (normalizedPayload is ObjectNode &&
            (normalizedPayload["movementModel"] == null || normalizedPayload["movementModel"].isNull)
        ) {
            normalizedPayload.put("movementModel", DEFAULT_MOVEMENT_MODEL.name)
        }
        val initialConditionsNode = normalizedPayload["ic"]
        if (initialConditionsNode is ObjectNode) {
            if (initialConditionsNode["sBall"] == null || initialConditionsNode["sBall"].isNull) {
                initialConditionsNode.put("sBall", DEFAULT_INITIAL_CONDITIONS.sBall)
            }
            if (initialConditionsNode["f81"] == null || initialConditionsNode["f81"].isNull) {
                initialConditionsNode.put("f81", DEFAULT_INITIAL_CONDITIONS.f81)
            }
        }
        return normalizedPayload
    }

    companion object {
        private val DEFAULT_INITIAL_CONDITIONS = InitialConditionsDTO()
        private val DEFAULT_MOVEMENT_MODEL = MovementModel.FOTO
    }
}
