//
// Copyright 2025 Circle Internet Group, Inc. All rights reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
// 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 CYBAVOWallet

@objc(RNAuth)
class RNAuth: NSObject {
    var domain = Bundle.main.bundleIdentifier ?? ""

    override init() {
        super.init()
        Auth.shared.addSignInStateDelegate(self)
    }

    @objc func signIn(_ token: String, withIdentityProvider identityProvider: String, withExtras extras: NSDictionary, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
        var e = [String:String]()
                for (key, value) in extras {
                    if let key = key as? String, let value = value as? String {
                        e[key] = value
                    }
                }
        Auth.shared.signIn(token: token, identityProvider: identityProvider, extras: e){ result in
            switch(result) {
            case .success(let e):
                print("signin success \(e)")
                let dict = RNWalletSDK.toDict(e)
                resolve(dict)
            case .failure(let error):
                let errorCode = error.code.rawValue
                do{
                    let e = NSError(domain: try String(error.message), code: errorCode, userInfo: RNWalletSDK.toErrorUserInfo(error))
                    reject(String(error.code.rawValue), error.message, e)
                }catch{
                    let e = NSError(domain: "failed to get error.message", code: errorCode, userInfo: nil)
                    reject(String(errorCode), "failed to get error.message", e)
                }
            }
        }
    }

    @objc public func signUp(_ token: String, withIdentityProvider identityProvider: String, withExtras extras: NSDictionary, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock){
        var e = [String:String]()
        for (key, value) in extras {
            if let key = key as? String, let value = value as? String {
                e[key] = value
            }
        }
        Auth.shared.signUp(token: token, identityProvider: identityProvider, extras: e){ result in
            switch(result) {
            case .success(let data):
                let dict = RNWalletSDK.toDict(data)
                resolve(dict)
            case .failure(let error):
                let errorCode = error.code.rawValue
                do{
                    let e = NSError(domain: try String(error.message), code: errorCode, userInfo: RNWalletSDK.toErrorUserInfo(error))
                    reject(String(error.code.rawValue), error.message, e)
                }catch{
                    let e = NSError(domain: "failed to get error.message", code: errorCode, userInfo: nil)
                    reject(String(errorCode), "failed to get error.message", e)
                }
            }
        }
    }

    @objc public func signOut(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock){
        Auth.shared.signOut()
        resolve(0)
    }

    @objc public func getSignInState(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
        var state: RNSignInState
        switch Auth.shared.getSignInState() {
        case .SignedIn:
            state = .SIGNED_IN
        case .SignedOut:
            state = .SIGNED_OUT
        case .SessionExpired:
            state = .SESSION_EXPIRED
        case .SessionInvalid:
            state = .SESSION_INVALID
        case .Unknown:
            state = .UNKNOWN
        case .needVerifyOtp:
            state = .SIGNED_IN
        case .needRegisterPhone:
            state = .SIGNED_IN
        @unknown default:
            state = .UNKNOWN
        }
        resolve(state.rawValue)
    }

    @objc public func getUserState(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock){
        Auth.shared.getUserState(){ result in
            switch(result) {
            case .success(let data):
                let user = RNWalletSDK.toDict(data)
                resolve(user)
            case .failure(let error):
                let errorCode = error.code.rawValue
                do{
                    let e = NSError(domain: try String(error.message), code: errorCode, userInfo: RNWalletSDK.toErrorUserInfo(error))
                    reject(String(error.code.rawValue), error.message, e)
                }catch{
                    let e = NSError(domain: "failed to get error.message", code: errorCode, userInfo: nil)
                    reject(String(errorCode), "failed to get error.message", e)
                }
            }
        }
    }

    @objc public func setupPinCode(_ pinCode: String, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock){
        Auth.shared.setupPinCode(pinCode: pinCode) { result in
            switch(result) {
            case .success(let data):
                let dict = RNWalletSDK.toDict(data)
                resolve(dict)
            case .failure(let error):
                let errorCode = error.code.rawValue
                do{
                    let e = NSError(domain: try String(error.message), code: errorCode, userInfo: RNWalletSDK.toErrorUserInfo(error))
                    reject(String(error.code.rawValue), error.message, e)
                }catch{
                    let e = NSError(domain: "failed to get error.message", code: errorCode, userInfo: nil)
                    reject(String(errorCode), "failed to get error.message", e)
                }
            }
        }
    }

    @objc public func setupPinCodeWithPinSecret(_ byPinSecret: NSDictionary, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock){
        guard let pinSecret = PinSecretBridge.fromDictionary(dict: byPinSecret) else{
            reject("0", "PinSecret not found", NSError(domain: "", code: 0, userInfo: nil))
            return
        }

        Auth.shared.setupPinCode(pinSecret: pinSecret) { result in
            switch(result) {
            case .success(let data):
                let dict = RNWalletSDK.toDict(data)
                resolve(dict)
            case .failure(let error):
                let errorCode = error.code.rawValue
                do{
                    let e = NSError(domain: try String(error.message), code: errorCode, userInfo: RNWalletSDK.toErrorUserInfo(error))
                    reject(String(error.code.rawValue), error.message, e)
                }catch{
                    let e = NSError(domain: "failed to get error.message", code: errorCode, userInfo: nil)
                    reject(String(errorCode), "failed to get error.message", e)
                }
            }
        }
    }

    @objc public func setupBackupChallenge(_ pinCode: String, withChallenge1 challenge1: NSDictionary, withChallenge2 challenge2: NSDictionary, withChallenge3 challenge3: NSDictionary, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock){
        do {
            guard let q1 = challenge1["question"] as? String, let q2 = challenge2["question"] as? String, let q3 = challenge3["question"] as? String,
                let a1 = challenge1["answer"] as? String, let a2 = challenge2["answer"] as? String, let a3 = challenge3["answer"] as? String else {
                    reject("0", "invalid question/answer pairs", NSError(domain: "", code: 0, userInfo: nil))
                    return
            }

            let c1 = try BackupChallenge(question: q1, answer: a1)
            let c2 = try BackupChallenge(question: q2, answer: a2)
            let c3 = try BackupChallenge(question: q3, answer: a3)

            Auth.shared.setupBackupChallenge(pinCode: pinCode, challenge1: c1, challenge2: c2, challenge3: c3){ result in
                switch(result) {
                case .success(let data):
                    let dict = RNWalletSDK.toDict(data)
                    resolve(dict)
                case .failure(let error):
                    let errorCode = error.code.rawValue
                    do{
                        let e = NSError(domain: try String(error.message), code: errorCode, userInfo: RNWalletSDK.toErrorUserInfo(error))
                        reject(String(error.code.rawValue), error.message, e)
                    }catch{
                        let e = NSError(domain: "failed to get error.message", code: errorCode, userInfo: nil)
                        reject(String(errorCode), "failed to get error.message", e)
                    }
                }
            }
        } catch(let error) {
            reject("0", error.localizedDescription, error)
        }
    }

    @objc public func setupBackupChallengeWithPinSecret(_ byPinSecret: NSDictionary, withChallenge1 challenge1: NSDictionary, withChallenge2 challenge2: NSDictionary, withChallenge3 challenge3: NSDictionary, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock){
        do {
            guard let q1 = challenge1["question"] as? String, let q2 = challenge2["question"] as? String, let q3 = challenge3["question"] as? String,
                let a1 = challenge1["answer"] as? String, let a2 = challenge2["answer"] as? String, let a3 = challenge3["answer"] as? String else {
                    reject("0", "invalid question/answer pairs", NSError(domain: "", code: 0, userInfo: nil))
                    return
            }

            guard let pinSecret = PinSecretBridge.fromDictionary(dict: byPinSecret) else{
                reject("0", "PinSecret not found", NSError(domain: "", code: 0, userInfo: nil))
                return
            }
            let c1 = try BackupChallenge(question: q1, answer: a1)
            let c2 = try BackupChallenge(question: q2, answer: a2)
            let c3 = try BackupChallenge(question: q3, answer: a3)

            Auth.shared.setupBackupChallenge(pinSecret: pinSecret, challenge1: c1, challenge2: c2, challenge3: c3){ result in
                switch(result) {
                case .success(let data):
                    let dict = RNWalletSDK.toDict(data)
                    resolve(dict)
                case .failure(let error):
                    let errorCode = error.code.rawValue
                    do{
                        let e = NSError(domain: try String(error.message), code: errorCode, userInfo: RNWalletSDK.toErrorUserInfo(error))
                        reject(String(error.code.rawValue), error.message, e)
                    }catch{
                        let e = NSError(domain: "failed to get error.message", code: errorCode, userInfo: nil)
                        reject(String(errorCode), "failed to get error.message", e)
                    }
                }
            }
        } catch(let error) {
            reject("0", error.localizedDescription, error)
        }
    }
    @objc public func restorePinCode(_ pinCode: String, withChallenge1 challenge1: NSDictionary, withChallenge2 challenge2: NSDictionary, withChallenge3 challenge3: NSDictionary, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock){
        do {
            guard let q1 = challenge1["question"] as? String, let q2 = challenge2["question"] as? String, let q3 = challenge3["question"] as? String,
                let a1 = challenge1["answer"] as? String, let a2 = challenge2["answer"] as? String, let a3 = challenge3["answer"] as? String else {
                    reject("0", "invalid question/answer pairs", NSError(domain: "", code: 0, userInfo: nil))
                    return
            }

            let c1 = try BackupChallenge(question: q1, answer: a1)
            let c2 = try BackupChallenge(question: q2, answer: a2)
            let c3 = try BackupChallenge(question: q3, answer: a3)

            Auth.shared.restorePinCode(pinCode: pinCode, challenge1: c1, challenge2: c2, challenge3: c3){ result in
                switch(result) {
                case .success(let data):
                    let dict = RNWalletSDK.toDict(data)
                    resolve(dict)
                case .failure(let error):
                    let errorCode = error.code.rawValue
                    do{
                        let e = NSError(domain: try String(error.message), code: errorCode, userInfo: RNWalletSDK.toErrorUserInfo(error))
                        reject(String(error.code.rawValue), error.message, e)
                    }catch{
                        let e = NSError(domain: "failed to get error.message", code: errorCode, userInfo: nil)
                        reject(String(errorCode), "failed to get error.message", e)
                    }
                }
            }
        } catch(let error) {
            reject("0", error.localizedDescription, error)
        }
    }

    @objc public func restorePinCodeWithPinSecret(_ byPinSecret: NSDictionary, withChallenge1 challenge1: NSDictionary, withChallenge2 challenge2: NSDictionary, withChallenge3 challenge3: NSDictionary, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock){
        do {
            guard let q1 = challenge1["question"] as? String, let q2 = challenge2["question"] as? String, let q3 = challenge3["question"] as? String,
                let a1 = challenge1["answer"] as? String, let a2 = challenge2["answer"] as? String, let a3 = challenge3["answer"] as? String else {
                    reject("0", "invalid question/answer pairs", NSError(domain: "", code: 0, userInfo: nil))
                    return
            }
            guard let pinSecret = PinSecretBridge.fromDictionary(dict: byPinSecret) else{
                reject("0", "PinSecret not found", NSError(domain: "", code: 0, userInfo: nil))
                return
            }

            let c1 = try BackupChallenge(question: q1, answer: a1)
            let c2 = try BackupChallenge(question: q2, answer: a2)
            let c3 = try BackupChallenge(question: q3, answer: a3)

            Auth.shared.restorePinCode(pinSecret: pinSecret, challenge1: c1, challenge2: c2, challenge3: c3){ result in
                switch(result) {
                case .success(let data):
                    let dict = RNWalletSDK.toDict(data)
                    resolve(dict)
                case .failure(let error):
                    let errorCode = error.code.rawValue
                    do{
                        let e = NSError(domain: try String(error.message), code: errorCode, userInfo: RNWalletSDK.toErrorUserInfo(error))
                        reject(String(error.code.rawValue), error.message, e)
                    }catch{
                        let e = NSError(domain: "failed to get error.message", code: errorCode, userInfo: nil)
                        reject(String(errorCode), "failed to get error.message", e)
                    }
                }
            }
        } catch(let error) {
            reject("0", error.localizedDescription, error)
        }
    }
    @objc public func changePinCode(_ newPinCode: String, current currentPinCode: String, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock){
        Auth.shared.changePinCode(newPinCode: newPinCode, currentPinCode: currentPinCode){ result in
            switch(result) {
            case .success(let data):
                let dict = RNWalletSDK.toDict(data)
                resolve(dict)
            case .failure(let error):
                let errorCode = error.code.rawValue
                do{
                    let e = NSError(domain: try String(error.message), code: errorCode, userInfo: RNWalletSDK.toErrorUserInfo(error))
                    reject(String(error.code.rawValue), error.message, e)
                }catch{
                    let e = NSError(domain: "failed to get error.message", code: errorCode, userInfo: nil)
                    reject(String(errorCode), "failed to get error.message", e)
                }
            }
        }
    }

    @objc public func changePinCodeWithPinSecret(_ byNewPinSecret: NSDictionary, byCurrentPinSecret: NSDictionary, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock){

        guard let newPinSecret = PinSecretBridge.fromDictionary(dict: byNewPinSecret) else{
            reject("0", "new PinSecret not found", NSError(domain: "", code: 0, userInfo: nil))
            return
        }
        guard let currentPinSecret = PinSecretBridge.fromDictionary(dict: byCurrentPinSecret) else{
            reject("0", "current PinSecret not found", NSError(domain: "", code: 0, userInfo: nil))
            return
        }
        Auth.shared.changePinCode(newPinSecret: newPinSecret, currentPinSecret: currentPinSecret){ result in
            switch(result) {
            case .success(let data):
                let dict = RNWalletSDK.toDict(data)
                resolve(dict)
            case .failure(let error):
                let errorCode = error.code.rawValue
                do{
                    let e = NSError(domain: try String(error.message), code: errorCode, userInfo: RNWalletSDK.toErrorUserInfo(error))
                    reject(String(error.code.rawValue), error.message, e)
                }catch{
                    let e = NSError(domain: "failed to get error.message", code: errorCode, userInfo: nil)
                    reject(String(errorCode), "failed to get error.message", e)
                }
            }
        }
    }

    @objc public func getRestoreQuestions(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
        Auth.shared.getRestoreQuestions(){ result in
            switch(result) {
            case .success(let data):
                if data.questions.count < 3 {
                    reject("0", "invalid parameter", NSError(domain: "", code: 0, userInfo: nil))
                    return
                }
                let questions: [String: Any] = [
                    "question1": data.questions[0],
                    "question2": data.questions[1],
                    "question3": data.questions[2]
                ]
                resolve(questions)
            case .failure(let error):
                let errorCode = error.code.rawValue
                do{
                    let e = NSError(domain: try String(error.message), code: errorCode, userInfo: RNWalletSDK.toErrorUserInfo(error))
                    reject(String(error.code.rawValue), error.message, e)
                }catch{
                    let e = NSError(domain: "failed to get error.message", code: errorCode, userInfo: nil)
                    reject(String(errorCode), "failed to get error.message", e)
                }
            }
        }
    }

    @objc public func verifyRestoreQuestions(_ challenge1: NSDictionary, withChallenge2 challenge2: NSDictionary, withChallenge3 challenge3: NSDictionary, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock){
        do {
            guard let q1 = challenge1["question"] as? String, let q2 = challenge2["question"] as? String, let q3 = challenge3["question"] as? String,
                let a1 = challenge1["answer"] as? String, let a2 = challenge2["answer"] as? String, let a3 = challenge3["answer"] as? String else {
                reject("0", "invalid question/answer pairs", NSError(domain: "", code: 0, userInfo: nil))
                return
            }

            let c1 = try BackupChallenge(question: q1, answer: a1)
            let c2 = try BackupChallenge(question: q2, answer: a2)
            let c3 = try BackupChallenge(question: q3, answer: a3)

            Auth.shared.verifyRestoreQuestions(challenge1: c1, challenge2: c2, challenge3: c3) { result in
                switch(result) {
                case .success(let data):
                    let dict = RNWalletSDK.toDict(data)
                    resolve(dict)
                case .failure(let error):
                    let errorCode = error.code.rawValue
                    do{
                        let e = NSError(domain: try String(error.message), code: errorCode, userInfo: RNWalletSDK.toErrorUserInfo(error))
                        reject(String(error.code.rawValue), error.message, e)
                    }catch{
                        let e = NSError(domain: "failed to get error.message", code: errorCode, userInfo: nil)
                        reject(String(errorCode), "failed to get error.message", e)
                    }
                }
            }
        } catch(let error) {
            reject("0", error.localizedDescription, error)
        }
    }

    @objc public func forgotPinCode(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock){
        Auth.shared.forgotPinCode() { result in
            switch(result) {
            case .success(let data):
                let dict = RNWalletSDK.toDict(data)
                resolve(dict)
            case .failure(let error):
                let errorCode = error.code.rawValue
                do{
                    let e = NSError(domain: try String(error.message), code: errorCode, userInfo: RNWalletSDK.toErrorUserInfo(error))
                    reject(String(error.code.rawValue), error.message, e)
                }catch{
                    let e = NSError(domain: "failed to get error.message", code: errorCode, userInfo: nil)
                    reject(String(errorCode), "failed to get error.message", e)
                }
            }
        }
    }

    @objc public func verifyRecoveryCode(_ recoveryCode: String, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock){
        print("verifyRecoveryCode \(recoveryCode)")
        Auth.shared.verifyRecoveryCode(recoveryCode: recoveryCode) { result in
            switch(result) {
            case .success(let data):
                let dict = RNWalletSDK.toDict(data)
                print("verifyRecoveryCode dict \(recoveryCode)")
                resolve(dict)
            case .failure(let error):
                let errorCode = error.code.rawValue
                do{
                    let e = NSError(domain: try String(error.message), code: errorCode, userInfo: RNWalletSDK.toErrorUserInfo(error))
                    reject(String(error.code.rawValue), error.message, e)
                }catch{
                    let e = NSError(domain: "failed to get error.message", code: errorCode, userInfo: nil)
                    reject(String(errorCode), "failed to get error.message", e)
                }
            }
        }
    }

    @objc public func recoverPinCode(_ pinCode: String, withCode recoveryCode: String, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
        Auth.shared.recoverPinCode(pinCode: pinCode, recoveryCode: recoveryCode) { result in
            switch(result) {
            case .success(let data):
                let dict = RNWalletSDK.toDict(data)
                resolve(dict)
            case .failure(let error):
                let errorCode = error.code.rawValue
                do{
                    let e = NSError(domain: try String(error.message), code: errorCode, userInfo: RNWalletSDK.toErrorUserInfo(error))
                    reject(String(error.code.rawValue), error.message, e)
                }catch{
                    let e = NSError(domain: "failed to get error.message", code: errorCode, userInfo: nil)
                    reject(String(errorCode), "failed to get error.message", e)
                }
            }
        }
    }

    @objc public func recoverPinCodeWithPinSecret(_ byPinSecret: NSDictionary, withCode recoveryCode: String, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
        guard let pinSecret = PinSecretBridge.fromDictionary(dict: byPinSecret) else{
            reject("0", "PinSecret not found", NSError(domain: "", code: 0, userInfo: nil))
            return
        }

        Auth.shared.recoverPinCode(pinSecret: pinSecret, recoveryCode: recoveryCode) { result in
            switch(result) {
            case .success(let data):
                let dict = RNWalletSDK.toDict(data)
                resolve(dict)
            case .failure(let error):
                let errorCode = error.code.rawValue
                do{
                    let e = NSError(domain: try String(error.message), code: errorCode, userInfo: RNWalletSDK.toErrorUserInfo(error))
                    reject(String(error.code.rawValue), error.message, e)
                }catch{
                    let e = NSError(domain: "failed to get error.message", code: errorCode, userInfo: nil)
                    reject(String(errorCode), "failed to get error.message", e)
                }
            }
        }
    }

    @objc public func setPushDeviceToken(_ pushDeviceToken: String, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
        Auth.shared.setPushDeviceToken(deviceToken: pushDeviceToken) { result in
            switch(result) {
            case .success(let data):
                let dict = RNWalletSDK.toDict(data)
                resolve(dict)
            case .failure(let error):
                let errorCode = error.code.rawValue
                do{
                    let e = NSError(domain: try String(error.message), code: errorCode, userInfo: RNWalletSDK.toErrorUserInfo(error))
                    reject(String(error.code.rawValue), error.message, e)
                }catch{
                    let e = NSError(domain: "failed to get error.message", code: errorCode, userInfo: nil)
                    reject(String(errorCode), "failed to get error.message", e)
                }
            }
        }
    }
    @objc public func registerPhoneNumber(_ countryCode: String, phone: String, duration: NSInteger, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
        Auth.shared.registerPhoneNumber(contryCode: countryCode, phone: phone, duration: Int64(duration)){result in
            switch(result) {
            case .success(let data):
                let dict = RNWalletSDK.toDict(data)
                resolve(dict)
            case .failure(let error):
                let errorCode = error.code.rawValue
                do{
                    let e = NSError(domain: try String(error.message), code: errorCode, userInfo: RNWalletSDK.toErrorUserInfo(error))
                    reject(String(error.code.rawValue), error.message, e)
                }catch{
                    let e = NSError(domain: "failed to get error.message", code: errorCode, userInfo: nil)
                    reject(String(errorCode), "failed to get error.message", e)
                }
            }

        }
    }
    @objc public func verifyOtp(_ actionToken: String, code: String, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
        Auth.shared.verifyOtp(actionToken: actionToken, code: code){result in
            switch(result) {
            case .success(let data):
                let dict = RNWalletSDK.toDict(data)
                resolve(dict)
            case .failure(let error):
                let errorCode = error.code.rawValue
                do{
                    let e = NSError(domain: try String(error.message), code: errorCode, userInfo: RNWalletSDK.toErrorUserInfo(error))
                    reject(String(error.code.rawValue), error.message, e)
                }catch{
                    let e = NSError(domain: "failed to get error.message", code: errorCode, userInfo: nil)
                    reject(String(errorCode), "failed to get error.message", e)
                }
            }

        }
    }

    @objc public func createKyc(_ country: String, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
        Auth.shared.createKyc(country: country){result in
            switch(result) {
            case .success(let data):
                let dict = RNWalletSDK.toDict(data)
                resolve(dict)
            case .failure(let error):
                let errorCode = error.code.rawValue
                do{
                    let e = NSError(domain: try String(error.message), code: errorCode, userInfo: RNWalletSDK.toErrorUserInfo(error))
                    reject(String(error.code.rawValue), error.message, e)
                }catch{
                    let e = NSError(domain: "failed to get error.message", code: errorCode, userInfo: nil)
                    reject(String(errorCode), "failed to get error.message", e)
                }
            }

        }
    }

    @objc public func getKycAccessToken(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock){
        Auth.shared.getKycAccessToken() { result in
            switch(result) {
            case .success(let data):
                let dict = RNWalletSDK.toDict(data)
                resolve(dict)
            case .failure(let error):
                let errorCode = error.code.rawValue
                do{
                    let e = NSError(domain: try String(error.message), code: errorCode, userInfo: RNWalletSDK.toErrorUserInfo(error))
                    reject(String(error.code.rawValue), error.message, e)
                }catch{
                    let e = NSError(domain: "failed to get error.message", code: errorCode, userInfo: nil)
                    reject(String(errorCode), "failed to get error.message", e)
                }
            }
        }
    }

    @objc public func getKycShareToken(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock){
        Auth.shared.getKycShareToken() { result in
            switch(result) {
            case .success(let data):
                let dict = RNWalletSDK.toDict(data)
                resolve(dict)
            case .failure(let error):
                let errorCode = error.code.rawValue
                do{
                    let e = NSError(domain: try String(error.message), code: errorCode, userInfo: RNWalletSDK.toErrorUserInfo(error))
                    reject(String(error.code.rawValue), error.message, e)
                }catch{
                    let e = NSError(domain: "failed to get error.message", code: errorCode, userInfo: nil)
                    reject(String(errorCode), "failed to get error.message", e)
                }
            }
        }
    }


    @objc public func checkKycSetting(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock){
        Auth.shared.checkKycSetting() { result in
            switch(result) {
            case .success(let data):
                let dict = RNWalletSDK.toDict(data)
                resolve(dict)
            case .failure(let error):
                let errorCode = error.code.rawValue
                do{
                    let e = NSError(domain: try String(error.message), code: errorCode, userInfo: RNWalletSDK.toErrorUserInfo(error))
                    reject(String(error.code.rawValue), error.message, e)
                }catch{
                    let e = NSError(domain: "failed to get error.message", code: errorCode, userInfo: nil)
                    reject(String(errorCode), "failed to get error.message", e)
                }
            }
        }
    }


    @objc public func getApplicantStatus(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock){
        Auth.shared.getApplicantStatus() { result in
            switch(result) {
            case .success(let data):
                let dict = RNWalletSDK.toDict(data)
                resolve(dict)
            case .failure(let error):
                let errorCode = error.code.rawValue
                do{
                    let e = NSError(domain: try String(error.message), code: errorCode, userInfo: RNWalletSDK.toErrorUserInfo(error))
                    reject(String(error.code.rawValue), error.message, e)
                }catch{
                    let e = NSError(domain: "failed to get error.message", code: errorCode, userInfo: nil)
                    reject(String(errorCode), "failed to get error.message", e)
                }
            }
        }
    }

    @objc public func searchUser(_ keyword: String, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
        Auth.shared.searchUser(keyword: keyword){result in
            switch(result) {
            case .success(let data):
                let dict = RNWalletSDK.toDict(data.infos)
                let newdict: [String: Any] = [
                    "infos": dict,
                ]
                resolve(newdict)
            case .failure(let error):
                let errorCode = error.code.rawValue
                do{
                    let e = NSError(domain: try String(error.message), code: errorCode, userInfo: RNWalletSDK.toErrorUserInfo(error))
                    reject(String(error.code.rawValue), error.message, e)
                }catch{
                    let e = NSError(domain: "failed to get error.message", code: errorCode, userInfo: nil)
                    reject(String(errorCode), "failed to get error.message", e)
                }
            }

        }
    }

    @objc public func registerReferralCode(_ code: String, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
        Auth.shared.registerReferralCode(code: code){result in
            switch(result) {
            case .success(let data):
                let dict = RNWalletSDK.toDict(data)
                resolve(dict)
            case .failure(let error):
                let errorCode = error.code.rawValue
                do{
                    let e = NSError(domain: try String(error.message), code: errorCode, userInfo: RNWalletSDK.toErrorUserInfo(error))
                    reject(String(error.code.rawValue), error.message, e)
                }catch{
                    let e = NSError(domain: "failed to get error.message", code: errorCode, userInfo: nil)
                    reject(String(errorCode), "failed to get error.message", e)
                }
            }

        }
    }

    @objc public func updateRealName(_ realName: String, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
        Auth.shared.updateRealName(realName: realName){result in
            switch(result) {
            case .success(let data):
                let dict = RNWalletSDK.toDict(data)
                resolve(dict)
            case .failure(let error):
                let errorCode = error.code.rawValue
                do{
                    let e = NSError(domain: try String(error.message), code: errorCode, userInfo: RNWalletSDK.toErrorUserInfo(error))
                    reject(String(error.code.rawValue), error.message, e)
                }catch{
                    let e = NSError(domain: "failed to get error.message", code: errorCode, userInfo: nil)
                    reject(String(errorCode), "failed to get error.message", e)
                }
            }

        }
    }

    @objc public func revokeUserWithNoPin(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock){
        Auth.shared.revokeUser() { result in
            switch(result) {
            case .success(let data):
                let dict = RNWalletSDK.toDict(data)
                resolve(dict)
            case .failure(let error):
                let errorCode = error.code.rawValue
                do{
                    let e = NSError(domain: try String(error.message), code: errorCode, userInfo: RNWalletSDK.toErrorUserInfo(error))
                    reject(String(error.code.rawValue), error.message, e)
                }catch{
                    let e = NSError(domain: "failed to get error.message", code: errorCode, userInfo: nil)
                    reject(String(errorCode), "failed to get error.message", e)
                }
            }
        }
    }

    @objc public func revokeUser(_ pinCode: String, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock){
        Auth.shared.revokeUser(pinCode: pinCode) { result in
            switch(result) {
            case .success(let data):
                let dict = RNWalletSDK.toDict(data)
                resolve(dict)
            case .failure(let error):
                let errorCode = error.code.rawValue
                do{
                    let e = NSError(domain: try String(error.message), code: errorCode, userInfo: RNWalletSDK.toErrorUserInfo(error))
                    reject(String(error.code.rawValue), error.message, e)
                }catch{
                    let e = NSError(domain: "failed to get error.message", code: errorCode, userInfo: nil)
                    reject(String(errorCode), "failed to get error.message", e)
                }
            }
        }
    }

    @objc public func revokeUserWithPinSecret(_ byPinSecret: NSDictionary, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock){
        guard let pinSecret = PinSecretBridge.fromDictionary(dict: byPinSecret) else{
            reject("0", "PinSecret not found", NSError(domain: "", code: 0, userInfo: nil))
            return
        }

        Auth.shared.revokeUser(pinSecret: pinSecret) { result in
            switch(result) {
            case .success(let data):
                let dict = RNWalletSDK.toDict(data)
                resolve(dict)
            case .failure(let error):
                let errorCode = error.code.rawValue
                do{
                    let e = NSError(domain: try String(error.message), code: errorCode, userInfo: RNWalletSDK.toErrorUserInfo(error))
                    reject(String(error.code.rawValue), error.message, e)
                }catch{
                    let e = NSError(domain: "failed to get error.message", code: errorCode, userInfo: nil)
                    reject(String(errorCode), "failed to get error.message", e)
                }
            }
        }
    }

    @objc public func validatePinCode(_ pinCode: String, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock){
        Auth.shared.validatePinCode(currentPinCode: pinCode) { result in
            switch(result) {
            case .success(let data):
                let dict = RNWalletSDK.toDict(data)
                resolve(dict)
            case .failure(let error):
                let errorCode = error.code.rawValue
                do{
                    let e = NSError(domain: try String(error.message), code: errorCode, userInfo: RNWalletSDK.toErrorUserInfo(error))
                    reject(String(error.code.rawValue), error.message, e)
                }catch{
                    let e = NSError(domain: "failed to get error.message", code: errorCode, userInfo: nil)
                    reject(String(errorCode), "failed to get error.message", e)
                }
            }
        }
    }

    @objc public func validatePinCodeWithPinSecret(_ byPinSecret: NSDictionary, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock){
        guard let pinSecret = PinSecretBridge.fromDictionary(dict: byPinSecret) else{
            reject("0", "PinSecret not found", NSError(domain: "", code: 0, userInfo: nil))
            return
        }

        Auth.shared.validatePinCode(currentPinSecret: pinSecret) { result in
            switch(result) {
            case .success(let data):
                let dict = RNWalletSDK.toDict(data)
                resolve(dict)
            case .failure(let error):
                let errorCode = error.code.rawValue
                do{
                    let e = NSError(domain: try String(error.message), code: errorCode, userInfo: RNWalletSDK.toErrorUserInfo(error))
                    reject(String(error.code.rawValue), error.message, e)
                }catch{
                    let e = NSError(domain: "failed to get error.message", code: errorCode, userInfo: nil)
                    reject(String(errorCode), "failed to get error.message", e)
                }
            }
        }
    }
}

extension RNAuth : SignInStateDelegate {
    func onUserStateChanged(state: SignInState) {
        var newState: RNSignInState
        switch state {
        case .SignedIn:
            newState = .SIGNED_IN
        case .SignedOut:
            newState = .SIGNED_OUT
        case .SessionExpired:
            newState = .SESSION_EXPIRED
        case .SessionInvalid:
            newState = .SESSION_INVALID
        case .Unknown:
            newState = .UNKNOWN
        case .needVerifyOtp:
            newState = .SIGNED_IN
            EventEmitter.sharedInstance.dispatch(name: "onSignInStateChanged", body: RNSignInState.NEED_VERIFY_OTP.rawValue)
            return
        case .needRegisterPhone:
            EventEmitter.sharedInstance.dispatch(name: "onSignInStateChanged", body: RNSignInState.NEED_REGISTER_PHONE.rawValue)
            newState = .SIGNED_IN
            return
        @unknown default:
            newState = .UNKNOWN
        }

        EventEmitter.sharedInstance.dispatch(name: "onSignInStateChanged", body: newState.rawValue)
    }
}

extension RNAuth {
    @objc func constantsToExport() -> [AnyHashable : Any]! {
        var constants = [AnyHashable : Any]()
        var signInState = [String : Any]()
        for state in RNSignInState.allCases {
            signInState[state.name] = state.rawValue
        }
        constants["SignInState"] = signInState

        let events: [AnyHashable : Any] = [
            "onSignInStateChanged": "onSignInStateChanged",
        ]
        constants["Events"] = events
        return constants
    }
    @objc static func requiresMainQueueSetup() -> Bool {
        return true
    }
}
