import Capacitor
import ContentsquareModule
import Foundation

// This is the protocol that the listeners passed by external bridges have to implement.
@objc
protocol _ExternalBridgeInterface {
    @objc
    func takeSnapshot(parameters: [String: Any])
       
    @objc
    func enableSessionReplay(parameters: [String: Any])
       
    @objc
    func enableAPIErrors(parameters: [String: Any])
       
    @objc
    func enableCrashReporter(parameters: [String: Any])
       
    @objc
    func setTagId(_: String)
       
    @objc
    func updateFeatureFlags(_: [[String: Any]])
    
    @objc
    func notifyCSInAppEnabled(parameters: [String: Any])
    
    @objc
    func updateBridgeConfig(_: Data)
    
    @objc
    func shouldHandleWebView(_: AnyObject)
    
    @objc
    func shouldHandleExternalBridge(_: AnyObject)
    
    @objc
    func enableUnmaskAll(parameters: [String : Any])
    
    @objc
    func notifySDKStateChanges(parameters: [String: Any])
    
    @objc
    func notifySrMaskingVisualisationEnabled(parameters: [String: Any])
}

@objc(ContentsquareCAPPlugin)
public class ContentsquareCAPPlugin: CAPPlugin {
    var telemetry: ContentsquareCAPTelemetry?
    var tagInjector: ContentsquareCAPTagInjector?
    
    @objc func optIn(_ call: CAPPluginCall) {
        DispatchQueue.main.async {
            Contentsquare.optIn()
        }
    }
    
    @objc func optOut(_ call: CAPPluginCall) {
        DispatchQueue.main.async {
            Contentsquare.optOut()
        }
    }
    
    @objc func sendScreenName(_ call: CAPPluginCall) {
        let name = call.getString("name", "")
        
        if call.getArray("customVarItems", JSObject.self) != nil {
            logger(.important, message: "CustomVar support not available. Screen name sent without customVars.")
        }
        
        guard let tagInjector = tagInjector else {
            logger(.error, message: "Tag Injector is not available")
            call.reject("Tag Injector is not available")
            return
        }
        let js = "window._uxa = window._uxa || [];\n" + "window._uxa.push([\"trackPageview\", \"" + name + "\"]);\n"
        tagInjector.addToJSQueue(tag: "TrackPageview: \(name)", jsToInject: js, isTagInjected: false)
        call.resolve([
            "result": "sendScreenName " + name,
        ])
    }
    
    @objc func sendTransaction(_ call: CAPPluginCall) {
        let transactionValue = call.getFloat("transactionValue", 0)
        let transactionCurrency = call.getString("transactionCurrency", "")
        let transactionId = call.getString("transactionId", "")
        
        Contentsquare.send(transaction: CustomerTransaction(id: transactionId, value: transactionValue, currency: transactionCurrency))
        call.resolve([
            "result": "sendTransaction " + transactionCurrency,
        ])
    }
    
    @objc func sendDynamicVarWithStringValue(_ call: CAPPluginCall) {
        let dynVarKey = call.getString("dynVarKey", "")
        let dynVarValue = call.getString("dynVarValue", "")
        
        Contentsquare.send(dynamicVar: DynamicVar(key: dynVarKey, value: dynVarValue))
        call.resolve([
            "result": "sendDynamicVarWithStringValue " + dynVarKey,
        ])
    }
    
    @objc func sendDynamicVarWithIntValue(_ call: CAPPluginCall) {
        let dynVarKey = call.getString("dynVarKey", "")
        let dynVarValue = UInt32(call.getInt("dynVarValue", 0))
        
        Contentsquare.send(dynamicVar: DynamicVar(key: dynVarKey, value: dynVarValue))
        call.resolve([
            "result": "sendDynamicVarWithIntValue " + dynVarKey,
        ])
    }
    
    @objc func setCapturedElementsSelector(_ call: CAPPluginCall) {
        guard let elements = call.getString("elements") else {
            call.reject("Missing 'elements' parameter")
            return
        }
        
        guard let tagInjector = tagInjector else {
            
            logger(.error, message: "Tag Injector is not available")
            call.reject("Tag Injector is not available")
            return
        }
        
        let js = """
                window._uxa = window._uxa || [];
                window._uxa.push(["setCapturedElementsSelector", "\(elements)"]);
            """
        
        tagInjector.addToJSQueue(tag: "setCapturedElementsSelector: \(elements)", jsToInject: js, isTagInjected: false)
        
        call.resolve()
    }
    
    @objc func excludeURLForReplay(_ call: CAPPluginCall) {
        guard let urlRegExPattern = call.getString("url") else {
            call.reject("Missing 'url' parameter")
            return
        }
        
        guard let tagInjector = tagInjector else {
            logger(.error, message: "Tag Injector is not available")
            call.reject("Tag Injector is not available")
            return
        }
        
        let js = """
                window._uxa = window._uxa || [];
                window._uxa.push(["excludeURLforReplay", \(urlRegExPattern)]);
            """
        
        tagInjector.addToJSQueue(tag: "excludeURLForReplay: \(urlRegExPattern)", jsToInject: js, isTagInjected: false)
        
        call.resolve()
    }
    
    @objc func setPIISelectors(_ call: CAPPluginCall) {
        guard let piiSelectors = call.getArray("PIISelectors") else {
            call.reject("Missing 'PIISelectors' parameter, got \(call)")
            return
        }

        guard let piiAttributes = call.getArray("Attributes") else {
            call.reject("Missing 'Attributes' parameter, got \(call)")
            return
        }
        
        guard let tagInjector = tagInjector else {
            logger(.error, message: "Tag Injector is not available")
            call.reject("Tag Injector is not available")
            return
        }

        var piiObject = [String: Any]()
        piiObject["PIISelectors"] = piiSelectors
        piiObject["Attributes"] = piiAttributes
        
        guard let jsonData = try? JSONSerialization.data(withJSONObject: piiObject, options: .prettyPrinted) else {
            logger(.error, message: "Error converting to JSON data.")
            return
        }

        guard let jsonString = String(data: jsonData, encoding: .utf8) else {
            logger(.error, message: "Error converting JSON data to String.")
            return
        }
        
       let js = """
               window._uxa = window._uxa || [];
               window._uxa.push(["setPIISelectors", \(jsonString)]);
           """
        
        tagInjector.addToJSQueue(tag: "setPIISelectors: \(jsonString)", jsToInject: js, isTagInjected: false)
        call.resolve()
        
    }
    
    @objc func collect(_ call: CAPPluginCall) {
        guard let telemetry = telemetry else {
            logger(.error, message: "Telemetry is not available")
            call.reject("Telemetry is not available")
            return
        }
        let name = call.getString("name", "")
        let value = call.getString("value", "")
        telemetry.collect(name: name, value: value)
        call.resolve()

    }
    
    
    // Called when DOM is ready
    @objc func onReady(_ call: CAPPluginCall) {
        guard let tagInjector = tagInjector else {
            logger(.error, message: "Tag Injector is not available")
            call.reject("Tag Injector is not available")
            return
        }
        call.resolve([
            "result": "onReady "
        ])
    }
    
    // This is called by Capacitor at the plugin init
    override public func load() {
        guard let webView = bridge?.webView else {
            logger(.error, message: "Webview doesn't exist. Can not init plugin")
            return
        }
        
        Contentsquare.register(webView: webView)
        logger(.important, message: "Webview injection successful")
        registerExternalBridge()
        // Init Telemetry
        initializeTelemetry()
        initializeTagInjector(webView: webView)
    }
    
    private func initializeTelemetry() {
        telemetry = ContentsquareCAPTelemetry()
        telemetry?.setXPFType()
    }
    
    private func registerExternalBridge() {
        Contentsquare.perform(
            NSSelectorFromString("_registerExternalBridgeWithParameters:"),
            with: [
                "interface": self,
                "type": 4,
            ] as [String : Any]
        )
    }
    
    private func initializeTagInjector(webView: WKWebView) {
        tagInjector = ContentsquareCAPTagInjector(webView: webView)
    }
    
}

extension ContentsquareCAPPlugin: _ExternalBridgeInterface {
    func notifySDKStateChanges(parameters: [String : Any]) {
        // Do nothing
    }
    
    func notifySrMaskingVisualisationEnabled(parameters: [String : Any]) {
        // Do nothing
    }
    
    func takeSnapshot(parameters: [String : Any]) {
        // Do nothing
    }
    
    func enableSessionReplay(parameters: [String : Any]) {
        // Do nothing
    }
    
    func enableAPIErrors(parameters: [String : Any]) {
        // Do nothing
    }
    
    func enableCrashReporter(parameters: [String : Any]) {
        // Do nothing
    }
    
    func setTagId(_: String) {
        // Do nothing
    }
    
    func updateFeatureFlags(_: [[String : Any]]) {
        // Do nothing
    }
    
    func notifyCSInAppEnabled(parameters: [String : Any]) {
        // Do nothing
    }
    
    func updateBridgeConfig(_: Data) {
        // Do nothing
    }
    
    func shouldHandleWebView(_: AnyObject){
        // Do nothing
    }
    
    func shouldHandleExternalBridge(_: AnyObject)  {
        // Do nothing
    }
    
    func enableUnmaskAll(parameters: [String : Any]) {
        // Do nothing
    }
}


