package space.nstart.pcp.tle_monitoring_service.message

import org.apache.kafka.clients.producer.ProducerRecord
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
import org.springframework.kafka.core.KafkaTemplate
import org.springframework.kafka.support.SendResult
import org.springframework.stereotype.Component
import space.nstart.pcp.pcp_types_lib.message.KafkaMessage
import space.nstart.pcp.tle_monitoring_service.configuration.KafkaConfig
import tools.jackson.core.JacksonException
import tools.jackson.databind.ObjectMapper
import java.nio.charset.StandardCharsets
import java.util.concurrent.CompletableFuture
import java.util.function.BiConsumer
import kotlin.Any
import kotlin.String


@ConditionalOnProperty(name=["spring.kafka.bootstrap-servers"], matchIfMissing = false)
@Component
class KafkaProducer {

    @Value("\${spring.kafka.template.default-topic}")
    private val defaultTopic: String? = null

    @Value("\${spring.application.name:opkat-ks-request-service}")
    private val applicationName: String = ""

    @Autowired
    private lateinit var kafkaTemplate: KafkaTemplate<String, Any>

    @Autowired
    private lateinit var objectMapper: ObjectMapper

    private val log: Logger = LoggerFactory.getLogger(this::class.java)

    fun send(message: KafkaMessage<Any>, topic: String? = null, extraHeaders: Map<String, Any>? = null) {
        try {
            message.source = applicationName

            val jsonMessage = objectMapper.writeValueAsString(message)
            val selectedTopic = topic ?: defaultTopic
            val record: ProducerRecord<String, Any> = ProducerRecord(
                selectedTopic,
                message.traceId,
                jsonMessage
            )
            record.headers().add(KafkaConfig.HEADER_NAME, message.type.enumCast()?.name?.toByteArray(StandardCharsets.UTF_8))
            extraHeaders?.forEach { record.headers().add(it.key, it.value.toString().toByteArray(StandardCharsets.UTF_8)) }
            val future: CompletableFuture<SendResult<String, Any>> = kafkaTemplate.send(record)
            future.whenCompleteAsync { result: SendResult<String, Any>, ex: Throwable? ->
                if (ex == null) log.debug(
                    "В Kafka отправлено сообщение. Topic: {}. Header: {}. Key: {}. Value: {}.",
                    result.producerRecord.topic(),
                    result.producerRecord.headers().lastHeader(KafkaConfig.HEADER_NAME),
                    result.producerRecord.key(),
                    result.producerRecord.value()
                )
                else log.error(
                    "Ошибка отправки сообщения в Kafka. Topic: {}. Header: {}. Key: {}. Value: {}.",
                    selectedTopic,
                    message.type.enumCast()?.name,
                    message.traceId,
                    jsonMessage
                )
            }
        } catch (e: JacksonException) {
            throw RuntimeException("Ошибка конвертации сообщения в json: " + e.message, e)
        }
    }
}
