package flutter.guru.guru_applovin_flutter

import android.app.Activity
import android.util.Log
import android.util.SparseArray
import com.amazon.device.ads.AdError
import com.amazon.device.ads.DTBAdCallback
import com.amazon.device.ads.DTBAdRequest
import com.amazon.device.ads.DTBAdResponse
import com.amazon.device.ads.DTBAdSize.DTBVideo
import com.applovin.mediation.*
import com.applovin.mediation.ads.MaxRewardedAd
import flutter.guru.guru_applovin_flutter.AdHelp.argumentsMap
import flutter.guru.guru_applovin_flutter.AdHelp.argumentsMapEx
import flutter.guru.guru_applovin_flutter.log.Logger
import io.flutter.plugin.common.MethodChannel
import org.json.JSONObject

/**
 *  Created by yaqiliu on 2020-03-05.
 */
class RewardedVideoAd(id: Int, channel: MethodChannel) : MaxRewardedAdListener,
    MaxAdRevenueListener {

    val id: Int = id
    private val channel: MethodChannel = channel
    var status: Int = AdStatus.CREATED
    private var isFirstLoad = true
    private var rewardedAd: MaxRewardedAd? = null

    companion object {

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

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

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


    }

    init {
        allAds.put(id, this)
    }

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

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

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

    internal fun load(activity: Activity, adUnitId: String, amazonSlotId: String) {
        status = AdStatus.LOADING
        rewardedAd = MaxRewardedAd.getInstance(adUnitId, activity)?.apply {
            setListener(this@RewardedVideoAd)
            setRevenueListener(this@RewardedVideoAd)
        }
        if (isFirstLoad && amazonSlotId.isNotEmpty()) {
            isFirstLoad = false
            val loader = DTBAdRequest()
            loader.setSizes(DTBVideo(320, 480, amazonSlotId))
            loader.loadAd(object : DTBAdCallback {
                // APS bid returned
                override fun onSuccess(dtbAdResponse: DTBAdResponse) {
                    logWarn("amazon reward ad load success")
                    rewardedAd?.setLocalExtraParameter("amazon_ad_response", dtbAdResponse)
                    rewardedAd?.loadAd()
                }

                // No APS bid available
                override fun onFailure(adError: AdError) {
                    logError("amazon reward ad load has failed: " + adError.message)
                    rewardedAd?.setLocalExtraParameter("amazon_ad_error", adError)
                    rewardedAd?.loadAd()
                }
            })
        } else {
            rewardedAd?.loadAd()
        }

    }

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

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

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

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

            else -> status
        }
    }

    override fun onAdLoadFailed(adUnitId: String, err: MaxError) {
        // Rewarded ad failed to display. We recommend loading the next ad
        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
        channel.invokeMethod(
            "onRewardedVideoAdLoadFailed", argumentsMapEx(
                id, parameters = mapOf(
                    "errorCode" to err.code,
                    "ad_unit_name" to (adUnitId ?: ""),
                    "ad_waterfall_name" to waterfallName
                )
            )
        )
        logDebug("onAdLoadFailed $err")
    }

    override fun onAdClicked(ad: MaxAd) {
        channel.invokeMethod("onRewardedVideoAdClicked", 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 ?: ""),
            "payload" to AdHelp.toAdPayload(ad)
        )
        channel.invokeMethod(
            "onRewardedVideoAdDisplayed", AdHelp.argumentsMapEx(
                id, parameters = parameters
            )
        )
        logDebug("onAdDisplayed $parameters")
    }

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

    override fun onUserRewarded(ad: MaxAd, reward: MaxReward) {
        // Rewarded ad was displayed and user should receive the reward
        channel.invokeMethod(
            "onRewardedVideoUserRewarded", argumentsMap(
                id, "rewardLabel", reward.label
                    ?: "", "rewardAmount", reward.amount ?: 0
            )
        )
        logDebug("onUserRewarded")
    }

    override fun onRewardedVideoStarted(ad: MaxAd) {
        channel.invokeMethod("onRewardedVideoStarted", argumentsMap(id))
        logDebug("onUserRewarded")
    }

    override fun onAdLoaded(ad: MaxAd) {
        // Rewarded ad is ready to be shown. rewardedAd!!.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("onRewardedVideoAdLoaded", argumentsMapEx(id, parameters = parameters))
    }

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

    override fun onAdDisplayFailed(ad: MaxAd, err: MaxError) {
        // Rewarded ad failed to display. We recommend loading the next ad
        status = AdStatus.FAILED
        channel.invokeMethod(
            "onRewardedVideoAdDisplayFailed", AdHelp.argumentsMapEx(
                id, parameters = mapOf(
                    "errorCode" to err.code
                )
            )
        )
        logDebug("onAdDisplayFailed $err")
    }

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