//
//  AdvertiseManager.swift
//  WidgetApp
//
//  Created by P21-0025 on 14/03/22.
//

import Foundation
import GoogleMobileAds
import UIKit

class AdvertiseManager : NSObject {
    
    static let sharedInstance = AdvertiseManager()            
    var delegateFullScreen: FullScreenAdDelegate?
    
    //Banner Advertise
    private var bannerAdView: GADBannerView!
    var bannerAdvertiseLoadView: GADBannerView!
    private var errorBannerAd : Error!
        
    //use for notify subscription active or not
    private var isSubscribed = false
    
    //use for checking the app open, interstitial or reward ad presenting or not
    private var isPreseningFullScreenAd  = false
    
    var dictAdIds: Dictionary<String, String> = [:]

    override init() {
        super.init()
        
        GADMobileAds.sharedInstance().start(completionHandler: nil)
    }
    
    func isUserSubscribed(_ isPurchased : Bool) {
        self.isSubscribed = isPurchased
        
        //PreLoad All Advertise as subscription is not active
        if self.isSubscribed == false {
            self.preLoadAdvertiseData()
        }
    }
            
    private func preLoadAdvertiseData() {
//        self.rewardedAd = GADRewardedAd()
        
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.02) {
            self.loadBannerAdData()
            self.loadInterstitialAdData()
        }
    }
        
    //MARK: - App Open Ad Load/Show -
    
    /*
        App Open Advertise Variables
     */
    private var appOpenAd: GADAppOpenAd?
    var delegateOpenAd: AppOpenAdDelegate?
    
    /*
     Request And Present App Open Advertise
     */
    func requestAndPresentAppOpenAd(){
        
        //If user has purchased subscription then don't request/show advertise
        if self.isSubscribed {
            self.delegateOpenAd?.handleAdvertiseCallBack(isPresented: false, errMsg: AdErrorMessage.SUBSCRIPTION_ACTIVE_MSG)
        }
        else {
            
            //Check advertisement id is set or not
            if self.dictAdIds[AdvertiseType.AppOpen.rawValue] == nil {
                self.delegateOpenAd?.handleAdvertiseCallBack(isPresented: false, errMsg: AdErrorMessage.AD_ID_NOT_CONFIGURE)
            }
            else {
                let adId = self.dictAdIds[AdvertiseType.AppOpen.rawValue] ?? ""
                
                let request =  GADRequest()
                GADAppOpenAd.load(withAdUnitID: adId, request: request, orientation: UIInterfaceOrientation.portrait) { (appOpenAdIn, error) in
                    
                    if let error = error {
                        debugPrint("Failed to load app open ad with error: \(error.localizedDescription)")
                        self.appOpenAd = nil
                        
                        if self.delegateOpenAd != nil {
                            self.delegateOpenAd?.handleAdvertiseCallBack(isPresented: false, errMsg: error.localizedDescription)
                        }
                        return
                    }
                    
                    self.appOpenAd = appOpenAdIn
                    self.appOpenAd?.fullScreenContentDelegate = self
                    
                    self.presentAppOpenAd()
                }
            }
        }
    }
    
    private func presentAppOpenAd() {
       if let scene = UIApplication.shared.connectedScenes.first?.delegate as? AppDelegate{
        
           if let gOpenAd = self.appOpenAd, let root = scene.window?.rootViewController {
            
            do {
                if ((try self.appOpenAd?.canPresent(fromRootViewController:  root)) != nil) {
                    if self.isPreseningFullScreenAd {
                        self.delegateOpenAd?.handleAdvertiseCallBack(isPresented: false, errMsg: AdErrorMessage.AD_ALREADY_PRESENTING)
                    }
                    else {
                        
                        gOpenAd.present(fromRootViewController: root)
                    }
                }
                else {
                    debugPrint("Already presenting app open ads")
                }
            } catch {
                
                debugPrint("Error in presenting app open ads = \(error.localizedDescription)")
                self.delegateOpenAd?.handleAdvertiseCallBack(isPresented: false, errMsg: error.localizedDescription)
            }
        } else {
            self.delegateOpenAd?.handleAdvertiseCallBack(isPresented: false, errMsg: AdErrorMessage.NOAD_TOSHOW_MSG)
        }
       }
    }

    //MARK: - Interstitial Ad Load/Show  -
        
    //Interstitial advertise object (Used to store the pre-fetch ad data)
    private var interstitial:GADInterstitialAd?
    private var errorInterstitialAd : Error!
    
    private func loadInterstitialAdData() {
        
        //Check advertisement id is set or not
        if self.dictAdIds[AdvertiseType.Interestitial.rawValue] != nil {
            
            let adId = self.dictAdIds[AdvertiseType.Interestitial.rawValue] ?? ""
            
            let request = GADRequest()
            GADInterstitialAd.load(withAdUnitID: adId,
                                   request: request,
                                   completionHandler: {  ad, error in
                if let error = error {
                    debugPrint("Failed to load interstitial ad with error: \(error.localizedDescription)")
                    self.interstitial = nil
                    self.errorInterstitialAd = error
                    return
                }
                self.interstitial = ad
                self.errorInterstitialAd = nil
                self.interstitial?.fullScreenContentDelegate = self
                
            })
        }
        
    }
    
    func showInterstitialAd(){
        
        let adType = AdvertiseType(rawValue: AdvertiseType.Interestitial.rawValue)

        //if user has purchased subscription then don't request/show advertise
        if self.isSubscribed {
            self.delegateFullScreen?.handleAdvertiseCallBack(isPresented: false, errMsg: AdErrorMessage.SUBSCRIPTION_ACTIVE_MSG, adType: adType!)
        }
        else {
                        
            if self.interstitial != nil {
                     
                if self.isPreseningFullScreenAd {
                    self.delegateFullScreen?.handleAdvertiseCallBack(isPresented: false, errMsg: AdErrorMessage.AD_ALREADY_PRESENTING, adType: adType!)
                }
                else {
                    if let root = UIApplication.getTopViewController() {
                        DispatchQueue.main.async {
                            self.interstitial?.present(fromRootViewController: root)
                        }
                    }
                }
            }
            else{
                if self.errorInterstitialAd == nil {
                    self.delegateFullScreen?.handleAdvertiseCallBack(isPresented: false, errMsg: AdErrorMessage.AD_ID_NOT_CONFIGURE, adType: adType!)
                }
                else {
                    self.delegateFullScreen?.handleAdvertiseCallBack(isPresented: false, errMsg: self.errorInterstitialAd.localizedDescription, adType: adType!)
                }
            }
            
        }
    }
}

//MARK: - Interestitial/Reward  Ad Delegate -
extension AdvertiseManager : GADFullScreenContentDelegate {
    
    func adDidDismissFullScreenContent(_ ad: GADFullScreenPresentingAd) {
                             
        self.isPreseningFullScreenAd = false
        let view = UIView()
        if type(of: ad) == GADAppOpenAd.self { //Manage App Open Ad
           // view.window!.rootViewController?.dismiss(animated: false, completion: nil)
            self.delegateOpenAd?.didDismissOpenAd()
//            if let scene = UIApplication.shared.connectedScenes.first?.delegate as? SceneDelegate {
//                scene.setUpRootViewController()
//            }
        }
        else { //Manage Interstitial & Reward Ad
         
            var adType = AdvertiseType(rawValue: AdvertiseType.Interestitial.rawValue)

            //Check Advertise Type
            if type(of: ad) == GADInterstitialAd.self {
                self.loadInterstitialAdData()
            }
            else if type(of: ad) == GADRewardedAd.self {
                adType = AdvertiseType(rawValue: AdvertiseType.Rewarded.rawValue)
               
            }
            
            self.delegateFullScreen?.didDismissFullScreenAd(adType: adType!)
        }
    }
   
    func adWillPresentFullScreenContent(_ ad: GADFullScreenPresentingAd) {
        
        self.isPreseningFullScreenAd = true
        
        if type(of: ad) == GADAppOpenAd.self { //Manage App Open Ad
            
            self.delegateOpenAd?.handleAdvertiseCallBack(isPresented: true, errMsg: "")
            self.delegateOpenAd?.wilPresentFullScreenAd()
        }
        else { //Manage Interstitial & Reward Ad
            
            var adType = AdvertiseType(rawValue: AdvertiseType.Interestitial.rawValue)

            //Check Advertise Type
            if type(of: ad) == GADInterstitialAd.self {
                adType = AdvertiseType(rawValue: AdvertiseType.Interestitial.rawValue)
            }
            else if type(of: ad) == GADRewardedAd.self {
                adType = AdvertiseType(rawValue: AdvertiseType.Rewarded.rawValue)
            }
            
            self.delegateFullScreen?.handleAdvertiseCallBack(isPresented: true, errMsg: "", adType: adType!)
        }
    }
    
    func ad(_ ad: GADFullScreenPresentingAd, didFailToPresentFullScreenContentWithError error: Error) {
        
        if type(of: ad) == GADAppOpenAd.self { //Manage App Open Ad
            
            self.delegateOpenAd?.handleAdvertiseCallBack(isPresented: false, errMsg: error.localizedDescription)
        }
        else { //Manage Interstitial & Reward Ad
            
            var adType = AdvertiseType(rawValue: AdvertiseType.Interestitial.rawValue)

            //Check Advertise Type
            if type(of: ad) == GADInterstitialAd.self {
                adType = AdvertiseType(rawValue: AdvertiseType.Interestitial.rawValue)
            }
            else if type(of: ad) == GADRewardedAd.self {
                adType = AdvertiseType(rawValue: AdvertiseType.Rewarded.rawValue)
            }
            
            self.delegateFullScreen?.handleAdvertiseCallBack(isPresented: false, errMsg: error.localizedDescription, adType: adType!)
        }
    }
}

//MARK: - Banner Ad Load/Show And Delegate -
extension AdvertiseManager : GADBannerViewDelegate {
           
    /*
       This method fetch banner advertise in advance to show advertise instantly.
    */
   func loadBannerAdData() {
        
        //Check advertisement id is set or not
        if self.dictAdIds[AdvertiseType.Banner.rawValue] != nil {
            
            let adId = self.dictAdIds[AdvertiseType.Banner.rawValue] ?? ""
            self.bannerAdView = GADBannerView(adSize: GADAdSizeBanner)
            self.bannerAdView.adUnitID = adId
            self.bannerAdView.rootViewController = UIApplication.getTopViewController()
            self.bannerAdView.delegate = self
            self.bannerAdView.load(GADRequest())
        }
    }
    
    /*
       Show Banner Advertise in your view and return the success/failure result in callback
    */
    func showBannerAdInView(targetView : UIView, completionHandler : @escaping ((Bool, String) -> Void)) {
        
        //if user has purchased subscription then don't request/show advertise
        if self.isSubscribed {
            completionHandler(false, AdErrorMessage.SUBSCRIPTION_ACTIVE_MSG)
        }
        else {
          
                if self.bannerAdvertiseLoadView == nil {
                    if self.bannerAdView == nil {
                        completionHandler(false, AdErrorMessage.AD_ID_NOT_CONFIGURE)
                    }
                    else if self.errorBannerAd != nil {
                        completionHandler(false, self.errorBannerAd!.localizedDescription)
                    }
                    else {
                        completionHandler(false, AdErrorMessage.NOAD_TOSHOW_MSG)
                    }
                    
                    return
                }
                self.addBannerViewToView(vwTarget: targetView)
                
                //Pre Load Banner Ad
                self.loadBannerAdData()
                
                completionHandler(true, "")
          
        }
    }
    
    /*
        Populate Banner Advertise in your designable view
    */
    private func addBannerViewToView(vwTarget : UIView) {
        self.bannerAdvertiseLoadView.translatesAutoresizingMaskIntoConstraints = false
        
        //Remove previously added view if advertise already added
        vwTarget.subviews.forEach( { $0.removeFromSuperview() } )
        
        vwTarget.addSubview(self.bannerAdvertiseLoadView)
        vwTarget.addConstraints(
            [
                NSLayoutConstraint(item: self.bannerAdvertiseLoadView!, attribute: .top, relatedBy: .equal, toItem: vwTarget, attribute: .top, multiplier: 1, constant: 0),
                NSLayoutConstraint(item: self.bannerAdvertiseLoadView!, attribute: .bottom, relatedBy: .equal, toItem: vwTarget, attribute: .bottom, multiplier: 1, constant: 0),
                NSLayoutConstraint(item: self.bannerAdvertiseLoadView!, attribute: .leading, relatedBy: .equal, toItem: vwTarget, attribute: .leading, multiplier: 1, constant: 0),
                NSLayoutConstraint(item: self.bannerAdvertiseLoadView!, attribute: .trailing, relatedBy: .equal, toItem: vwTarget, attribute: .trailing, multiplier: 1, constant: 0),
            ])
    }
    
    /*
     GADBannerViewDelegate Methods
     */
    func bannerViewDidReceiveAd(_ bannerView: GADBannerView) {
        debugPrint("bannerViewDidReceiveAd")
        
        self.bannerAdvertiseLoadView = bannerView
        self.errorBannerAd = nil
    }
    
    func bannerView(_ bannerView: GADBannerView, didFailToReceiveAdWithError error: Error) {
        
        debugPrint("bannerView:didFailToReceiveAdWithError: \(error.localizedDescription)")
        
        self.errorBannerAd = error
        self.bannerAdvertiseLoadView = nil
    }
    
    func bannerViewDidRecordImpression(_ bannerView: GADBannerView) {
        debugPrint("bannerViewDidRecordImpression")
    }
    
    func bannerViewWillPresentScreen(_ bannerView: GADBannerView) {
        debugPrint("bannerViewWillPresentScreen")
    }
    
    func bannerViewWillDismissScreen(_ bannerView: GADBannerView) {
        debugPrint("bannerViewWillDismissScreen")
    }
    
    func bannerViewDidDismissScreen(_ bannerView: GADBannerView) {
        debugPrint("bannerViewDidDismissScreen")
    }
}

//MARK: - AppOpen, Interstitial & Reward Ad Custom Delegate -

/*
    Interstitial/Reward success/failure advertise and dismiss advertise delegate
    Get Rewards after watching full ads delegate
*/
protocol FullScreenAdDelegate {
    
    func handleAdvertiseCallBack(isPresented : Bool, errMsg : String, adType : AdvertiseType)
    func didDismissFullScreenAd(adType : AdvertiseType) //Optional Method
    func willPresentFullScreenAd(adType:AdvertiseType)
}

extension FullScreenAdDelegate {
    
    func didDismissFullScreenAd(adType : AdvertiseType){
        debugPrint("")
    }
  
}

/*
  AppOpen success/failure advertise and dismiss advertise delegate
*/
protocol AppOpenAdDelegate {
    func handleAdvertiseCallBack(isPresented : Bool, errMsg : String)
    func didDismissOpenAd() //Optional Method
    func wilPresentFullScreenAd()
}

//extension AppOpenAdDelegate {
//    func didDismissOpenAd(){
//        debugPrint("")
//    }
//}



private struct AdErrorMessage {
    
    static let NOAD_TOSHOW_MSG = "No Advertise to show at the time."
    static let SUBSCRIPTION_ACTIVE_MSG = "Advertise is not loaded due to subscription is active."
    static let AD_ID_NOT_CONFIGURE = "Advertisement Id is not configured"
    static let AD_ALREADY_PRESENTING = "Other FullScreen Ad is already Presenting"
}

enum AdvertiseType : String {
    
    case AppOpen
    case Banner
    case Interestitial
    case Rewarded
    case Native
}
