package com.base.datarecovery.ads.admob

import android.app.Activity
import android.content.Context
import android.os.Bundle
import android.util.Log
import android.view.ViewGroup
import android.view.ViewTreeObserver
import androidx.core.view.isVisible
import com.applovin.mediation.MaxAd
import com.applovin.mediation.MaxAdListener
import com.applovin.mediation.MaxAdViewAdListener
import com.applovin.mediation.MaxError
import com.applovin.mediation.ads.MaxAdView
import com.applovin.mediation.ads.MaxAppOpenAd
import com.applovin.mediation.ads.MaxInterstitialAd
import com.applovin.mediation.nativeAds.MaxNativeAdListener
import com.applovin.mediation.nativeAds.MaxNativeAdLoader
import com.applovin.mediation.nativeAds.MaxNativeAdView
import com.applovin.sdk.AppLovinMediationProvider
import com.applovin.sdk.AppLovinSdk
import com.applovin.sdk.AppLovinSdkInitializationConfiguration
import com.base.datarecovery.BuildConfig
import com.base.datarecovery.GlobalConfig.DEFAULT_SWITCH
import com.base.datarecovery.GlobalConfig.ID_ADMOB_BANNER
import com.base.datarecovery.GlobalConfig.ID_ADMOB_INTER
import com.base.datarecovery.GlobalConfig.ID_ADMOB_NATIVE
import com.base.datarecovery.GlobalConfig.ID_ADMOB_OPEN
import com.base.datarecovery.GlobalConfig.ID_MAX_BANNER
import com.base.datarecovery.GlobalConfig.ID_MAX_INTER
import com.base.datarecovery.GlobalConfig.ID_MAX_NATIVE
import com.base.datarecovery.GlobalConfig.ID_MAX_OPEN
import com.base.datarecovery.GlobalConfig.KEY_MAX
import com.base.datarecovery.GlobalConfig.useAdmob
import com.base.datarecovery.GlobalConfig.useBlacklist
import com.base.datarecovery.R
import com.base.datarecovery.utils.AppPreferences
import com.google.ads.mediation.admob.AdMobAdapter
import com.google.android.gms.ads.AdError
import com.google.android.gms.ads.AdListener
import com.google.android.gms.ads.AdLoader
import com.google.android.gms.ads.AdRequest
import com.google.android.gms.ads.AdSize
import com.google.android.gms.ads.AdView
import com.google.android.gms.ads.FullScreenContentCallback
import com.google.android.gms.ads.LoadAdError
import com.google.android.gms.ads.appopen.AppOpenAd
import com.google.android.gms.ads.identifier.AdvertisingIdClient
import com.google.android.gms.ads.interstitial.InterstitialAd
import com.google.android.gms.ads.interstitial.InterstitialAdLoadCallback
import com.google.android.gms.ads.nativead.NativeAd
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
import java.lang.ref.WeakReference
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Locale
import java.util.UUID
import java.util.concurrent.ConcurrentLinkedDeque
import kotlin.random.Random

object AdmobManager {

    //
    private const val TAG = "AdmobManager"

    //
    private const val KEY_AD_INTERVAL = "adInterval"
    private const val KEY_AD_INTER_INTERVAL = "adInterInterval"
    private const val KEY_AD_INTER_INTERVAL_START = "adInterIntervalStart"
    private const val KEY_AD_INTER_INTERVAL_END = "adInterIntervalEnd"
    private const val KEY_AD_NATIVE_INTERVAL = "adNativeInterval"
    private const val KEY_AD_REQUEST = "adRequest"
    private const val KEY_AD_CLICK = "adClick"
    private const val KEY_AD_SHOW = "openadShow"
    private const val KEY_INTER_AD_SHOW = "interadShow"
    private const val KEY_NATIVE_AD_SHOW = "nativeadShow"

    private const val KEY_IS_SHOW_AD_BANNER = "isShowAdBanner"
    private const val KEY_IS_SHOW_AD_NATIVE = "isShowAdNative"
    private const val KEY_IS_SHOW_AD_NATIVE_BOTTOM = "isShowAdNativeBottom"
    private const val KEY_NA_ADS = "naAdS"
    private const val KEY_IS_SHOW_AD_INTER = "isShowAdInter"
    private const val KEY_IS_LOADING_INTER = "isloadingInter"
    private const val KEY_IS_SHOW_AD_BACK_INTER = "isShowAdBackInter"
    private const val KEY_IS_SHOW_AD_RESULT_BACK_INTER = "isShowAdResultBackInter"

    private const val DEFAULT_AD_INTERVAL = "20"
    private const val DEFAULT_AD_INTER_INTERVAL = "30"
    private const val DEFAULT_AD_INTER_INTERVAL_START = "40"
    private const val DEFAULT_AD_INTER_INTERVAL_END = "70"
    private const val DEFAULT_AD_NATIVE = "20"
    private const val DEFAULT_AD_REQUEST = "100"
    private const val DEFAULT_AD_CLICK = "10"
    private const val DEFAULT_OPEN_AD_SHOW = "10"
    private const val DEFAULT_INTER_AD_SHOW = "10"
    private const val DEFAULT_NATIVE_AD_SHOW = "20"

    //
    private const val AD_UNIT_APP_OPEN = "openAd"
    private const val AD_UNIT_INTER = "interAd"
    private const val AD_UNIT_NATIVE = "nativeAd"
    private const val NO_AD = "no_ad"

    private const val NUMBER_REQUEST = "number_request"
    private const val NUMBER_DISPLAY = "number_display"
    private const val NUMBER_INTER_DISPLAY = "number_inter_display"
    private const val NUMBER_NATIVE_DISPLAY = "number_native_display"
    private const val NUMBER_CLICK = "number_click"

    private const val LIMIT_ERROR_BLACKLIST = "limit_error_blacklist"
    private const val LIMIT_ERROR_REQUEST = "limit_error_request"
    private const val LIMIT_ERROR_DISPLAY = "limit_error_display"
    private const val LIMIT_ERROR_CLICK = "limit_error_click"
    private const val LIMIT_ERROR_INTERVAL = "limit_error_interval"

    private const val SAVE_DATE = "save_date"

    private var appOpenAd: AppOpenAd? = null
    private var appOpenMaxAd: MaxAppOpenAd? = null
    private var appOpenAdLoadTime: Long = 0
    private var isLoadingAppOpenAd = false

    private var interstitialAd: InterstitialAd? = null
    private var interstitialMaxAd: MaxInterstitialAd? = null
    private var interstitialAdLoadTime: Long = 0
    private var isLoadingInterstitialAd = false

    private var nativeAdLoadTime: Long = 0
    private var isLoadingNativeAd = false
    private val nativeCacheItems = ConcurrentLinkedDeque<NativeAd>()
    private var maxAdCacheItems = ConcurrentLinkedDeque<MaxAd>()
    private var maxNativeAdLoaderCacheItems = ConcurrentLinkedDeque<MaxNativeAdLoader>()

    private var appOpenAdLastShowTime = 0L
        get() {
            return AppPreferences.getInstance().getLong("OPEN_AD_LAST_SHOW_TIME", field)
        }
        set(value) {
            field = value
            AppPreferences.getInstance().put("OPEN_AD_LAST_SHOW_TIME", value)
        }

    private var interstitialAdLastShowTime = 0L
        get() {
            return AppPreferences.getInstance().getLong("INTER_AD_LAST_SHOW_TIME", field)
        }
        set(value) {
            field = value
            AppPreferences.getInstance().put("INTER_AD_LAST_SHOW_TIME", value)
        }

    private var nativeAdLastShowTime = 0L
        get() {
            return AppPreferences.getInstance().getLong("NATIVE_AD_LAST_SHOW_TIME", field)
        }
        set(value) {
            field = value
            AppPreferences.getInstance().put("NATIVE_AD_LAST_SHOW_TIME", value)
        }

    private var showTime
        get() = AppPreferences.getInstance().getString(SAVE_DATE,"")
        set(value) = AppPreferences.getInstance().put(SAVE_DATE, value)

    private const val SHOW_INTER_COUNT = "showInterCount"
    private var showInterCount
        get() = AppPreferences.getInstance().getInt(SHOW_INTER_COUNT,0)
        set(value) = AppPreferences.getInstance().put(SHOW_INTER_COUNT, value)

    fun init(
        context: Context,
        loaded: (Int) -> Unit,
        failed: () -> Unit,
    ) {
        if (isBlacklist()) {
            failed.invoke()
            return
        }
        if (isAppOpenAdAvailable()) {
            loaded.invoke(0)
            return
        }
        if (isInterstitialAdAvailable()) {
            loaded.invoke(1)
            loadAppOpenAd(context)
            return
        }
        loadAppOpenAd(
            context,
            { loaded.invoke(0) },
            { loadInterstitialAd(context, { loaded.invoke(1) }, failed) }
        )
    }

    fun Context.initMax() {
        MainScope().launch(CoroutineExceptionHandler { _, _ -> } + Dispatchers.Default) {
            val initConfig = AppLovinSdkInitializationConfiguration.builder(
                KEY_MAX, this@initMax
            ).setMediationProvider(AppLovinMediationProvider.MAX)
            if (BuildConfig.DEBUG) initConfig.testDeviceAdvertisingIds = arrayListOf(
                AdvertisingIdClient.getAdvertisingIdInfo(this@initMax).id
            )
            AppLovinSdk.getInstance(this@initMax).initialize(initConfig.build()) {

            }
        }
    }

    fun loadAppOpenAd(
        context: Context,
        loaded: (() -> Unit?)? = null,
        failed: (() -> Unit?)? = null,
    ) {
        if (!checkAvailable(AD_UNIT_APP_OPEN)) {
            failed?.invoke()
            return
        }
        if (isLoadingAppOpenAd) {
            failed?.invoke()
            return
        }
        isLoadingAppOpenAd = true
        Log.d(TAG, "app open Ad load.")
        val reqId = UUID.randomUUID().toString()
        ReportAdUtils.pullStartAd(
            reqId,
            AD_UNIT_APP_OPEN,
            (context as Activity).javaClass.simpleName
        )
        if (isAdmob()) {
            val adRequest = AdRequest.Builder().build()
            AppOpenAd.load(
                context,
                ID_ADMOB_OPEN,
                adRequest,
                object : AppOpenAd.AppOpenAdLoadCallback() {
                    override fun onAdFailedToLoad(loadAdError: LoadAdError) {
                        Log.d(TAG, "app open failed to load: ${loadAdError.message}")
                        isLoadingAppOpenAd = false
                        failed?.invoke()
                        ReportAdUtils.pullAd(
                            loadAdError.responseInfo,
                            AD_UNIT_APP_OPEN,
                            loadAdError.message,
                            reqId
                        )
                    }

                    override fun onAdLoaded(ad: AppOpenAd) {
                        Log.d(TAG, "app open Ad was loaded.")
                        appOpenAd = ad
                        appOpenAdLoadTime = System.currentTimeMillis()
                        isLoadingAppOpenAd = false
                        loaded?.invoke()
                        calculate(NUMBER_REQUEST)
                        ReportAdUtils.pullAd(ad.responseInfo, AD_UNIT_APP_OPEN, reqId = reqId)
                        ad.onPaidEventListener = ReportAdUtils.EventOnPaidEventListener(ad)
                    }
                })
        } else {
            appOpenMaxAd = MaxAppOpenAd(ID_MAX_OPEN, context)
            appOpenMaxAd?.setListener(object : MaxAdListener {
                override fun onAdLoaded(ad: MaxAd) {
                    Log.d(TAG, "app open Ad Max was loaded.")
                    appOpenAdLoadTime = System.currentTimeMillis()
                    loaded?.invoke()
                    calculate(NUMBER_REQUEST)
                    isLoadingAppOpenAd = false
                }

                override fun onAdDisplayed(p0: MaxAd) {

                }

                override fun onAdHidden(p0: MaxAd) {
                }

                override fun onAdClicked(p0: MaxAd) {
                }

                override fun onAdLoadFailed(p0: String, p1: MaxError) {
                    Log.d(TAG, "app open Max failed to load: $p0 ${p1.message}")
                    isLoadingAppOpenAd = false
                    failed?.invoke()
                }

                override fun onAdDisplayFailed(p0: MaxAd, p1: MaxError) {
                }

            })
            appOpenMaxAd?.loadAd()
        }
    }

    fun loadInterstitialAd(
        context: Context,
        loaded: (() -> Unit?)? = null,
        failed: (() -> Unit?)? = null,
    ) {
        if (!checkAvailable(AD_UNIT_INTER)) {
            failed?.invoke()
            return
        }
        if (isLoadingInterstitialAd) {
            failed?.invoke()
            return
        }
        isLoadingInterstitialAd = true
        Log.d(TAG, "interstitial Ad load.")
        val reqId = UUID.randomUUID().toString()
        ReportAdUtils.pullStartAd(
            reqId,
            AD_UNIT_INTER,
            (context as Activity).javaClass.simpleName
        )
        if (isAdmob()) {
            val adRequest = AdRequest.Builder().build()
            InterstitialAd.load(
                context,
                ID_ADMOB_INTER,
                adRequest,
                object : InterstitialAdLoadCallback() {
                    override fun onAdFailedToLoad(loadAdError: LoadAdError) {
                        Log.d(TAG, "interstitial Ad failed to load: ${loadAdError.message}")
                        interstitialAd = null
                        isLoadingInterstitialAd = false
                        failed?.invoke()
                        ReportAdUtils.pullAd(
                            loadAdError.responseInfo,
                            AD_UNIT_INTER,
                            loadAdError.message,
                            reqId
                        )
                    }

                    override fun onAdLoaded(ad: InterstitialAd) {
                        Log.d(TAG, "interstitial Ad was loaded.")
                        interstitialAd = ad
                        interstitialAdLoadTime = System.currentTimeMillis()
                        isLoadingInterstitialAd = false
                        loaded?.invoke()
                        calculate(NUMBER_REQUEST)
                        ReportAdUtils.pullAd(ad.responseInfo, AD_UNIT_INTER, reqId = reqId)
                        ad.onPaidEventListener = ReportAdUtils.EventOnPaidEventListener(ad)
                    }
                })
        } else {
            interstitialMaxAd = MaxInterstitialAd(ID_MAX_INTER, context)
            interstitialMaxAd?.setListener(object : MaxAdListener {
                override fun onAdLoaded(p0: MaxAd) {
                    Log.d(TAG, "interstitial Ad Max was loaded.")
                    interstitialAdLoadTime = System.currentTimeMillis()
                    isLoadingInterstitialAd = false
                    loaded?.invoke()
                    calculate(NUMBER_REQUEST)
                }

                override fun onAdDisplayed(p0: MaxAd) {
                }

                override fun onAdHidden(p0: MaxAd) {
                }

                override fun onAdClicked(p0: MaxAd) {
                }

                override fun onAdLoadFailed(p0: String, p1: MaxError) {
                    Log.d(TAG, "interstitial Ad failed to load: ${p1.message}")
                    interstitialMaxAd = null
                    isLoadingInterstitialAd = false
                    failed?.invoke()
                }

                override fun onAdDisplayFailed(p0: MaxAd, p1: MaxError) {
                }

            })
            interstitialMaxAd?.loadAd()
        }
    }

    fun loadNativeAd(activity: Activity, loaded: (() -> Unit?)? = null) {
        if (isBlacklist()) {
            return
        }

        Log.d(TAG, "native Ad load.")
        val reqId = UUID.randomUUID().toString()
        ReportAdUtils.pullStartAd(reqId, AD_UNIT_NATIVE, activity.javaClass.simpleName)
        if (isAdmob()) {
            val adLoader = AdLoader.Builder(
                activity,
                ID_ADMOB_NATIVE
            ).forNativeAd {
                Log.d(TAG, "native Ad loaded.")
                nativeAdLoadTime = System.currentTimeMillis()
                nativeCacheItems.offer(it)
                ReportAdUtils.pullAd(it.responseInfo, AD_UNIT_NATIVE, reqId = reqId)
                it.setOnPaidEventListener(ReportAdUtils.EventOnPaidEventListener(it))

                isLoadingNativeAd = false
                loaded?.invoke()
            }.withAdListener(object : AdListener() {
                override fun onAdLoaded() {
                    super.onAdLoaded()
                }

                override fun onAdClicked() {
                    Log.d(TAG, "native Ad clicked.")
                    ReportAdUtils.clickAd(
                        nativeCacheItems.lastOrNull()?.responseInfo,
                        AD_UNIT_NATIVE
                    )
                }

                override fun onAdFailedToLoad(loadAdError: LoadAdError) {
                    Log.d(TAG, "native Ad failed to load. ${loadAdError.message}")
                    ReportAdUtils.pullAd(
                        loadAdError.responseInfo,
                        AD_UNIT_NATIVE,
                        loadAdError.message,
                        reqId
                    )
                }
            }).build()
            adLoader.loadAd(AdRequest.Builder().build())
        } else {
            val nativeAdLoader = MaxNativeAdLoader(ID_MAX_NATIVE, activity)
            nativeAdLoader.setNativeAdListener(object : MaxNativeAdListener() {
                override fun onNativeAdLoaded(nativeAdView: MaxNativeAdView?, ad: MaxAd) {
                    Log.d(TAG, "native Ad Max loaded.")
                    nativeAdLoadTime = System.currentTimeMillis()
                    maxAdCacheItems.offer(ad)
                    maxNativeAdLoaderCacheItems.offer(nativeAdLoader)
                    loaded?.invoke()
                }

                override fun onNativeAdLoadFailed(adUnitId: String, error: MaxError) {
                    Log.d(TAG, "native Ad Max failed to load. ${error.message}")
                }

                override fun onNativeAdClicked(ad: MaxAd) {
                    Log.d(TAG, "native Ad Max clicked.")
                }
            })
            nativeAdLoader.loadAd()
        }
    }

    fun showAppOpenAd(
        activity: Activity,
        dismissed: (() -> Unit?)? = null,
        completed: (() -> Unit)? = null,
        failed: (() -> Unit?)? = null,
        isRetry: Boolean = false
    ) {
        if (activity.isFinishing || activity.isDestroyed) return

        if (!checkAvailable(AD_UNIT_APP_OPEN, true)) {
            failed?.invoke()
            return
        }

        if (!isRetry) ReportAdUtils.showPrepareAd(AD_UNIT_APP_OPEN)
        if (!isAppOpenAdAvailable()) {
            Log.d(TAG, "The app open ad is not ready yet.")
//            ReportAdUtils.limitErrorAd(NO_AD, AD_UNIT_APP_OPEN)
            loadAppOpenAd(activity, {
                showAppOpenAd(activity, dismissed, completed, failed, true)
            }, {
                failed?.invoke()
            })
            return
        }

        if (isAdmob()) {
            appOpenAd?.fullScreenContentCallback = object : FullScreenContentCallback() {
                override fun onAdClicked() {
                    Log.d(TAG, "app open Ad was clicked")
                    calculate(NUMBER_CLICK)
                    ReportAdUtils.clickAd(appOpenAd?.responseInfo, AD_UNIT_APP_OPEN)
                }

                override fun onAdDismissedFullScreenContent() {
                    Log.d(TAG, "app open Ad dismissed fullscreen content.")
                    appOpenAdLastShowTime = System.currentTimeMillis()
                    dismissed?.invoke()
                }

                override fun onAdFailedToShowFullScreenContent(adError: AdError) {
                    Log.d(TAG, "app open failed to show: ${adError.message}")
                    appOpenAd = null
                    failed?.invoke()
                    ReportAdUtils.showErrorAd(adError.message, AD_UNIT_APP_OPEN)
                    loadAppOpenAd(activity)
                }

                override fun onAdShowedFullScreenContent() {
                    Log.d(TAG, "app open Ad showed fullscreen content.")
                    calculate(NUMBER_DISPLAY)
                    completed?.invoke()
                    ReportAdUtils.showAd(appOpenAd?.responseInfo, AD_UNIT_APP_OPEN, activity)
                    appOpenAd = null
                    loadAppOpenAd(activity)
                }
            }
            appOpenAd?.show(activity)
        } else {
            appOpenMaxAd?.setListener(object : MaxAdListener {
                override fun onAdLoaded(ad: MaxAd) {

                }

                override fun onAdDisplayed(p0: MaxAd) {
                    Log.d(TAG, "app open Ad Max showed fullscreen content.")
                    appOpenMaxAd = null
                    calculate(NUMBER_DISPLAY)
                    completed?.invoke()
                    loadAppOpenAd(activity)
                }

                override fun onAdHidden(p0: MaxAd) {
                    Log.d(TAG, "app open Ad Max dismissed fullscreen content.")
                    appOpenAdLastShowTime = System.currentTimeMillis()
                    dismissed?.invoke()
                }

                override fun onAdClicked(p0: MaxAd) {
                    Log.d(TAG, "app open Ad Max was clicked")
                    calculate(NUMBER_CLICK)
                }

                override fun onAdLoadFailed(p0: String, p1: MaxError) {
                }

                override fun onAdDisplayFailed(p0: MaxAd, p1: MaxError) {
                    Log.d(TAG, "app open Max failed to show: ${p1.message}")
                    appOpenMaxAd = null
                    failed?.invoke()
                    ReportAdUtils.showErrorAd(p1.message, AD_UNIT_APP_OPEN)
                    loadAppOpenAd(activity)
                }

            })
            appOpenMaxAd?.showAd()
        }
    }

    private var dialogRef: WeakReference<AdDialog>? = null
    fun showInterstitialAd(
        activity: Activity,
        dismissed: (() -> Unit?)? = null,
        completed: (() -> Unit)? = null,
        failed: (() -> Unit?)? = null,
        isRetry: Boolean = false
    ) {
        if (activity.isFinishing || activity.isDestroyed) return

        if (!checkAvailable(AD_UNIT_INTER, true)) {
            failed?.invoke()
            return
        }

        if (!isRetry) ReportAdUtils.showPrepareAd(AD_UNIT_INTER)

        val currentDialog = dialogRef?.get()
        if (currentDialog == null || !currentDialog.isShowing) {
            val dialog = AdDialog(activity) { showInterAd(activity, dismissed, completed, failed) }
            runCatching {
                dialog.show()
                dialogRef = WeakReference(dialog)
            }
        }

        if (!isInterstitialAdAvailable()) {
            Log.d(TAG, "The interstitial ad is not ready yet.")
            // ReportAdUtils.limitErrorAd(NO_AD, INTERSTITIAL_AD_UNIT)
            loadInterstitialAd(activity, {
                showInterstitialAd(activity, dismissed, completed, failed, true)
            }, {
                failed?.invoke()
                if (!activity.isFinishing && !activity.isDestroyed)
                    dialogRef?.get()?.dismiss()
            })
            return
        }
        dialogRef?.get()?.start()
    }

    private fun showInterAd(
        activity: Activity,
        dismissed: (() -> Unit?)? = null,
        completed: (() -> Unit)? = null,
        failed: (() -> Unit?)? = null,
    ) {
        if (isAdmob()) {
            interstitialAd?.fullScreenContentCallback = object : FullScreenContentCallback() {
                override fun onAdClicked() {
                    Log.d(TAG, "interstitial Ad was clicked")
                    calculate(NUMBER_CLICK)
                    ReportAdUtils.clickAd(interstitialAd?.responseInfo, AD_UNIT_INTER)
                }

                override fun onAdDismissedFullScreenContent() {
                    Log.d(TAG, "interstitial Ad was dismissed.")
                    interstitialAdLastShowTime = System.currentTimeMillis()
                    dismissed?.invoke()
                }

                override fun onAdFailedToShowFullScreenContent(adError: AdError) {
                    Log.d(TAG, "interstitial Ad failed to show: ${adError.message}")
                    interstitialAd = null
                    failed?.invoke()
                    ReportAdUtils.showErrorAd(adError.message, AD_UNIT_INTER)
                    loadInterstitialAd(activity)
                }

                override fun onAdShowedFullScreenContent() {
                    Log.d(TAG, "interstitial Ad showed fullscreen content.")
                    completed?.invoke()
                    calculate(NUMBER_INTER_DISPLAY)
                    ReportAdUtils.showAd(
                        interstitialAd?.responseInfo,
                        AD_UNIT_INTER,
                        activity
                    )
                    interstitialAd = null
                    loadInterstitialAd(activity)
                    showInterCount++

                }
            }
            interstitialAd?.show(activity)
        } else {
            interstitialMaxAd?.setListener(object : MaxAdListener {
                override fun onAdLoaded(p0: MaxAd) {

                }

                override fun onAdDisplayed(p0: MaxAd) {
                    Log.d(TAG, "interstitial Ad Max showed fullscreen content.")
                    interstitialMaxAd = null
                    completed?.invoke()
                    calculate(NUMBER_INTER_DISPLAY)
                    loadInterstitialAd(activity)
                }

                override fun onAdHidden(p0: MaxAd) {
                    Log.d(TAG, "interstitial Ad Max was dismissed.")
                    interstitialAdLastShowTime = System.currentTimeMillis()
                    dismissed?.invoke()
                }

                override fun onAdClicked(p0: MaxAd) {
                    Log.d(TAG, "interstitial Ad Max was clicked")
                    calculate(NUMBER_CLICK)
                }

                override fun onAdLoadFailed(p0: String, p1: MaxError) {

                }

                override fun onAdDisplayFailed(p0: MaxAd, p1: MaxError) {
                    Log.d(TAG, "interstitial Ad MaX failed to show: ${p1.message}")
                    interstitialMaxAd = null
                    failed?.invoke()
                    ReportAdUtils.showErrorAd(p1.message, AD_UNIT_INTER)
                    loadInterstitialAd(activity)
                }

            })
            interstitialMaxAd?.showAd(activity)
        }
    }

    fun showNativeAd(
        activity: Activity,
        parent: ViewGroup,
        layout: Int = R.layout.layout_native_custom,
        isShowAdNative: Boolean = isShowAdNative(),
        isRetry: Boolean = false
    ) {
        if (activity.isFinishing || activity.isDestroyed) return

        if (!isShowAdNative || isBlacklist()) {
            return
        }

        if (!isRetry) ReportAdUtils.showPrepareAd(AD_UNIT_NATIVE)
        if (isAdmob()) {
            if (!checkAvailable(AD_UNIT_NATIVE)) {
                return
            }
            val nativeAd = getNativeAd()
            if (nativeAd == null) {
                Log.d(TAG, "The native ad is not ready yet.")
//                ReportAdUtils.limitErrorAd(NO_AD, AD_UNIT_NATIVE)
                loadNativeAd(activity) {
                    showNativeAd(
                        activity,
                        parent,
                        layout,
                        isShowAdNative,
                        true
                    )
                }
                return
            }
            NativeView(parent.context, layout).run {
                nativeAdLastShowTime = System.currentTimeMillis()
                parent.removeAllViews()
                setNativeAd(nativeAd)
                parent.addView(this)
                parent.isVisible = true
                ReportAdUtils.showAd(nativeAd.responseInfo, AD_UNIT_NATIVE, activity)
                calculate(NUMBER_NATIVE_DISPLAY)
            }
            if (nativeCacheItems.size == 0)
                loadNativeAd(activity)
        } else {
            val max = getMaxAd()
            if (max == null) {
                Log.d(TAG, "The native Ad Max is not ready yet.")
                if (isRetry) return
                loadNativeAd(activity) {
                    showNativeAd(
                        activity,
                        parent,
                        layout,
                        isShowAdNative,
                        true
                    )
                }
                return
            }
            NativeView(parent.context, layout).run {
                parent.removeAllViews()
                setNativeAd(max.second, max.first)
                parent.addView(this)
                parent.isVisible = true
            }
            if (maxAdCacheItems.size == 0)
                loadNativeAd(activity)
        }
    }

    private fun getMaxAd(): Pair<MaxAd, MaxNativeAdLoader>? {
        val maxAd = maxAdCacheItems.poll()
        val maxNativeAdLoader = maxNativeAdLoaderCacheItems.poll()
        if (maxAd == null || maxNativeAdLoader == null) return null
        if (!isNativeAdAvailable()) {
            return getMaxAd()
        }
        return Pair(maxAd, maxNativeAdLoader)
    }

    fun checkNative(): Boolean {
        val lastShowTime = nativeAdLastShowTime
        var adInterval = AppPreferences.getInstance().getString(KEY_AD_NATIVE_INTERVAL, DEFAULT_AD_NATIVE)
            .toInt() * 1000
        val interval = adInterval
        val flag = System.currentTimeMillis() - lastShowTime > interval
        return flag
    }

    private fun getNativeAd(): NativeAd? {
        val nativeAd = nativeCacheItems.poll() ?: return null
        if (!isNativeAdAvailable()) {
            return getNativeAd()
        }
        return nativeAd
    }

    fun showBannerAd(
        context: Context,
        adViewContainer: ViewGroup,
        collapsible: String? = "bottom",
        completed: (() -> Unit?)? = null,
        failed: (() -> Unit?)? = null
    ) {
        if (!isAdmob()) return
        adViewContainer.viewTreeObserver.addOnGlobalLayoutListener(object :
            ViewTreeObserver.OnGlobalLayoutListener {
            override fun onGlobalLayout() {

                val screenPixelDensity = context.resources.displayMetrics.density
                val adWidth = (adViewContainer.width / screenPixelDensity).toInt()
                val adSize =
                    AdSize.getCurrentOrientationAnchoredAdaptiveBannerAdSize(context, adWidth)
                showBannerAd(context, adViewContainer, adSize, collapsible, completed, failed)

                adViewContainer.viewTreeObserver.removeOnGlobalLayoutListener(this)
            }
        })
    }

    fun showBannerAd(
        context: Context,
        adViewContainer: ViewGroup,
        adSize: AdSize,
        collapsible: String? = "bottom",
        completed: (() -> Unit?)? = null,
        failed: (() -> Unit?)? = null
    ) {
        if (!isShowAdBanner() || isBlacklist()) {
            failed?.invoke()
            return
        }

        Log.d(TAG, "banner Ad load.")
        val adView = AdView(context)
        adView.adUnitId = ID_ADMOB_BANNER
        adView.setAdSize(adSize)
        val extras = Bundle()
        // extras.putString("collapsible", collapsible)
        val adRequest = AdRequest.Builder()
            .addNetworkExtrasBundle(AdMobAdapter::class.java, extras)
            .build()
        adView.loadAd(adRequest)
        adView.adListener = object : AdListener() {
            override fun onAdClicked() {
                Log.d(TAG, "banner Ad clicked.")
            }

            override fun onAdFailedToLoad(loadAdError: LoadAdError) {
                Log.d(TAG, "banner Ad failed to load: ${loadAdError.message}")
                failed?.invoke()
            }

            override fun onAdLoaded() {
                Log.d(TAG, "banner Ad loaded.")
                completed?.invoke()
            }
        }
        adViewContainer.removeAllViews()
        adViewContainer.addView(adView)
    }

    private fun showBannerAd(
        context: Context,
        adViewContainer: ViewGroup,
        completed: (() -> Unit?)? = null,
        failed: (() -> Unit?)? = null
    ) {
        if (!isShowAdBanner() || isBlacklist()) {
            failed?.invoke()
            return
        }

        Log.d(TAG, "banner Ad Max load.")
        val adView = MaxAdView(ID_MAX_BANNER, context)
        adView.setListener(object : MaxAdViewAdListener {
            override fun onAdLoaded(p0: MaxAd) {
                Log.d(TAG, "banner Ad Max loaded.")
                completed?.invoke()
            }

            override fun onAdDisplayed(p0: MaxAd) {
            }

            override fun onAdHidden(p0: MaxAd) {

            }

            override fun onAdClicked(p0: MaxAd) {
                Log.d(TAG, "banner Ad Max clicked.")
            }

            override fun onAdLoadFailed(p0: String, p1: MaxError) {
                Log.d(TAG, "banner Ad Max failed to load: ${p1.message}")
                failed?.invoke()
            }

            override fun onAdDisplayFailed(p0: MaxAd, p1: MaxError) {

            }

            override fun onAdExpanded(p0: MaxAd) {

            }

            override fun onAdCollapsed(p0: MaxAd) {

            }

        })
        adViewContainer.removeAllViews()
        adViewContainer.addView(adView)
        adView.loadAd()

    }

    fun isShowAdBanner(): Boolean {
        val isShowAdBanner =
            AppPreferences.getInstance().getString(KEY_IS_SHOW_AD_BANNER, DEFAULT_SWITCH).toInt()
        return isShowAdBanner == 1
    }

    fun isShowAdNative(): Boolean {
        val isShowAdNative =
            AppPreferences.getInstance().getString(KEY_IS_SHOW_AD_NATIVE, DEFAULT_SWITCH).toInt()
        return isShowAdNative == 1
    }

    fun isShowAdNativeBottom(): Boolean {
        val isShowAdNative =
            AppPreferences.getInstance().getString(KEY_IS_SHOW_AD_NATIVE_BOTTOM, DEFAULT_SWITCH).toInt()
        return isShowAdNative == 1
    }
    fun isShowNads(): Boolean {
        val isShowAdNative =
            AppPreferences.getInstance().getString(KEY_NA_ADS, DEFAULT_SWITCH).toInt()
        return isShowAdNative == 1
    }
    fun isShowAdInter(): Boolean {
        val isShowAdInter =
            AppPreferences.getInstance().getString(KEY_IS_SHOW_AD_INTER, DEFAULT_SWITCH).toInt()
        return isShowAdInter == 1
    }
    fun isShowLoadingAdInter(): Boolean {
        val isShowAdInter =
            AppPreferences.getInstance().getString(KEY_IS_LOADING_INTER, "0").toInt()
        return isShowAdInter == 1
    }
    fun isShowAdBackInter(): Boolean {
        val isShowAdBackInter =
            AppPreferences.getInstance().getString(KEY_IS_SHOW_AD_BACK_INTER, "0").toInt()
        return isShowAdBackInter == 1
    }

    fun isShowAdResultBackInter(): Boolean {
        val isShowAdInter =
            AppPreferences.getInstance().getString(KEY_IS_SHOW_AD_RESULT_BACK_INTER, DEFAULT_SWITCH)
                .toInt()
        return isShowAdInter == 1
    }

    fun isAppOpenAdAvailable(): Boolean {
        return (if (isAdmob()) appOpenAd != null else appOpenMaxAd?.isReady == true) && wasLoadTimeLessThanNHoursAgo(
            1,
            appOpenAdLoadTime
        )
    }

    fun isInterstitialAdAvailable(): Boolean {
        return (if (isAdmob()) interstitialAd != null else interstitialMaxAd?.isReady == true) && wasLoadTimeLessThanNHoursAgo(
            1,
            interstitialAdLoadTime
        )
    }

    fun isNativeAdAvailable(): Boolean {
        return wasLoadTimeLessThanNHoursAgo(1, nativeAdLoadTime)
    }

    private fun wasLoadTimeLessThanNHoursAgo(numHours: Long, time: Long): Boolean {
        val dateDifference: Long = System.currentTimeMillis() - time
        val numMilliSecondsPerHour: Long = 3600000
        return dateDifference < numMilliSecondsPerHour * numHours
    }

    private fun isBlacklist(): Boolean {
        return useBlacklist
    }

    private fun isAdmob(): Boolean {
        //return false
        return useAdmob == 1
    }

    private fun checkAvailable(adUnit: String, checkIntervalTime: Boolean = false): Boolean {
        if (isBlacklist()) {
            ReportAdUtils.limitErrorAd(LIMIT_ERROR_BLACKLIST, adUnit)
            return false
        }
        reset()
        var displayNumber = AppPreferences.getInstance().getInt(NUMBER_DISPLAY,0)
        val requestNumber = AppPreferences.getInstance().getInt(NUMBER_REQUEST,0)
        val clickNumber = AppPreferences.getInstance().getInt(NUMBER_CLICK,0)
        var adShow = AppPreferences.getInstance().getString(KEY_AD_SHOW, DEFAULT_OPEN_AD_SHOW).toInt()
        if(adUnit== AD_UNIT_INTER){
            adShow = AppPreferences.getInstance().getString(KEY_INTER_AD_SHOW, DEFAULT_INTER_AD_SHOW).toInt()
            displayNumber= AppPreferences.getInstance().getInt(NUMBER_INTER_DISPLAY,0)
        }else if(adUnit== AD_UNIT_NATIVE){
            adShow = AppPreferences.getInstance().getString(KEY_NATIVE_AD_SHOW, DEFAULT_NATIVE_AD_SHOW).toInt()
            displayNumber= AppPreferences.getInstance().getInt(NUMBER_NATIVE_DISPLAY,0)
        }

        val adRequest = AppPreferences.getInstance().getString(KEY_AD_REQUEST, DEFAULT_AD_REQUEST).toInt()
        val adClick = AppPreferences.getInstance().getString(KEY_AD_CLICK, DEFAULT_AD_CLICK).toInt()
        if (displayNumber >= adShow) {
            ReportAdUtils.limitErrorAd(LIMIT_ERROR_DISPLAY, adUnit)
            return false
        }
        if(adUnit== AD_UNIT_NATIVE){
            return checkNative()
        }

        if (requestNumber >= adRequest) {
            ReportAdUtils.limitErrorAd(LIMIT_ERROR_REQUEST, adUnit)
            return false
        }
        if (clickNumber >= adClick) {
            ReportAdUtils.limitErrorAd(LIMIT_ERROR_CLICK, adUnit)
            return false
        }
        if (!checkIntervalTime) return true

        var lastShowTime = appOpenAdLastShowTime
        var adInterval =
            AppPreferences.getInstance().getString(KEY_AD_INTERVAL, DEFAULT_AD_INTERVAL).toInt() * 1000
        if (adUnit == AD_UNIT_INTER) {
            lastShowTime = interstitialAdLastShowTime
            if (showInterCount >1) {
                val start = AppPreferences.getInstance()
                    .getString(KEY_AD_INTER_INTERVAL_START, DEFAULT_AD_INTER_INTERVAL_START).toInt()
                val end = AppPreferences.getInstance()
                    .getString(KEY_AD_INTER_INTERVAL_END, DEFAULT_AD_INTER_INTERVAL_END).toInt()
                adInterval = Random.nextInt(start, end) * 1000
            } else {
                adInterval = AppPreferences.getInstance()
                    .getString(KEY_AD_INTER_INTERVAL, DEFAULT_AD_INTER_INTERVAL).toInt() * 1000
            }
        }

        val interval = adInterval
        val flag = System.currentTimeMillis() - lastShowTime > interval

        if (!flag) {
            ReportAdUtils.limitErrorAd(LIMIT_ERROR_INTERVAL, adUnit)
        }
        return flag
    }

    fun Long.toFormatTime4(): String {
        return SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH).format(this)
    }

    fun areTimestampsSameDay(timestamp1: Long, timestamp2: Long): Boolean {
        val calendar1 = Calendar.getInstance().apply { timeInMillis = timestamp1 }
        val calendar2 = Calendar.getInstance().apply { timeInMillis = timestamp2 }

        // 比较年、月、日是否相同
        return calendar1.get(Calendar.YEAR) == calendar2.get(Calendar.YEAR) &&
                calendar1.get(Calendar.MONTH) == calendar2.get(Calendar.MONTH) &&
                calendar1.get(Calendar.DAY_OF_MONTH) == calendar2.get(Calendar.DAY_OF_MONTH)
    }

    fun calculate(key: String) {
        reset()
        val value = AppPreferences.getInstance().getInt(key,0)
        AppPreferences.getInstance().put(key, value + 1)
    }

    private fun reset() {
        val currentTime = System.currentTimeMillis().toFormatTime4()
        if (showTime.isEmpty() || showTime != currentTime) {
            showTime = currentTime
            AppPreferences.getInstance().put(NUMBER_REQUEST, 0)
            AppPreferences.getInstance().put(NUMBER_DISPLAY, 0)
            AppPreferences.getInstance().put(NUMBER_INTER_DISPLAY, 0)
            AppPreferences.getInstance().put(NUMBER_NATIVE_DISPLAY, 0)
            AppPreferences.getInstance().put(NUMBER_CLICK, 0)
        }
    }
}