package space.nstart.pcp.pcp_mission_planing_service.service

import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import org.apache.kafka.clients.producer.ProducerRecord
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
import org.mockito.ArgumentCaptor
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import org.springframework.beans.factory.ObjectProvider
import org.springframework.kafka.core.KafkaTemplate
import space.nstart.pcp.pcp_types_lib.dto.requests.slots.BookedSlotStatus
import space.nstart.pcp.pcp_types_lib.message.PcpKafkaEvent
import java.util.UUID

class BookedSlotsEventPublisherTest {

    private val objectMapper = jacksonObjectMapper().findAndRegisterModules()
    private val kafkaTemplate = mock(KafkaTemplate::class.java) as KafkaTemplate<String, Any>
    private val publisher = BookedSlotsEventPublisher(
        kafkaTemplateProvider = SingleObjectProvider(kafkaTemplate),
        objectMapperProvider = SingleObjectProvider(objectMapper),
        topic = "pcp.slots.status.v1",
        applicationName = "pcp-mission-planing-service"
    )

    @Test
    fun `publish sends booked slots lifecycle event to kafka`() {
        val missionId = UUID.randomUUID()

        publisher.publish(
            bookedSlotIds = listOf(10L, 20L, 10L),
            status = BookedSlotStatus.PROCESSED,
            missionId = missionId,
            modeId = 77L,
            satelliteId = 88L
        )

        val captor = ArgumentCaptor.forClass(ProducerRecord::class.java) as ArgumentCaptor<ProducerRecord<String, Any>>
        verify(kafkaTemplate).send(captor.capture())

        val record = captor.value
        assertEquals("pcp.slots.status.v1", record.topic())
        assertEquals(missionId.toString(), record.key())
        val typeHeader = record.headers().lastHeader("type")
        assertEquals(PcpKafkaEvent.BookedSlotsStatusChangedEvent.name, typeHeader.value().decodeToString())

        val payload = objectMapper.readTree(record.value() as String)
        assertEquals(PcpKafkaEvent.BookedSlotsStatusChangedEvent.name, payload.path("type").asText())
        assertEquals("pcp-mission-planing-service", payload.path("data").path("sourceService").asText())
        assertEquals("PROCESSED", payload.path("data").path("status").asText())
        assertEquals(2, payload.path("data").path("bookedSlotIds").size())
        assertTrue(payload.path("data").path("bookedSlotIds").map { it.asLong() }.containsAll(listOf(10L, 20L)))
    }

    private class SingleObjectProvider<T : Any>(private val value: T) : ObjectProvider<T> {
        override fun getObject(vararg args: Any?): T = value
        override fun getIfAvailable(): T = value
        override fun getIfUnique(): T = value
        override fun getObject(): T = value
    }
}
