package space.nstart.pcp.pcp_mission_planing_service.service

import com.fasterxml.jackson.core.JsonProcessingException
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import org.apache.kafka.clients.producer.ProducerRecord
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.ObjectProvider
import org.springframework.beans.factory.annotation.Value
import org.springframework.kafka.core.KafkaTemplate
import org.springframework.stereotype.Component
import space.nstart.pcp.pcp_types_lib.dto.requests.slots.BookedSlotStatus
import space.nstart.pcp.pcp_types_lib.dto.requests.slots.BookedSlotsStatusChangedEvent
import space.nstart.pcp.pcp_types_lib.message.KafkaMessage
import space.nstart.pcp.pcp_types_lib.message.PcpKafkaEvent
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
import java.nio.charset.StandardCharsets
import java.time.Clock
import java.util.UUID

@Component
/**
 * Kafka-first publisher for booked slots lifecycle updates.
 * Legacy HTTP processing in slots-service is retired from the main pipeline.
 */
class BookedSlotsEventPublisher(
    kafkaTemplateProvider: ObjectProvider<KafkaTemplate<String, Any>>,
    objectMapperProvider: ObjectProvider<ObjectMapper>,
    @Value("\${app.kafka.topics.booked-slots:pcp.booked-slots}") private val topic: String,
    @Value("\${spring.application.name:pcp-mission-planing-service}") private val applicationName: String,
    private val clock: Clock = Clock.systemUTC()
) {

    private val logger = LoggerFactory.getLogger(this::class.java)
    private val kafkaTemplate = kafkaTemplateProvider.ifAvailable
    private val objectMapper = objectMapperProvider.ifAvailable ?: jacksonObjectMapper().findAndRegisterModules()

    fun publish(
        bookedSlotIds: List<Long>,
        status: BookedSlotStatus,
        missionId: UUID? = null,
        modeId: Long? = null,
        satelliteId: Long? = null
    ) {
        val normalizedIds = bookedSlotIds.distinct().filter { it > 0 }
        if (normalizedIds.isEmpty()) {
            return
        }
        val template = kafkaTemplate
        if (template == null) {
            logger.warn(
                "Kafka booked slots publisher is unavailable, event skipped: status={}, bookedSlots={}, missionId={}, modeId={}",
                status,
                normalizedIds.size,
                missionId,
                modeId
            )
            return
        }

        val event = BookedSlotsStatusChangedEvent(
            bookedSlotIds = normalizedIds,
            status = status,
            occurredAt = UtcDateTimes.now(clock),
            sourceService = applicationName,
            missionId = missionId,
            modeId = modeId,
            satelliteId = satelliteId
        )
        val message = KafkaMessage(
            type = PcpKafkaEvent.BookedSlotsStatusChangedEvent,
            data = event
        ).apply {
            source = applicationName
        }

        val payload = try {
            objectMapper.writeValueAsString(message)
        } catch (exception: JsonProcessingException) {
            throw IllegalStateException("Failed to serialize booked slots event", exception)
        }

        val record = ProducerRecord<String, Any>(topic, missionId?.toString() ?: modeId?.toString() ?: normalizedIds.first().toString(), payload)
        record.headers().add("type", PcpKafkaEvent.BookedSlotsStatusChangedEvent.name.toByteArray(StandardCharsets.UTF_8))
        template.send(record)
        logger.info(
            "Published Kafka-first booked slots status event: status={}, bookedSlots={}, missionId={}, modeId={}, satelliteId={}",
            status,
            normalizedIds.size,
            missionId,
            modeId,
            satelliteId
        )
    }
}
