//
// OneTrust.swift
//  NativeUIReact
//
//  Created by Oliver Spirito on 8/11/20.
//

import Foundation
#if os(iOS)
import OTPublishersHeadlessSDK
#else
import OTPublishersHeadlessSDKtvOS
#endif
import AppTrackingTransparency

@objc(OneTrust) class OneTrust: NSObject{
    ///Initializer
    @objc(startSDK:domainIdentifier:languageCode:params:autoShowBanner:resolve:rejecter:)
    public func startSDK(_ storageLocation:String!,
                         domainIdentifier:String!,
                         languageCode:String!,
                         params:NSDictionary?,
                         autoShowBanner:Bool=false,
                         resolve:@escaping RCTPromiseResolveBlock,
                         rejecter reject: @escaping RCTPromiseRejectBlock) -> Void{
        
        let otParams = OTSdkParams(countryCode: params?["countryCode"] as? String, regionCode: params?["regionCode"] as? String)
        
        if let version = params?["sdkVersion"] as? String{
            otParams.setSDKVersion(version)
        }
        
        
        ///Check for profile sync params
        if let syncParams = params?["profileSyncParams"] as? NSDictionary,
           let identifier = syncParams["identifier"] as? String,
           let jwt = syncParams["syncProfileAuth"] as? String{
            let profileSyncParams = OTProfileSyncParams()
            profileSyncParams.setIdentifier(identifier)
            profileSyncParams.setSyncProfileAuth(jwt)
            profileSyncParams.setSyncProfile("true")
            
            otParams.setShouldCreateProfile("true") //force profile to sync
            otParams.setProfileSyncParams(profileSyncParams) //Add sync params to otParams object
        }
        
        ///Initialize SDK
        OTPublishersHeadlessSDK.shared.startSDK(storageLocation: storageLocation, domainIdentifier: domainIdentifier, languageCode: languageCode, params: otParams){(response) in
            DispatchQueue.main.async{
                if let vc = UIApplication.shared.keyWindow?.rootViewController{
                    OTPublishersHeadlessSDK.shared.setupUI(vc)
                }

                if(response.status){
                    if(autoShowBanner && OTPublishersHeadlessSDK.shared.shouldShowBanner()){
                        OTPublishersHeadlessSDK.shared.showBannerUI()
                    }
                    resolve(["status":response.status, "error":response.error.debugDescription, "responseString":response.responseString ?? ""])
                }else{
                    reject("Error downloading OneTrust Data", response.error.debugDescription, response.error)
                }
            }
        }
    }
    
    @objc(getOTConsentJSForWebView:rejecter:)
    public func getOTConsentJSForWebView(_ resolve:@escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock){
        let js = OTPublishersHeadlessSDK.shared.getOTConsentJSForWebView()
        resolve(js)
    }

    @objc(getDomainInfo:rejecter:)
    public func getDomainInfo(_ resolve:@escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock){
        if let domainInfo = OTPublishersHeadlessSDK.shared.getDomainInfo(),
           let domainJson = try? JSONSerialization.data(withJSONObject: domainInfo){
            resolve(String(data: domainJson, encoding: .utf8))
        }else{
            reject("Error parsing domain info JSON","When trying to parse domainInfo, the iOS call getDomainInfo returned nil or the dictionary was not able to be parsed into a JSON string.",nil)
        }
    }

    @objc(getOTGoogleConsentModeData:rejecter:)
    public func getOTGoogleConsentModeData(_ resolve:@escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock){
            let gcmOTData = OTPublishersHeadlessSDK.shared.getOTGoogleConsentModeData()
            let analyticsStorageValue = gcmOTData.consentType.analyticsStorage == .granted ? "granted" : "denied"
            let addStorageValue = gcmOTData.consentType.adStorage == .granted ? "granted" : "denied"
            let adUserDataValue = gcmOTData.consentType.adUserData == .granted ? "granted" : "denied"
            let adPersonalizationValue = gcmOTData.consentType.adPersonalization == .granted ? "granted" : "denied"
            let functionalityStorageValue = gcmOTData.consentType.functionalityStorage == .granted ? "granted" : "denied"
            let personalizationStorageValue = gcmOTData.consentType.personalizationStorage == .granted ? "granted" : "denied"
            let securityStorageValue = gcmOTData.consentType.securityStorage == .granted ? "granted" : "denied"
        
            let gcmOTDataJSONObject: [String: String] = [ 
                "analytics_storage": analyticsStorageValue,
                "ad_storage": addStorageValue,
                "ad_user_data": adUserDataValue,
                "ad_personalization": adPersonalizationValue,
                "functionality_storage": functionalityStorageValue,
                "personalization_storage": personalizationStorageValue,
                "security_storage": securityStorageValue
            ] 
            let encoder = JSONEncoder()
            if let jsonData = try? encoder.encode(gcmOTDataJSONObject) {
              if let jsonString = String(data: jsonData, encoding: .utf8) {
                  resolve(jsonString)
                  } 
            }
           
    }
    
    @objc(shouldShowBanner:rejecter:)
    public func shouldShowBanner(_ resolve:@escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) -> Void{
        let shouldShowBanner = OTPublishersHeadlessSDK.shared.shouldShowBanner()
        resolve(shouldShowBanner)
    }
    
    @objc(getConsentStatusForCategory:resolve:rejecter:)
    public func getConsentStatusForCategory(_ categoryId:NSString, resolve:@escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) -> Void{
        let consentStatus = OTPublishersHeadlessSDK.shared.getConsentStatus(forCategory: categoryId as String)
        resolve(consentStatus)
    }
    
    @objc(getConsentStatusForSDKId:resolve:rejecter:)
    public func getConsentStatusForSDKId(_ sdkId:NSString, resolve:@escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) -> Void{
        let consentStatus = OTPublishersHeadlessSDK.shared.getConsentStatus(forSDKId: sdkId as String)
        resolve(consentStatus)
    }
    
    //MARK: Display CMP UI
    @objc(showBannerUI)
    public func showBannerUI(){
        DispatchQueue.main.async{
            if let vc = UIApplication.shared.keyWindow?.rootViewController{
                OTPublishersHeadlessSDK.shared.setupUI(vc)
                OTPublishersHeadlessSDK.shared.showBannerUI()
            }
        }
    }
    
    @objc(showPreferenceCenterUI)
    public func showPreferenceCenterUI(){
        DispatchQueue.main.async{
            if let vc = UIApplication.shared.keyWindow?.rootViewController{
                OTPublishersHeadlessSDK.shared.setupUI(vc)
                OTPublishersHeadlessSDK.shared.showPreferenceCenterUI()
            }
        }
    }
    
    #if os(iOS)
    //MARK: App Tracking Transparency
    @objc(getATTStatus:rejecter:)
    public func getATTStatus(_ resolve:@escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock){
        resolve(getATTStatusAsString())
    }
    #endif

    @objc(showConsentUI:resolve:rejecter:)
    public func showConsentUI(_ permissionType:Int, resolve:@escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock){
        guard #available(iOS 14, *) else { //version check, returns immediately if lower than iOS 14
            resolve(nil)
            return
        }
        var type:AppPermissionType? = nil
        
        switch permissionType{
        case 0:
            type = .idfa
        default:
            reject("Permission type not recognized", "Only pass in enum OTdevicePermission", nil)
            return
        }
        
        DispatchQueue.main.async{
            if let type = type,
               let vc = UIApplication.shared.keyWindow?.rootViewController{
                OTPublishersHeadlessSDK.shared.showConsentUI(for: type, from: vc, completion: {
                    resolve(nil)
                })
            }
        }
    }

    #if os(iOS)
    //MARK: Universal Consent
    @objc(showConsentPurposesUI)
    public func showConsentPurposesUI(){
        DispatchQueue.main.async{
            if let vc = UIApplication.shared.keyWindow?.rootViewController{
                OTPublishersHeadlessSDK.shared.showConsentPurposesUI(vc)
            }
        }
    }
    #endif

    #if os(iOS)
    //Gets the consent value (int) for the UC Purpose passed in
    @objc(getUCPurposeConsent:resolve:rejecter:)
    public func getUCPurposeConsent(_ purposeId:String!,
                                    resolve:@escaping RCTPromiseResolveBlock,
                                    rejecter reject: @escaping RCTPromiseRejectBlock){
        let consent = OTPublishersHeadlessSDK.shared.getUCPurposeConsent(purposeID: purposeId)
        resolve(consent)
    }
    #endif

    #if os(iOS)
    //Gets the consent value (int) for the custom pref passed in
    @objc(getUCCustomPreferenceConsent:customPreferenceId:purposeId:resolve:reject:)
    public func getUCCustomPreferenceConsent(_ customPreferenceOptionId:String!,
                                             customPreferenceId:String!,
                                             purposeId:String!,
                                             resolve:@escaping RCTPromiseResolveBlock,
                                             rejecter reject: @escaping RCTPromiseRejectBlock){
        let consent = OTPublishersHeadlessSDK.shared.getUCPurposeConsent(customPreferenceOptionID: customPreferenceOptionId, customPreferenceID: customPreferenceId, purposeID: purposeId)
        resolve(consent)
    }
    #endif

    #if os(iOS)
    @objc(getUCTopicConsent:purposeId:resolve:reject:)
    public func getUCTopicConsent(_ topicOption:String!,
                                  purposeId:String!,
                                  resolve:@escaping RCTPromiseResolveBlock,
                                  rejecter reject: @escaping RCTPromiseRejectBlock){
        let consent = OTPublishersHeadlessSDK.shared.getUCPurposeConsent(topicID: topicOption, purposeID: purposeId)
        resolve(consent)
    }
    #endif

    #if os(iOS)
    @objc(updateUCCustomPreferenceConsent:customPrefId:purposeId:consent:)
    public func updateUCCustomPreferenceConsent(_ customPrefOptionId:String!,
                                                customPrefId:String,
                                                purposeId:String!,
                                                consent:Bool){
        
        OTPublishersHeadlessSDK.shared.updateUCPurposeConsent(cpOptionId: customPrefOptionId,
                                                              cpId: customPrefId,
                                                              purposeId: purposeId,
                                                              withConsent: consent)
    }
    #endif

    #if os(iOS)

    @objc(updateUCPurposeConsent:consent:)
    public func updateUCPurposeConsent(_ purposeId:String!,
                                       consent:Bool){
        
        OTPublishersHeadlessSDK.shared.updateUCPurposeConsent(purposeId: purposeId,
                                                              withConsent: consent)
    }
    #endif

    #if os(iOS)

    @objc(updateUCTopicConsent:purposeId:consent:)
    public func updateUCTopicConsent(_ topicOptionId:String!,
                                     purposeId:String!,
                                     consent:Bool){
        
        OTPublishersHeadlessSDK.shared.updateUCPurposeConsent(topicOptionId: topicOptionId,
                                                              purposeId: purposeId,
                                                              withConsent: consent)
    }
    #endif

    #if os(iOS)
    @objc(saveUCConsent)
    public func saveUCConsent(){
        OTPublishersHeadlessSDK.shared.saveConsent(type: .ucPreferenceCenterConfirm)
    }
    #endif

    //MARK: Multi-Profile Consent
    @objc(getCurrentActiveProfile:rejecter:)
    public func getCurrentActiveProfile(_ resolve:@escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock){
        resolve(OTPublishersHeadlessSDK.shared.currentActiveProfile)
    }
    
    #if os(iOS)
    private func getATTStatusAsString() -> String?{
        guard #available(iOS 14, *) else {return nil}
        let statusMap:[ATTrackingManager.AuthorizationStatus:String] = [.authorized:"authorized", .denied:"denied", .notDetermined:"notDetermined", .restricted:"restricted"]
        let status = statusMap[ATTrackingManager.trackingAuthorizationStatus]
        return status
    }
    #endif

    #if os(iOS)
    //MARK: Update Consent Category Consent
    @objc(updatePurposeConsent:consentValue:)
    public func updatePurposeConsent(_ category:String, consentValue:Bool){
        OTPublishersHeadlessSDK.shared.updatePurposeConsent(forGroup: category, consentValue: consentValue)
    }
    #endif

    @objc (resetUpdatedConsent)
    public func resetUpdatedConsent(){
        OTPublishersHeadlessSDK.shared.resetUpdatedConsent();
    }

    // MARK: Save and log consent
    @objc(saveConsent:resolve:rejecter:)
    public func saveConsent(_ consentType:Int, resolve:@escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock){
        guard let interactionType = ConsentInteractionType(rawValue: consentType) else{
            reject("Invalid interaction type passed.","Only pass in enum OTConsentInteractionType", nil)
            return
        }
        
        OTPublishersHeadlessSDK.shared.saveConsent(type: interactionType){
            resolve(nil)
        }
    }

    @objc(enableOTSDKLog:resolve:rejecter:)
    public func enableOTSDKLog(_ logType:Int, resolve:@escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock){
        switch logType {
            case 1:
                OTPublishersHeadlessSDK.shared.enableOTSDKLog(.noLogs)
            case 2:
                OTPublishersHeadlessSDK.shared.enableOTSDKLog(.verbose)
            case 3:
                OTPublishersHeadlessSDK.shared.enableOTSDKLog(.debug)
            case 4:
                OTPublishersHeadlessSDK.shared.enableOTSDKLog(.info)
            case 5:
                OTPublishersHeadlessSDK.shared.enableOTSDKLog(.warning)
            case 6:
                OTPublishersHeadlessSDK.shared.enableOTSDKLog(.error)
            default:
                OTPublishersHeadlessSDK.shared.enableOTSDKLog(.debug)
        }
       
    }

    @objc(clearOTSDKData)
    public func clearOTSDKData(){
        OTPublishersHeadlessSDK.shared.clearOTSDKData();
    }
}
