import Flutter
import UIKit
import FirebaseAnalytics
import GuruAnalyticsLib

public class SwiftGuruAnalyticsFlutterPlugin: NSObject, FlutterPlugin {
    public static var debugMode: Bool = false
    
    private var registeredChannel: FlutterMethodChannel?
    public static func register(with registrar: FlutterPluginRegistrar) {
        let instance = SwiftGuruAnalyticsFlutterPlugin()
        instance.setup(registrar: registrar)
    }
    
    private func setup(registrar: FlutterPluginRegistrar) {
        let channel = FlutterMethodChannel(name: "flutter.guru.guru_analytics_flutter", binaryMessenger: registrar.messenger())
        registrar.addMethodCallDelegate(self, channel: channel)
        registeredChannel = channel
    }
    
    private func callInitialize(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
        let arguments = call.arguments as? [String : Any] ?? [String : Any]()
        let batchLimit = arguments[AnalyticsFieldName.BATCH_LIMIT] as? Int ?? 32
        let uploadPeriodInSeconds = arguments[AnalyticsFieldName.UPLOAD_PERIOD_IN_SECONDS] as? Int ?? 60
        let debug = arguments[AnalyticsFieldName.DEBUG] as? Bool ?? false
        let delayedInSeconds = arguments[AnalyticsFieldName.DELAYED_IN_SECONDS] as? Int ?? 5
        let eventExpiredInDays = arguments[AnalyticsFieldName.EVENT_EXPIRED_IN_DAYS] as? Int ?? 7
        let saasXAPPID = arguments[AnalyticsFieldName.X_APP_ID] as? String ?? ""
        let saasXDEVICEINFO = arguments[AnalyticsFieldName.X_DEVICE_INFO] as? String ?? ""
        
        NSLog("[GuruAnalytics] initialize batchLimit:\(batchLimit) uploadPeriodInSeconds:\(uploadPeriodInSeconds) eventExpiredInDays:\(eventExpiredInDays) delayedInSeconds:\(delayedInSeconds) saasXAPPID:\(saasXAPPID)  saasXDEVICEINFO:\(saasXDEVICEINFO) debug:\(debug)")
        GuruAnalytics.registerInternalEventObserver(reportCallback: processAnalyticsCallback)
        GuruAnalytics.initializeLib(uploadPeriodInSecond: Double(uploadPeriodInSeconds), batchLimit: batchLimit, eventExpiredSeconds: Double(eventExpiredInDays * 86400), initializeTimeout: Double(delayedInSeconds), saasXAPPID: saasXAPPID, saasXDEVICEINFO: saasXDEVICEINFO, loggerDebug: debug)
        SwiftGuruAnalyticsFlutterPlugin.debugMode = debug
        result(true)
    }
    
    private func callGetAppInstanceId(result: @escaping FlutterResult) {
        let appInstanceId = Analytics.appInstanceID()
        NSLog("[GuruAnalytics] ==> appInstanceID \(String(describing: appInstanceId))")
        result(appInstanceId)
        //        result(FlutterError.init(code: error.errorCode.description, message: error.errorMessage, details: "\(sessionId) \(error.errorMessage) [\(msg)])
    }
    
    private func getConsentStatus(_ arguments: [String : Any],  consentField: String)-> ConsentStatus {
        return ((arguments[consentField] as? Bool) ?? false) ? ConsentStatus.granted : ConsentStatus.denied
    }

    private func callSetConsents(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
        guard let arguments = call.arguments as? [String : Any]
        else {
            let error = FlutterError.init(
                code: AnalyticsErrorCode.SET_CONSENTS_ERROR,
                message: "arguments is Empty",
                details: "arguments is Empty")
            result(error)
            return
        }

        let adStorageStatus = getConsentStatus(arguments, consentField: ConsentsFieldName.AD_STORAGE)
        let analyticsStorageStatus = getConsentStatus(arguments, consentField: ConsentsFieldName.ANALYTICS_STORAGE)
        let adPersonalizationStatus = getConsentStatus(arguments, consentField: ConsentsFieldName.AD_PERSONALIZATION)
        let adUserDataStatus = getConsentStatus(arguments, consentField: ConsentsFieldName.AD_USER_DATA)

        Analytics.setConsent([
            .adStorage : adStorageStatus,
            .analyticsStorage: analyticsStorageStatus,
            .adPersonalization: adPersonalizationStatus,
            .adUserData: adUserDataStatus
        ])
        NSLog("[GuruAnalytics] ==> setConsents(\(adStorageStatus) \(analyticsStorageStatus) \(adPersonalizationStatus) \(adUserDataStatus)")
        result(true)

    }

    private func processAnalyticsCallback(_ errorCode: Int, _ info: String) {
        registeredChannel?.invokeMethod("onAnalyticsCallback", arguments: [
            "code": errorCode,
            "errorInfo": info
        ])
    }

    private func callLogEvent(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
        guard let arguments = call.arguments as? [String : Any],
              let eventName = arguments[AnalyticsFieldName.EVENT_NAME] as? String
        else {
            let error = FlutterError.init(
                code: AnalyticsErrorCode.EVENT_ERROR,
                message: "eventName is Empty",
                details: "eventName is Empty")
            result(error)
            return
        }

        let parametersMap = arguments[AnalyticsFieldName.PARAMETERS] as? [String : Any] ?? [String : Any]()

        GuruAnalytics.logEvent(eventName, parameters: parametersMap)
        if (SwiftGuruAnalyticsFlutterPlugin.debugMode) {
            NSLog("[GuruAnalytics] ==> logEvent(\(eventName)")
        }

        result(true)
    }

    private func callSetUserProperty(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
        guard let arguments = call.arguments as? [String : Any],
              let name = arguments[AnalyticsFieldName.NAME] as? String,
              let value = arguments[AnalyticsFieldName.VALUE] as? String
        else {
            let error = FlutterError.init(
                code: AnalyticsErrorCode.PROPERTY_ERROR,
                message: "Property Name or Value is NullOrBlank!",
                details: "Property Name or Value is NullOrBlank!")
            result(error)
            return
        }
        GuruAnalytics.setUserProperty(value, forName: name)
        if (SwiftGuruAnalyticsFlutterPlugin.debugMode) {
            NSLog("[GuruAnalytics] ==> setUserProperty \(name) : \(value)")
        }
        result(true)

    }

    private func callSetDeviceId(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
        guard let arguments = call.arguments as? [String : Any],
              let deviceId = arguments[AnalyticsFieldName.DEVICE_ID] as? String
        else {
            let error = FlutterError.init(
                code: AnalyticsErrorCode.PROPERTY_ERROR,
                message: "Device ID is Null",
                details: "Device ID is Null")
            result(error)
            return
        }
        GuruAnalytics.setDeviceId(deviceId)
        if (SwiftGuruAnalyticsFlutterPlugin.debugMode) {
            NSLog("[GuruAnalytics] ==> setDeviceId \(deviceId)")
        }
        result(true)

    }

    private func callSetUid(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
        guard let arguments = call.arguments as? [String : Any],
              let userId = arguments[AnalyticsFieldName.USER_ID] as? String
        else {
            let error = FlutterError.init(
                code: AnalyticsErrorCode.PROPERTY_ERROR,
                message: "User ID is Null",
                details: "User ID is Null")
            result(error)
            return
        }
        GuruAnalytics.setUserID(userId)
        if (SwiftGuruAnalyticsFlutterPlugin.debugMode) {
            NSLog("[GuruAnalytics] ==> setUserID \(userId)")
        }
        result(true)
    }

    private func callSetAdjustId(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
        guard let arguments = call.arguments as? [String : Any],
              let adjustId = arguments[AnalyticsFieldName.ADJUST_ID] as? String
        else {
            let error = FlutterError.init(
                code: AnalyticsErrorCode.PROPERTY_ERROR,
                message: "Adjust ID is Null",
                details: "Adjust ID is Null")
            result(error)
            return
        }
        GuruAnalytics.setAdjustId(adjustId)
        if (SwiftGuruAnalyticsFlutterPlugin.debugMode) {
            NSLog("[GuruAnalytics] ==> setAdjustId \(adjustId)")
        }
        result(true)

    }

    private func callSetAdId(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
        guard let arguments = call.arguments as? [String : Any],
              let adId = arguments[AnalyticsFieldName.AD_ID] as? String
        else {
            let error = FlutterError.init(
                code: AnalyticsErrorCode.PROPERTY_ERROR,
                message: "AD ID is Null",
                details: "AD ID is Null")
            result(error)
            return
        }
        GuruAnalytics.setAdId(adId)
        if (SwiftGuruAnalyticsFlutterPlugin.debugMode) {
            NSLog("[GuruAnalytics] ==> setAdId \(adId)")
        }
        result(true)
    }

    private func callSetFirebaseId(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
        guard let arguments = call.arguments as? [String : Any],
              let firebaseId = arguments[AnalyticsFieldName.FIREBASE_ID] as? String
        else {
            let error = FlutterError.init(
                code: AnalyticsErrorCode.PROPERTY_ERROR,
                message: "Firebase ID is Null",
                details: "Firebase ID is Null")
            result(error)
            return
        }
        GuruAnalytics.setFirebaseId(firebaseId)
        if (SwiftGuruAnalyticsFlutterPlugin.debugMode) {
            NSLog("[GuruAnalytics] ==> setFirebaseId \(firebaseId)")
        }
        result(true)

    }

    private func callSetScreen(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
        guard let arguments = call.arguments as? [String : Any],
              let screen = arguments[AnalyticsFieldName.SCREEN] as? String
        else {
            let error = FlutterError.init(
                code: AnalyticsErrorCode.PROPERTY_ERROR,
                message: "ScreenName is Null",
                details: "ScreenName is Null")
            result(error)
            return
        }
        GuruAnalytics.setScreen(screen)
        if (SwiftGuruAnalyticsFlutterPlugin.debugMode) {
            NSLog("[GuruAnalytics] ==> setScreen \(screen)")
        }
        result(true)
    }

    private func callZipLogs(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
        if (SwiftGuruAnalyticsFlutterPlugin.debugMode) {
            NSLog("[GuruAnalytics] ==> callZipLogs")
        }
        GuruAnalytics.eventsLogsArchive { url in
            result(url?.absoluteString ?? "")
        }
    }

    private func callGetStatistic(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
        if (SwiftGuruAnalyticsFlutterPlugin.debugMode) {
            NSLog("[GuruAnalytics] ==> callGetStatistic")
        }
        GuruAnalytics.debug_eventsStatistics{ uploadedEventCount, loggedEventCount in
            result(["logged" : loggedEventCount, "uploaded" : uploadedEventCount])
        }
    }

    public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
        //        result("iOS " + UIDevice.current.systemVersion)
        NSLog("[GuruAnalytics] methodHandler \(call.method)")
        switch(call.method) {
        case AnalyticsMethod.Initialize:
            callInitialize(call, result: result)
            break;
        case AnalyticsMethod.GetAppInstanceId:
            callGetAppInstanceId(result: result)
            break;
        case AnalyticsMethod.LogEvent:
            callLogEvent(call, result: result)
            break;
        case AnalyticsMethod.SetUserProperty:
            callSetUserProperty(call, result: result)
            break;
        case AnalyticsMethod.SetDeviceId:
            callSetDeviceId(call, result: result)
            break;
        case AnalyticsMethod.SetUid:
            callSetUid(call,  result: result)
            break;
        case AnalyticsMethod.SetAdjustId:
            callSetAdjustId(call, result: result)
            break;
        case AnalyticsMethod.SetAdId:
            callSetAdId(call, result: result)
            break;
        case AnalyticsMethod.SetFirebaseId:
            callSetFirebaseId(call, result: result)
            break;
        case AnalyticsMethod.SetScreen:
            callSetScreen(call, result: result)
            break;
        case AnalyticsMethod.ZipLogs:
            callZipLogs(call, result: result)
            break;
        case AnalyticsMethod.GetStatistic:
            callGetStatistic(call, result: result)
            break;
        case AnalyticsMethod.SetConsents:
            callSetConsents(call, result: result)
            break;
        default:
            result(FlutterMethodNotImplemented)
        }
    }
    
}
