#import "UqudoId.h"
#import <UqudoSDK/UqudoSDK.h>
#import "MyTracer.h"

@implementation UqudoId

MyTracer *tracer;

RCT_EXPORT_MODULE()

RCT_EXPORT_METHOD(init:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
    tracer = [[MyTracer alloc] init];
    tracer.emitter = self;
    dispatch_async(dispatch_get_main_queue(), ^(void) {
        [[UQBuilderController alloc] initWithTracer:tracer];
    });
}

RCT_EXPORT_METHOD(setLocale:(NSString *)locale resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
    [[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:locale, nil]forKey:@"AppleLanguages"];
    [[NSUserDefaults standardUserDefaults] synchronize];
}

RCT_EXPORT_METHOD(enroll:(NSString *)enrollObj resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
    self.promiseResolve = resolve;
    self.promiseReject = reject;

    @try {
        if (enrollObj != nil && [enrollObj length] > 0) {
            UQBuilderController *builderController = [UQBuilderController defaultBuilder];
            dispatch_sync(dispatch_get_main_queue(), ^{
                NSData* data = [enrollObj dataUsingEncoding:NSUTF8StringEncoding];
                id json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];

                NSDictionary* documentList = json[@"documentList"];
                NSDictionary* facialRecognitionSpecification = json[@"facialRecognitionSpecification"];
                NSDictionary* backgroundCheckConfiguration = json[@"backgroundCheckConfiguration"];
                NSDictionary* lookupConfiguration = json[@"lookupConfiguration"];

                // Config enrollment builder
                UQEnrollmentBuilder *enrollmentBuilder = [[UQEnrollmentBuilder alloc] init];
                enrollmentBuilder.authorizationToken = json[@"authorizationToken"];
                NSString* nonce = json[@"nonce"];
                if (nonce && nonce.length > 0) {
                    enrollmentBuilder.nonce = nonce;
                }
                NSString* sessionId = json[@"sessionId"];
                if (sessionId && sessionId.length > 0) {
                    [enrollmentBuilder setSessionID:sessionId];
                }
                NSString* userIdentifier = json[@"userIdentifier"];
                if (userIdentifier && userIdentifier.length > 0) {
                    NSUUID *userID = [[NSUUID alloc] initWithUUIDString:userIdentifier];
                    if (userID) {
                        [enrollmentBuilder setUserIdentifier:userID];
                    }
                }
                if (json[@"isReturnDataForIncompleteSession"]) {
                    enrollmentBuilder.returnDataForIncompleteSession = [[json valueForKey:@"isReturnDataForIncompleteSession"] boolValue];
                }
                if(json[@"isAllowNonPhysicalDocuments"]){
                    enrollmentBuilder.allowNonPhysicalDocuments = [[json valueForKey:@"isAllowNonPhysicalDocuments"] boolValue];
                }
                if (json[@"isDisableTamperingRejection"]) {
                    enrollmentBuilder.disableTamperingRejection = [[json valueForKey:@"isDisableTamperingRejection"] boolValue];
                }
                if (facialRecognitionSpecification) {
                    // Enable help page for face recognition
                    UQFacialRecognitionConfig *config = [[UQFacialRecognitionConfig alloc] init];
                    config.enableFacialRecognition = YES;

                    // Enable enroll face option
                    if (facialRecognitionSpecification[@"enrollFace"]) {
                        config.enrollFace = [[facialRecognitionSpecification valueForKey:@"enrollFace"] boolValue];
                    }

                    if (facialRecognitionSpecification[@"scanMinimumMatchLevel"]) {
                        config.scanMinimumMatchLevel = [[facialRecognitionSpecification valueForKey:@"scanMinimumMatchLevel"] integerValue];
                    }

                    if (facialRecognitionSpecification[@"readMinimumMatchLevel"]) {
                        config.readMinimumMatchLevel = [[facialRecognitionSpecification valueForKey:@"readMinimumMatchLevel"] integerValue];
                    }
                    if (facialRecognitionSpecification[@"maxAttempts"]) {
                        int maxAttempts = [[facialRecognitionSpecification valueForKey:@"maxAttempts"] intValue];
                        if (maxAttempts > 0) {
                            config.maxAttempts = maxAttempts;
                        }
                    }
                    if (facialRecognitionSpecification[@"allowClosedEyes"]) {
                        config.allowClosedEyes = [[facialRecognitionSpecification valueForKey:@"allowClosedEyes"] boolValue];
                    }
                    if (facialRecognitionSpecification[@"obfuscationType"]) {
                        if ([facialRecognitionSpecification[@"obfuscationType"]  isEqualToString:@"BLURRED"]) {
                            config.obfuscationType = BLURRED;
                        } else if ([facialRecognitionSpecification[@"obfuscationType"]  isEqualToString:@"FILLED"]) {
                            config.obfuscationType = FILLED;
                        } else if ([facialRecognitionSpecification[@"obfuscationType"]  isEqualToString:@"FILLED_WHITE"]) {
                            config.obfuscationType = FILLED_WHITE;
                        }
                    }

                    if (facialRecognitionSpecification[@"isOneToNVerificationEnabled"]) {
                        config.enableOneToNVerification = [[facialRecognitionSpecification valueForKey:@"isOneToNVerificationEnabled"] boolValue];
                    }

                    if (facialRecognitionSpecification[@"enableActiveLiveness"] && [facialRecognitionSpecification[@"enableActiveLiveness"] boolValue]) {
                        config.enableActiveLiveness = YES;
                    }

                    if (facialRecognitionSpecification[@"disableLivenessGesture"]) {
                        NSString *gesture = facialRecognitionSpecification[@"disableLivenessGesture"];
                        if ([gesture isEqualToString:@"FACE_MOVE"]) {
                            config.disableLivenessGesture = FACE_MOVE;
                        } else if ([gesture isEqualToString:@"FACE_TILT"]) {
                            config.disableLivenessGesture = FACE_TILT;
                        } else if ([gesture isEqualToString:@"FACE_TURN"]) {
                            config.disableLivenessGesture = FACE_TURN;
                        }
                    }

                    enrollmentBuilder.facialRecognitionConfig = config;
                }

                if(lookupConfiguration){
                    NSMutableArray *lookupDocumentTypes = [[NSMutableArray alloc] init];
                    if(lookupConfiguration[@"documentList"]){
                        for (NSString *document in lookupConfiguration[@"documentList"]) {
                            UQDocumentConfig *documentConfig = [[UQDocumentConfig alloc] initWithDocumentTypeName:document];
                            [lookupDocumentTypes addObject:[NSNumber numberWithInteger:[documentConfig documentType]]];
                        }
                    }
                    if (lookupDocumentTypes.count) {
                        [enrollmentBuilder enableLookup:lookupDocumentTypes];
                    }
                    else {
                        [enrollmentBuilder enableLookup];
                    }
                }
                if (backgroundCheckConfiguration) {
                    BOOL isDisableConsent = FALSE;
                    BackgroundCheckType type = RDC;
                    if (backgroundCheckConfiguration[@"disableConsent"]) {
                        isDisableConsent = [[backgroundCheckConfiguration valueForKey:@"disableConsent"] boolValue];
                    }
                    if (backgroundCheckConfiguration[@"backgroundCheckType"]) {
                        if ([backgroundCheckConfiguration[@"backgroundCheckType"] isEqual:@"RDC"]) {
                            type = RDC;
                        } else {
                            type = DOW_JONES;
                        }
                    }
                    BOOL isMonitoringEnabled = FALSE;
                    if (backgroundCheckConfiguration[@"monitoringEnabled"]) {
                        isMonitoringEnabled = [[backgroundCheckConfiguration valueForKey:@"monitoringEnabled"] boolValue];
                    }
                    BOOL isSkipView = FALSE;
                    if (backgroundCheckConfiguration[@"skipView"]) {
                        isSkipView = [[backgroundCheckConfiguration valueForKey:@"skipView"] boolValue];
                    }
                    [enrollmentBuilder enableBackgroundCheck:isDisableConsent type:type monitoring:isMonitoringEnabled skipView:isSkipView];
                }
                for (NSDictionary *document in documentList) {
                    NSString* documentType = document[@"documentType"];
                    UQDocumentConfig *documentObject = [[UQDocumentConfig alloc] initWithDocumentTypeName:documentType];
                    if(documentObject.documentType == UNSPECIFY){
                        continue;
                    }

                    UQScanConfig *scanConfig = [[UQScanConfig alloc] init];
                    if (document[@"isHelpPageDisabled"]) {
                        scanConfig.disableHelpPage = [[document valueForKey:@"isHelpPageDisabled"] boolValue];
                    }
                    if (document[@"faceScanMinimumMatchLevel"]) {
                        scanConfig.faceMinimumMatchLevel = [[document valueForKey:@"faceScanMinimumMatchLevel"] intValue];
                    }
                    if (document[@"minimumAge"]) {
                        int minimumAge = [[document valueForKey:@"minimumAge"] intValue];
                        if (minimumAge > 0) {
                            documentObject.enableAgeVerification = minimumAge;
                        }
                    }

                    BOOL isEnableFrontSideReview = false;
                    BOOL isEnableBackSideReview = false;

                    if (document[@"isFrontSideReviewEnabled"]) {
                        isEnableFrontSideReview = [[document valueForKey:@"isFrontSideReviewEnabled"] boolValue];
                    }
                    if (document[@"isBackSideReviewEnabled"]) {
                        isEnableBackSideReview = [[document valueForKey:@"isBackSideReviewEnabled"] boolValue];
                    }
                    [scanConfig enableScanReview:isEnableFrontSideReview backSide:isEnableBackSideReview];
                    if (document[@"isUploadEnabled"]) {
                        scanConfig.enableUpload = [[document valueForKey:@"isUploadEnabled"] boolValue];
                    }
                    documentObject.scan = scanConfig;

                    if (document[@"isExpiredDocumentValidateDisabled"]) {
                        documentObject.disableExpiryValidation = [[document valueForKey:@"isExpiredDocumentValidateDisabled"] boolValue];
                    }

                    if (document[@"isUserDataReviewDisabled"]) {
                        documentObject.disableUserDataReview = [[document valueForKey:@"isUserDataReviewDisabled"] boolValue];
                    }

                    if (document[@"readingConfiguration"]) {
                        NSDictionary *readingConfiguration = document[@"readingConfiguration"];

                        UQReadingConfig *readConfig = [[UQReadingConfig alloc] init];
                        readConfig.enableReading = TRUE;
                        if (readingConfiguration[@"forceReading"]) {
                            [readConfig forceReading:[[readingConfiguration valueForKey:@"forceReading"] boolValue]];
                        }
                        if (readingConfiguration[@"forceReadingIfSupported"]) {
                            [readConfig forceReadingIfSupported:[[readingConfiguration valueForKey:@"forceReadingIfSupported"] boolValue]];
                        }
                        if (document[@"faceReadMinimumMatchLevel"]) {
                            readConfig.faceMinimumMatchLevel = [[document valueForKey:@"faceReadMinimumMatchLevel"] intValue];
                        }
                        if (readingConfiguration[@"timeoutInSeconds"]) {
                            int timeoutInSeconds = [[readingConfiguration valueForKey:@"timeoutInSeconds"] intValue];
                            if (timeoutInSeconds > 0) {
                                readConfig.forceReadingTimeout = timeoutInSeconds;
                            }
                        }

                        documentObject.reading = readConfig;
                    }

                    [enrollmentBuilder add:documentObject];
                }
                builderController.delegate = self;
                UIViewController *rootViewController = [UIApplication sharedApplication].delegate.window.rootViewController;
                builderController.appViewController = rootViewController;
                [builderController setEnrollment:enrollmentBuilder];
                NSInteger appearanceMode = SYSTEM;
                if (json[@"appearanceMode"]) {
                    if ([json[@"appearanceMode"] isEqualToString:@"LIGHT"]) {
                        appearanceMode = LIGHT;
                    } else if ([json[@"appearanceMode"] isEqualToString:@"DARK"]) {
                        appearanceMode = DARK;
                    }
                }
                [builderController setAppearanceMode:appearanceMode];
            });
            [builderController performEnrollment];
        } else {
            UQSessionStatus *status =[[UQSessionStatus alloc] init];
            status.statusCode = UNEXPECTED_ERROR;
            status.message = @"Expected enrollment object as argument.";
            status.statusTask = -1;
            [self sendPluginError:status];
        }
    }
    @catch (NSException *exception) {
        NSLog(@"%@", exception.callStackSymbols);
        UQSessionStatus *status =[[UQSessionStatus alloc] init];
        status.statusCode = UNEXPECTED_ERROR;
        status.message = exception.reason;
        status.statusTask = -1;
        [self sendPluginError:status];
    }
}

- (void) didEnrollmentCompleteWithInfo:(NSString *)jwsString {
    NSMutableDictionary *jsonResults = [[NSMutableDictionary alloc]init];
    [jsonResults setValue:jwsString forKey:@"result"];
    self.promiseResolve(jsonResults);
}

- (void) didEnrollmentIncompleteWithStatus:(UQSessionStatus *)status {
    [self sendPluginError:status];
}

- (void)didEnrollmentFailWithError:(NSError *)error {
    // IGNORE, deprecated in the SDK
}

RCT_EXPORT_METHOD(recover:(NSString *)recoverObj resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
    self.promiseResolve = resolve;
    self.promiseReject = reject;
    @try {
        if (recoverObj != nil && [recoverObj length] > 0) {
            UQBuilderController *builderController = [UQBuilderController defaultBuilder];
            dispatch_sync(dispatch_get_main_queue(), ^{
                NSData *data = [recoverObj dataUsingEncoding:NSUTF8StringEncoding];
                id json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];

                UQAccountRecoveryBuilder *accountRecoveryBuilder = [[UQAccountRecoveryBuilder alloc] init];
                accountRecoveryBuilder.authorizationToken = json[@"token"];
                accountRecoveryBuilder.enrollmentIdentifier = json[@"enrollmentIdentifier"];
                accountRecoveryBuilder.nonce = json[@"nonce"];
                if (json[@"minimumMatchLevel"]) {
                    accountRecoveryBuilder.minimumMatchLevel = [[json valueForKey:@"minimumMatchLevel"] intValue];
                }

                if (json[@"maxAttempts"]) {
                    int maxAttempts = [[json valueForKey:@"maxAttempts"] intValue];
                    if (maxAttempts > 0) {
                        accountRecoveryBuilder.maxAttempts = maxAttempts;
                    }
                }

                if (json[@"allowClosedEyes"]) {
                    accountRecoveryBuilder.allowClosedEyes = [[json valueForKey:@"allowClosedEyes"] boolValue];
                }

                if (json[@"isReturnDataForIncompleteSession"]) {
                    accountRecoveryBuilder.returnDataForIncompleteSession = [[json valueForKey:@"isReturnDataForIncompleteSession"] boolValue];
                }

                builderController.delegate = self;
                UIViewController *rootViewController = [UIApplication sharedApplication].delegate.window.rootViewController;
                builderController.appViewController = rootViewController;
                [builderController setAccountRecovery:accountRecoveryBuilder];

                NSInteger appearanceMode = SYSTEM;
                if (json[@"appearanceMode"]) {
                    if ([json[@"appearanceMode"] isEqualToString:@"LIGHT"]) {
                        appearanceMode = LIGHT;
                    } else if ([json[@"appearanceMode"] isEqualToString:@"DARK"]) {
                        appearanceMode = DARK;
                    }
                }
                [builderController setAppearanceMode:appearanceMode];
            });
            [builderController performAccountRecovery];
        } else {
            UQSessionStatus *status =[[UQSessionStatus alloc] init];
            status.statusCode = UNEXPECTED_ERROR;
            status.message = @"Expected account recovery object as argument.";
            status.statusTask = -1;
            [self sendPluginError:status];
        }
    }
    @catch (NSException *exception) {
        NSLog(@"%@", exception.callStackSymbols);
        UQSessionStatus *status =[[UQSessionStatus alloc] init];
        status.statusCode = UNEXPECTED_ERROR;
        status.message = exception.reason;
        status.statusTask = -1;
        [self sendPluginError:status];
    }
}

- (void)didAccountRecoveryCompleteWithInfo:(nonnull NSString *)jwsString {
    NSMutableDictionary *jsonResults = [[NSMutableDictionary alloc]init];
    [jsonResults setValue:jwsString forKey:@"result"];
    self.promiseResolve(jsonResults);
}

- (void)didAccountRecoveryIncompleteWithStatus:(UQSessionStatus *)status {
    [self sendPluginError:status];
}

- (void)didAccountRecoveryFailWithError:(nonnull NSError *)error {
    // IGNORE, deprecated in the SDK
}

RCT_EXPORT_METHOD(faceSession:(NSString *)configuration resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
    self.promiseResolve = resolve;
    self.promiseReject = reject;
    @try {
        if (configuration != nil && [configuration length] > 0) {
            UQBuilderController *builderController = [UQBuilderController defaultBuilder];
            dispatch_sync(dispatch_get_main_queue(), ^{
                NSData *data = [configuration dataUsingEncoding:NSUTF8StringEncoding];
                id json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];

                UQFaceSessionBuilder *faceSessionBuilder = [[UQFaceSessionBuilder alloc] init];
                faceSessionBuilder.authorizationToken = json[@"token"];
                faceSessionBuilder.sessionId = json[@"sessionId"];
                faceSessionBuilder.nonce = json[@"nonce"];

                NSString* userIdentifier = json[@"userIdentifier"];
                if (userIdentifier && userIdentifier.length > 0) {
                    NSUUID *userID = [[NSUUID alloc] initWithUUIDString:userIdentifier];
                    if (userID) {
                        faceSessionBuilder.userIdentifier = userID;
                    }
                }

                if (json[@"minimumMatchLevel"]) {
                    faceSessionBuilder.minimumMatchLevel = [[json valueForKey:@"minimumMatchLevel"] intValue];
                }

                if (json[@"maxAttempts"]) {
                    int maxAttempts = [[json valueForKey:@"maxAttempts"] intValue];
                    if (maxAttempts > 0) {
                        faceSessionBuilder.maxAttempts = maxAttempts;
                    }
                }

                if (json[@"allowClosedEyes"]) {
                    faceSessionBuilder.allowClosedEyes = [[json valueForKey:@"allowClosedEyes"] boolValue];
                }

                if (json[@"isReturnDataForIncompleteSession"]) {
                    faceSessionBuilder.returnDataForIncompleteSession = [[json valueForKey:@"isReturnDataForIncompleteSession"] boolValue];
                }
                if (json[@"obfuscationType"]) {
                    if ([json[@"obfuscationType"]  isEqualToString:@"BLURRED"]) {
                        [faceSessionBuilder enableAuditTrailImageObfuscation:BLURRED];
                    } else if ([json[@"obfuscationType"]  isEqualToString:@"FILLED"]) {
                        [faceSessionBuilder enableAuditTrailImageObfuscation:FILLED];
                    } else if ([json[@"obfuscationType"]  isEqualToString:@"FILLED_WHITE"]) {
                        [faceSessionBuilder enableAuditTrailImageObfuscation:FILLED_WHITE];
                    }
                }

                if (json[@"enableActiveLiveness"] && [json[@"enableActiveLiveness"] boolValue]) {
                    faceSessionBuilder.enableActiveLiveness = YES;
                }

                if (json[@"disableLivenessGesture"]) {
                    NSString *gesture = json[@"disableLivenessGesture"];
                    if ([gesture isEqualToString:@"FACE_MOVE"]) {
                        faceSessionBuilder.disableLivenessGesture = FACE_MOVE;
                    } else if ([gesture isEqualToString:@"FACE_TILT"]) {
                        faceSessionBuilder.disableLivenessGesture = FACE_TILT;
                    } else if ([gesture isEqualToString:@"FACE_TURN"]) {
                        faceSessionBuilder.disableLivenessGesture = FACE_TURN;
                    }
                }

                builderController.delegate = self;
                UIViewController *rootViewController = [UIApplication sharedApplication].delegate.window.rootViewController;
                builderController.appViewController = rootViewController;
                [builderController setFaceSession:faceSessionBuilder];

                NSInteger appearanceMode = SYSTEM;
                if (json[@"appearanceMode"]) {
                    if ([json[@"appearanceMode"] isEqualToString:@"LIGHT"]) {
                        appearanceMode = LIGHT;
                    } else if ([json[@"appearanceMode"] isEqualToString:@"DARK"]) {
                        appearanceMode = DARK;
                    }
                }
                [builderController setAppearanceMode:appearanceMode];
            });
            [builderController performFaceSession];
        } else {
            UQSessionStatus *status =[[UQSessionStatus alloc] init];
            status.statusCode = UNEXPECTED_ERROR;
            status.message = @"Expected face session configuration as argument.";
            status.statusTask = -1;
            [self sendPluginError:status];
        }
    }
    @catch (NSException *exception) {
        NSLog(@"%@", exception.callStackSymbols);
        UQSessionStatus *status =[[UQSessionStatus alloc] init];
        status.statusCode = UNEXPECTED_ERROR;
        status.message = exception.reason;
        status.statusTask = -1;
        [self sendPluginError:status];
    }
}

- (void)didFaceSessionCompleteWithInfo:(nonnull NSString *)jwsString {
    NSMutableDictionary *jsonResults = [[NSMutableDictionary alloc]init];
    [jsonResults setValue:jwsString forKey:@"result"];
    self.promiseResolve(jsonResults);
}

- (void)didFaceSessionIncompleteWithStatus:(UQSessionStatus *)status {
    [self sendPluginError:status];
}

- (void)didFaceSessionFailWithError:(nonnull NSError *)error {
    // IGNORE, deprecated in the SDK
}

RCT_EXPORT_METHOD(lookup:(NSString *)lookupObj resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
    self.promiseResolve = resolve;
    self.promiseReject = reject;

    @try {
        if (lookupObj != nil && [lookupObj length] > 0) {
            UQBuilderController *builderController = [UQBuilderController defaultBuilder];
            dispatch_sync(dispatch_get_main_queue(), ^{
                NSData* data = [lookupObj dataUsingEncoding:NSUTF8StringEncoding];
                id json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];

                UQLookupBuilder *lookupBuilder = [[UQLookupBuilder alloc] init];
                lookupBuilder.authorizationToken = json[@"authorizationToken"];
                NSString* nonce = json[@"nonce"];
                if (nonce && nonce.length > 0) {
                    lookupBuilder.nonce = nonce;
                }
                NSString* documentType = json[@"documentType"];
                UQDocumentConfig *documentConfig = [[UQDocumentConfig alloc] initWithDocumentTypeName:documentType];
                [lookupBuilder setDocumentType:documentConfig.documentType];
                NSString* sessionId = json[@"sessionId"];
                if (sessionId && sessionId.length > 0) {
                    [lookupBuilder setSessionID:sessionId];
                }
                NSString* userIdentifier = json[@"userIdentifier"];
                if (userIdentifier && userIdentifier.length > 0) {
                    NSUUID *userID = [[NSUUID alloc] initWithUUIDString:userIdentifier];
                    if (userID) {
                        [lookupBuilder setUserIdentifier:userID];
                    }
                }
                if (json[@"isReturnDataForIncompleteSession"]) {
                    lookupBuilder.returnDataForIncompleteSession = [[json valueForKey:@"isReturnDataForIncompleteSession"] boolValue];
                }
                NSDictionary* facialRecognitionSpecification = json[@"facialRecognitionSpecification"];
                if (facialRecognitionSpecification) {

                    UQFacialRecognitionConfig *config = [[UQFacialRecognitionConfig alloc] init];
                    // Enable help page for face recognition
                    config.enableFacialRecognition = YES;
                    // Enable enroll face option
                    if (facialRecognitionSpecification[@"enrollFace"]) {
                        config.enrollFace = [[facialRecognitionSpecification valueForKey:@"enrollFace"] boolValue];
                    }
                    if (facialRecognitionSpecification[@"lookupMinimumMatchLevel"]) {
                        config.minimumMatchLevel = [[facialRecognitionSpecification valueForKey:@"lookupMinimumMatchLevel"] integerValue];
                    }
                    if (facialRecognitionSpecification[@"maxAttempts"]) {
                        int maxAttempts = [[facialRecognitionSpecification valueForKey:@"maxAttempts"] intValue];
                        if (maxAttempts > 0) {
                            config.maxAttempts = maxAttempts;
                        }
                    }
                    if (facialRecognitionSpecification[@"allowClosedEyes"]) {
                        config.allowClosedEyes = [[facialRecognitionSpecification valueForKey:@"allowClosedEyes"] boolValue];
                    }
                    if (facialRecognitionSpecification[@"obfuscationType"]) {
                        if ([facialRecognitionSpecification[@"obfuscationType"]  isEqualToString:@"BLURRED"]) {
                            config.obfuscationType = BLURRED;
                        } else if ([facialRecognitionSpecification[@"obfuscationType"]  isEqualToString:@"FILLED"]) {
                            config.obfuscationType = FILLED;
                        } else if ([facialRecognitionSpecification[@"obfuscationType"]  isEqualToString:@"FILLED_WHITE"]) {
                            config.obfuscationType = FILLED_WHITE;
                        }
                    }

                    if (facialRecognitionSpecification[@"isOneToNVerificationEnabled"]) {
                        config.enableOneToNVerification = [[facialRecognitionSpecification valueForKey:@"isOneToNVerificationEnabled"] boolValue];
                    }

                    if (facialRecognitionSpecification[@"enableActiveLiveness"] && [facialRecognitionSpecification[@"enableActiveLiveness"] boolValue]) {
                        config.enableActiveLiveness = YES;
                    }

                    if (facialRecognitionSpecification[@"disableLivenessGesture"]) {
                        NSString *gesture = facialRecognitionSpecification[@"disableLivenessGesture"];
                        if ([gesture isEqualToString:@"FACE_MOVE"]) {
                            config.disableLivenessGesture = FACE_MOVE;
                        } else if ([gesture isEqualToString:@"FACE_TILT"]) {
                            config.disableLivenessGesture = FACE_TILT;
                        } else if ([gesture isEqualToString:@"FACE_TURN"]) {
                            config.disableLivenessGesture = FACE_TURN;
                        }
                    }

                    lookupBuilder.facialRecognitionConfig = config;

                }
                NSDictionary* backgroundCheckConfiguration = json[@"backgroundCheckConfiguration"];
                if (backgroundCheckConfiguration) {
                    BOOL isDisableConsent = FALSE;
                    BackgroundCheckType type = RDC;
                    if (backgroundCheckConfiguration[@"disableConsent"]) {
                        isDisableConsent = [[backgroundCheckConfiguration valueForKey:@"disableConsent"] boolValue];
                    }
                    if (backgroundCheckConfiguration[@"backgroundCheckType"]) {
                        if ([backgroundCheckConfiguration[@"backgroundCheckType"] isEqual:@"RDC"]) {
                            type = RDC;
                        } else {
                            type = DOW_JONES;
                        }
                    }
                    BOOL isMonitoringEnabled = FALSE;
                    if (backgroundCheckConfiguration[@"monitoringEnabled"]) {
                        isMonitoringEnabled = [[backgroundCheckConfiguration valueForKey:@"monitoringEnabled"] boolValue];
                    }
                    BOOL isSkipView = FALSE;
                    if (backgroundCheckConfiguration[@"skipView"]) {
                        isSkipView = [[backgroundCheckConfiguration valueForKey:@"skipView"] boolValue];
                    }
                    [lookupBuilder enableBackgroundCheck:isDisableConsent type:type monitoring:isMonitoringEnabled skipView:isSkipView];
                }
                builderController.delegate = self;
                [builderController setLookup:lookupBuilder];
                UIViewController *rootViewController = [UIApplication sharedApplication].delegate.window.rootViewController;
                builderController.appViewController = rootViewController;
                NSInteger appearanceMode = SYSTEM;
                if (json[@"appearanceMode"]) {
                    if ([json[@"appearanceMode"] isEqualToString:@"LIGHT"]) {
                        appearanceMode = LIGHT;
                    } else if ([json[@"appearanceMode"] isEqualToString:@"DARK"]) {
                        appearanceMode = DARK;
                    }
                }
                [builderController setAppearanceMode:appearanceMode];
            });
            [builderController performLookup];
        } else {
            UQSessionStatus *status =[[UQSessionStatus alloc] init];
            status.statusCode = UNEXPECTED_ERROR;
            status.message = @"Expected lookup object as argument.";
            status.statusTask = -1;
            [self sendPluginError:status];
        }
    }
    @catch (NSException *exception) {
        NSLog(@"%@", exception.callStackSymbols);
        UQSessionStatus *status =[[UQSessionStatus alloc] init];
        status.statusCode = UNEXPECTED_ERROR;
        status.message = exception.reason;
        status.statusTask = -1;
        [self sendPluginError:status];
    }
}

- (void) didLookupFlowCompleteWithInfo:(NSString *)jwsString {
    NSMutableDictionary *jsonResults = [[NSMutableDictionary alloc]init];
    [jsonResults setValue:jwsString forKey:@"result"];
    self.promiseResolve(jsonResults);
}

- (void) didLookupFlowIncompleteWithStatus:(UQSessionStatus *)status {
    [self sendPluginError:status];
}

RCT_EXPORT_METHOD(isReadingSupported:(NSString *)documentType resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
    self.promiseReject = reject;
    @try{
        UQDocumentConfig *documentObject = [[UQDocumentConfig alloc] initWithDocumentTypeName:documentType];
        resolve(@(documentObject.isSupportReading));
    }
    @catch (NSException *exception) {
        UQSessionStatus *status =[[UQSessionStatus alloc] init];
        status.statusCode = UNEXPECTED_ERROR;
        status.message = @"Expected valid document type as argument";
        status.statusTask = -1;
        [self sendPluginError:status];
    }
}

RCT_EXPORT_METHOD(isFacialRecognitionSupported:(NSString *)documentType resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
    self.promiseReject = reject;
    @try{
        UQDocumentConfig *documentObject = [[UQDocumentConfig alloc] initWithDocumentTypeName:documentType];
        resolve(@(documentObject.isSupportFaceRecognition));
    }
    @catch (NSException *exception) {
        UQSessionStatus *status =[[UQSessionStatus alloc] init];
        status.statusCode = UNEXPECTED_ERROR;
        status.message = @"Expected valid document type as argument";
        status.statusTask = -1;
        [self sendPluginError:status];
    }
}

RCT_EXPORT_METHOD(isEnrollmentSupported:(NSString *)documentType resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
    self.promiseReject = reject;
    @try{
        UQDocumentConfig *documentObject = [[UQDocumentConfig alloc] initWithDocumentTypeName:documentType];
        resolve(@(documentObject.isEnrollmentSupported));
    }
    @catch (NSException *exception) {
        UQSessionStatus *status =[[UQSessionStatus alloc] init];
        status.statusCode = UNEXPECTED_ERROR;
        status.message = @"Expected valid document type as argument";
        status.statusTask = -1;
        [self sendPluginError:status];
    }
}

RCT_EXPORT_METHOD(isLookupSupported:(NSString *)documentType resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
    self.promiseReject = reject;
    @try{
        UQDocumentConfig *documentObject = [[UQDocumentConfig alloc] initWithDocumentTypeName:documentType];
        resolve(@(documentObject.isLookupSupported));
    }
    @catch (NSException *exception) {
        UQSessionStatus *status =[[UQSessionStatus alloc] init];
        status.statusCode = UNEXPECTED_ERROR;
        status.message = @"Expected valid document type as argument";
        status.statusTask = -1;
        [self sendPluginError:status];
    }
}

RCT_EXPORT_METHOD(isLookupFacialRecognitionSupported:(NSString *)documentType resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
    self.promiseReject = reject;
    @try{
        UQDocumentConfig *documentObject = [[UQDocumentConfig alloc] initWithDocumentTypeName:documentType];
        resolve(@(documentObject.isLookupFacialRecognitionSupported));
    }
    @catch (NSException *exception) {
        UQSessionStatus *status =[[UQSessionStatus alloc] init];
        status.statusCode = UNEXPECTED_ERROR;
        status.message = @"Expected valid document type as argument";
        status.statusTask = -1;
        [self sendPluginError:status];
    }
}

RCT_EXPORT_METHOD(reading:(NSString *)readingObj resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
    self.promiseResolve = resolve;
    self.promiseReject = reject;

    @try {
        if (readingObj != nil && [readingObj length] > 0) {
            UQBuilderController *builderController = [UQBuilderController defaultBuilder];
            dispatch_sync(dispatch_get_main_queue(), ^{
                NSData *data = [readingObj dataUsingEncoding:NSUTF8StringEncoding];
                NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];

                UQReadingBuilder *readingBuilder = [[UQReadingBuilder alloc] init];
                readingBuilder.delegate = self;

                if (json[@"authorizationToken"]) {
                    readingBuilder.authorizationToken = json[@"authorizationToken"];
                }

                if (json[@"sessionId"]) {
                    readingBuilder.sessionID = json[@"sessionId"];
                }

                NSString* userIdentifier = json[@"userIdentifier"];
                if (userIdentifier && userIdentifier.length > 0) {
                    readingBuilder.userIdentifier = userIdentifier;
                }

                if (json[@"nonce"]) {
                    readingBuilder.nonce = json[@"nonce"];
                }

                if (json[@"documentType"]) {
                    UQDocumentConfig *documentObject = [[UQDocumentConfig alloc] initWithDocumentTypeName:json[@"documentType"]];
                    readingBuilder.documentType = documentObject.documentType;
                }

                if (json[@"documentNumber"]) {
                    readingBuilder.documentNumber = json[@"documentNumber"];
                }

                if (json[@"dateOfBirth"]) {
                    readingBuilder.dateOfBirth = json[@"dateOfBirth"];
                }

                if (json[@"dateOfExpiry"]) {
                    readingBuilder.dateOfExpiry = json[@"dateOfExpiry"];
                }

                if (json[@"mrz"]) {
                    readingBuilder.mrz = json[@"mrz"];
                }

                if (json[@"isReturnDataForIncompleteSession"]) {
                    readingBuilder.returnDataForIncompleteSession = [[json valueForKey:@"isReturnDataForIncompleteSession"] boolValue];
                }

                if (json[@"facialRecognitionSpecification"]) {
                    NSDictionary *frConfig = json[@"facialRecognitionSpecification"];
                    UQFacialRecognitionConfig *config = [[UQFacialRecognitionConfig alloc] init];
                    config.enableFacialRecognition = YES;

                    if (frConfig[@"enrollFace"] && [frConfig[@"enrollFace"] boolValue]) {
                        config.enrollFace = YES;
                    }

                    if (frConfig[@"scanMinimumMatchLevel"]) {
                        config.scanMinimumMatchLevel = [frConfig[@"scanMinimumMatchLevel"] intValue];
                    }

                    if (frConfig[@"readMinimumMatchLevel"]) {
                        config.readMinimumMatchLevel = [frConfig[@"readMinimumMatchLevel"] intValue];
                    }

                    if (frConfig[@"lookupMinimumMatchLevel"]) {
                        config.minimumMatchLevel = [frConfig[@"lookupMinimumMatchLevel"] intValue];
                    }

                    if (frConfig[@"maxAttempts"]) {
                        int maxAttempts = [frConfig[@"maxAttempts"] intValue];
                        if (maxAttempts > 0) {
                            config.maxAttempts = maxAttempts;
                        }
                    }

                    if (frConfig[@"allowClosedEyes"] && [frConfig[@"allowClosedEyes"] boolValue]) {
                        config.allowClosedEyes = YES;
                    }

                    if (frConfig[@"obfuscationType"]) {
                        if ([frConfig[@"obfuscationType"] isEqualToString:@"BLURRED"]) {
                            config.obfuscationType = BLURRED;
                        } else if ([frConfig[@"obfuscationType"] isEqualToString:@"FILLED"]) {
                            config.obfuscationType = FILLED;
                        } else if ([frConfig[@"obfuscationType"] isEqualToString:@"FILLED_WHITE"]) {
                            config.obfuscationType = FILLED_WHITE;
                        }
                    }

                    if (frConfig[@"isOneToNVerificationEnabled"] && [frConfig[@"isOneToNVerificationEnabled"] boolValue]) {
                        config.enableOneToNVerification = YES;
                    }

                    if (frConfig[@"enableActiveLiveness"] && [frConfig[@"enableActiveLiveness"] boolValue]) {
                        config.enableActiveLiveness = YES;
                    }

                    if (frConfig[@"disableLivenessGesture"]) {
                        NSString *gesture = frConfig[@"disableLivenessGesture"];
                        if ([gesture isEqualToString:@"FACE_MOVE"]) {
                            config.disableLivenessGesture = FACE_MOVE;
                        } else if ([gesture isEqualToString:@"FACE_TILT"]) {
                            config.disableLivenessGesture = FACE_TILT;
                        } else if ([gesture isEqualToString:@"FACE_TURN"]) {
                            config.disableLivenessGesture = FACE_TURN;
                        }
                    }

                    readingBuilder.facialRecognitionConfig = config;
                }

                if (json[@"backgroundCheckConfiguration"]) {
                    NSDictionary *bgConfig = json[@"backgroundCheckConfiguration"];

                    BOOL isDisableConsent = bgConfig[@"disableConsent"] && [bgConfig[@"disableConsent"] boolValue];
                    BOOL isMonitoringEnabled = bgConfig[@"monitoringEnabled"] && [bgConfig[@"monitoringEnabled"] boolValue];
                    BOOL isSkipView = bgConfig[@"skipView"] && [bgConfig[@"skipView"] boolValue];

                    BackgroundCheckType type = RDC;
                    if (bgConfig[@"backgroundCheckType"]) {
                        if ([bgConfig[@"backgroundCheckType"] isEqualToString:@"DOW_JONES"]) {
                            type = DOW_JONES;
                        }
                    }

                    [readingBuilder enableBackgroundCheck:isDisableConsent type:type monitoring:isMonitoringEnabled skipView:isSkipView];
                }

                if (json[@"isLookupEnabled"] && [json[@"isLookupEnabled"] boolValue]) {
                    [readingBuilder enableLookup];
                }

                builderController.delegate = self;
                [builderController setReading:readingBuilder];

                UIViewController *rootViewController = [UIApplication sharedApplication].delegate.window.rootViewController;
                builderController.appViewController = rootViewController;

                NSInteger appearanceMode = SYSTEM;
                if (json[@"appearanceMode"]) {
                    if ([json[@"appearanceMode"] isEqualToString:@"LIGHT"]) {
                        appearanceMode = LIGHT;
                    } else if ([json[@"appearanceMode"] isEqualToString:@"DARK"]) {
                        appearanceMode = DARK;
                    }
                }
                [builderController setAppearanceMode:appearanceMode];
            });
            [builderController performReading];
        } else {
            UQSessionStatus *status = [[UQSessionStatus alloc] init];
            status.statusCode = UNEXPECTED_ERROR;
            status.message = @"Expected reading object as argument.";
            status.statusTask = -1;
            [self sendPluginError:status];
        }
    }
    @catch (NSException *exception) {
        NSLog(@"%@", exception.callStackSymbols);
        UQSessionStatus *status = [[UQSessionStatus alloc] init];
        status.statusCode = UNEXPECTED_ERROR;
        status.message = exception.reason;
        status.statusTask = -1;
        [self sendPluginError:status];
    }
}

- (void)didReadingCompleteWithInfo:(nonnull NSString *)jwsString {
    NSMutableDictionary *jsonResults = [[NSMutableDictionary alloc]init];
    [jsonResults setValue:jwsString forKey:@"result"];
    self.promiseResolve(jsonResults);
}

- (void)didReadingIncompleteWithStatus:(UQSessionStatus *)status {
    [self sendPluginError:status];
}

- (void)sendPluginError:(UQSessionStatus *)status {
    NSString *code = nil;
    switch (status.statusCode) {
        case USER_CANCEL:
            code = @"USER_CANCEL";
            break;
        case SESSION_EXPIRED:
            code = @"SESSION_EXPIRED";
            break;
        case UNEXPECTED_ERROR:
            code = @"UNEXPECTED_ERROR";
            break;
        case SESSION_INVALIDATED_CHIP_VALIDATION_FAILED:
            code = @"SESSION_INVALIDATED_CHIP_VALIDATION_FAILED";
            break;
        case SESSION_INVALIDATED_READING_NOT_SUPPORTED:
            code = @"SESSION_INVALIDATED_READING_NOT_SUPPORTED";
            break;
        case SESSION_INVALIDATED_READING_INVALID_DOCUMENT:
            code = @"SESSION_INVALIDATED_READING_INVALID_DOCUMENT";
            break;
        case SESSION_INVALIDATED_FACE_RECOGNITION_TOO_MANY_ATTEMPTS:
            code = @"SESSION_INVALIDATED_FACE_RECOGNITION_TOO_MANY_ATTEMPTS";
            break;
        case SESSION_INVALIDATED_OTP_TOO_MANY_ATTEMPTS:
            code = @"SESSION_INVALIDATED_OTP_TOO_MANY_ATTEMPTS";
            break;
        case SESSION_INVALIDATED_CAMERA_NOT_AVAILABLE:
            code = @"SESSION_INVALIDATED_CAMERA_NOT_AVAILABLE";
            break;
        case SESSION_INVALIDATED_CAMERA_PERMISSION_NOT_GRANTED:
            code = @"SESSION_INVALIDATED_CAMERA_PERMISSION_NOT_GRANTED";
            break;
        case SESSION_INVALIDATED_READING_AUTHENTICATION_FAILED:
            code = @"SESSION_INVALIDATED_READING_AUTHENTICATION_FAILED";
            break;
    }
    NSString *task = nil;
    switch (status.statusTask) {
        case SCAN:
            task = @"SCAN";
            break;
        case READING:
            task = @"READING";
            break;
        case FACE:
            task = @"FACE";
            break;
        case BACKGROUND_CHECK:
            task = @"BACKGROUND_CHECK";
            break;
        case LOOKUP:
            task = @"LOOKUP";
            break;
    }
    NSMutableDictionary *error = [[NSMutableDictionary alloc]init];
    [error setValue:code forKey:@"code"];
    [error setValue:status.message forKey:@"message"];
    [error setValue:task forKey:@"task"];
    [error setValue:status.data forKey:@"data"];

    NSError *err = nil;
    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:error options:NSJSONWritingPrettyPrinted error:&err];

    NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
    self.promiseReject(jsonString, jsonString, nil);
}

-(void)startObserving {
    if(tracer != nil) {
        tracer.hasListeners = YES;
    }
}

-(void)stopObserving {
    if(tracer != nil) {
        tracer.hasListeners = NO;
    }
}

- (NSArray<NSString *> *)supportedEvents {
    return @[@"TraceEvent"];
}

@end


@implementation MyTracer

- (void)trace:(UQTrace *)trace {
    if(_hasListeners) {
        NSMutableDictionary* parameters = [[NSMutableDictionary alloc] init];

        if(nil != trace.sessionId){
            [parameters setValue:trace.sessionId forKey:@"sessionId"];
        }
        [parameters setValue:trace.category->name forKey:@"category"];
        [parameters setValue:trace.event->name forKey:@"event"];
        [parameters setValue:trace.status->name forKey:@"status"];

        NSString *timeStamp = [self timeStamp:trace.timestamp];
        [parameters setValue:timeStamp forKey:@"timestamp"];

        if (TP_NULL != trace.page) {
            [parameters setValue:trace.page->name forKey:@"page"];
        }
        if (TSC_NULL != trace.statusCode) {
            [parameters setValue:trace.statusCode->name forKey:@"statusCode"];
        }
        if (trace.documentType != UNSPECIFY) {
            UQDocumentConfig *document = [[UQDocumentConfig alloc] initWithDocumentType:trace.documentType];
            [parameters setValue:document.documentName forKey:@"documentType"];
        }
        if (trace.statusMessage) {
            [parameters setValue:trace.statusMessage forKey:@"statusMessage"];
        }

        NSError *err = nil;
        NSData *jsonData = [NSJSONSerialization dataWithJSONObject:parameters options:NSJSONWritingPrettyPrinted error:&err];

        NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
        [_emitter sendEventWithName:@"TraceEvent" body:jsonString];
    }
}

- (NSString *)timeStamp:(NSDate *)currentDate {
    NSISO8601DateFormatter *dateFormatter = [[NSISO8601DateFormatter alloc] init];
    return [dateFormatter stringFromDate:currentDate];
}

- (void)didRootedDeviceDetected:(NSString *)info {
    // IGNORE, deprecated in the SDK
}

@end

