import Foundation
import Capacitor
import MoEngagePluginBase
import MoEngageSDK
/**
 * Please read the Capacitor iOS Plugin Development Guide
 * here: https://capacitorjs.com/docs/plugins/ios
 */
@objc(MoECapacitorCorePlugin)
public class MoECapacitorCorePlugin: CAPPlugin , CAPBridgedPlugin {

    public let identifier = "MoECapacitorCorePlugin"
    public let jsName = "MoECapacitorCore"
    
    public let pluginMethods: [CAPPluginMethod] = [

        CAPPluginMethod(name: "initialize", returnType: CAPPluginReturnNone),
        CAPPluginMethod(name: "trackEvent", returnType: CAPPluginReturnNone),
        CAPPluginMethod(name: "setAlias", returnType: CAPPluginReturnNone),
        CAPPluginMethod(name: "setUniqueId", returnType: CAPPluginReturnNone),
        CAPPluginMethod(name: "setUserName", returnType: CAPPluginReturnNone),
        CAPPluginMethod(name: "setFirstName", returnType: CAPPluginReturnNone),
        CAPPluginMethod(name: "setLastName", returnType: CAPPluginReturnNone),
        CAPPluginMethod(name: "setEmailId", returnType: CAPPluginReturnNone),
        CAPPluginMethod(name: "setMobileNumber", returnType: CAPPluginReturnNone),
        CAPPluginMethod(name: "setGender", returnType: CAPPluginReturnNone),
        CAPPluginMethod(name: "setBirthDate", returnType: CAPPluginReturnNone),
        CAPPluginMethod(name: "setUserLocation", returnType: CAPPluginReturnNone),
        CAPPluginMethod(name: "setUserAttributeDate", returnType: CAPPluginReturnNone),
        CAPPluginMethod(name: "setUserAttributeLocation", returnType: CAPPluginReturnNone),
        CAPPluginMethod(name: "setUserAttribute", returnType: CAPPluginReturnNone),
        CAPPluginMethod(name: "logoutUser", returnType: CAPPluginReturnNone),
        CAPPluginMethod(name: "disableDataTracking", returnType: CAPPluginReturnNone),
        CAPPluginMethod(name: "enableDataTracking", returnType: CAPPluginReturnNone),
        CAPPluginMethod(name: "enableSdk", returnType: CAPPluginReturnNone),
        CAPPluginMethod(name: "disableSdk", returnType: CAPPluginReturnNone),
        CAPPluginMethod(name: "registerForPush", returnType: CAPPluginReturnNone),
        CAPPluginMethod(name: "showInApp", returnType: CAPPluginReturnNone),
        CAPPluginMethod(name: "getSelfHandledInApp", returnType: CAPPluginReturnNone),
        CAPPluginMethod(name: "selfHandledShown", returnType: CAPPluginReturnNone),
        CAPPluginMethod(name: "selfHandledClicked", returnType: CAPPluginReturnNone),
        CAPPluginMethod(name: "selfHandledDismissed", returnType: CAPPluginReturnNone),
        CAPPluginMethod(name: "setInAppContext", returnType: CAPPluginReturnNone),
        CAPPluginMethod(name: "resetInAppContext", returnType: CAPPluginReturnNone),
        CAPPluginMethod(name: "setAppStatus", returnType: CAPPluginReturnNone),
        CAPPluginMethod(name: "enableAdIdTracking", returnType: CAPPluginReturnNone),
        CAPPluginMethod(name: "disableAdIdTracking", returnType: CAPPluginReturnNone),
        CAPPluginMethod(name: "enableAndroidIdTracking", returnType: CAPPluginReturnNone),
        CAPPluginMethod(name: "disableAndroidIdTracking", returnType: CAPPluginReturnNone),
        CAPPluginMethod(name: "navigateToSettingsAndroid", returnType: CAPPluginReturnNone),
        CAPPluginMethod(name: "requestPushPermissionAndroid", returnType: CAPPluginReturnNone),
        CAPPluginMethod(name: "pushPermissionResponseAndroid", returnType: CAPPluginReturnNone),
        CAPPluginMethod(name: "passFcmPushToken", returnType: CAPPluginReturnNone),
        CAPPluginMethod(name: "passFcmPushPayload", returnType: CAPPluginReturnNone),
        CAPPluginMethod(name: "setupNotificationChannelsAndroid", returnType: CAPPluginReturnNone),
        CAPPluginMethod(name: "enableDeviceIdTracking", returnType: CAPPluginReturnNone),
        CAPPluginMethod(name: "disableDeviceIdTracking", returnType: CAPPluginReturnNone),
        CAPPluginMethod(name: "updatePushPermissionRequestCountAndroid", returnType: CAPPluginReturnNone),
        CAPPluginMethod(name: "deleteUser", returnType: CAPPluginReturnNone),
        CAPPluginMethod(name: "showNudge", returnType: CAPPluginReturnNone),
        CAPPluginMethod(name: "identifyUser", returnType: CAPPluginReturnNone),
        CAPPluginMethod(name: "getUserIdentities", returnType: CAPPluginReturnPromise),
        CAPPluginMethod(name: "registerForProvisionalPush", returnType: CAPPluginReturnNone),
        CAPPluginMethod(name: "getSelfHandledInApps", returnType: CAPPluginReturnPromise),
        CAPPluginMethod(name: "passAuthenticationDetails", returnType: CAPPluginReturnNone)
    ]
    
    //MARK: Initialize
    
    @objc func initialize(_ call: CAPPluginCall) {
        guard let appId = call.getString(MoECapacitorConstants.PayloadKeys.appId)
        else
        {
            MoEngageLogger.logDefault(message: "Account Identifier is invalid")
            return
        }
        
        guard let initConfig = call.getObject(MoEngagePluginConstants.General.initConfig)
        else {
              MoEngageLogger.logDefault(message: "InitConfig JSObject value is invalid")
              return
        }
        
        var accountPayload: [String:Any] = [:]
        accountPayload[MoEngagePluginConstants.General.accountMeta] = MoEngagePluginUtils.createAccountPayload(identifier: appId)
        accountPayload[MoEngagePluginConstants.General.initConfig] = createInitConfig(initConfig: initConfig)
        
        MoEngagePluginBridge.sharedInstance.setPluginBridgeDelegate(self, payload: accountPayload)
        MoEngagePluginBridge.sharedInstance.pluginInitialized(accountPayload)
    }
    
    private func createInitConfig(initConfig: JSObject)->[String: Any] {
        var initConfigDict: [String:Any] = [:]
        var analyticsConfigDict: [String:Any] = [:]
        
        guard let analyticsConfig = initConfig[MoEngagePluginConstants.General.analyticsConfig] as? [String: any JSValue],
              let shouldTrackUserAttributeBooleanAsNumber = analyticsConfig[MoEngagePluginConstants.General.shouldTrackUserAttributeBooleanAsNumber] as? Bool else {
            analyticsConfigDict[MoEngagePluginConstants.General.shouldTrackUserAttributeBooleanAsNumber] = false
            initConfigDict[MoEngagePluginConstants.General.analyticsConfig] = analyticsConfigDict
            return initConfigDict
        }
        
        analyticsConfigDict[MoEngagePluginConstants.General.shouldTrackUserAttributeBooleanAsNumber] = shouldTrackUserAttributeBooleanAsNumber
        initConfigDict[MoEngagePluginConstants.General.analyticsConfig] = analyticsConfigDict
        return initConfigDict
    }

    //MARK: Track event
    
    @objc func trackEvent(_ call: CAPPluginCall) {
        guard let appId = call.getString(MoECapacitorConstants.PayloadKeys.appId),
              let eventName = call.getString(MoECapacitorConstants.PayloadKeys.eventName)
        else
        {
            MoEngageLogger.logDefault(logLevel: .debug, message: "Account Identifier / Event name is invalid for Track Event")
            return
        }
        
        var eventAttributesDict = [String: Any]()
        var generalAttributeDict = [String: Any]()
        var dateAttributeDict = [String: Any]()
        var locationAttributeDict = [String: Any]()
        var isNonInteractive = false
        
        if let eventAttributes = call.getObject(MoECapacitorConstants.PayloadKeys.eventAttributes) {
            
            if let generalProperties = eventAttributes[MoECapacitorConstants.PayloadKeys.generalAttributes] as? [Any] {
                getAttributesDict(generalProperties, &generalAttributeDict)
            }
            if let dateProperties = eventAttributes[MoECapacitorConstants.PayloadKeys.dateTimeAttributes] as? [Any] {
                
                getAttributesDict(dateProperties, &dateAttributeDict)
            }
            
            if let locationProperties = eventAttributes[MoECapacitorConstants.PayloadKeys.locationAttributes] as? [Any] {
                getAttributesDict(locationProperties, &locationAttributeDict)
            }
            
            if let isNonInteractiveEvent = eventAttributes[MoECapacitorConstants.PayloadKeys.isNonInteractive] as? Bool {
                isNonInteractive = isNonInteractiveEvent
            }
        }
        
        eventAttributesDict[MoECapacitorConstants.PayloadKeys.generalAttributes] = generalAttributeDict
        eventAttributesDict[MoECapacitorConstants.PayloadKeys.dateTimeAttributes] = dateAttributeDict
        eventAttributesDict[MoECapacitorConstants.PayloadKeys.locationAttributes] = locationAttributeDict
        eventAttributesDict[MoECapacitorConstants.PayloadKeys.isNonInteractive] = isNonInteractive
        
        let eventData = [
            MoECapacitorConstants.PayloadKeys.eventName: eventName,
            MoECapacitorConstants.PayloadKeys.eventAttributes:eventAttributesDict
        ] as [String: Any]
        
        let eventPayload = [MoEngagePluginConstants.General.accountMeta: MoEngagePluginUtils.createAccountPayload(identifier: appId), MoEngagePluginConstants.General.data: eventData]
        
        MoEngagePluginBridge.sharedInstance.trackEvent(eventPayload)
    }
    
    //MARK: User Attributes
    
    @objc func setAlias(_ call: CAPPluginCall) {
        guard let appId = call.getString(MoECapacitorConstants.PayloadKeys.appId),
              let alias = call.getString(MoECapacitorConstants.PayloadKeys.alias),
              alias.count > 0
        else
        {
            MoEngageLogger.logDefault(logLevel: .debug, message: "User-Alias is not tracked as its invalid")
            return
        }
        
        let aliasData = [
            MoECapacitorConstants.PayloadKeys.alias: alias
        ]
        
        let aliasPayload = [MoEngagePluginConstants.General.accountMeta: MoEngagePluginUtils.createAccountPayload(identifier: appId), MoEngagePluginConstants.General.data: aliasData]
        MoEngagePluginBridge.sharedInstance.setAlias(aliasPayload)
        
    }
    
    // MARK: General User Attribute
    
    @objc func setUniqueId(_ call: CAPPluginCall) {
        guard let appId = call.getString(MoECapacitorConstants.PayloadKeys.appId),
              let uniqueId = call.getString(MoECapacitorConstants.PayloadKeys.uniqueId),
              uniqueId.count > 0
        else
        {
            MoEngageLogger.logDefault(logLevel: .debug, message: "UserUnique ID is not tracked as its invalid")
            return
        }
        
        setGeneralAttributes(attributeName:"USER_ATTRIBUTE_UNIQUE_ID" , attributeValue: uniqueId, appId: appId)
    }
    
    @objc func setUserName(_ call: CAPPluginCall) {
        guard let appId = call.getString(MoECapacitorConstants.PayloadKeys.appId),
              let userName = call.getString(MoECapacitorConstants.PayloadKeys.userName),
              userName.count > 0
        else
        {
            MoEngageLogger.logDefault(logLevel: .debug, message: "Username is not tracked as its invalid")
            return
        }
        
        setGeneralAttributes(attributeName: "USER_ATTRIBUTE_USER_NAME", attributeValue: userName, appId: appId)
    }
    
    @objc func setFirstName(_ call: CAPPluginCall) {
        guard let appId = call.getString(MoECapacitorConstants.PayloadKeys.appId),
              let userFirstName = call.getString(MoECapacitorConstants.PayloadKeys.firstName),
              userFirstName.count > 0
        else
        {
            MoEngageLogger.logDefault(logLevel: .debug, message: "UserFirstName is not tracked as its invalid")
            return
        }
        setGeneralAttributes(attributeName: "USER_ATTRIBUTE_USER_FIRST_NAME", attributeValue: userFirstName, appId: appId)
    }
    
    @objc func setLastName(_ call: CAPPluginCall) {
        guard let appId = call.getString(MoECapacitorConstants.PayloadKeys.appId),
              let userLastName = call.getString(MoECapacitorConstants.PayloadKeys.lastName),
              userLastName.count > 0
        else
        {
            MoEngageLogger.logDefault(logLevel: .debug, message: "UserLastName is not tracked as its invalid")
            return
        }
        
        setGeneralAttributes(attributeName: "USER_ATTRIBUTE_USER_LAST_NAME", attributeValue: userLastName, appId: appId)
    }
    
    @objc func setEmailId(_ call: CAPPluginCall) {
        guard let appId = call.getString(MoECapacitorConstants.PayloadKeys.appId),
              let userEmailId = call.getString(MoECapacitorConstants.PayloadKeys.emailId),
              userEmailId.count > 0
        else
        {
            MoEngageLogger.logDefault(logLevel: .debug, message: "User Email ID is not tracked as its invalid")
            return
        }
        
        setGeneralAttributes(attributeName: "USER_ATTRIBUTE_USER_EMAIL", attributeValue: userEmailId, appId: appId)
    }
    
    @objc func setMobileNumber(_ call: CAPPluginCall) {
        guard let appId = call.getString(MoECapacitorConstants.PayloadKeys.appId),
              let userMobileNumber = call.getString(MoECapacitorConstants.PayloadKeys.mobileNumber),
              userMobileNumber.count > 0
        else
        {
            MoEngageLogger.logDefault(logLevel: .debug, message: "User MobileNumber is not tracked as its invalid")
            return
        }
        setGeneralAttributes(attributeName: "USER_ATTRIBUTE_USER_MOBILE", attributeValue: userMobileNumber, appId: appId)
    }
    
    @objc func setGender(_ call: CAPPluginCall) {
        guard let appId = call.getString(MoECapacitorConstants.PayloadKeys.appId),
              let userGender = call.getString(MoECapacitorConstants.PayloadKeys.gender)
        else
        {
            MoEngageLogger.logDefault(logLevel: .debug, message: "User Gender is not tracked as its invalid")
            return
        }
        
        setGeneralAttributes(attributeName: "USER_ATTRIBUTE_USER_GENDER", attributeValue: userGender, appId: appId)
    }
    
    @objc func setUserAttribute(_ call: CAPPluginCall) {
        guard let appId = call.getString(MoECapacitorConstants.PayloadKeys.appId),
              let attributeName = call.getString(MoECapacitorConstants.PayloadKeys.name),
              let attributeValue = call.options[MoECapacitorConstants.PayloadKeys.value]
        else
        {
            MoEngageLogger.logDefault(logLevel: .debug, message: "Custom UserAttribute is not tracked as its invalid")
            return
        }
        setGeneralAttributes(attributeName: attributeName, attributeValue: attributeValue, appId: appId)
    }
    
    // MARK: Time Stamp User Attribute
    @objc func setBirthDate(_ call: CAPPluginCall) {
        guard let appId = call.getString(MoECapacitorConstants.PayloadKeys.appId),
              let userBirthDate = call.getString(MoECapacitorConstants.PayloadKeys.birthdate),
              userBirthDate.count > 0
        else
        {
            MoEngageLogger.logDefault(logLevel: .debug, message: "User BirthDate is not tracked as its invalid")
            return
        }
        
        setTimeStampUserAttribute(attributeName: "USER_ATTRIBUTE_USER_BDAY", attributeValue: userBirthDate, appId: appId)
    }
    
    @objc func setUserAttributeDate(_ call: CAPPluginCall) {
        guard let appId = call.getString(MoECapacitorConstants.PayloadKeys.appId),
              let attributeName = call.getString(MoECapacitorConstants.PayloadKeys.name),
              let attributeValue = call.getString(MoECapacitorConstants.PayloadKeys.value)
        else
        {
            MoEngageLogger.logDefault(logLevel: .debug, message: "UserAttribute with ISO date is not tracked as its invalid")
            return
        }
        setTimeStampUserAttribute(attributeName: attributeName, attributeValue: attributeValue, appId: appId)
    }
    
    // MARK: Location User Attribute
    
    @objc func setUserAttributeLocation(_ call: CAPPluginCall) {
        guard let appId = call.getString(MoECapacitorConstants.PayloadKeys.appId),
              let attributeName = call.getString(MoECapacitorConstants.PayloadKeys.name),
              let location = call.getObject(MoECapacitorConstants.PayloadKeys.location)
        else
        {
            MoEngageLogger.logDefault(logLevel: .debug, message: "User Location is not tracked as its invalid")
            return
        }
        setLocationUserAttribute(attributeName: attributeName, location: location, appId: appId)
    }
    
    @objc func setUserLocation(_ call: CAPPluginCall) {
        guard let appId = call.getString(MoECapacitorConstants.PayloadKeys.appId),
              let location = call.getObject(MoECapacitorConstants.PayloadKeys.location)
        else
        {
            MoEngageLogger.logDefault(logLevel: .debug, message: "User Location is not tracked as its invalid")
            return
        }
        setLocationUserAttribute(attributeName: "USER_ATTRIBUTE_USER_LOCATION", location: location, appId: appId)
    }
    
    // MARK: App Status
    @objc func setAppStatus(_ call: CAPPluginCall) {
        guard let appId = call.getString(MoECapacitorConstants.PayloadKeys.appId),
              let appStatus = call.getString(MoECapacitorConstants.PayloadKeys.appStatus)
        else
        {
            MoEngageLogger.logDefault(logLevel: .debug, message: "App status isn't tracked as its invalid")
            return
        }
        
        let appStatusData = [MoECapacitorConstants.PayloadKeys.appStatus:appStatus.uppercased()]
        let appStatusPayload = [MoEngagePluginConstants.General.accountMeta: MoEngagePluginUtils.createAccountPayload(identifier: appId), MoEngagePluginConstants.General.data: appStatusData]
        MoEngagePluginBridge.sharedInstance.setAppStatus(appStatusPayload)
    }
    
    @objc func logoutUser(_ call: CAPPluginCall) {
        guard let appId = call.getString(MoECapacitorConstants.PayloadKeys.appId)
        else
        {
            MoEngageLogger.logDefault(logLevel: .debug, message: "Account identifier is invalid for LogOut")
            return
        }
        
        let resetPayload = [MoEngagePluginConstants.General.accountMeta: MoEngagePluginUtils.createAccountPayload(identifier: appId)]
        MoEngagePluginBridge.sharedInstance.resetUser(resetPayload)
    }
    
    @objc func enableSdk(_ call: CAPPluginCall) {
        guard let appId = call.getString(MoECapacitorConstants.PayloadKeys.appId)
        else
        {
            MoEngageLogger.logDefault(logLevel: .debug, message: "Account identifier is invalid for enableSdk")
            return
        }
        
        updateSDKState(state: true, appId: appId)
    }
    
    @objc func disableSdk(_ call: CAPPluginCall) {
        guard let appId = call.getString(MoECapacitorConstants.PayloadKeys.appId)
        else
        {
            MoEngageLogger.logDefault(logLevel: .debug, message: "Account identifier is invalid for disableSdk")
            return
        }
        
        updateSDKState(state: false, appId: appId)
    }
    
    //MARK: Enable/disbale data Tracking
    @objc func enableDataTracking(_ call: CAPPluginCall) {
        guard let appId = call.getString(MoECapacitorConstants.PayloadKeys.appId)
        else
        {
            MoEngageLogger.logDefault(logLevel: .debug, message: "Account Identifier value is invalid for enableDataTracking")
            return
        }
        
        updateOptOutState(false, appId: appId)
    }
    
    @objc func disableDataTracking(_ call: CAPPluginCall) {
        guard let appId = call.getString(MoECapacitorConstants.PayloadKeys.appId)
        else
        {
            MoEngageLogger.logDefault(logLevel: .debug, message: "Account Identifier value is invalid for disableDataTracking")
            return
        }
        
        updateOptOutState(true, appId: appId)
    }
    
    //MARK: Push Notification
    
    @objc func registerForPush(_ call: CAPPluginCall) {
        MoEngagePluginBridge.sharedInstance.registerForPush()
    }

    //MARK: Provisional Push Notification

     @objc func registerForProvisionalPush(_ call: CAPPluginCall) {
         MoEngagePluginBridge.sharedInstance.registerForProvisionalPush()
    }

    //MARK: InApp
    
    @objc func showInApp(_ call: CAPPluginCall) {
        guard let appId = call.getString(MoECapacitorConstants.PayloadKeys.appId)
        else
        {
            MoEngageLogger.logDefault(logLevel: .debug, message: "Account identifier is invalid for showInApp")
            return
        }
        MoEngagePluginBridge.sharedInstance.showInApp([MoEngagePluginConstants.General.accountMeta: MoEngagePluginUtils.createAccountPayload(identifier: appId)])
    }
    
    @objc func getSelfHandledInApp(_ call: CAPPluginCall) {
        guard let appId = call.getString(MoECapacitorConstants.PayloadKeys.appId)
        else
        {
            MoEngageLogger.logDefault(logLevel: .debug, message: "Account identifier is invalid for SelfHandledInApp")
            return
        }
        
        MoEngagePluginBridge.sharedInstance.getSelfHandledInApp([MoEngagePluginConstants.General.accountMeta: MoEngagePluginUtils.createAccountPayload(identifier: appId)])
    }

     @objc func getSelfHandledInApps(_ call: CAPPluginCall) {
        guard let appId = call.getString(MoECapacitorConstants.PayloadKeys.appId)
        else
        {
            MoEngageLogger.logDefault(logLevel: .debug, message: "Account identifier is invalid for SelfHandledInApp")
            return
        }
        MoEngagePluginBridge.sharedInstance.getSelfHandledInApps([MoEngagePluginConstants.General.accountMeta: MoEngagePluginUtils.createAccountPayload(identifier: appId)]) { inApps in
            let selfHandledInApps = MoECapacitorCorePlugin.getTransformedSelfHandledInAppsPayload(inApps)
            call.resolve(selfHandledInApps)
        }
    }
    
    @objc func selfHandledShown(_ call: CAPPluginCall) {
        updateSelfHandledImpression(type: MoEngagePluginConstants.InApp.impression, call: call)
    }
    
    @objc func selfHandledClicked(_ call: CAPPluginCall) {
        updateSelfHandledImpression(type: MoEngagePluginConstants.InApp.click, call: call)
    }
    
    @objc func selfHandledDismissed(_ call: CAPPluginCall) {
        updateSelfHandledImpression(type: MoEngagePluginConstants.InApp.dismissed, call: call)
    }
    
    @objc func setInAppContext(_ call: CAPPluginCall) {
        guard let appId = call.getString(MoECapacitorConstants.PayloadKeys.appId),
              let contextArray = call.getArray("contexts")
        else
        {
            MoEngageLogger.logDefault(logLevel: .debug, message: "InApp context isnt tracked as its invalid")
            return
        }
        
        let contextArrayDict = [MoEngagePluginConstants.InApp.contexts: contextArray]
        let contextDict: [String: Any] = [MoEngagePluginConstants.General.accountMeta: MoEngagePluginUtils.createAccountPayload(identifier: appId), MoEngagePluginConstants.General.data: contextArrayDict]
        MoEngagePluginBridge.sharedInstance.setInAppContext(contextDict)
    }
    
    @objc func resetInAppContext(_ call: CAPPluginCall) {
        guard let appId = call.getString(MoECapacitorConstants.PayloadKeys.appId)
        else
        {
            MoEngageLogger.logDefault(logLevel: .debug, message: "Account Identifier is invalid for ResetContext")
            return
        }
        MoEngagePluginBridge.sharedInstance.resetInAppContext([MoEngagePluginConstants.General.accountMeta: MoEngagePluginUtils.createAccountPayload(identifier: appId)])
    }
    
    @objc func showNudge(_ call: CAPPluginCall) {
        guard let appId = call.getString(MoECapacitorConstants.PayloadKeys.appId),
              let position = call.getString(MoECapacitorConstants.PayloadKeys.position)
        else
        {
            MoEngageLogger.logDefault(logLevel: .debug, message: "ShowNudge Request is invalid")
            return
        }
        
        let payload: [String: Any] = [MoEngagePluginConstants.General.accountMeta: MoEngagePluginUtils.createAccountPayload(identifier: appId), MoEngagePluginConstants.General.data:[MoECapacitorConstants.PayloadKeys.position: position]];
        MoEngagePluginBridge.sharedInstance.showNudge(payload)
    }
 
   // MARK: Unimplemented API
    @objc func enableAdIdTracking(_ call: CAPPluginCall) {
        call.unimplemented("enableAdIdTracking() : This API is only for Android")
    }
    
    @objc func disableAdIdTracking(_ call: CAPPluginCall) {
        call.unimplemented("disableAdIdTracking(): This API is only for Android")
    }
    
    @objc func enableAndroidIdTracking(_ call: CAPPluginCall) {
        call.unimplemented("enableAndroidIdTracking() : This API is only for Android")
    }
    
    @objc func disableAndroidIdTracking(_ call: CAPPluginCall) {
        call.unimplemented("disableAndroidIdTracking(): This API is only for Android")
    }
    
    @objc func navigateToSettingsAndroid(_ call: CAPPluginCall) {
        call.unimplemented("navigateToSettingsAndroid(): This API is only for Android")
    }
    
    @objc func requestPushPermissionAndroid(_ call: CAPPluginCall) {
        call.unimplemented("requestPushPermissionAndroid(): This API is only for Android")
    }
    
    @objc func pushPermissionResponseAndroid(_ call: CAPPluginCall) {
        call.unimplemented("pushPermissionResponseAndroid(): This API is only for Android")
    }
    
    @objc func passFcmPushToken(_ call: CAPPluginCall) {
        call.unimplemented("passFcmPushToken(): This API is only for Android")
    }
    
    @objc func passFcmPushPayload(_ call: CAPPluginCall) {
        call.unimplemented("passFcmPushPayload(): This API is only for Android")
    }
    
    @objc func setupNotificationChannelsAndroid(_ call: CAPPluginCall) {
        call.unimplemented("setupNotificationChannelsAndroid(): This API is only for Android")
    }
    
    @objc func enableDeviceIdTracking(_ call: CAPPluginCall) {
        call.unimplemented("enableDeviceIdTracking() : This API is only for Android")
    }

    @objc func disableDeviceIdTracking(_ call: CAPPluginCall) {
        call.unimplemented("disableDeviceIdTracking() : This API is only for Android")
    }


    @objc func updatePushPermissionRequestCountAndroid(_ call: CAPPluginCall) {
        call.unimplemented("updatePushPermissionRequestCountAndroid(): This API is only for Android")
    }
    
    @objc func deleteUser(_ call: CAPPluginCall) {
        call.unimplemented("deleteUser(): This API is only for Android")
    }
    
    @objc func identifyUser(_ call: CAPPluginCall) {
        guard let appId = call.getString(MoECapacitorConstants.PayloadKeys.appId) else {
            MoEngageLogger.logDefault(logLevel: .debug, message: "Account identifier is invalid for identifyUser")
            return
        }
                    
        guard let identitiesDict = call.getObject(MoECapacitorConstants.PayloadKeys.identity) as? [String: String] ??
                call.getString(MoECapacitorConstants.PayloadKeys.identity).map({ [MoECapacitorConstants.PayloadKeys.uid: $0] }) as? [String: String] else {
            MoEngageLogger.logDefault(logLevel: .debug, message: "Identities is invalid for identifyUser")
            return
        }

        let payload: [String: Any] = [MoEngagePluginConstants.General.accountMeta: MoEngagePluginUtils.createAccountPayload(identifier: appId), MoEngagePluginConstants.General.data:[MoECapacitorConstants.PayloadKeys.identity: identitiesDict]];
        
        MoEngagePluginBridge.sharedInstance.identifyUser(payload)
    }
    
    @objc func getUserIdentities(_ call: CAPPluginCall) {
        guard let appId = call.getString(MoECapacitorConstants.PayloadKeys.appId) else {
            MoEngageLogger.logDefault(logLevel: .debug, message: "Account identifier is invalid for identifyUser")
            return
        }
        
        let payload: [String: Any] = [MoEngagePluginConstants.General.accountMeta: MoEngagePluginUtils.createAccountPayload(identifier: appId)]
        
        MoEngagePluginBridge.sharedInstance.getUserIdentities(payload) { identities in
            call.resolve(identities)
        }
    }

    @objc func passAuthenticationDetails(_ call: CAPPluginCall) {
        MoEngageLogger.logDefault(logLevel: .verbose, message: "MoECapacitorCorePlugin passAuthenticationDetails()")
        guard let payload = MoECapacitorCoreUtils.getAuthenticationDetailsPayload(from: call) else {
            return
        }
        MoEngageLogger.logDefault(logLevel: .verbose, message: "MoECapacitorCorePlugin passAuthenticationDetails() with data :\(payload)")

        MoEngagePluginBridge.sharedInstance.passAuthenticationDetails(payload)
    }
    
    // MARK: Utils method
    
    private func setGeneralAttributes(attributeName: String, attributeValue: Any, appId: String) {
        let userAttributeData = [
            MoEngagePluginConstants.UserAttribute.attributeName: attributeName,
            MoEngagePluginConstants.UserAttribute.attributeValue: attributeValue,
            MoEngagePluginConstants.UserAttribute.type: MoEngagePluginConstants.UserAttribute.general
        ]
        
        let userAttributePayload = [MoEngagePluginConstants.General.accountMeta: MoEngagePluginUtils.createAccountPayload(identifier: appId), MoEngagePluginConstants.General.data: userAttributeData]
        
        MoEngagePluginBridge.sharedInstance.setUserAttribute(userAttributePayload)
    }
    
    private func setLocationUserAttribute(attributeName:String , location: [String: Any], appId: String) {
        guard let latitude = location[MoECapacitorConstants.PayloadKeys.latitude],
              let longitude = location[MoECapacitorConstants.PayloadKeys.longitude]
        else
        {
            MoEngageLogger.logDefault(message: "User Location is not tracked as its latitude/longitude is invalid")
            return
        }
        
        let userAttributeData = [
            MoEngagePluginConstants.UserAttribute.attributeName: attributeName,
            MoEngagePluginConstants.UserAttribute.locationAttribute : [
                MoEngagePluginConstants.UserAttribute.latitude: latitude,
                MoEngagePluginConstants.UserAttribute.longitude: longitude
            ],
            MoEngagePluginConstants.UserAttribute.type: MoEngagePluginConstants.UserAttribute.location
        ] as [String : Any]
        
        let userAttributePayload = [MoEngagePluginConstants.General.accountMeta: MoEngagePluginUtils.createAccountPayload(identifier: appId), MoEngagePluginConstants.General.data: userAttributeData]
        MoEngagePluginBridge.sharedInstance.setUserAttribute(userAttributePayload)
    }
    
    private func setTimeStampUserAttribute(attributeName: String, attributeValue: String, appId: String) {
        let userAttributeData = [
            MoEngagePluginConstants.UserAttribute.attributeName: attributeName,
            MoEngagePluginConstants.UserAttribute.attributeValue: attributeValue,
            MoEngagePluginConstants.UserAttribute.type: MoEngagePluginConstants.UserAttribute.timestamp
        ]
        
        let userAttributePayload = [MoEngagePluginConstants.General.accountMeta: MoEngagePluginUtils.createAccountPayload(identifier: appId), MoEngagePluginConstants.General.data: userAttributeData]
        MoEngagePluginBridge.sharedInstance.setUserAttribute(userAttributePayload)
    }
    
    
    private func getAttributesDict(_ generalProperties: [Any], _ attributeDict: inout [String : Any]) {
        for(_, value) in generalProperties.enumerated() {
            if let dict = value as? [String: Any],
               let currentKey = dict[MoECapacitorConstants.PayloadKeys.name] as? String,
               let currentValue = dict[MoECapacitorConstants.PayloadKeys.value] {
                attributeDict[currentKey] = currentValue
            }
        }
    }
    
    private func updateSelfHandledImpression(type: String, call: CAPPluginCall) {
        guard let accountMeta = call.getObject(MoEngagePluginConstants.General.accountMeta),
              let campaignDataDict = call.getObject(MoECapacitorConstants.CallbackPayloadKeys.kCampaignData),
              let selfHandled = call.getObject(MoECapacitorConstants.CallbackPayloadKeys.kSelfHandled),
              let platform = call.getString(MoEngagePluginConstants.General.platform)
        else
        {
            MoEngageLogger.logDefault(logLevel: .debug, message: "SelfHandled - \(type) event is not tracked as campaign info is invalid")
            return
        }
        
        var dataDict: [String: Any] = campaignDataDict
        let accountMetaDict: [String: Any] = accountMeta
        
        if let campaignContext = dataDict[MoEngagePluginConstants.InApp.campaignContext] as? [String: Any],
           let finalCampaignContext = campaignContext[MoECapacitorConstants.PayloadKeys.attributes] as? [String: Any] {
            dataDict[MoEngagePluginConstants.InApp.campaignContext] = finalCampaignContext
        }
        
        dataDict[MoEngagePluginConstants.General.type] = type
        dataDict[MoEngagePluginConstants.InApp.selfHandled] = selfHandled
        dataDict[MoEngagePluginConstants.General.platform] = platform
        
        let selfHandledImpressionPayload = [MoEngagePluginConstants.General.accountMeta: accountMetaDict, MoEngagePluginConstants.General.data: dataDict]
        MoEngagePluginBridge.sharedInstance.updateSelfHandledImpression(selfHandledImpressionPayload)
    }
    
    private func updateSDKState(state: Bool, appId: String) {
        let sdkStateDataDict = [MoEngagePluginConstants.SDKState.isSdkEnabled: state]
        let sdkStatePayloadDict = [MoEngagePluginConstants.General.accountMeta: MoEngagePluginUtils.createAccountPayload(identifier: appId), MoEngagePluginConstants.General.data: sdkStateDataDict]
        MoEngagePluginBridge.sharedInstance.updateSDKState(sdkStatePayloadDict)
    }
    
    private func updateOptOutState(_ status: Bool, appId: String) {
        let optOutData = [
            MoEngagePluginConstants.General.type: MoEngagePluginConstants.SDKState.data,
            MoEngagePluginConstants.SDKState.state: status
        ] as [String : Any]
        
        let optOutPayload = [MoEngagePluginConstants.General.accountMeta: MoEngagePluginUtils.createAccountPayload(identifier: appId), MoEngagePluginConstants.General.data: optOutData]
        MoEngagePluginBridge.sharedInstance.optOutDataTracking(optOutPayload)
    }
    static func getTransformedSelfHandledInAppsPayload(_ inApps: [String: Any]) -> [String: Any] {
         var selfHandledInApps: [String: Any] = [
            "campaigns": (inApps["campaigns"] as? [[String: Any]])?
                .compactMap {
                    let payload = MoECapacitorCorePlugin.getInAppPayload($0)
                    return payload.isEmpty ? nil : payload
                } ?? []
        ]
        if let accountMeta = inApps[MoEngagePluginConstants.General.accountMeta] as? [String: Any] {
            selfHandledInApps[MoEngagePluginConstants.General.accountMeta] = accountMeta
        }
        return selfHandledInApps
    }
}

extension MoECapacitorCorePlugin: MoEngagePluginBridgeDelegate {
    public func sendMessage(event: String, message: [String : Any]) {
        guard let eventName = MoECapacitorCorePlugin.getCallbackName(forEventName: event)
        else
        {
            MoEngageLogger.logDefault(logLevel: .debug, message: "Failed to deliver the callback!!")
            return
        }
        
        var capacitorPluginPayload = [String:Any]()
        
        if (eventName == MoECapacitorConstants.CallbackNames.kPushTokenGenerated || eventName == MoECapacitorConstants.CallbackNames.kLogOutCompleted) {
            capacitorPluginPayload = message
        } else if (eventName == MoECapacitorConstants.CallbackNames.kPushClicked) {
            capacitorPluginPayload = MoECapacitorCorePlugin.getPushClickedPayload(message)
        } else if (eventName == MoECapacitorConstants.CallbackNames.kAuthenticationError) {
            capacitorPluginPayload = MoECapacitorCoreUtils.getAuthenticationErrorPayload(message)
        } else {
            capacitorPluginPayload = MoECapacitorCorePlugin.getInAppPayload(message)
        }
        self.notifyListeners(eventName, data: capacitorPluginPayload, retainUntilConsumed: true)
    }
    
    //MARK: Utility methods
    static func getCallbackName(forEventName name: String) -> String?{
        switch name {
        case MoEngagePluginConstants.CallBackEvents.pushTokenGenerated:
            return MoECapacitorConstants.CallbackNames.kPushTokenGenerated
        case MoEngagePluginConstants.CallBackEvents.pushClicked:
            return MoECapacitorConstants.CallbackNames.kPushClicked
        case MoEngagePluginConstants.CallBackEvents.inAppShown:
            return MoECapacitorConstants.CallbackNames.kInAppShown
        case MoEngagePluginConstants.CallBackEvents.inAppClicked:
            return MoECapacitorConstants.CallbackNames.kInAppClicked
        case MoEngagePluginConstants.CallBackEvents.inAppCustomAction:
            return MoECapacitorConstants.CallbackNames.kInAppClickedCustomAction
        case MoEngagePluginConstants.CallBackEvents.inAppDismissed:
            return MoECapacitorConstants.CallbackNames.kInAppDismissed
        case MoEngagePluginConstants.CallBackEvents.inAppSelfHandled:
            return MoECapacitorConstants.CallbackNames.kInAppSelfHandled
        case MoEngagePluginConstants.CallBackEvents.logOutCompleted:
            return MoECapacitorConstants.CallbackNames.kLogOutCompleted
        case MoEngagePluginConstants.CallBackEvents.authenticationError:
            return MoECapacitorConstants.CallbackNames.kAuthenticationError
        default:
            return nil
        }
    }
    
    static func getInAppPayload(_ payload: [String: Any]) -> [String: Any] {
        var campaignDataDict = [String: Any]()
        
        guard let accountMeta = payload[MoEngagePluginConstants.General.accountMeta] as? [String: Any],
              var dataDict = payload[MoEngagePluginConstants.General.data] as? [String: Any]
        else
        {
            return [:]
        }
        
        campaignDataDict[MoEngagePluginConstants.General.accountMeta] = accountMeta
        
        if dataDict[MoEngagePluginConstants.General.platform] != nil {
            campaignDataDict[MoEngagePluginConstants.General.platform] = dataDict[MoEngagePluginConstants.General.platform]
            dataDict.removeValue(forKey: MoEngagePluginConstants.General.platform)
        }
        
        if let campaignContext = dataDict[MoEngagePluginConstants.InApp.campaignContext] as? [String: Any] {
            let formattedCampaignId = campaignContext[MoECapacitorConstants.PayloadKeys.cid];
            let attributes = campaignContext
            dataDict.removeValue(forKey: MoEngagePluginConstants.InApp.campaignContext)
            let newCampaignContext = [MoECapacitorConstants.PayloadKeys.formattedCampaignId: formattedCampaignId,MoECapacitorConstants.PayloadKeys.attributes: attributes]
            dataDict[MoECapacitorConstants.PayloadKeys.campaignContext] = newCampaignContext
        }
        
        if var navigationActionDict = dataDict[MoECapacitorConstants.CallbackPayloadKeys.kNavigation] as? [String: Any] {
            navigationActionDict[MoECapacitorConstants.PayloadKeys.actionType] = MoECapacitorConstants.PayloadKeys.inAppNavigationAction
            navigationActionDict[MoECapacitorConstants.CallbackPayloadKeys.navigationUrl] = navigationActionDict[MoECapacitorConstants.CallbackPayloadKeys.value]
            navigationActionDict.removeValue(forKey: MoECapacitorConstants.CallbackPayloadKeys.value)
            campaignDataDict[MoECapacitorConstants.CallbackPayloadKeys.kNavigation] = navigationActionDict
            dataDict.removeValue(forKey: MoECapacitorConstants.CallbackPayloadKeys.kNavigation)
        }
        
        if var customActionDict = dataDict[MoEngagePluginConstants.InApp.customAction] as? [String: Any] {
            customActionDict[MoECapacitorConstants.PayloadKeys.actionType] = MoECapacitorConstants.PayloadKeys.inAppCustomAction
            campaignDataDict[MoECapacitorConstants.CallbackPayloadKeys.customAction] = customActionDict
            dataDict.removeValue(forKey: MoEngagePluginConstants.InApp.customAction)
            dataDict.removeValue(forKey: MoEngagePluginConstants.General.actionType)
        }
        
        if let selfHandledDict = dataDict[MoECapacitorConstants.CallbackPayloadKeys.kSelfHandled] as? [String: Any] {
            dataDict.removeValue(forKey: MoECapacitorConstants.CallbackPayloadKeys.kSelfHandled)
            campaignDataDict[MoECapacitorConstants.CallbackPayloadKeys.kSelfHandled] = selfHandledDict
        }
        campaignDataDict[MoECapacitorConstants.CallbackPayloadKeys.kCampaignData] = dataDict
        
        return campaignDataDict
    }
    
    static func getPushClickedPayload(_ payload: [String: Any]) -> [String: Any] {
        var pushClickedPayload = payload
        
        if var dataDict = payload[MoEngagePluginConstants.General.data] as? [String: Any] {
            
            if dataDict[MoEngagePluginConstants.General.platform] != nil {
                pushClickedPayload[MoEngagePluginConstants.General.platform] = dataDict[MoEngagePluginConstants.General.platform]
                dataDict.removeValue(forKey: MoEngagePluginConstants.General.platform)
                dataDict[MoECapacitorConstants.CallbackPayloadKeys.isDefaultAction] = false
                pushClickedPayload[MoECapacitorConstants.CallbackPayloadKeys.kPushCampaign] = dataDict
                pushClickedPayload.removeValue(forKey: MoEngagePluginConstants.General.data)
                
            }
        }
        
        return pushClickedPayload
    }
}
