import WebIdVideoIdentProductPlugin
import WebIdIosSdk
import WebIdPluginCore
import WebIdMetaPlugin
import WebIdUiKitComponents
import React
import WebIdAutoIdentOnServerProductPlugin
import WebIdEIdOnServerProductPlugin
import WebIdPayOnServerProductPlugin

@objc(WebIdMetaPlugin)
class WebIdMetaPlugin: RCTEventEmitter {
    
    var metaPlugin: MetaPlugin!
    private var verifyActionIdResult: VerifyActionIdResult?
    
    
    override func supportedEvents() -> [String]! {
        return [WebIdSdkEvent.finishedFailed.codeValue, WebIdSdkEvent.finishedSuccess.codeValue]
    }
    
    @objc(createMetaPlugin:username:apiKey:certsBase64:plugins:withResolver:withRejecter:)
    func createMetaPlugin(uri: String, username: String, apiKey: String, certsBase64: [String], plugins: [String], resolve:RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void {
                
        var selectedPlugins = [any IProductPluginWebId]()
                
        if (plugins.contains("AutoIdOnServer")) {
            selectedPlugins.append(AutoIdentOnServerProductPlugin())
        }
        if (plugins.contains("PayOnServer")) {
            selectedPlugins.append(PayOnServerProductPlugin())
        }
        if (plugins.contains("VideoId")) {
            selectedPlugins.append(VideoIdentProductPlugin())
        }
        if (plugins.contains("EIdOnServer")) {
            selectedPlugins.append(EidOnServerProductPlugin())
        }
        
        
        do {
            metaPlugin = MetaPlugin(
                username: username,
                apiKey: apiKey,
                env: try WebIdSdkEnvironment(
                    url: URLComponents(string: uri)!,
                    pinningCertificates: WebIdMetaPlugin.getPinningCerts(certsBase64: certsBase64)
                ),
                plugins: selectedPlugins
            )
            
            resolve(metaPlugin.debugDescription)
        } catch {
            if let error = error as? WebIdIosSdk.WebIdSdkEnvironmentError {
                reject(error.codeValue, error.messageValue, nil)
            } else {
                reject(WebIdSdkGenericError.UnknownException.codeValue, error.localizedDescription, nil)
            }
        }
    }
    
    @objc(verifyActionId:withResolver:withRejecter:)
    func verifyActionId(actionId: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
        Task {
            
            guard let plugin = self.metaPlugin else {
                reject(WebIdSdkGenericError.SDKNotInitialized.codeValue, WebIdSdkGenericError.SDKNotInitialized.messageValue, nil)
                return
            }
            do {
                let result =  try await plugin.verify(for: actionId)
                verifyActionIdResult = result
                
                do {
                    let resultString = try buildResultJSONString(result: result)
                    resolve(resultString)
                } catch {
                    reject("", error.localizedDescription, nil)
                }
                
            } catch {
                if let error = error as? WebIdIosSdk.WebIdMobileAppSdkError {
                    reject(error.codeValue, error.messageValue, nil)
                } else if let error = error as? WebIdIosSdk.UserActionError {
                    reject(error.codeValue, error.messageValue, nil)
                } else {
                    reject(WebIdSdkGenericError.UnknownException.codeValue, error.localizedDescription, nil)
                }
            }
        }
    }
    
    private func extractColor(map: [String:AnyObject]?, key: String) -> UIColor? {
        return map?.keys.contains(key) ?? false ? UIColor(hex: map?[key] as! String) : nil;
    }
    
    private func extractFont(map: [String:AnyObject]?, key: String) -> UIFont? {
        return map?.keys.contains(key) ?? false ? UIFont(name: map?[key] as! String, size: 1) : nil;
    }
    
    private func extractTextCase(map: [String:AnyObject]?, key: String) -> TextCase? {
        return map?.keys.contains(key) ?? false ? (map?[key] as! String == "upper" ? TextCase.upper : TextCase.normal) : nil;
    }
    
    private func extractCGFloat(map: [String:AnyObject]?, key: String) -> CGFloat? {
        return map?.keys.contains(key) ?? false ? map?[key] as? CGFloat : nil;
    }
    
    private func createThemeFromJSONDict(map: [String:AnyObject]?) -> WebidAppTheme{
        return WebidAppTheme();
        
    }
    
    
    @objc(start:darkThemeJSON:withResolver:withRejecter:)
    @MainActor func start(lightThemeJSON: String, darkThemeJSON: String, resolve:RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void {
        
        
        let lightMap = stringToDict(jsonString: lightThemeJSON)
        let darkMap = stringToDict(jsonString: darkThemeJSON)
        let darkTheme = createThemeFromJSONDict(map: darkMap);
        let lightTheme = createThemeFromJSONDict(map: lightMap);
        
        
        if (metaPlugin == nil) {
            reject(WebIdSdkGenericError.SDKNotInitialized.codeValue,WebIdSdkGenericError.SDKNotInitialized.messageValue,nil)
            return
        }
        
        if (verifyActionIdResult == nil) {
            reject(WebIdSdkGenericError.MissingVerifyActionIdResult.codeValue,WebIdSdkGenericError.MissingVerifyActionIdResult.messageValue,nil)
            return
        }
        
        DispatchQueue.main.async { [self] in
            do {
                try metaPlugin.startPlugin(parentVC: (UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.rootViewController)!, delegate: self, lightTheme: lightTheme, darkTheme: darkTheme)
                
            } catch {
                print(error)
            }
        }
        
        resolve(WebIdSdkSuccess.PluginStarted.codeValue)
    }
    
    private func stringToDict(jsonString: String) -> [String:AnyObject]? {
        if let data = jsonString.data(using: .utf8) {
            do {
                let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String:AnyObject]
                return json
            } catch {
            }
        }
        return nil
    }
    
    
    
    static func getPinningCerts(certsBase64: [String]) -> [SecCertificate] {
        var certificates: [SecCertificate] = []
        
        for certBase64 in certsBase64 {
            if let certData = Data(base64Encoded: certBase64, options: .ignoreUnknownCharacters) {
                
                if let certificate = SecCertificateCreateWithData(nil, certData as CFData) {
                    certificates.append(certificate)
                }
            }
        }
        return certificates
    }
    
    
    private func buildResultJSONString(result: VerifyActionIdResult) throws -> String {
        let iso8601 = ISO8601DateFormatter()
        
        var userActionStatusDict: [String: Any] = [
            "identState": result.userAction.userActionStatus.identState.stringValue,
            "qesState": result.userAction.userActionStatus.qesState.stringValue,
            "identTanState": result.userAction.userActionStatus.identTanState.stringValue,
            "timestamp": iso8601.string(from: result.userAction.userActionStatus.timestamp),
        ]
        
        if let mismatch = result.userAction.userActionStatus.mismatch {
            userActionStatusDict["mismatch"] = String(mismatch)
        }
        
        if let url = result.userAction.userActionStatus.webIdEntryUrl.link.url {
            userActionStatusDict["webIdEntryUrl"] = url.absoluteString
        }
        
        
        if let videoCallInfo = result.userAction.userActionStatus.videoCallInfo {
            var videoCallInfoDict: [String: String] = [
                "nextCallcenterOpening" :iso8601.string(from: videoCallInfo.nextCallcenterOpening),
            ]
            
            if let expirationDate = videoCallInfo.expirationDate {
                videoCallInfoDict["expirationDate"] = iso8601.string(from: expirationDate)
            }
            userActionStatusDict["videoCallInfo"] = videoCallInfoDict
        }
        
        
        let userActionDict: [String: Any] = [
            "actionId" : result.userAction.actionId,
            "customerNo" : result.userAction.customerNo,
            "expirationDate": iso8601.string(from: result.userAction.expirationDate),
            "envURI": result.userAction.envURI.absoluteString,
            "userActionType": result.userAction.userActionType.stringValue,
            "userActionStatus": userActionStatusDict
        ]
        
        
        let resultDict: [String: Any] = [
            "userAction" : userActionDict
        ]
        
        
        let jsonData = try JSONSerialization.data(withJSONObject: resultDict, options: .prettyPrinted)
        let jsonString = String(data: jsonData, encoding: .utf8)
        return jsonString!
    }
    
    
}

extension WebIdMetaPlugin: IProductPluginWebidDelegate {
    func finishedSuccess(additionalResult: String?, processFinished: Bool) {
        let resultDict: [String: String] = [
            "additionalResult" : additionalResult != nil ? additionalResult! : "",
            "processFinished" : String(processFinished)
        ]
        
        var jsonString = ""
        do {
            let jsonData = try JSONSerialization.data(withJSONObject: resultDict, options: .prettyPrinted)
            jsonString = String(data: jsonData, encoding: .utf8)!
        } catch {
            jsonString = error.localizedDescription
        }
        self.sendEvent(withName: WebIdSdkEvent.finishedSuccess.codeValue, body: jsonString )
    }
    
    
    func finishedFailed(failreason: WebIdPluginCore.EProductPluginErrors, additionalResult: String?, faultOriginator: String) {
        let resultDict: [String: String] = [
            "additionalResult" : additionalResult != nil ? additionalResult! : "",
            "faultOriginator" : faultOriginator,
            "genericResultCode" : failreason.codeValue,
            "genericResultMessage" : failreason.messageValue
        ]
        
        var jsonString = ""
        do {
            let jsonData = try JSONSerialization.data(withJSONObject: resultDict, options: .prettyPrinted)
            jsonString = String(data: jsonData, encoding: .utf8)!
        } catch {
            jsonString = error.localizedDescription
        }
        self.sendEvent(withName: WebIdSdkEvent.finishedFailed.codeValue, body: jsonString)
    }
    
    func finishedFailed(failreasonContainer: WebIdPluginCore.PluginFailReasonContainer, additionalResult: String?, faultOriginator: String){
        var resultDict: [String: String] = [
            "additionalResult" : additionalResult != nil ? additionalResult! : "",
            "faultOriginator" : faultOriginator,
        ]
        
        if let fc = failreasonContainer as? VideoIdentProductPluginFailReasonContainer {
            resultDict["specificResultCode"] = fc.failreason.stringValue
        }
        
        var jsonString = ""
        do {
            let jsonData = try JSONSerialization.data(withJSONObject: resultDict, options: .prettyPrinted)
            jsonString = String(data: jsonData, encoding: .utf8)!
        } catch {
            jsonString = error.localizedDescription
        }
        self.sendEvent(withName: WebIdSdkEvent.finishedFailed.codeValue, body: jsonString)
    }
}




extension UIColor {
    public convenience init?(hex: String) {
        let r, g, b, a: CGFloat
        
        if hex.hasPrefix("#") {
            let start = hex.index(hex.startIndex, offsetBy: 1)
            let hexColor = String(hex[start...])
            
            if hexColor.count == 8 {
                let scanner = Scanner(string: hexColor)
                var hexNumber: UInt64 = 0
                
                if scanner.scanHexInt64(&hexNumber) {
                    r = CGFloat((hexNumber & 0xff000000) >> 24) / 255
                    g = CGFloat((hexNumber & 0x00ff0000) >> 16) / 255
                    b = CGFloat((hexNumber & 0x0000ff00) >> 8) / 255
                    a = CGFloat(hexNumber & 0x000000ff) / 255
                    
                    self.init(red: r, green: g, blue: b, alpha: a)
                    return
                }
            }
            if hexColor.count == 6 {
                let scanner = Scanner(string: hexColor)
                var hexNumber: UInt64 = 0
                
                if scanner.scanHexInt64(&hexNumber) {
                    r = CGFloat((hexNumber & 0xff0000) >> 16) / 255
                    g = CGFloat((hexNumber & 0x00ff00) >> 8) / 255
                    b = CGFloat(hexNumber & 0x0000ff) / 255
                    
                    self.init(red: r, green: g, blue: b, alpha: 255)
                    return
                }
            }
        }
        
        return nil
    }
}


