//
//  MFFGHSAnalyticsProtocol.swift
//  MVM5G
//
//  Created by Gujuluva Santharam, Ajai Prabhu on 20/05/20.
//  Copyright © 2020 Kyle. All rights reserved.
//
import Foundation
import UIKit
import MVMCore
import MVMCoreUI

protocol MFFGHSAnalyticsProtocol {
    func trackPage(data: [String: Any]?, additionalData: [String:Any]?)
    func trackAction(action: [String: Any]?, pageData:[String: Any]?, additionalData: [String: Any]?)
    func trackPageAppear(with pageJSON: [String: Any]?)
    func trackPageDisappear(with pageJSON: [String: Any]?)
    
    /** true means it will stop sending data to server when view appear to reduce data load. Temporary bool, will remove next release */
    func stopInitialEvent() -> Bool
}

enum AnalyticsLogType: String {
    case UI = "UI"
    case BLE = "BLE"
}

struct AnalyticsPageInfo {
    var pageType: String?
}

extension MFFGHSAnalyticsProtocol {
    
    /** this boolean enable entire analytics feature. enable = false means nothing will send to server */
    private var enable: Bool {
        let initialParams:[String: Any] = [:]
        return !initialParams.boolForKey("isFGAnalyticsDisabled")
    }
    
    /** enable time tracking feature. TODO - we are reducing call not sending data on start event. */
    private var enableTimeTracker: Bool { return true }
    
    /** true means it will stop sending data to server when view appear to reduce data load. Temporary bool, will remove next release */
    func stopInitialEvent() -> Bool { return false }
    
    func trackPage(data: [String: Any]?, additionalData:[String:Any]?) {
        let params: [String: Any] = [
            "eventType" : additionalData?["eventType"] ?? "pageDisplay",
            "pageType"  : data?["pageType"] ?? "NA",
            "template"  : data?["template"] ?? "NA",
            "eventName" : additionalData?["eventName"] ?? "start",
            "pageClass" : additionalData?["pageClass"] ?? String(describing: Self.self),
            "epochSec"  : Int(Date().timeIntervalSince1970),
            "tdn"       : getVendorId(),
            "os"        : "iOS"
        ]
        let mergedParams = params.merging(additionalData ?? [:]) { $1 }
        performRequest(params: ["data": mergedParams])
    }
        
    func trackAction(action: [String: Any]?, pageData:[String: Any]?, additionalData: [String: Any]?) {
        /*
        let params: [String: Any] = [
            "eventType"     : "action",
            "actionPageType": action?["pageType"] ?? "NA",
            "actionTitle"   : action?["title"] ?? "NA",
            "pageType"      : pageData?.stringForkey("pageType") ?? "NA",
            "template"      : pageData?.stringForkey("template") ?? "NA",
            "epochSec"      : Int(Date().timeIntervalSince1970)
        ]
        performRequest(params: params)
    */
    }
    
    func trackGemini(value: [String: Any], logType: AnalyticsLogType, pageInfo: AnalyticsPageInfo? = nil) {
        var result: [String: Any] = [
            "os"        : "iOS",
            "LogType"   : logType.rawValue,
            "tdn"       : getVendorId()
        ]
        if let pageInfo = pageInfo {
            result["pageType"] = pageInfo.pageType
        }
        let payload = result.merging(value) { $1 }
        performRequest(params: ["data": payload])
    }
    
    func trackPKI(processStep: String, attributeList: GMFGAnalyticsAttributeList?) {
        var payload: [String: Any] = [
            "processStep"   : processStep,
            "os"            : "iOS",
            "tdn"           : getVendorId()
        ]
        if let attributeList = attributeList {
            payload["attributeList"] = attributeList.getList()
            //GlassboxManager.glassboxCustomEvent(forKey: GMFGConstant.Glassbox.fivegSetup, withParameters: ["gen3Data": attributeList.getList()])
        }
        performRequest(params: payload, forPageType: "gen3Data")
    }
    
    func trackGen3Data(processStep: String, attributeList: GMFGAnalyticsAttributeList?) {
        var payload: [String: Any] = [
            "processStep"   : processStep,
            "os"            : "iOS",
            "tdn"           : getVendorId()
        ]
        if let attributeList = attributeList {
            payload["attributeList"] = attributeList.getList()
        }
        performRequest(params: payload, forPageType: "gen3Data", priority: .high)
    }
    
    func resetGMAnalytics() {
        LogRequestSerialiser.shared.resetTimer()
    }
    
    // MARK:- Get the Device UUID
    func getVendorId() -> String {
        let uuid = UIDevice.current.identifierForVendor?.uuidString
        return uuid ?? GMFGConstant.empty
    }
    
    private func performRequest(params: [String: Any], forPageType: String = "logUIData", priority: LogPriority = .normal) {
        if enable {
            LogRequestSerialiser.shared.queueLogRequest(params: params, forPageType: forPageType, priority: priority)
        }
    }
    
    // MARK:- Public API - Time Tracker
    
    func trackPageAppear(with pageJSON: [String: Any]?) {
        if !enableTimeTracker { return }
        //GMFGSelfInstallTimeTracker.shared.trackPageAppear(with: pageJSON)
    }
    
    func trackPageDisappear(with pageJSON: [String: Any]?) {
        if !enableTimeTracker { return }
        //GMFGSelfInstallTimeTracker.shared.trackPageDisappear(with: pageJSON)
    }
}

final class LogRequestSerialiser {
    
    static let shared: LogRequestSerialiser = LogRequestSerialiser()
    private lazy var requestQueue: [RequestItem] = []
    private lazy var timer: Timer? = nil
    private init() { /* this avoid creating new instance */ }
    private var index = 0
    
    fileprivate func queueLogRequest(params: [String: Any], forPageType: String, priority: LogPriority) {
        let requestItem = RequestItem(priority: priority, endpoint: forPageType, parameters: params)
        requestQueue.append(requestItem)
        startTimer()
    }
    
    @objc fileprivate func performRequestIfAvailable() {
        if requestQueue.count > index {
            let objectToBePerformed = requestQueue[index]
            if let reqParams = MVMCoreRequestParameters(pageType: objectToBePerformed.endpoint, extraParameters: nil) {
                index = index + 1
                reqParams.add(objectToBePerformed.parameters)
                MVMCoreLoadHandler.sharedGlobal()?.loadBackgroundRequest(reqParams, dataForPage: nil, delegateObject: nil)
            }
        }
        if requestQueue.count <= index { resetTimer() }
    }
    
    fileprivate func resetTimer() {
        timer?.invalidate()
        timer = nil
        index = 0
        requestQueue.removeAll()
    }
    
    fileprivate func startTimer() {
        if timer == nil {
            DispatchQueue.main.async {
                self.timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(self.performRequestIfAvailable), userInfo: nil, repeats: true)
                self.timer?.tolerance = 0.5
            }
        }
    }
}

fileprivate struct RequestItem {
    var priority: LogPriority
    var endpoint: String
    var parameters: [String: Any]
}

fileprivate enum LogPriority {
    case high
    case normal
}

struct GMFGAnalyticsAttributeList {
    private var attrList: [[String: Any]] = []
    
    @available(*, deprecated, message: "use add(name:, value:)")
    mutating func addAttribute(name: String, value: Any) {
        attrList.append([
            "attributeName": name,
            "attributeValue": value
        ])
    }
    
    mutating func add(_ name: String, _ value: Any) {
        attrList.append([
            "attributeName": name,
            "attributeValue": value
        ])
    }
    
    func getList() -> [[String: Any]] {
        return attrList
    }
}
