package ru.yandex.qe.dispenser.ws.dispatchers

import com.google.common.util.concurrent.ThreadFactoryBuilder
import mu.KotlinLogging
import org.slf4j.MDC
import org.springframework.stereotype.Component
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
import javax.annotation.PreDestroy

private val logger = KotlinLogging.logger {}

/**
 * Observability executor.
 *
 * @author Dmitriy Timashov <dm-tim@yandex-team.ru>
 */
@Component("observabilityExecutor")
class ObservabilityExecutorImpl: CustomExecutor {

    private val executorService: ExecutorService

    init {
        val threadFactory = ThreadFactoryBuilder()
            .setDaemon(true)
            .setNameFormat("observability-pool-%d")
            .setUncaughtExceptionHandler { t: Thread, e: Throwable? ->
                logger.error(e) { "Uncaught exception in observability pool thread $t" }
            }
            .build()
        val threadCount = (Runtime.getRuntime().availableProcessors() / 4).coerceAtLeast(2)
        executorService = Executors.newFixedThreadPool(threadCount, threadFactory)
    }

    @PreDestroy
    fun preDestroy() {
        logger.info("Stopping observability executor...")
        try {
            executorService.awaitTermination(1, TimeUnit.SECONDS)
        } catch (e: InterruptedException) {
            Thread.currentThread().interrupt()
        }
        executorService.shutdownNow()
        logger.info("Stopped observability executor")
    }

    override fun launch(block: () -> Unit) {
        val mdc = MDC.getCopyOfContextMap()
        executorService.submit {
            MDC.setContextMap(mdc)
            try {
                block()
            } finally {
                MDC.clear()
            }
        }
    }

}
