package space.nstart.pcp.tle_monitoring_service.configuration

import org.apache.kafka.clients.admin.AdminClientConfig
import org.apache.kafka.clients.admin.NewTopic
import org.apache.kafka.clients.consumer.ConsumerConfig
import org.apache.kafka.clients.consumer.ConsumerRecord
import org.apache.kafka.clients.producer.ProducerConfig
import org.apache.kafka.common.header.Header
import org.apache.kafka.common.serialization.StringDeserializer
import org.apache.kafka.common.serialization.StringSerializer
import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory
import org.springframework.kafka.core.ConsumerFactory
import org.springframework.kafka.core.DefaultKafkaConsumerFactory
import org.springframework.kafka.core.DefaultKafkaProducerFactory
import org.springframework.kafka.core.KafkaAdmin
import org.springframework.kafka.core.KafkaTemplate
import org.springframework.kafka.core.ProducerFactory
import org.springframework.kafka.listener.adapter.RecordFilterStrategy
import space.nstart.pcp.pcp_types_lib.message.PcpKafkaEvent
import java.nio.charset.StandardCharsets
import kotlin.String


@ConditionalOnProperty(name=["spring.kafka.bootstrap-servers"], matchIfMissing = false)
@Configuration
class KafkaConfig {
    companion object {
        const val HEADER_NAME: String = "type"
        const val TARGET_CONSUMER_HEADER_NAME: String = "target_consumer"
    }

    @Value("\${spring.kafka.template.default-topic}")
    private lateinit var defaultTopic: String

    @Bean
    fun tleTopic(): NewTopic =
        NewTopic(defaultTopic, 1, 1)


    @Value("\${spring.kafka.bootstrap-servers}")
    private lateinit var addr: String

    @Value("\${spring.kafka.consumer.group-id}")
    private lateinit var groupId: String

    @Bean
    fun template(): KafkaTemplate<String, Any> {
        return KafkaTemplate(producerFactory())
    }

    @Bean
    fun kafkaAdmin(): KafkaAdmin {

        val configs = hashMapOf<String, Any>()
        configs[AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG] = addr

        return KafkaAdmin(configs)
    }

    @Bean
    fun consumerFactory(): ConsumerFactory<String, Any> {

        val props = hashMapOf<String, Any>()
        props[ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG] = addr
        props[ConsumerConfig.GROUP_ID_CONFIG] = groupId
        props[ConsumerConfig.AUTO_OFFSET_RESET_CONFIG] = "latest"
        props[ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG] = false
        props[ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG] = StringDeserializer::class.java
        props[ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG] = StringDeserializer::class.java

        return DefaultKafkaConsumerFactory(props)
    }

    @Bean
    fun kafkaListenerContainerFactory(): ConcurrentKafkaListenerContainerFactory<String, Any> =
        ConcurrentKafkaListenerContainerFactory<String, Any>()
            .apply { setConsumerFactory(consumerFactory()) }

    @Bean
    fun producerFactory(): ProducerFactory<String, Any> {

        val configProps = hashMapOf<String, Any>()
        configProps[ProducerConfig.BOOTSTRAP_SERVERS_CONFIG] = addr
        configProps[ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG] = StringSerializer::class.java
        configProps[ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG] = StringSerializer::class.java

        return DefaultKafkaProducerFactory(configProps)
    }

    private fun getRecordFilterStrategy(event: PcpKafkaEvent): RecordFilterStrategy<String, String> {
        return RecordFilterStrategy { consumerRecord: ConsumerRecord<String, String> ->
            val eventTypeHeader: Header? = consumerRecord.headers().lastHeader(HEADER_NAME)
            if (eventTypeHeader != null) {
                val headerValue = String(eventTypeHeader.value(), StandardCharsets.UTF_8)
                return@RecordFilterStrategy event.name != headerValue
            }
            true
        }
    }

    private fun getRecordFilterStrategyByConsumer(): RecordFilterStrategy<String, String> {
        return RecordFilterStrategy { consumerRecord: ConsumerRecord<String, String> ->
            val consumerGroupHeader: Header? = consumerRecord.headers().lastHeader(TARGET_CONSUMER_HEADER_NAME)
            if (consumerGroupHeader != null) {
                val headerValue = String(consumerGroupHeader.value(), StandardCharsets.UTF_8)
                return@RecordFilterStrategy "respondedListener" != headerValue
            }
            true
        }
    }

    @Bean("placedIcFilter")
    fun placedFilter(): RecordFilterStrategy<String, String> {
        return getRecordFilterStrategy(PcpKafkaEvent.ICPlacedEvent)
    }

    @Bean("updatedIcFilter")
    fun updatedIcFilter(): RecordFilterStrategy<String, String> {
        return getRecordFilterStrategy(PcpKafkaEvent.ICUpdatedEvent)
    }

    @Bean("satelliteDeletedFilter")
    fun satelliteDeletedFilter(): RecordFilterStrategy<String, String> {
        return getRecordFilterStrategy(PcpKafkaEvent.SatelliteDeletedEvent)
    }

    @Bean("respondedFilter")
    fun respondedFilter(): RecordFilterStrategy<String, String> {
        return getRecordFilterStrategyByConsumer()
    }
}
