//
/*
 * Copyright (c) 2021 BWI GmbH
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import Foundation
import MatomoTracker

@objcMembers class BWIAnalytics : NSObject {
    static let sharedTracker = BWIAnalytics()
    
    public var running: Bool {
        get {
            guard let session = session else {
                return false
            }
            return BWIAnalyticsAccountDataService(mxSession: session).isEnabled()
        }
        set(enabled) {
            guard let session = session else {
                return
            }
            fastRunning = enabled
            BWIAnalyticsAccountDataService(mxSession: session).setEnabled(enabled)
        }
    }
    
    private var matomo: MatomoTracker?
    private let appName: String
    private let appVersion: String
    private let appPlattform = "iOS"
    
    // use internally to not always check for Account data
    private var fastRunning: Bool = false
    
    private var session: MXSession? = nil
    
    private var analyticsConfig: AnalyticsConfiguration
    
    private override init() {
        if !BWIBuildSettings.shared.secondaryAppName.isEmpty {
            appName = BWIBuildSettings.shared.secondaryAppName
        } else {
            appName = Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName") as! String
        }
        appVersion = AppInfo.current.appVersion?.bundleShortVersion ?? ""
        
        analyticsConfig = AnalyticsConfiguration()
        
        super.init()
        
        MXSDKOptions.sharedInstance().analyticsDelegate = self
        
        resetMatomo()
    }
    
    func setSession(session: MXSession) {
        self.session = session
        fastRunning = running
    }
    
    func firstCall() {
        fastRunning = running
    }
    
    func isEnabled() -> Bool {
        return BWIBuildSettings.shared.bwiMatomoEnabled
    }
    
    // reset clientId on logout using internal matomo keys
    func resetUserdefaults() {
        // bwi: Analytics use custom config
        if let siteID = analyticsConfig.selectedSiteID(),
            let baseURL = analyticsConfig.selectedBaseURL(),
            let userdefaults = UserDefaults(suiteName: siteID + baseURL) {
                userdefaults.removeObject(forKey: "PiwikTotalNumberOfVistsKey")
                userdefaults.removeObject(forKey: "PiwikCurrentVisitTimestampKey")
                userdefaults.removeObject(forKey: "PiwikPreviousVistsTimestampKey")
                userdefaults.removeObject(forKey: "PiwikFirstVistsTimestampKey")
                userdefaults.removeObject(forKey: "PiwikVisitorIDKey")
                userdefaults.removeObject(forKey: "PiwikForcedVisitorIDKey")
                userdefaults.removeObject(forKey: "PiwikVisitorUserIDKey")
                userdefaults.removeObject(forKey: "PiwikOptOutKey")
                userdefaults.removeObject(forKey: "PiwikLastOrderDateKey")
        }
        
        resetMatomo()
    }
    
    // Uses Userdefaults instead of Account Data because it needs to be shown on every login
    func needsToShowPromt() -> Bool {
        if !BWIBuildSettings.shared.bwiMatomoEnabled {
            return false
        }
        
        guard let session = session else {
            return false
        }
        
        return BWIAnalyticsAccountDataService(mxSession: session).needsToShowPromt()
    }
    
    func resetMatomo() {
        if BWIBuildSettings.shared.bwiMatomoEnabled {

            readUserConfig()
            
            // bwi: Analytics use custom config
            guard let urlPath = analyticsConfig.selectedBaseURL(),
                  let url = URL(string: urlPath),
                  let siteID = analyticsConfig.selectedSiteID() else {
                matomo = nil
                return
            }
            
            matomo = MatomoTracker.init(siteId: siteID, baseURL:url, userAgent: nil)
            
            matomo?.isOptedOut = false
            matomo?.dispatchInterval = 5.0
            
            matomo?.logger = DefaultLogger(minLevel: .verbose)
            
            matomo?.setCustomVariable(withIndex: 1, name: "App Platform", value: appPlattform)
            matomo?.setCustomVariable(withIndex: 2, name: "App Version", value: appVersion)
        } else {
            matomo = nil
        }
    }
    
    func deleteMatomo() {
        matomo = nil
    }
    
    func readUserConfig() {
        if let path = Bundle.main.path(forResource: "trackingConfig", ofType: "json") {
            analyticsConfig.readConfig(jsonFile: path)
        }
    }
    
    func dispatchAll() {
        if fastRunning {
            matomo?.dispatch()
        }
    }
    
    func trackScreen(_ screenName: String) {
        if fastRunning {
            matomo?.track(view: ["ios", appName, appVersion, screenName], url: nil)
        }
    }
    
    func trackEvent(_ category: String, action: String) {
        if fastRunning {
            matomo?.track(eventWithCategory: category, action: action, url:nil)
        }
    }
    
    func trackEvent(category: String, action: String, name: String, number: NSNumber?) {
        if fastRunning {
            matomo?.track(eventWithCategory: category, action: action, name: name, number: number, url: nil)
        }
    }
    
    func trackBwiDuration(_ duration: TimeInterval, _ category: String, _ name: String) {
        if fastRunning {
            matomo?.track(eventWithCategory: "Metrics", action: category, name: name, number: NSNumber(value: duration), url:nil)
        }
    }
    
    func trackBwiValue(_ value: NSNumber, _ category: String, _ action: String) {
        if fastRunning {
            matomo?.track(eventWithCategory: category, action: action, name: nil, number: value, url:nil)
        }
    }
    
    func trackBwiValue(_ value: NSNumber, _ category: String, _ action: String, _ name: String) {
        if fastRunning {
            matomo?.track(eventWithCategory: category, action: action, name: name, number: value, url:nil)
        }
    }
    
    func trackSlowMessage( dimension: String, value: Int) {
        if fastRunning {
            // bwi: Analytics use custom config
            if let dimensionIndex = analyticsConfig.selectedSendMessageDimensionIndex() {
                matomo?.setDimension(dimension, forIndex: dimensionIndex)
                matomo?.track(eventWithCategory: "Performance", action: "SendMessage", name: "send_message_default", number: NSNumber(value: value), url:nil)
                matomo?.remove(dimensionAtIndex: dimensionIndex)
            }
        }
    }
    
    func trackEventWithDimension(category: String, action: String, dimension: String, value: NSNumber?, name: String?) {
        if fastRunning {
            // bwi: Analytics use custom config
            if let dimensionIndex = analyticsConfig.selectedSendMessageDimensionIndex() {
                matomo?.setDimension(dimension, forIndex: dimensionIndex)
                matomo?.track(eventWithCategory: category, action: action, name: name, number: value, url:nil) // name optional unwrap?
                matomo?.remove(dimensionAtIndex: dimensionIndex)
            }
        }
    }
}

extension BWIAnalytics : MXAnalyticsDelegate {
    func trackJoinedRoom(asDM isDM: Bool, isSpace: Bool, memberCount: UInt) {
        // dont track NV specific logs
    }
    
    func trackValue(_ value: NSNumber, category: String, name: String) {
        // dont track NV specific logs
    }
    
    func trackDuration(_ milliseconds: Int, name: MXTaskProfileName, units: UInt) {
        // dont track NV specific logs
    }
    
    func startDurationTracking(forName name: String, operation: String) -> StopDurationTracking {
        return {}
    }
    
    func trackCreatedRoom(asDM isDM: Bool) {
        // dont track NV specific logs
    }
    
    func trackCallStarted(withVideo isVideo: Bool, numberOfParticipants: Int, incoming isIncoming: Bool) {
        // dont track NV specific logs
    }
    
    func trackCallEnded(withDuration duration: Int, video isVideo: Bool, numberOfParticipants: Int, incoming isIncoming: Bool) {
        // dont track NV specific logs
    }
    func trackCallError(with reason: __MXCallHangupReason, video isVideo: Bool, numberOfParticipants: Int, incoming isIncoming: Bool) {
        // dont track NV specific logs
    }
    
    func trackJoinedRoom(asDM isDM: Bool, memberCount: UInt) {
        // dont track NV specific logs
    }
    
    func trackContactsAccessGranted(_ granted: Bool) {
        // dont track NV specific logs
    }
    
    func trackBwiSDKValue(_ value: NSNumber, category: String, action: String) {
        self.trackBwiValue(value, category, action)
    }
    
    func trackBwiSDKValue(_ value: NSNumber, category: String, action: String, name: String) {
        self.trackBwiValue(value, category, action, name);
    }
    
    func trackBwiEvent(_ category: String, action: String) {
        self.trackEvent(category, action: action)
    }
    
    func trackComposerEvent(inThread: Bool, isEditing: Bool, isReply: Bool, startsThread: Bool) {
        // dont track NV specific logs
    }
    
    func trackNonFatalIssue(_ issue: String, details: [String : Any]?) {
        // dont track NV specific logs
    }
    
    func trackE2EEError(_ errorCode: Int) {
        if let errorCode = AnalyticsE2EEErrorCode(rawValue: errorCode) {
            self.trackBwiValue(NSNumber(value: 1), "Encryption", "ViewMessage", errorCode.description)
        } else {
            self.trackBwiValue(NSNumber(value: 1), "Encryption", "ViewMessage", "Unknown_Error")
        }
        
    }
    
    func trackE2EEErrors(_ reason: DecryptionFailureReason, count: Int) {
        switch reason {
        case .unspecified:
            self.trackBwiValue(NSNumber(value: count), "Encryption", "ViewMessage", "Unknown_Error")
        case .olmIndexError:
            self.trackBwiValue(NSNumber(value: count), "Encryption", "ViewMessage", AnalyticsE2EEErrorCode(rawValue: 3)!.description)
        case .olmKeysNotSent:
            self.trackBwiValue(NSNumber(value: count), "Encryption", "ViewMessage", AnalyticsE2EEErrorCode(rawValue: 4)!.description)
        }
    }
}

