import Flutter
import UIKit
import AppLovinSDK
import DTBiOSSDK
import GuruConsent
import FBAudienceNetwork
import OpenWrapSDK

public class SwiftGuruApplovinFlutterPlugin: NSObject, FlutterPlugin {
    
    private var channel: FlutterMethodChannel? = nil
    
    public static func register(with registrar: FlutterPluginRegistrar) {
        let channel = FlutterMethodChannel(name: "guru_applovin_flutter", binaryMessenger: registrar.messenger())
        let instance = SwiftGuruApplovinFlutterPlugin()
        instance.channel = channel
        registrar.addMethodCallDelegate(instance, channel: channel)
    }
    
    public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
        let arguments = call.arguments as? [String: Any] ?? [String: Any]()
        let id = arguments["id"] as? Int
        NSLog("applovin handle")
        switch call.method {
        case "initialize":
            callInitialize(arguments: arguments, result: result)
            break
        case "gatherConsentAndInitialize":
            callGatherConsentAndInitialize(arguments: arguments, result: result)
            break
        case "requestGdpr":
            callRequestGdpr(arguments: arguments,result:result)
        case "resetGdpr":
            callRestGdpr(result:result)
        case "showPrivacyOptionsForm":
            callShowPrivacyOptionsForm(arguments: arguments, result: result)
        case "openDebugger":
            openDebugger(result: result)
        case "loadRewardedVideoAd":
            callLoadRewardedVideoAd(id: id, call, result: result)
            break
        case "showRewardedVideoAd":
            callShowRewardedVideoAd(id: id, call, result: result)
            break
        case "loadInterstitialAd":
            callLoadInterstitialAd(id: id, call, result: result)
            break
        case "showInterstitialAd":
            callShowInterstitialAd(id: id, call, result: result)
            break
        case "isInterstitialAdLoaded":
            callInterstitialAdLoaded(id: id, result: result)
            break
        case "loadBannerAd":
            callLoadBannerAd(id: id, call, result: result)
            break
        case "showBannerAd":
            callShowBannerAd(id: id, call, result: result)
            break
        case "hideBannerAd":
            callHideBannerAd(id: id, result: result)
            break
        case "disposeBannerAd":
            callDisposeBannerAd(id: id, result: result)
            break
        case "disposeInterstitialAd":
            callDisposeInterstitialAd(id: id, result: result)
            break
        case "disposeRewardedAd":
            callDisposeRewardedAd(id: id, result: result)
            break
        case "afterAcceptPrivacy":
            callAfterAcceptPrivacy(arguments: arguments, result: result)
            break
        case "getInterstitialAdState":
            callGetInterstitialAdsState(id: id, result: result)
            break;
        case "getRewardedAdState":
            callGetRewardedAdsState(id: id, result: result)
            break;
        case "checkConsentDialogStatus":
            callCheckConsentDialogStatus(result: result)
            break
        case "hasUserConsent":
            callHasUserConsent(result: result)
        case "isTablet":
            callIsTablet(result: result)
            break
        case "setKeywords":
            callSetKeywords(call, result: result)
            break
        case "clearTargetingData":
            callClearTargetingData(call, result: result)
            break;
        case "getBannerAdSize":
            callGetBannerAdSize(call, result: result)
            break;
        case "updateeOrientation":
            callClearTargetingData(call, result: result)
            break;
        default:
            result(FlutterMethodNotImplemented)
        }
    }
    
    private func callInitialize(arguments: [String: Any], result: @escaping FlutterResult) {
        FBAdSettings.setAdvertiserTrackingEnabled(true)
        let debugLog = arguments["debug_mode"] as? Bool ?? false
        //init amazon
        let amazonAppId = arguments["amazon_appId"] as? String ?? ""
        if (amazonAppId != "") {
            DTBAds.sharedInstance().setAppKey(amazonAppId)
            DTBAds.sharedInstance().setAdNetworkInfo(DTBAdNetworkInfo.init(networkName: DTBADNETWORK_MAX))
            DTBAds.sharedInstance().mraidCustomVersions = ["1.0", "2.0", "3.0"]
            DTBAds.sharedInstance().mraidPolicy = CUSTOM_MRAID
            DTBAds.sharedInstance().useGeoLocation = true
            
            if (debugLog) {
                DTBAds.sharedInstance().setLogLevel(DTBLogLevelAll)
            }
            //            DTBAds.sharedInstance().testMode = true
        }
        
        let pubmaticStoreUrl = arguments["pubmatic_store_url"] as? String ?? ""
        if(pubmaticStoreUrl != ""){
           let appInfo = POBApplicationInfo()
           appInfo.storeURL = URL(string: pubmaticStoreUrl)!
           OpenWrapSDK.setApplicationInfo(appInfo)
        }
        
        let userId = arguments["user_id"] as? String ?? ""
        if (userId != "") {
            ALSdk.shared()!.userIdentifier = userId
            NSLog("userIdentifier SET Success \(userId)")
        } else {
            NSLog("userIdentifier Is Null")
        }
        ALSdk.shared()!.mediationProvider = "max"
        ALSdk.shared()!.settings.isMuted = true
        ALSdk.shared()!.initializeSdk { (configuration: ALSdkConfiguration) in
            // AppLovin SDK is initialized, start loading ads
            result(true)
        }
    }
    
    private func callRequestGdpr(arguments: [String: Any],result: @escaping FlutterResult) {
        let testGeography = arguments["debug_geography"] as? Int
        let testDeviceId = arguments["test_device_id"] as? String
        guard let controller = UIViewController.top() else {
            result(ConsentStatus.UNKNOWN.rawValue)
            return
        }
        
        if let debugGeography = testGeography,let debugDeviceId = testDeviceId{
            
            var debug = GuruConsent.DebugSettings()
            debug.testDeviceIdentifiers = [debugDeviceId]
            debug.geography = GuruConsent.DebugSettings.Geography(rawValue: debugGeography) ?? GuruConsent.DebugSettings.Geography.EEA
            GuruConsent.debug = debug
        }
        GuruConsent.start(from: controller) { res in
            switch res{
            case .success(let value):
                print("gdpr result\(value)")
                switch value{
                case .obtained:
                    result(ConsentStatus.OBTAINED.rawValue)
                case .notRequired:
                    result(ConsentStatus.NOT_REQUIRED.rawValue)
                case .required:
                    result(ConsentStatus.REQUIRED.rawValue)
                case .unknown:
                    result(ConsentStatus.UNKNOWN.rawValue)
                default:
                    result(ConsentStatus.UNKNOWN.rawValue)
                }
            case.failure(let error):
                result(ConsentStatus.UNKNOWN.rawValue)
            }
        }
        
    }
    
    func callGatherConsentAndInitialize(arguments: [String: Any], result: @escaping FlutterResult){
        let testGeography = arguments["debug_geography"] as? Int
        let testDeviceId = arguments["test_device_id"] as? String
        
        guard let controller = UIViewController.top() else {
            result(ConsentStatus.UNKNOWN.rawValue)
            return
        }
        if(GuruConsent.canRequestAds){
            self.callInitialize(arguments: arguments, result: result)
            return;
        }
        
        if let debugGeography = testGeography,let debugDeviceId = testDeviceId{
            
            var debug = GuruConsent.DebugSettings()
            debug.testDeviceIdentifiers = [debugDeviceId]
            debug.geography = GuruConsent.DebugSettings.Geography(rawValue: debugGeography) ?? GuruConsent.DebugSettings.Geography.EEA
            GuruConsent.debug = debug
        }
        GuruConsent.start(from: controller) { res in
            switch res{
            case .success(let value):
                print("gdpr result\(value)")
               
                
            case.failure(let error):
//                result(false)
                print("\(error)")
            }
            if(GuruConsent.canRequestAds){
                self.callInitialize(arguments: arguments, result: result)
            }else{
                result(false);
            }
        }
        
    }
    
    private func callShowPrivacyOptionsForm(arguments: [String: Any], result: @escaping FlutterResult){
        guard let controller = UIViewController.top() else {
            result(ConsentStatus.UNKNOWN.rawValue)
            return
        }
        GuruConsent.openPrivacyOptions(from: controller) { error in
            result(ConsentStatus.UNKNOWN.rawValue)
        }
    }
    
    private func callRestGdpr(result: @escaping FlutterResult) {
        GuruConsent.reset()
        DispatchQueue.global().async {
            result(true)
        }
    }
    
    private func openDebugger(result: @escaping FlutterResult) {
        ALSdk.shared()!.showMediationDebugger()
        DispatchQueue.global().async {
            result(true)
        }
    }
    
    private func callLoadRewardedVideoAd(id: Int?, _ call: FlutterMethodCall, result: @escaping FlutterResult) {
        guard let id = id else {
            result(FlutterError(code: "no_hash_id", message: "load rewardedVideoAd lose hash id", details: nil))
            return
        }
        
        guard let channel = channel else {
            result(FlutterError(code: "no_channel", message: "load rewardedVideoAd lose channel", details: nil))
            return
        }
        
        let rewardedVideoAd = RewardedVideoAd.createRewardedVideo(id: id, channel: channel)
        if (rewardedVideoAd.status == AdStatus.LOADED) {
            result(true)
            return
        }
        
        let arguments = call.arguments as? [String: Any] ?? [String: Any]()
        guard let adUnitId = arguments["adUnitId"] as? String, adUnitId.isEmpty == false else {
            result(FlutterError(code: "no_adunit_id", message: "a null or empty adUnitId was provided for rewarded video id=\(rewardedVideoAd.id)", details: nil))
            return
        }
        let amazonSlotId = arguments["adAmazonSlotId"] as? String ?? ""
        rewardedVideoAd.load(adUnitId: adUnitId, amazonSlotId: amazonSlotId)
        result(true)
    }
    
    private func callShowRewardedVideoAd(id: Int?, _ call: FlutterMethodCall, result: @escaping FlutterResult) {
        guard let id = id else {
            result(FlutterError(code: "no_hash_id", message: "show rewardedVideoAd lose hash id", details: nil))
            return
        }
        
        guard let rewardedVideoAd = RewardedVideoAd.getAdForId(id: id), rewardedVideoAd.status == AdStatus.LOADED else {
            result(FlutterError(code: "ad_not_loaded", message: "show failed for rewardedVideo ad, no ad was loaded", details: nil))
            return
        }
        
        let arguments = call.arguments as? [String: Any] ?? [String: Any]()
        let placement = arguments["placement"] as? String ?? "Unknown"
        result(rewardedVideoAd.show(placement: placement))
    }
    
    private func callLoadInterstitialAd(id: Int?, _ call: FlutterMethodCall, result: @escaping FlutterResult) {
        guard let id = id else {
            result(FlutterError(code: "no_hash_id", message: "load interstitialAd lose hash id", details: nil))
            return
        }
        
        guard let channel = channel else {
            result(FlutterError(code: "no_channel", message: "load interstitialAd lose channel", details: nil))
            return
        }
        
        let interstitialAd = InterstitialAd.createInterstitialAd(id: id, channel: channel)
        if (interstitialAd.status == AdStatus.LOADED) {
            result(true)
            return
        }
        
        let arguments = call.arguments as? [String: Any] ?? [String: Any]()
        guard let adUnitId = arguments["adUnitId"] as? String, adUnitId.isEmpty == false else {
            result(FlutterError(code: "no_adunit_id", message: "a null or empty adUnitId was provided for interstitialAd id=\(interstitialAd.id)", details: nil))
            return
        }
        
        let adAmazonSlotId = arguments["adAmazonSlotId"] as? String ?? ""
        
        interstitialAd.load(adUnitId: adUnitId, adAmazonSlotId: adAmazonSlotId)
        result(true)
    }
    
    private func callShowInterstitialAd(id: Int?, _ call: FlutterMethodCall, result: @escaping FlutterResult) {
        guard let id = id else {
            result(FlutterError(code: "no_hash_id", message: "show interstitialAd lose hash id", details: nil))
            return
        }
        
        guard let interstitialAd = InterstitialAd.getAdForId(id: id), interstitialAd.status == AdStatus.LOADED else {
            result(FlutterError(code: "ad_not_loaded", message: "show failed for interstitial ad, no ad was loaded", details: nil))
            return
        }
        
        let arguments = call.arguments as? [String: Any] ?? [String: Any]()
        let placement = arguments["placement"] as? String ?? "Unknown"
        result(interstitialAd.show(placement: placement))
    }
    
    private func callInterstitialAdLoaded(id: Int?, result: @escaping FlutterResult) {
        guard let id = id else {
            result(FlutterError(code: "no_hash_id", message: "is interstitialAd loaded lose hash id", details: nil))
            return
        }
        
        guard let interstitialAd = InterstitialAd.getAdForId(id: id) else {
            result(FlutterError(code: "no_ad_for_id", message: "isAdLoaded failed, no add exists for interstitialAd", details: nil))
            return
        }
        
        if (interstitialAd.status == AdStatus.LOADED) {
            result(true)
        } else {
            result(false)
        }
    }
    
    private func callLoadBannerAd(id: Int?, _ call: FlutterMethodCall, result: @escaping FlutterResult) {
        guard let id = id else {
            result(FlutterError(code: "no_hash_id", message: "load bannerAd lose hash id", details: nil))
            return
        }
        
        guard let channel = channel else {
            result(FlutterError(code: "no_channel", message: "load bannerAd lose channel", details: nil))
            return
        }
        
        let bannerAd = BannerAd.createBannerAd(id: id, channel: channel)
        
        let arguments = call.arguments as? [String: Any] ?? [String: Any]()
        guard let adUnitId = arguments["adUnitId"] as? String, adUnitId.isEmpty == false else {
            result(FlutterError(code: "no_adunit_id", message: "a null or empty adUnitId was provided for bannerAd id=\(bannerAd.id)", details: nil))
            return
        }
        
        let adAmazonSlotId = arguments["adAmazonSlotId"] as? String ?? ""
        
        let anchorOffset = arguments["anchorOffset"] as? String
        if (anchorOffset != nil) {
            bannerAd.anchorOffset = (anchorOffset! as NSString).doubleValue
        }
        let horizontalCenterOffset = arguments["horizontalCenterOffset"] as? String
        if (horizontalCenterOffset != nil) {
            bannerAd.horizontalCenterOffset = (horizontalCenterOffset! as NSString).doubleValue
        }
        let anchorType = arguments["anchorType"] as? Int
        if (anchorType != nil) {
            bannerAd.anchorType = anchorType ?? 0
        }
        
        let placement = arguments["placement"] as? String ?? "Unknown"
        
        bannerAd.load(adUnitId: adUnitId, adAmazonSlotId: adAmazonSlotId, placement: placement)
        result(true)
    }
    
    private func callShowBannerAd(id: Int?, _ call: FlutterMethodCall, result: @escaping FlutterResult) {
        guard let id = id else {
            result(FlutterError(code: "no_hash_id", message: "show bannerAd lose hash id", details: nil))
            return
        }
        
        guard let bannerAd = BannerAd.getAdForId(id: id), bannerAd.status == AdStatus.LOADED else {
            result(FlutterError(code: "ad_not_loaded", message: "show failed for banner ad, no ad was loaded", details: nil))
            return
        }
        
        let arguments = call.arguments as? [String: Any] ?? [String: Any]()
        let anchorOffset = arguments["anchorOffset"] as? String
        if (anchorOffset != nil) {
            bannerAd.anchorOffset = (anchorOffset! as NSString).doubleValue
        }
        let horizontalCenterOffset = arguments["horizontalCenterOffset"] as? String
        if (horizontalCenterOffset != nil) {
            bannerAd.horizontalCenterOffset = (horizontalCenterOffset! as NSString).doubleValue
        }
        let anchorType = arguments["anchorType"] as? Int
        if (anchorType != nil) {
            bannerAd.anchorType = anchorType ?? 0
        }
        
        bannerAd.show()
        result(true)
    }
    
    private func callHideBannerAd(id: Int?, result: @escaping FlutterResult) {
        guard let id = id else {
            result(FlutterError(code: "no_hash_id", message: "hide bannerAd lose hash id", details: nil))
            return
        }
        
        guard let bannerAd = BannerAd.getAdForId(id: id) else {
            result(FlutterError(code: "ad_is_null", message: "bannerAd is null", details: nil))
            return
        }
        
        bannerAd.hide()
        result(true)
    }
    
    private func callDisposeBannerAd(id: Int?, result: @escaping FlutterResult) {
        guard let id = id else {
            result(FlutterError(code: "no_hash_id", message: "dispose bannerAd lose hash id", details: nil))
            return
        }
        
        guard let bannerAd = BannerAd.getAdForId(id: id) else {
            result(FlutterError(code: "ad_not_initialized", message: "bannerAd is null", details: nil))
            return
        }
        
        bannerAd.dispose()
        result(true)
    }
    
    private func callDisposeInterstitialAd(id: Int?, result: @escaping FlutterResult) {
        guard let id = id else {
            result(FlutterError(code: "no_hash_id", message: "dispose interstitialAd lose hash id", details: nil))
            return
        }
        
        guard let interstitialAd = InterstitialAd.getAdForId(id: id) else {
            result(FlutterError(code: "ad_not_initialized", message: "interstitialAd is null", details: nil))
            return
        }
        
        interstitialAd.dispose()
        result(true)
    }
    
    private func callDisposeRewardedAd(id: Int?, result: @escaping FlutterResult) {
        guard let id = id else {
            result(FlutterError(code: "no_hash_id", message: "dispose rewardedVideoAd lose hash id", details: nil))
            return
        }
        
        guard let rewardedAd = RewardedVideoAd.getAdForId(id: id) else {
            result(FlutterError(code: "ad_not_initialized", message: "rewardedVideoAd is null", details: nil))
            return
        }
        
        rewardedAd.dispose()
        result(true)
    }
    
    private func callAfterAcceptPrivacy(arguments: [String: Any], result: @escaping FlutterResult) {
        //         let consentResult = arguments["consentResult"] as? Bool
        //         if (consentResult != nil) {
        //             DTBAds.sharedInstance().setConsentStatus(consentResult == true ? DTBConsentStatus.EXPLICIT_YES : DTBConsentStatus.EXPLICIT_NO)
        //         } else {
        //             DTBAds.sharedInstance().setConsentStatus(DTBConsentStatus.UNKNOWN)
        //         }
        ALPrivacySettings.setHasUserConsent(true)
        result(true)
    }
    
    private func callCheckConsentDialogStatus(result: @escaping FlutterResult) {
        guard let configuration = ALSdk.shared()?.configuration else {
            result(FlutterError(code: "not_found_config", message: "check consent dialog error! not found configuration", details: nil))
            return
        }
        switch (configuration.consentDialogState) {
        case .applies:
            result(ConsentResult.ShouldShow)
            break
        case .doesNotApply:
            result(ConsentResult.GdprNotApplies)
            break
        default:
            result(ConsentResult.Unknown)
            break
        }
    }
    
    private func callGetInterstitialAdsState(id: Int?, result: @escaping FlutterResult) {
        guard let id = id else {
            result(FlutterError(code: "no_hash_id", message: "get interstitialAd state loss hash id", details: nil))
            return
        }
        
        guard let interstitialAd = InterstitialAd.getAdForId(id: id) else {
            result(FlutterError(code: "ad_not_initialized", message: "interstitialAd is null", details: nil))
            return
        }
        result(interstitialAd.getState())
    }
    
    private func callGetRewardedAdsState(id: Int?, result: @escaping FlutterResult) {
        guard let id = id else {
            result(FlutterError(code: "no_hash_id", message: "get rewardedAd state loss hash id", details: nil))
            return
        }
        
        guard let rewardedAd = RewardedVideoAd.getAdForId(id: id) else {
            result(FlutterError(code: "ad_not_initialized", message: "rewardedAd is null", details: nil))
            return
        }
        result(rewardedAd.getState())
    }
    
    private func callHasUserConsent(result: @escaping FlutterResult) {
        result(ALPrivacySettings.hasUserConsent())
    }
    
    private func callIsTablet(result: @escaping FlutterResult) {
        let isTablet = (UIDevice.current.userInterfaceIdiom == .pad)
        result(isTablet)
    }
    
    private func callSetKeywords(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
        let keywords = call.arguments as? [String] ?? [String]()
        ALSdk.shared()?.targetingData.keywords = keywords
        result(true)
    }
    
    private func callClearTargetingData(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
        ALSdk.shared()?.targetingData.clearAll()
        result(true)
    }
    
    private func callUpdateOrientation(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
        let arguments = call.arguments as? [String: Any] ?? [String: Any]()
        guard let orientation = arguments["orientation"] as? Int else {
            result(FlutterError(code: "invalid_orientation", message: "a null or empty Orientation was provided", details: nil))
            return
        }
        BannerAd.orientationChanged(orientation: orientation)
        result(true)
    }

    private func callGetBannerAdSize(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
        let height = (UIDevice.current.userInterfaceIdiom == .pad) ? 90.0 : 50.0
        let width = UIScreen.main.bounds.width
        result(["width": width, "height": height] as [String : Any])
    }

    
}
