import Foundation
import React
import FaceLiveness

private struct CafFaceLivenessEvents {
    static let faceLivenessSuccessEvent: String = "FaceLiveness_Success"
    static let faceLivenessErrorEvent: String = "FaceLiveness_Error"
    static let faceLivenessFailureEvent: String = "FaceLiveness_Failure"
    static let faceLivenessCancelledEvent: String = "FaceLiveness_Cancelled"
    static let faceLivenessLoadingEvent: String = "FaceLiveness_Loading"
    static let faceLivenessLoadedEvent: String = "FaceLiveness_Loaded"
}

private struct CafMessageExceptions {
    static let tokenException: String = "Internal Server Error"
    static let cameraException: String = "Camera Permission Denied"
    static let networkException: String = "Service Unavailable"
    static let serverException: String = "Internal Server Error"
    static let unsupportedException: String = "Device Not Supported"
    static let unknownException: String = "Unknown Error: contact support"
}

private struct CafTypeExceptions {
    static let tokenException: String = "TokenException"
    static let cameraException: String = "CameraPermission"
    static let networkException: String = "NetworkException"
    static let serverException: String = "ServerException"
    static let unsupportedException: String = "UnsupportedDevice"
    static let unknownException: String = "UnknownException"
    static let certificateException: String = "CertificateException"
    static let captureAlreadyActiveException: String = "CaptureAlreadyActiveException"
    static let unexpectedErrorException: String = "UnexpectedErrorException"
    static let userTimeoutException: String = "UserTimeoutException"
    static let imageNotFoundException: String = "ImageNotFoundException"
    static let tooManyRequestsException: String = "TooManyRequestsException"
    
}

private struct CafMutableDictonaries {
    static let signedResponse: String = "signedResponse"
    static let message: String = "message"
    static let type: String = "type"
}

@objc(CafFaceLiveness)
class CafFaceLiveness: RCTEventEmitter {
    private var faceLiveness: FaceLivenessSDK? = nil
    
    static let shared = CafFaceLiveness()

    @objc
    override static func requiresMainQueueSetup() -> Bool {
        return true
    }

    @objc(startFaceLiveness:personId:config:)
    public func startFaceLiveness(token: String, personId: String, config: String) {
        let config = CafFaceLivenessConfig(config: config)

        DispatchQueue.main.async {
            let builder = FaceLivenessSDK.Build()
            
            if let cafStage = config.getCafStage {
                _ = builder.setStage(stage: cafStage)
            }
            
            if let filter = config.getFilter {
                _ = builder.setFilter(filter: filter)
            }
            
            if let loadingScreen = config.getLoadingScreen {
                _ = builder.setLoadingScreen(withLoading: loadingScreen)
            }
            
            if let imageUrlExpirationTime = config.getImageUrlExpirationTime {
                _ = builder.setImageUrlExpirationTime(time: imageUrlExpirationTime)
            }
            
            if let faceLivenessBaseUrl = config.getFaceLivenessBaseUrl {
                _ = builder.setFaceLivenessBaseUrl(livenessBaseUrl: faceLivenessBaseUrl)
            }
            
            if let authenticationBaseUrl = config.getAuthenticationBaseUrl {
                _ = builder.setAuthenticationBaseUrl(authBaseUrl: authenticationBaseUrl)
            }
            
            if let certificates = config.getCertificates {
                _ = builder.setCertificates(certificates: certificates)
            }
                
            self.faceLiveness = builder.build()
            self.faceLiveness?.delegate = self
            
            guard let rootViewController = UIApplication.shared.windows.first(where: { $0.isKeyWindow })?.rootViewController else {
                return
            }
            
            var currentViewController = rootViewController;
            
            while let presentedViewController = currentViewController.presentedViewController {
                currentViewController = presentedViewController
            }
            
            self.faceLiveness?.startSDK(viewController: currentViewController, mobileToken: token, personId: personId, debugEnabled: true)
        }
    }

    override func supportedEvents() -> [String]! {
        return [
            CafFaceLivenessEvents.faceLivenessSuccessEvent,
            CafFaceLivenessEvents.faceLivenessErrorEvent,
            CafFaceLivenessEvents.faceLivenessFailureEvent,
            CafFaceLivenessEvents.faceLivenessCancelledEvent,
            CafFaceLivenessEvents.faceLivenessLoadingEvent,
            CafFaceLivenessEvents.faceLivenessLoadedEvent
        ]
    }
}

extension CafFaceLiveness: FaceLivenessDelegate {
    func didFinishLiveness(with livenessResult: FaceLiveness.LivenessResult) {
        let response : NSMutableDictionary = [:]
        response[CafMutableDictonaries.signedResponse] = livenessResult.signedResponse
        sendEvent(withName: CafFaceLivenessEvents.faceLivenessSuccessEvent, body: response)
        faceLiveness = nil
    }
    
    func didFinishWithCancelled() {
        sendEvent(withName: CafFaceLivenessEvents.faceLivenessCancelledEvent, body: true)
        faceLiveness = nil
    }
    
    func didFinishWithFailure(with sdkFailure: FaceLiveness.SDKFailure) {
        let response : NSMutableDictionary = [:]
        response[CafMutableDictonaries.message] = sdkFailure.description
        response[CafMutableDictonaries.type] = sdkFailure.failureType
        response[CafMutableDictonaries.signedResponse] = sdkFailure.signedResponse
        sendEvent(withName: CafFaceLivenessEvents.faceLivenessFailureEvent, body: response)
    }
    
    func didFinishWithError(with sdkError: FaceLiveness.SDKError) {
        switch(sdkError.errorType) {
        case .tokenException:
            sendErrorEvent(message: CafMessageExceptions.tokenException, type: CafTypeExceptions.tokenException)
        case .cameraPermission:
            sendErrorEvent(message: CafMessageExceptions.cameraException, type: CafTypeExceptions.cameraException)
        case .networkException:
            sendErrorEvent(message: CafMessageExceptions.networkException, type: CafTypeExceptions.networkException)
        case .serverException:
            sendErrorEvent(message: CafMessageExceptions.serverException, type: CafTypeExceptions.serverException)
        case .unsupportedDevice:
            sendErrorEvent(message: CafMessageExceptions.unsupportedException, type: CafTypeExceptions.unsupportedException)
        case .certificateException:
            sendErrorEvent(message: sdkError.description ?? "", type: CafTypeExceptions.certificateException)
        case .captureAlreadyActiveException:
            sendErrorEvent(message: sdkError.description ?? "", type: CafTypeExceptions.captureAlreadyActiveException)
        case .unexpectedErrorException:
            sendErrorEvent(message: sdkError.description ?? "", type: CafTypeExceptions.unexpectedErrorException)
        case .userTimeoutException:
            sendErrorEvent(message: sdkError.description ?? "", type: CafTypeExceptions.userTimeoutException)
        case .imageNotFoundException:
            sendErrorEvent(message: sdkError.description ?? "", type: CafTypeExceptions.imageNotFoundException)
        case .tooManyRequestsException:
            sendErrorEvent(message: sdkError.description ?? "", type: CafTypeExceptions.tooManyRequestsException)
        default:
            sendErrorEvent(message: CafMessageExceptions.unknownException, type: CafTypeExceptions.unknownException)
        }
        faceLiveness = nil
    }
    
    func openLoadingScreenStartSDK() {
        sendEvent(withName: CafFaceLivenessEvents.faceLivenessLoadingEvent, body: true)
    }
    
    func closeLoadingScreenStartSDK() {
        
    }
    
    func openLoadingScreenValidation() {
        
    }
    
    func closeLoadingScreenValidation() {
        sendEvent(withName: CafFaceLivenessEvents.faceLivenessLoadedEvent, body: true)
    }
    
    func onConnectionChanged(_ state: FaceLiveness.LivenessState) {
        
    }
    
    private func sendErrorEvent(message: String, type: String) {
        let response : NSMutableDictionary = [:]
        response[CafMutableDictonaries.message] = message
        response[CafMutableDictonaries.type] = type
        sendEvent(withName: CafFaceLivenessEvents.faceLivenessErrorEvent, body: response)
    }
}
