package ru.yandex.qe.dispenser.ws

import mu.KotlinLogging
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.beans.factory.annotation.Value
import org.springframework.context.MessageSource
import org.springframework.stereotype.Component
import ru.yandex.monlib.metrics.labels.Labels
import ru.yandex.monlib.metrics.primitives.Rate
import ru.yandex.qe.dispenser.domain.QuotaChangeRequest
import ru.yandex.qe.dispenser.domain.exception.MultiMessageException
import ru.yandex.qe.dispenser.domain.exception.SingleMessageException
import ru.yandex.qe.dispenser.domain.jns.JnsMessage
import ru.yandex.qe.dispenser.domain.jns.JnsResult
import ru.yandex.qe.dispenser.domain.util.LocalizationUtils
import ru.yandex.qe.dispenser.solomon.SolomonHolder
import ru.yandex.qe.dispenser.ws.dispatchers.CustomExecutor
import java.util.*

private val logger = KotlinLogging.logger {}

private const val FAILED_TO_ALLOCATE_TITLE = "Failed to allocate resource for quota request"

@Component
class QuotaRequestObservabilityService(
    private val jnsApiHelper: JnsApiHelper,
    @Qualifier("observabilityExecutor")
    private val executor: CustomExecutor,
    @Value("\${jns.project}")
    private val jnsProject: String,
    @Value("\${jns.targets.requests.targetProject}")
    private val jnsTargetProject: String,
    @Value("\${jns.targets.requests.channel}")
    private val jnsChannel: String,
    @Value("\${jns.targets.requests.template}")
    private val jnsTemplate: String,
    @Qualifier("errorMessageSource")
    private val errorMessageSource: MessageSource,
    solomonHolder: SolomonHolder
) {
    private val accountAllocationErrorRate: Rate

    init {
        val rootRegistry = solomonHolder.rootRegistry
        accountAllocationErrorRate = rootRegistry.rate("quota_request.allocation_error_rate", Labels.of())
    }

    fun allocationFail(quotaRequest: QuotaChangeRequest, exception: Exception) {
        accountAllocationErrorRate.inc()
        executor.launch {
            val jnsResult = jnsApiHelper.send(JnsMessage(
                project = jnsProject,
                template = jnsTemplate,
                targetProject = jnsTargetProject,
                channel = jnsChannel,
                parameters = prepareParams(FAILED_TO_ALLOCATE_TITLE, quotaRequest, exception)
            ))
            when (jnsResult) {
                is JnsResult.Success -> {}
                is JnsResult.Error -> { logger.error("Failed to send jns notification." +
                    " Status ${jnsResult.statusCode}, response: ${jnsResult.errorText}") }
                is JnsResult.Failure -> { logger.error("Failed to send jns notification", jnsResult.error) }
            }
        }
    }

    private fun prepareParams(title: String, quotaRequest: QuotaChangeRequest, e: Exception): Map<String, String> {
        val details = details(e)
        return mapOf(
            "title" to title,
            "quotaRequestId" to quotaRequest.id.toString(),
            "details" to details
        )
    }

    private fun details(e: Exception) = when (e) {
        is SingleMessageException -> translate(e)
        is MultiMessageException -> translate(e)
        else -> e.toString()
    }

    private fun translate(e: SingleMessageException): String {
        return LocalizationUtils.resolveWithDefaultAsKey(errorMessageSource, e.localizableString, Locale.ENGLISH)
    }

    private fun translate(e: MultiMessageException): String {
        return e.errors.errors.joinToString(separator = "\n") {
            LocalizationUtils.resolveWithDefaultAsKey(errorMessageSource, it, Locale.ENGLISH)
        }
    }
}
