package com.gh.common.exposure

import com.aliyun.sls.android.producer.Log
import com.gh.gamecenter.BuildConfig
import com.gh.gamecenter.common.loghub.LoghubHelper
import com.gh.gamecenter.common.utils.FixedSizeLinkedHashSet
import com.gh.gamecenter.common.utils.toJson
import com.lightgame.utils.Utils

/**
 * A handful tool for committing logs to aliyun loghub.
 *
 * 如何简单地统计列表中每个 item 的曝光事件？
 *
 * 1. Adapter 实现 IExposable 接口，在 BindView 阶段更新 ExposureEvent，ExposureEvent 供 getEventByPosition(pos) 方法获取用
 * 2. 构建一个 ExposureListener 并作为入参添加至 recyclerview 的 onScroll 回调中
 * 3. 没了
 */
object ExposureManager {

    private const val STORE_SIZE = 100
    private const val LOG_STORE = BuildConfig.EXPOSURE_REPO

    // exposureCache 用来过滤掉具有相同 id 的曝光事件，避免重复发送事件
    private val exposureSet by lazy { hashSetOf<ExposureEvent>() }
    private val exposureCache by lazy { FixedSizeLinkedHashSet<String>(300) }

    /**
     * Log a single exposure event.
     */
    fun log(event: ExposureEvent) {
        if (!exposureCache.contains(event.id)) {
            exposureSet.add(event)
            exposureCache.add(event.id)
        } else {
            Utils.log("Exposure", "遇到重复曝光事件，自动过滤 （${event.id} - ${event.payload.gameName}）")
        }
    }

    /**
     * Log a collection of exposure event.
     */
    fun log(eventList: List<ExposureEvent>) {
        for (event in eventList) {
            if (!exposureCache.contains(event.id)) {
                exposureSet.add(event)
                exposureCache.add(event.id)
            } else {
                Utils.log("Exposure", "遇到重复曝光事件，自动过滤 （${event.id} - ${event.payload.gameName}）")
            }
        }
        commitSavedExposureEvents()
    }

    /**
     * @param forcedUpload Ignore all restrictions.
     */
    fun commitSavedExposureEvents(forcedUpload: Boolean = false) {
        if (exposureSet.size < STORE_SIZE && !forcedUpload || exposureSet.size == 0) return

        uploadExposures(exposureSet.toList(), forcedUpload)

        Utils.log("Exposure", "提交了${exposureSet.size}条曝光记录")
        exposureSet.clear()
    }

    private fun eliminateMultipleBrackets(jsonWithMultipleBracket: String): String {
        return jsonWithMultipleBracket.replace("[[", "[").replace("]]", "]")
    }

    private fun uploadExposures(eventList: List<ExposureEvent>, forced: Boolean) {
        eventList.forEach {
            LoghubHelper.uploadLog(buildLog(it), LOG_STORE, forced)
            it.recycle()
        }
    }

    private fun buildLog(event: ExposureEvent) = Log().apply {
        putContent("id", event.id)
        putContent("payload", event.payload.toJson())
        putContent("event", event.event.toString())
        putContent("source", eliminateMultipleBrackets(event.source.toJson()))
        putContent("meta", event.meta.toJson())
        putContent("real_millisecond", event.timeInMillisecond.toString())
        putContent(
            "e-traces", if (event.eTrace != null) {
                eliminateMultipleBrackets(event.eTrace?.toJson() ?: "")
            } else ""
        )
        logTime = event.time.toLong()
    }

}