package com.gh.gamecenter.feature.exposure

import android.os.Parcelable
import androidx.annotation.Keep
import androidx.room.PrimaryKey
import com.alibaba.android.arouter.launcher.ARouter
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.common.entity.ExposureEntity
import com.gh.gamecenter.common.exposure.meta.Meta
import com.gh.gamecenter.common.exposure.meta.MetaUtil
import com.gh.gamecenter.common.utils.getFirstElementDividedByDivider
import com.gh.gamecenter.core.provider.IBrowserInstallHelperProvider
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.feature.exposure.time.TimeUtil
import com.gh.gamecenter.feature.provider.IRegionSettingHelperProvider
import com.lightgame.download.DownloadEntity
import com.lightgame.utils.Utils
import kotlinx.parcelize.IgnoredOnParcel
import kotlinx.parcelize.Parcelize
import java.util.*

@Keep
@Parcelize
data class ExposureEvent(
    var payload: ExposureEntity,
    var source: List<ExposureSource>,
    var eTrace: List<ExposureEvent>? = arrayListOf(),
    var event: ExposureType,
    val meta: Meta = MetaUtil.getMeta(),
    var time: Int = TimeUtil.currentTime(),
    var timeInMillisecond: Long = System.currentTimeMillis(),
    @PrimaryKey
    var id: String = UUID.randomUUID().toString(),
) : Parcelable, Cloneable {

    @Transient
    @IgnoredOnParcel
    var next: ExposureEvent? = null

    @Transient
    @IgnoredOnParcel
    var flags = 0

    private fun initExposureEventData(
        gameEntity: GameEntity?,
        source: List<ExposureSource>,
        eTrace: List<ExposureEvent>? = null,
        event: ExposureType = ExposureType.EXPOSURE
    ) {
        if (gameEntity?.adIconActive == true) {
            gameEntity.isAdData = true
        }
        val browserInstallHelper = ARouter.getInstance().build(RouteConsts.provider.browserInstallHelper)
            .navigation() as? IBrowserInstallHelperProvider
        val regionSettingHelper = ARouter.getInstance().build(RouteConsts.provider.regionSettingHelper)
            .navigation() as? IRegionSettingHelperProvider

        val firstTracePayload = eTrace?.firstOrNull()?.payload

        this.payload = ExposureEntity(
            gameId = gameEntity?.id?.getFirstElementDividedByDivider(DownloadEntity.GAME_ID_DIVIDER),
            gameName = if (!firstTracePayload?.gameName.isNullOrEmpty()) {
                firstTracePayload?.gameName
            } else {
                gameEntity?.name?.removeSuffix(Constants.GAME_NAME_DECORATOR)
            },
            gameVersion = firstTracePayload?.gameVersion ?: gameEntity?.gameVersion,
            subjectId = firstTracePayload?.subjectId ?: gameEntity?.subjectId ?: gameEntity?.subjectData?.id,
            isMirrorData = firstTracePayload?.isMirrorData ?: (gameEntity?.getMirrorPosition() == 1),
            isMirrorData2 = firstTracePayload?.isMirrorData2 ?: (gameEntity?.getMirrorPosition() == 2),
            isWebDownload = (browserInstallHelper?.isUseBrowserToInstallEnabled()
                ?: false) && browserInstallHelper?.shouldUseBrowserToInstall() ?: false, // 实时的值，不用从 eTrace 里取
            sequence = firstTracePayload?.sequence ?: gameEntity?.sequence,
            outerSequence = firstTracePayload?.outerSequence ?: gameEntity?.outerSequence,
            platform = firstTracePayload?.platform ?: gameEntity?.platform,
            downloadType = gameEntity?.downloadType,
            downloadCompleteType = gameEntity?.downloadCompleteType,
            displayType = firstTracePayload?.displayType ?: gameEntity?.displayContent,
            isPlatformRecommend = gameEntity?.isPlatformRecommend,
            // ugly
            welcomeDialogId = gameEntity?.welcomeDialogId ?: firstTracePayload?.welcomeDialogId,
            welcomeDialogLinkTitle = gameEntity?.welcomeDialogTitle ?: firstTracePayload?.welcomeDialogLinkTitle,
            isAdData = gameEntity?.isAdData ?: firstTracePayload?.isAdData ?: false,
            country = regionSettingHelper?.getIpInfo()?.country ?: "",
            province = regionSettingHelper?.getIpInfo()?.province ?: "",
            city = regionSettingHelper?.getIpInfo()?.city ?: ""
        )
        this.id = UUID.randomUUID().toString()
        this.timeInMillisecond = System.currentTimeMillis()
        this.time = TimeUtil.currentTime()
        this.source = source
        this.eTrace = eTrace
        this.event = event
        this.also {
            it.payload.categoryId = firstTracePayload?.categoryId
            it.payload.categoryV2Id = firstTracePayload?.categoryV2Id
            it.payload.testServerId = firstTracePayload?.testServerId
            it.payload.blockId = firstTracePayload?.blockId

            it.payload.setContainerInfo(
                firstTracePayload?.containerId ?: gameEntity?.containerId,
                firstTracePayload?.containerType ?: gameEntity?.containerType
            )

            it.payload.sourcePage = firstTracePayload?.sourcePage
            it.payload.sourcePageId = firstTracePayload?.sourcePageId
            it.payload.sourcePageName = firstTracePayload?.sourcePageName
            gameEntity?.exposureEvent = it
        }
    }

    fun recycle() {
        if (inUse()) {
            Utils.log(
                EXPOSURE_EVENT_LOG_TAG,
                "This ExposureEvent $this cannot be recycled because it is still in use."
            )
            return
        }
        recycleUnchecked()
    }

    private fun recycleUnchecked() {
        this.flags = FLAG_IN_USE
        synchronized(sPoolSync) {
            if (sPoolSize < EXPOSURE_EVENT_MAX_POOL_SIZE) {
                next = sPool
                sPool = this
                sPoolSize++
                Utils.log(EXPOSURE_EVENT_LOG_TAG, "This ExposureEvent $this Recycled")
            }
        }
    }

    private fun inUse(): Boolean {
        return flags == FLAG_IN_USE
    }

    fun deepCopy(): ExposureEvent {
        return super.clone() as ExposureEvent
    }

    companion object {
        private val sPoolSync = Any()
        private var sPool: ExposureEvent? = null
        private var sPoolSize: Int = 0

        private const val EXPOSURE_EVENT_MAX_POOL_SIZE: Int = 300
        private const val FLAG_IN_USE = 1
        private const val EXPOSURE_EVENT_LOG_TAG = "ExposureEvent"

        @JvmStatic
        fun createEvent(
            gameEntity: GameEntity?,
            source: List<ExposureSource>,
            eTrace: List<ExposureEvent>? = null,
            event: ExposureType = ExposureType.EXPOSURE
        ): ExposureEvent {
            if (gameEntity?.getApk()?.size == 1) {
                gameEntity.gameVersion = gameEntity.getApk().elementAtOrNull(0)?.version ?: ""
            }
            synchronized(sPoolSync) {
                if (sPool != null) {
                    val exposureEvent = sPool
                    sPool = exposureEvent?.next
                    exposureEvent?.next = null
                    exposureEvent?.flags = 0
                    sPoolSize--
                    exposureEvent?.initExposureEventData(gameEntity, source, eTrace, event)
                    Utils.log(
                        EXPOSURE_EVENT_LOG_TAG,
                        "ExposureEvent 对象 $this 正在被循环使用, 当前对象池数量还有 $sPoolSize"
                    )
                    return exposureEvent!!
                }
            }
            Utils.log(
                EXPOSURE_EVENT_LOG_TAG,
                "new ExposureEvent 对象"
            )
            val exposureEvent = ExposureEvent(
                payload = ExposureEntity(),
                source = source,
                eTrace = eTrace,
                event = event
            )
            exposureEvent.initExposureEventData(gameEntity, source, eTrace, event)
            return exposureEvent
        }

        @JvmStatic
        fun createEventWithSourceConcat(
            gameEntity: GameEntity?,
            basicSource: List<ExposureSource>,
            source: List<ExposureSource>,
            eTrace: List<ExposureEvent>? = null,
            event: ExposureType = ExposureType.EXPOSURE
        ): ExposureEvent {
            val concatSourceList = ArrayList<ExposureSource>().apply {
                addAll(basicSource)
                addAll(source)
            }
            return createEvent(gameEntity, concatSourceList, eTrace, event)
        }

    }
}