package clickstream.internal.eventprocessor

import clickstream.api.CSInfo
import clickstream.config.CSEventProcessorConfig
import clickstream.extension.protoName
import clickstream.health.constant.CSEventNamesConstant
import clickstream.health.constant.CSEventTypesConstant
import clickstream.health.intermediate.CSHealthEventRepository
import clickstream.health.model.CSHealthEventDTO
import clickstream.internal.eventscheduler.CSForegroundEventScheduler
import clickstream.logger.CSLogger
import clickstream.model.CSEvent
import java.util.Locale
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.ExperimentalCoroutinesApi

/**
 * This is responsible for ingesting analytics events generated by app,adding relevant keys,
 * and forwarding the events to [CSForegroundEventScheduler].
 *
 * @param eventScheduler used for scheduling events
 * @param dispatcher used for dispatching events serially
 * @param logger used for logging
 * @param healthEventRepository used for tracking health events
 */
@ExperimentalCoroutinesApi
internal class CSEventProcessor(
    private val config: CSEventProcessorConfig,
    private val eventScheduler: CSForegroundEventScheduler,
    private val dispatcher: CoroutineDispatcher,
    private val logger: CSLogger,
    private val healthEventRepository: CSHealthEventRepository,
    private val info: CSInfo
) {

    /**
     * Creates event object and forwards to EventScheduler.
     *
     * @param event [CSEvent] which holds guid, timestamp and message
     */
    suspend fun trackEvent(event: CSEvent) {
        logger.debug { "CSEventProcessor#trackEvent" }
        recordHealthEvent(
            eventName = CSEventNamesConstant.Flushed.ClickStreamEventReceived.value,
            eventGuid = event.guid
        )
        recordHealthEvent(
            eventName = CSEventNamesConstant.Flushed.ClickStreamEventReceivedForDropRate.value,
            eventGuid = event.guid.plus("_").plus(event.message.protoName().toLowerCase(Locale.getDefault()))
        )
        val eventName = event.message.protoName()
        when {
            config.realtimeEvents.contains(eventName) -> eventScheduler.scheduleEvent(event)
            config.instantEvent.contains(eventName) -> eventScheduler.sendInstantEvent(event)
            else -> eventScheduler.scheduleEvent(event)
        }
    }

    private suspend fun recordHealthEvent(eventName: String, eventGuid: String) {
        healthEventRepository.insertHealthEvent(
            CSHealthEventDTO(
                eventName = eventName,
                eventType = CSEventTypesConstant.AGGREGATE,
                eventGuid = eventGuid,
                appVersion = info.appInfo.appVersion
            )
        )
    }
}
