package flutter.guru.guru_applovin_flutter

import android.app.Activity
import android.util.SparseArray
import com.amazon.device.ads.*
import com.applovin.mediation.*
import com.applovin.mediation.ads.MaxInterstitialAd
import flutter.guru.guru_applovin_flutter.log.Logger
import io.flutter.plugin.common.MethodChannel


/**
 *  Created by yaqiliu on 2020-03-06.
 */
class InterstitialAd(id: Int, channel: MethodChannel) : MaxAdListener, MaxAdRevenueListener {

    val id: Int = id
    private val channel: MethodChannel = channel
    var status: Int = AdStatus.CREATED

    private var interstitialAd: MaxInterstitialAd? = null

    private var isFirstLoad = true

    val adUnitId: String?
        get() = interstitialAd?.adUnitId

    companion object {

        private val allAds: SparseArray<InterstitialAd> = SparseArray()

        fun createInterstitial(
            id: Int,
            channel: MethodChannel
        ): InterstitialAd {
            var interstitialAd = getAdForId(id)
            if (interstitialAd == null) {
                interstitialAd = InterstitialAd(
                    id,
                    channel
                )
            }
            return interstitialAd
        }

        fun getAdForId(id: Int): InterstitialAd? {
            return allAds.get(id)
        }
    }

    init {
        allAds.put(id, this)
    }

    private fun logWarn(message: String) {
        Logger.w("InterstitialAd", "[${interstitialAd?.adUnitId}] $message")
    }

    private fun logDebug(message: String) {
        Logger.d("InterstitialAd", "[${interstitialAd?.adUnitId}] $message")
    }

    private fun logError(message: String) {
        Logger.e("InterstitialAd", "[${interstitialAd?.adUnitId}] $message")
    }

    internal fun load(activity: Activity, adUnitId: String, adAmazonSlotId: String?) {
        status = AdStatus.LOADING
        interstitialAd?.apply {
            setListener(null)
            setRevenueListener(null)
            destroy()
        }
        interstitialAd = MaxInterstitialAd(adUnitId, activity)
        interstitialAd?.apply {
            setListener(this@InterstitialAd)
            setRevenueListener(this@InterstitialAd)
        }
        if (!adAmazonSlotId.isNullOrBlank() && isFirstLoad) {
            // APS LoadAd only needs to be called once.
            isFirstLoad = false
            val loader = DTBAdRequest()
            loader.setSizes(DTBAdSize(320, 480, adAmazonSlotId))
            loader.loadAd(object : DTBAdCallback {
                override fun onFailure(adError: AdError) {
                    logDebug(
                        "Amazon:Oops interstitial ad load has failed: ${adError.message}"
                    )
                    interstitialAd?.setLocalExtraParameter("amazon_ad_error", adError)
                    interstitialAd?.loadAd()
                }

                override fun onSuccess(dtbAdResponse: DTBAdResponse) {
                    logWarn("Amazon InterstitialAd load success")
                    interstitialAd?.setLocalExtraParameter("amazon_ad_response", dtbAdResponse)
                    interstitialAd?.loadAd()
                }
            })
        } else {
            Logger.d(
                "InterstitialAd",
                "load interstitial ad $adUnitId"
            )
            interstitialAd?.loadAd()
        }

    }

    internal fun show(placement: String? = null): Boolean {
        val result = interstitialAd?.let {
            if (it.isReady) {
                logWarn("show true!")
                it.showAd(placement)
                true
            } else false
        } ?: false
        logWarn("show2 $result")
        return result
    }

    internal fun dispose() {
        interstitialAd?.destroy()
        logDebug("dispose")
        allAds.remove(id)
    }

    internal fun getState(): Int {
        return when (status) {
            AdStatus.LOADED -> {
                if (interstitialAd?.isReady == true) {
                    AdStatus.LOADED
                } else {
                    AdStatus.FAILED
                }
            }

            AdStatus.LOADING -> {
                if (interstitialAd?.isReady == true) {
                    AdStatus.LOADED
                } else {
                    AdStatus.LOADING
                }
            }

            else -> status
        }
    }

    override fun onAdLoadFailed(adUnitId: String, err: MaxError) {

        // Interstitial ad failed to load. We recommend re-trying in 3 seconds.
        val waterfallName = try {
            err.waterfall?.let { waterfall ->
                logWarn("Waterfall Name: " + waterfall.name + " and Test Name: " + waterfall.testName)
                logWarn("Waterfall latency was: " + waterfall.latencyMillis + " milliseconds")

                for (networkResponse: MaxNetworkResponseInfo in waterfall.networkResponses) {
                    logWarn(
                        "Network -> " + networkResponse.mediatedNetwork +
                                "...latency: " + networkResponse.latencyMillis +
                                "...credentials: " + networkResponse.credentials + " milliseconds" +
                                "...error: " + networkResponse.error
                    )
                }
                return@let waterfall.name
            } ?: "unknown"
        } catch (throwable: Throwable) {
            logError("onAdLoadFailed error: ${throwable.message}")
            "unknown"
        }
        status = AdStatus.FAILED
        logWarn("onAdLoadFailed[$adUnitId] ${err.message}")
        channel.invokeMethod(
            "onInterstitialAdLoadFailed",
            AdHelp.argumentsMapEx(
                id, parameters = mapOf(
                    "errorCode" to err.code,
                    "ad_unit_name" to (adUnitId ?: ""),
                    "ad_waterfall_name" to waterfallName
                )
            )
        )
    }

    override fun onAdClicked(ad: MaxAd) {
        channel.invokeMethod("onInterstitialAdClicked", AdHelp.argumentsMap(id))
        logDebug("onAdClicked")
    }

    override fun onAdDisplayed(ad: MaxAd) {
        val parameters = mapOf(
            "ad_revenue" to (ad.revenue ?: -1.0),
            "ad_format" to (ad.format?.label ?: ""),
            "ad_source" to (ad.networkName ?: ""),
            "ad_unit_name" to (ad.adUnitId ?: ""),
            "ad_creative_id" to (ad.creativeId ?: ""),
            "ad_network_name" to (ad.networkName ?: "")
        )
        channel.invokeMethod(
            "onInterstitialAdDisplayed", AdHelp.argumentsMapEx(
                id, parameters = parameters
            )
        )
        logDebug("onAdDisplayed $parameters")
    }

    override fun onAdLoaded(ad: MaxAd) {
        // Interstitial ad is ready to be shown. interstitialAd!!.isReady() will now return 'true'
        val waterfallName = try {
            ad.waterfall?.let { waterfall ->
                logWarn("Waterfall Name: " + waterfall.name + " and Test Name: " + waterfall.testName)
                logWarn("Waterfall latency was: " + waterfall.latencyMillis + " milliseconds")

                for (networkResponse: MaxNetworkResponseInfo in waterfall.networkResponses) {
                    logWarn(
                        "Network -> " + networkResponse.mediatedNetwork +
                                "...latency: " + networkResponse.latencyMillis +
                                "...credentials: " + networkResponse.credentials + " milliseconds" +
                                "...error: " + networkResponse.error
                    )
                }
                return@let waterfall.name
            } ?: "unknown"
        } catch (throwable: Throwable) {
            logError("onAdLoadFailed error: ${throwable.message}")
            "unknown"
        }
        val parameters = mapOf(
            "ad_waterfall_name" to waterfallName,
        )
        status = AdStatus.LOADED
        channel.invokeMethod(
            "onInterstitialAdLoaded",
            AdHelp.argumentsMapEx(id, parameters = parameters)
        )
        logDebug("onAdLoaded")
    }

    override fun onAdHidden(ad: MaxAd) {
        // Interstitial ad is hidden. Pre-load the next ad
        status = AdStatus.CREATED
        channel.invokeMethod("onInterstitialAdHidden", AdHelp.argumentsMap(id))
        logDebug("onAdHidden")
    }

    override fun onAdDisplayFailed(ad: MaxAd, err: MaxError) {
        // Interstitial ad failed to display. We recommend loading the next ad
        status = AdStatus.FAILED
        channel.invokeMethod(
            "onInterstitialAdDisplayFailed", AdHelp.argumentsMapEx(
                id, parameters = mapOf(
                    "errorCode" to err.code,
                    "ad_format" to (ad.format?.label ?: ""),
                    "ad_source" to (ad.networkName ?: ""),
                    "ad_unit_name" to (ad.adUnitId ?: ""),
                    "ad_creative_id" to (ad.creativeId ?: ""),
                    "ad_network_name" to (ad.networkName ?: "")
                )
            )
        )
        logDebug("onAdDisplayFailed")

    }

    override fun onAdRevenuePaid(ad: MaxAd) {
        channel.invokeMethod(
            "onAdImpression", mapOf(
                "payload" to AdHelp.toAdPayload(ad)
            )
        )
    }

}
