package io.sdk.uqudoid;

import android.app.Activity;
import android.content.Intent;
import android.util.Log;
import android.util.SparseArray;
import android.content.Context;
import androidx.appcompat.app.AppCompatDelegate;
import com.facebook.react.bridge.ActivityEventListener;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;

import org.jetbrains.annotations.NotNull;
import org.json.JSONArray;
import org.json.JSONObject;

import java.util.UUID;

import org.json.JSONException;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;


import io.uqudo.sdk.core.DocumentBuilder;
import io.uqudo.sdk.core.SessionStatus;
import io.uqudo.sdk.core.SessionStatusCode;
import io.uqudo.sdk.core.UqudoBuilder;
import io.uqudo.sdk.core.UqudoSDK;
import io.uqudo.sdk.core.analytics.Trace;
import io.uqudo.sdk.core.analytics.Tracer;
import io.uqudo.sdk.core.builder.BackgroundCheckConfigurationBuilder;
import io.uqudo.sdk.core.builder.FacialRecognitionConfigurationBuilder;
import io.uqudo.sdk.core.builder.ReadingConfigurationBuilder;
import io.uqudo.sdk.core.domain.model.BackgroundCheckType;
import io.uqudo.sdk.core.domain.model.ObfuscationType;
import io.uqudo.sdk.core.domain.model.Document;
import io.uqudo.sdk.core.domain.model.DocumentType;
import io.uqudo.sdk.core.domain.model.LivenessGesture;
import com.facebook.react.bridge.UiThreadUtil;

import java.text.SimpleDateFormat;
import java.util.TimeZone;

public class UqudoIdModule extends ReactContextBaseJavaModule implements ActivityEventListener {

    private final ReactApplicationContext context;
    private final SparseArray<Promise> mPromises;
    public static final int REQUEST_CODE_ENROLLMENT = 1001;
    public static final int REQUEST_CODE_ACCOUNT_RECOVERY = 1002;
    public static final int REQUEST_CODE_FACE_SESSION = 1003;
    public static final int REQUEST_CODE_LOOKUP = 1004;
    public static final int REQUEST_CODE_READING = 1005;

    public UqudoIdModule(ReactApplicationContext reactContext) {
        super(reactContext);
        this.context = reactContext;
        mPromises = new SparseArray<>();
    }

    @Override
    public String getName() {
        return "UqudoId";
    }

    @Override
    public void initialize() {
        super.initialize();
        getReactApplicationContext().addActivityEventListener(this);
    }

    @Override
    public void onCatalystInstanceDestroy() {
        super.onCatalystInstanceDestroy();
        getReactApplicationContext().removeActivityEventListener(this);
    }

    @ReactMethod
    public void init() {
        UqudoSDK.init(context, new TraceObject());
    }

    private class TraceObject implements Tracer {
        @Override
        public void trace(@NotNull Trace trace) {
            try {
                JSONObject json = new JSONObject();
                json.put("category", trace.getCategory());
                json.put("sessionId", trace.getSessionId());
                json.put("event", trace.getEvent());
                json.put("page", trace.getPage());
                json.put("statusCode", trace.getStatusCode());
                json.put("status", trace.getStatus());
                json.put("message", trace.getStatusMessage());
                SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
                dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
                json.put("timestamp", dateFormat.format(trace.getTimestamp()));
                json.put("documentType", trace.getDocumentType());
                String data = json.toString();
                context.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
                        .emit("TraceEvent", data);
            } catch (Exception e) {
                Log.e("UqudoIdModule", e.getMessage(), e);
            }
        }
    }

    @ReactMethod
    public void setLocale(String locale) {
        UqudoSDK.setLocale(context, locale);
    }

    @ReactMethod
    public void enroll(String message, Promise promise) {
        if (message != null && message.length() > 0) {
            try {
                JSONObject json = new JSONObject(message);
                UqudoBuilder.Enrollment enrollment = new UqudoBuilder.Enrollment();
                if (json.has("isSecuredWindowsDisabled") && json.getBoolean("isSecuredWindowsDisabled")) {
                    enrollment.disableSecureWindow();
                }
                if (json.has("nonce")) {
                    enrollment.setNonce(json.getString("nonce"));
                }
                if (json.has("sessionId")) {
                    enrollment.setSessionId(json.getString("sessionId"));
                }
                if (json.has("isAllowNonPhysicalDocuments")) {
                    enrollment.allowNonPhysicalDocuments();
                }
                if (json.has("isDisableTamperingRejection")) {
                    enrollment.disableTamperingRejection();
                }
                if (json.has("userIdentifier")) {
                    enrollment.setUserIdentifier(UUID.fromString(json.getString("userIdentifier")));
                }
                if (json.has("isReturnDataForIncompleteSession") && json.getBoolean("isReturnDataForIncompleteSession")) {
                    enrollment.returnDataForIncompleteSession();
                }
                if (json.has("facialRecognitionSpecification")) {
                    FacialRecognitionConfigurationBuilder faceBuilder = new FacialRecognitionConfigurationBuilder();
                    JSONObject faceObject = json.getJSONObject("facialRecognitionSpecification");
                    if (faceObject.has("enrollFace") && faceObject.getBoolean("enrollFace")) {
                        faceBuilder.enrollFace();
                    }
                    if (faceObject.has("scanMinimumMatchLevel") && faceObject.getInt("scanMinimumMatchLevel") > 0) {
                        faceBuilder.setScanMinimumMatchLevel(faceObject.getInt("scanMinimumMatchLevel"));
                    }
                    if (faceObject.has("readMinimumMatchLevel") && faceObject.getInt("readMinimumMatchLevel") > 0) {
                        faceBuilder.setReadMinimumMatchLevel(faceObject.getInt("readMinimumMatchLevel"));
                    }
                    if (faceObject.has("maxAttempts") && faceObject.getInt("maxAttempts") > 0) {
                        faceBuilder.setMaxAttempts(faceObject.getInt("maxAttempts"));
                    }
                    if (faceObject.has("allowClosedEyes") && faceObject.getBoolean("allowClosedEyes")) {
                        faceBuilder.allowClosedEyes();
                    }
                    if (faceObject.has("obfuscationType")) {
                        if (faceObject.getString("obfuscationType").equals("FILLED")) {
                            faceBuilder.enableAuditTrailImageObfuscation(ObfuscationType.FILLED);
                        } else if (faceObject.getString("obfuscationType").equals("BLURRED")) {
                            faceBuilder.enableAuditTrailImageObfuscation(ObfuscationType.BLURRED);
                        } else if (faceObject.getString("obfuscationType").equals("FILLED_WHITE")) {
                            faceBuilder.enableAuditTrailImageObfuscation(ObfuscationType.FILLED_WHITE);
                        }
                    }

                    if (faceObject.has("isOneToNVerificationEnabled") && faceObject.getBoolean("isOneToNVerificationEnabled")) {
                        faceBuilder.enableOneToNVerification();
                    }

                    if (faceObject.has("enableActiveLiveness") && faceObject.getBoolean("enableActiveLiveness")) {
                        LivenessGesture disableGesture = null;
                        if (faceObject.has("disableLivenessGesture")) {
                            String gesture = faceObject.getString("disableLivenessGesture");
                            switch (gesture) {
                                case "FACE_MOVE":
                                    disableGesture = LivenessGesture.FACE_MOVE;
                                    break;
                                case "FACE_TILT":
                                    disableGesture = LivenessGesture.FACE_TILT;
                                    break;
                                case "FACE_TURN":
                                    disableGesture = LivenessGesture.FACE_TURN;
                                    break;
                            }
                        }
                        faceBuilder.enableActiveLiveness(disableGesture);
                    }

                    enrollment.enableFacialRecognition(faceBuilder.build());
                }
                if (json.has("lookupConfiguration")) {
                    JSONObject lookupObject = json.getJSONObject("lookupConfiguration");
                    if (lookupObject.has("documentList")) {
                        JSONArray lookupDocuments = lookupObject.getJSONArray("documentList");
                        if (lookupDocuments.length() > 0) {
                            DocumentType[] documents = new DocumentType[lookupDocuments.length()];
                            for (int i = 0; i < lookupDocuments.length(); i++) {
                                String parsedDocument = lookupDocuments.getString(i);
                                documents[i] = DocumentType.valueOf(parsedDocument);
                            }
                            enrollment.enableLookup(documents);
                        } else {
                            enrollment.enableLookup();
                        }
                    } else {
                        enrollment.enableLookup();
                    }
                }
                if (json.has("backgroundCheckConfiguration")) {
                    BackgroundCheckConfigurationBuilder backgroundCheckConfigurationBuilder = new BackgroundCheckConfigurationBuilder();
                    JSONObject backgroundObject = json.getJSONObject("backgroundCheckConfiguration");
                    if (backgroundObject.has("disableConsent") && backgroundObject.getBoolean("disableConsent")) {
                        backgroundCheckConfigurationBuilder.disableConsent();
                    }
                    if (backgroundObject.has("backgroundCheckType")) {
                        backgroundCheckConfigurationBuilder.setBackgroundCheckType(BackgroundCheckType.valueOf(backgroundObject.getString("backgroundCheckType")));
                    }
                    if (backgroundObject.has("monitoringEnabled") && backgroundObject.getBoolean("monitoringEnabled")) {
                        backgroundCheckConfigurationBuilder.enableMonitoring();
                    }
                    if (backgroundObject.has("skipView") && backgroundObject.getBoolean("skipView")) {
                        backgroundCheckConfigurationBuilder.skipView();
                    }
                    enrollment.enableBackgroundCheck(backgroundCheckConfigurationBuilder.build());
                }
                enrollment.setToken(json.getString("authorizationToken"));
                JSONArray documentList = json.getJSONArray("documentList");
                for (int i = 0; i < documentList.length(); i++) {
                    JSONObject documentJson = documentList.getJSONObject(i);
                    DocumentBuilder documentBuilder = new DocumentBuilder(context);
                    documentBuilder.setDocumentType(DocumentType.valueOf(documentJson.getString("documentType")));
                    if (documentJson.has("isHelpPageDisabled") && documentJson.getBoolean("isHelpPageDisabled")) {
                        documentBuilder.disableHelpPage();
                    }
                    if (documentJson.has("isExpiredDocumentValidateDisabled") && documentJson.getBoolean("isExpiredDocumentValidateDisabled")) {
                        documentBuilder.disableExpiryValidation();
                    }
                    if (documentJson.has("isUserDataReviewDisabled") && documentJson.getBoolean("isUserDataReviewDisabled")) {
                        documentBuilder.disableUserDataReview();
                    }
                    if (documentJson.has("isFrontSideReviewEnabled") || documentJson.has("isBackSideReviewEnabled")) {
                        documentBuilder.enableScanReview(documentJson.optBoolean("isFrontSideReviewEnabled", false), documentJson.optBoolean("isBackSideReviewEnabled", false));
                    }
                    if (documentJson.has("isUploadEnabled") && documentJson.getBoolean("isUploadEnabled")) {
                        documentBuilder.enableUpload();
                    }
                    if (documentJson.has("readingConfiguration")) {
                        JSONObject readConfiguration = documentJson.getJSONObject("readingConfiguration");
                        if (readConfiguration.has("forceReading")) {
                            ReadingConfigurationBuilder readingConfigurationBuilder = new ReadingConfigurationBuilder();
                            if (readConfiguration.has("forceReading")) {
                                readingConfigurationBuilder.forceReading(readConfiguration.getBoolean("forceReading"));
                            }
                            if (readConfiguration.has("forceReadingIfSupported")) {
                                readingConfigurationBuilder.forceReadingIfSupported(readConfiguration.getBoolean("forceReadingIfSupported"));
                            }
                            if (readConfiguration.has("timeoutInSeconds")) {
                                readingConfigurationBuilder.forceReadingTimeout(readConfiguration.getInt("timeoutInSeconds"));
                            }
                            documentBuilder.enableReading(readingConfigurationBuilder.build());
                        } else {
                            documentBuilder.enableReading();
                        }
                    }
                    if (documentJson.has("faceScanMinimumMatchLevel") && documentJson.getInt("faceScanMinimumMatchLevel") > 0) {
                        documentBuilder.setFaceScanMinimumMatchLevel(documentJson.getInt("faceScanMinimumMatchLevel"));
                    }
                    if (documentJson.has("faceReadMinimumMatchLevel") && documentJson.getInt("faceReadMinimumMatchLevel") > 0) {
                        documentBuilder.setFaceReadMinimumMatchLevel(documentJson.getInt("faceReadMinimumMatchLevel"));
                    }
                    if (documentJson.has("minimumAge") && documentJson.getInt("minimumAge") > 0) {
                        documentBuilder.enableAgeVerification(documentJson.getInt("minimumAge"));
                    }

                    Document document = documentBuilder.build();
                    enrollment.add(document);
                }
                final Intent intent = enrollment.build(context);
                UiThreadUtil.runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            handleAppearanceMode(json);
                            Activity currentActivity = getCurrentActivity();
                            if (currentActivity != null) {
                                mPromises.put(REQUEST_CODE_ENROLLMENT, promise);
                                currentActivity.startActivityForResult(intent, REQUEST_CODE_ENROLLMENT);
                            } else {
                                sendError(promise, SessionStatusCode.UNEXPECTED_ERROR.name(),
                                        "The operation could not start because the app is in the background or the activity is unavailable.", null);
                            }
                        } catch (Exception e) {
                            sendError(promise, SessionStatusCode.UNEXPECTED_ERROR.name(), e.getMessage(), null);
                        }
                    }
                });
            } catch (Exception e) {
                Log.e("UqudoIdModule", e.getMessage(), e);
                sendError(promise, SessionStatusCode.UNEXPECTED_ERROR.name(), e.getMessage(), null);
            }
        } else {
            sendError(promise, SessionStatusCode.UNEXPECTED_ERROR.name(), "Expected enrollment object as argument.", null);
        }
    }

    @ReactMethod
    public void recover(String message, Promise promise) {
        if (message != null && message.length() > 0) {
            try {
                JSONObject json = new JSONObject(message);
                UqudoBuilder.AccountRecovery recovery = new UqudoBuilder.AccountRecovery();
                recovery.setToken(json.getString("token"));
                recovery.setEnrollmentIdentifier(json.getString("enrollmentIdentifier"));
                if (json.has("nonce")) {
                    recovery.setNonce(json.getString("nonce"));
                }
                if (json.has("isSecuredWindowsDisabled")) {
                    recovery.disableSecureWindow();
                }
                if (json.has("minimumMatchLevel") && json.getInt("minimumMatchLevel") > 0) {
                    recovery.setMinimumMatchLevel(json.getInt("minimumMatchLevel"));
                }
                if (json.has("maxAttempts") && json.getInt("maxAttempts") > 0) {
                    recovery.setMaxAttempts(json.getInt("maxAttempts"));
                }
                if (json.has("allowClosedEyes") && json.getBoolean("allowClosedEyes")) {
                    recovery.allowClosedEyes();
                }
                if (json.has("isReturnDataForIncompleteSession") && json.getBoolean("isReturnDataForIncompleteSession")) {
                    recovery.returnDataForIncompleteSession();
                }
                final Intent intent = recovery.build(context);

                UiThreadUtil.runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            handleAppearanceMode(json);
                            Activity currentActivity = getCurrentActivity();
                            if (currentActivity != null) {
                                mPromises.put(REQUEST_CODE_ACCOUNT_RECOVERY, promise);
                                currentActivity.startActivityForResult(intent, REQUEST_CODE_ACCOUNT_RECOVERY);
                            } else {
                                sendError(promise, SessionStatusCode.UNEXPECTED_ERROR.name(),
                                        "The operation could not start because the app is in the background or the activity is unavailable.", null);
                            }
                        } catch (Exception e) {
                            sendError(promise, SessionStatusCode.UNEXPECTED_ERROR.name(), e.getMessage(), null);
                        }
                    }
                });
            } catch (Exception e) {
                Log.e("UqudoIdModule", e.getMessage(), e);
                sendError(promise, SessionStatusCode.UNEXPECTED_ERROR.name(), e.getMessage(), null);
            }
        } else {
            sendError(promise, SessionStatusCode.UNEXPECTED_ERROR.name(), "Expected account recovery object as argument.", null);
        }
    }

    @ReactMethod
    public void faceSession(String configuration, Promise promise) {
        if (configuration != null && configuration.length() > 0) {
            try {
                JSONObject json = new JSONObject(configuration);
                UqudoBuilder.FaceSession faceSession = new UqudoBuilder.FaceSession();
                faceSession.setToken(json.getString("token"));
                faceSession.setSessionId(json.getString("sessionId"));
                if (json.has("nonce")) {
                    faceSession.setNonce(json.getString("nonce"));
                }
                if (json.has("userIdentifier")) {
                    faceSession.setUserIdentifier(UUID.fromString(json.getString("userIdentifier")));
                }
                if (json.has("isSecuredWindowsDisabled")) {
                    faceSession.disableSecureWindow();
                }
                if (json.has("minimumMatchLevel") && json.getInt("minimumMatchLevel") > 0) {
                    faceSession.setMinimumMatchLevel(json.getInt("minimumMatchLevel"));
                }
                if (json.has("maxAttempts") && json.getInt("maxAttempts") > 0) {
                    faceSession.setMaxAttempts(json.getInt("maxAttempts"));
                }
                if (json.has("allowClosedEyes") && json.getBoolean("allowClosedEyes")) {
                    faceSession.allowClosedEyes();
                }
                if (json.has("isReturnDataForIncompleteSession") && json.getBoolean("isReturnDataForIncompleteSession")) {
                    faceSession.returnDataForIncompleteSession();
                }
                if (json.has("obfuscationType")) {
                    if (json.getString("obfuscationType").equals("FILLED")) {
                        faceSession.enableAuditTrailImageObfuscation(ObfuscationType.FILLED);
                    } else if (json.getString("obfuscationType").equals("BLURRED")) {
                        faceSession.enableAuditTrailImageObfuscation(ObfuscationType.BLURRED);
                    } else if (json.getString("obfuscationType").equals("FILLED_WHITE")) {
                        faceSession.enableAuditTrailImageObfuscation(ObfuscationType.FILLED_WHITE);
                    }
                }
                if (json.has("enableActiveLiveness") && json.getBoolean("enableActiveLiveness")) {
                    if (json.has("disableLivenessGesture")) {
                        LivenessGesture gesture = LivenessGesture.valueOf(json.getString("disableLivenessGesture"));
                        faceSession.enableActiveLiveness(gesture);
                    } else {
                        faceSession.enableActiveLiveness(null);
                    }
                }
                final Intent intent = faceSession.build(context);

                UiThreadUtil.runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            handleAppearanceMode(json);
                            Activity currentActivity = getCurrentActivity();
                            if (currentActivity != null) {
                                mPromises.put(REQUEST_CODE_FACE_SESSION, promise);
                                currentActivity.startActivityForResult(intent, REQUEST_CODE_FACE_SESSION);
                            } else {
                                sendError(promise, SessionStatusCode.UNEXPECTED_ERROR.name(),
                                        "The operation could not start because the app is in the background or the activity is unavailable.", null);
                            }
                        } catch (Exception e) {
                            sendError(promise, SessionStatusCode.UNEXPECTED_ERROR.name(), e.getMessage(), null);
                        }
                    }
                });
            } catch (Exception e) {
                Log.e("UqudoIdModule", e.getMessage(), e);
                sendError(promise, SessionStatusCode.UNEXPECTED_ERROR.name(), e.getMessage(), null);
            }
        } else {
            sendError(promise, SessionStatusCode.UNEXPECTED_ERROR.name(), "Expected face session configuration as argument.", null);
        }
    }

    @ReactMethod
    public void lookup(String message, Promise promise) {
        if (message != null && message.length() > 0) {
            try {
                JSONObject json = new JSONObject(message);
                UqudoBuilder.Lookup lookupBuilder = new UqudoBuilder.Lookup();
                if (json.has("isSecuredWindowsDisabled") && json.getBoolean("isSecuredWindowsDisabled")) {
                    lookupBuilder.disableSecureWindow();
                }
                if (json.has("nonce")) {
                    lookupBuilder.setNonce(json.getString("nonce"));
                }
                if (json.has("sessionId")) {
                    lookupBuilder.setSessionId(json.getString("sessionId"));
                }
                if (json.has("userIdentifier")) {
                    lookupBuilder.setUserIdentifier(UUID.fromString(json.getString("userIdentifier")));
                }
                if (json.has("isReturnDataForIncompleteSession") && json.getBoolean("isReturnDataForIncompleteSession")) {
                    lookupBuilder.returnDataForIncompleteSession();
                }
                if (json.has("facialRecognitionSpecification")) {
                    FacialRecognitionConfigurationBuilder faceBuilder = new FacialRecognitionConfigurationBuilder();
                    JSONObject faceObject = json.getJSONObject("facialRecognitionSpecification");
                    if (faceObject.has("enrollFace") && faceObject.getBoolean("enrollFace")) {
                        faceBuilder.enrollFace();
                    }
                    if (faceObject.has("lookupMinimumMatchLevel") && faceObject.getInt("lookupMinimumMatchLevel") > 0) {
                        faceBuilder.setLookupMinimumMatchLevel(faceObject.getInt("lookupMinimumMatchLevel"));
                    }
                    if (faceObject.has("maxAttempts") && faceObject.getInt("maxAttempts") > 0) {
                        faceBuilder.setMaxAttempts(faceObject.getInt("maxAttempts"));
                    }
                    if (faceObject.has("allowClosedEyes") && faceObject.getBoolean("allowClosedEyes")) {
                        faceBuilder.allowClosedEyes();
                    }
                    if (faceObject.has("obfuscationType")) {
                        if (faceObject.getString("obfuscationType").equals("FILLED")) {
                            faceBuilder.enableAuditTrailImageObfuscation(ObfuscationType.FILLED);
                        } else if (faceObject.getString("obfuscationType").equals("BLURRED")) {
                            faceBuilder.enableAuditTrailImageObfuscation(ObfuscationType.BLURRED);
                        } else if (faceObject.getString("obfuscationType").equals("FILLED_WHITE")) {
                            faceBuilder.enableAuditTrailImageObfuscation(ObfuscationType.FILLED_WHITE);
                        }
                    }

                    if (faceObject.has("isOneToNVerificationEnabled") && faceObject.getBoolean("isOneToNVerificationEnabled")) {
                        faceBuilder.enableOneToNVerification();
                    }

                    if (faceObject.has("enableActiveLiveness") && faceObject.getBoolean("enableActiveLiveness")) {
                        LivenessGesture disableGesture = null;
                        if (faceObject.has("disableLivenessGesture")) {
                            String gesture = faceObject.getString("disableLivenessGesture");
                            switch (gesture) {
                                case "FACE_MOVE":
                                    disableGesture = LivenessGesture.FACE_MOVE;
                                    break;
                                case "FACE_TILT":
                                    disableGesture = LivenessGesture.FACE_TILT;
                                    break;
                                case "FACE_TURN":
                                    disableGesture = LivenessGesture.FACE_TURN;
                                    break;
                            }
                        }
                        faceBuilder.enableActiveLiveness(disableGesture);
                    }
                    lookupBuilder.enableFacialRecognition(faceBuilder.build());
                }
                if (json.has("backgroundCheckConfiguration")) {
                    BackgroundCheckConfigurationBuilder backgroundCheckConfigurationBuilder = new BackgroundCheckConfigurationBuilder();
                    JSONObject backgroundObject = json.getJSONObject("backgroundCheckConfiguration");
                    if (backgroundObject.has("disableConsent") && backgroundObject.getBoolean("disableConsent")) {
                        backgroundCheckConfigurationBuilder.disableConsent();
                    }
                    if (backgroundObject.has("backgroundCheckType")) {
                        backgroundCheckConfigurationBuilder.setBackgroundCheckType(BackgroundCheckType.valueOf(backgroundObject.getString("backgroundCheckType")));
                    }
                    if (backgroundObject.has("monitoringEnabled") && backgroundObject.getBoolean("monitoringEnabled")) {
                        backgroundCheckConfigurationBuilder.enableMonitoring();
                    }
                    if (backgroundObject.has("skipView") && backgroundObject.getBoolean("skipView")) {
                        backgroundCheckConfigurationBuilder.skipView();
                    }
                    lookupBuilder.enableBackgroundCheck(backgroundCheckConfigurationBuilder.build());
                }
                lookupBuilder.setToken(json.getString("authorizationToken"));
                lookupBuilder.setDocumentType(DocumentType.valueOf(json.getString("documentType")));

                final Intent intent = lookupBuilder.build(context);

                UiThreadUtil.runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            handleAppearanceMode(json);
                            Activity currentActivity = getCurrentActivity();
                            if (currentActivity != null) {
                                mPromises.put(REQUEST_CODE_LOOKUP, promise);
                                currentActivity.startActivityForResult(intent, REQUEST_CODE_LOOKUP);
                            } else {
                                sendError(promise, SessionStatusCode.UNEXPECTED_ERROR.name(),
                                        "The operation could not start because the app is in the background or the activity is unavailable.", null);
                            }
                        } catch (Exception e) {
                            sendError(promise, SessionStatusCode.UNEXPECTED_ERROR.name(), e.getMessage(), null);
                        }
                    }
                });
            } catch (Exception e) {
                Log.e("UqudoIdModule", e.getMessage(), e);
                sendError(promise, SessionStatusCode.UNEXPECTED_ERROR.name(), e.getMessage(), null);
            }
        } else {
            sendError(promise, SessionStatusCode.UNEXPECTED_ERROR.name(), "Expected lookup object as argument.", null);
        }
    }

    @ReactMethod
    public void isReadingSupported(String documentType, Promise promise) {
        try {
            promise.resolve(DocumentType.valueOf(documentType).getReadingSupported());
        } catch (Exception e) {
            sendError(promise, SessionStatusCode.UNEXPECTED_ERROR.name(), "Expected valid document type as argument", null);
        }
    }

    @ReactMethod
    public void isFacialRecognitionSupported(String documentType, Promise promise) {
        try {
            promise.resolve(DocumentType.valueOf(documentType).getFacialRecognitionSupported());
        } catch (Exception e) {
            sendError(promise, SessionStatusCode.UNEXPECTED_ERROR.name(), "Expected valid document type as argument", null);
        }
    }

    @ReactMethod
    public void isLookupSupported(String documentType, Promise promise) {
        try {
            promise.resolve(DocumentType.valueOf(documentType).getLookupSupported());
        } catch (Exception e) {
            sendError(promise, SessionStatusCode.UNEXPECTED_ERROR.name(), "Expected valid document type as argument", null);
        }
    }

    @ReactMethod
    public void isLookupFacialRecognitionSupported(String documentType, Promise promise) {
        try {
            promise.resolve(DocumentType.valueOf(documentType).getLookupFacialRecognitionSupported());
        } catch (Exception e) {
            sendError(promise, SessionStatusCode.UNEXPECTED_ERROR.name(), "Expected valid document type as argument", null);
        }
    }

    @ReactMethod
    public void isEnrollmentSupported(String documentType, Promise promise) {
        try {
            promise.resolve(DocumentType.valueOf(documentType).getEnrollmentSupported());
        } catch (Exception e) {
            sendError(promise, SessionStatusCode.UNEXPECTED_ERROR.name(), "Expected valid document type as argument", null);
        }
    }

    @ReactMethod
    public void reading(String message, Promise promise) {
        if (message != null && message.length() > 0) {
            try {
                JSONObject json = new JSONObject(message);
                UqudoBuilder.Reading readingBuilder = new UqudoBuilder.Reading();

                if (json.has("authorizationToken")) {
                    readingBuilder.setToken(json.getString("authorizationToken"));
                }

                if (json.has("sessionId")) {
                    readingBuilder.setSessionId(json.getString("sessionId"));
                }

                if (json.has("userIdentifier")) {
                    readingBuilder.setUserIdentifier(UUID.fromString(json.getString("userIdentifier")));
                }

                if (json.has("nonce")) {
                    readingBuilder.setNonce(json.getString("nonce"));
                }

                if (json.has("documentType")) {
                    readingBuilder.setDocumentType(DocumentType.valueOf(json.getString("documentType")));
                }

                if (json.has("documentNumber")) {
                    readingBuilder.setDocumentNumber(json.getString("documentNumber"));
                }

                if (json.has("dateOfBirth")) {
                    readingBuilder.setDateOfBirth(json.getString("dateOfBirth"));
                }

                if (json.has("dateOfExpiry")) {
                    readingBuilder.setDateOfExpiry(json.getString("dateOfExpiry"));
                }

                if (json.has("mrz")) {
                    readingBuilder.setMRZ(json.getString("mrz"));
                }

                if (json.has("isReturnDataForIncompleteSession") && json.getBoolean("isReturnDataForIncompleteSession")) {
                    readingBuilder.returnDataForIncompleteSession();
                }

                if (json.has("isSecuredWindowsDisabled") && json.getBoolean("isSecuredWindowsDisabled")) {
                    readingBuilder.disableSecureWindow();
                }

                if (json.has("facialRecognitionSpecification")) {
                    FacialRecognitionConfigurationBuilder faceBuilder = new FacialRecognitionConfigurationBuilder();
                    JSONObject faceObject = json.getJSONObject("facialRecognitionSpecification");
                    if (faceObject.has("enrollFace") && faceObject.getBoolean("enrollFace")) {
                        faceBuilder.enrollFace();
                    }
                    if (faceObject.has("scanMinimumMatchLevel") && faceObject.getInt("scanMinimumMatchLevel") > 0) {
                        faceBuilder.setScanMinimumMatchLevel(faceObject.getInt("scanMinimumMatchLevel"));
                    }
                    if (faceObject.has("readMinimumMatchLevel") && faceObject.getInt("readMinimumMatchLevel") > 0) {
                        faceBuilder.setReadMinimumMatchLevel(faceObject.getInt("readMinimumMatchLevel"));
                    }
                    if (faceObject.has("lookupMinimumMatchLevel") && faceObject.getInt("lookupMinimumMatchLevel") > 0) {
                        faceBuilder.setLookupMinimumMatchLevel(faceObject.getInt("lookupMinimumMatchLevel"));
                    }
                    if (faceObject.has("maxAttempts") && faceObject.getInt("maxAttempts") > 0) {
                        faceBuilder.setMaxAttempts(faceObject.getInt("maxAttempts"));
                    }
                    if (faceObject.has("allowClosedEyes") && faceObject.getBoolean("allowClosedEyes")) {
                        faceBuilder.allowClosedEyes();
                    }
                    if (faceObject.has("obfuscationType")) {
                        if (faceObject.getString("obfuscationType").equals("FILLED")) {
                            faceBuilder.enableAuditTrailImageObfuscation(ObfuscationType.FILLED);
                        } else if (faceObject.getString("obfuscationType").equals("BLURRED")) {
                            faceBuilder.enableAuditTrailImageObfuscation(ObfuscationType.BLURRED);
                        } else if (faceObject.getString("obfuscationType").equals("FILLED_WHITE")) {
                            faceBuilder.enableAuditTrailImageObfuscation(ObfuscationType.FILLED_WHITE);
                        }
                    }

                    if (faceObject.has("isOneToNVerificationEnabled") && faceObject.getBoolean("isOneToNVerificationEnabled")) {
                        faceBuilder.enableOneToNVerification();
                    }

                    if (faceObject.has("enableActiveLiveness") && faceObject.getBoolean("enableActiveLiveness")) {
                        LivenessGesture disableGesture = null;
                        if (faceObject.has("disableLivenessGesture")) {
                            String gesture = faceObject.getString("disableLivenessGesture");
                            switch (gesture) {
                                case "FACE_MOVE":
                                    disableGesture = LivenessGesture.FACE_MOVE;
                                    break;
                                case "FACE_TILT":
                                    disableGesture = LivenessGesture.FACE_TILT;
                                    break;
                                case "FACE_TURN":
                                    disableGesture = LivenessGesture.FACE_TURN;
                                    break;
                            }
                        }
                        faceBuilder.enableActiveLiveness(disableGesture);
                    }

                    readingBuilder.enableFacialRecognition(faceBuilder.build());
                }

                if (json.has("backgroundCheckConfiguration")) {
                    BackgroundCheckConfigurationBuilder backgroundCheckConfigurationBuilder = new BackgroundCheckConfigurationBuilder();
                    JSONObject backgroundObject = json.getJSONObject("backgroundCheckConfiguration");
                    if (backgroundObject.has("disableConsent") && backgroundObject.getBoolean("disableConsent")) {
                        backgroundCheckConfigurationBuilder.disableConsent();
                    }
                    if (backgroundObject.has("backgroundCheckType")) {
                        backgroundCheckConfigurationBuilder.setBackgroundCheckType(BackgroundCheckType.valueOf(backgroundObject.getString("backgroundCheckType")));
                    }
                    if (backgroundObject.has("monitoringEnabled") && backgroundObject.getBoolean("monitoringEnabled")) {
                        backgroundCheckConfigurationBuilder.enableMonitoring();
                    }
                    if (backgroundObject.has("skipView") && backgroundObject.getBoolean("skipView")) {
                        backgroundCheckConfigurationBuilder.skipView();
                    }
                    readingBuilder.enableBackgroundCheck(backgroundCheckConfigurationBuilder.build());
                }

                if (json.has("isLookupEnabled") && json.getBoolean("isLookupEnabled")) {
                    readingBuilder.enableLookup();
                }

                final Intent intent = readingBuilder.build(context);

                UiThreadUtil.runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            handleAppearanceMode(json);
                            Activity currentActivity = getCurrentActivity();
                            if (currentActivity != null) {
                                mPromises.put(REQUEST_CODE_READING, promise);
                                currentActivity.startActivityForResult(intent, REQUEST_CODE_READING);
                            } else {
                                sendError(promise, SessionStatusCode.UNEXPECTED_ERROR.name(),
                                        "The operation could not start because the app is in the background or the activity is unavailable.", null);
                            }
                        } catch (Exception e) {
                            sendError(promise, SessionStatusCode.UNEXPECTED_ERROR.name(), e.getMessage(), null);
                        }
                    }
                });
            } catch (Exception e) {
                Log.e("UqudoIdModule", e.getMessage(), e);
                sendError(promise, SessionStatusCode.UNEXPECTED_ERROR.name(), e.getMessage(), null);
            }
        } else {
            sendError(promise, SessionStatusCode.UNEXPECTED_ERROR.name(), "Expected reading object as argument.", null);
        }
    }

    private void sendError(Promise promise, String code, String message, String task) {
        sendError(promise, code, message, task, null);
    }

    private void sendError(Promise promise, String code, String message, String task, String data) {
        JSONObject error = new JSONObject();
        try {
            error.put("code", code);
            error.put("message", message);
            error.put("task", task);
            error.put("data", data);
        } catch (Exception e) {
            e.printStackTrace();
        }
        promise.reject(error.toString(), "");
    }

    @Override
    public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
        Promise promise = mPromises.get(requestCode);
        if (promise == null) {
            return;
        }
        if (requestCode == REQUEST_CODE_ENROLLMENT || requestCode == REQUEST_CODE_ACCOUNT_RECOVERY || requestCode == REQUEST_CODE_FACE_SESSION || requestCode == REQUEST_CODE_LOOKUP || requestCode == REQUEST_CODE_READING) {
            if (resultCode == Activity.RESULT_OK) {
                WritableMap map = Arguments.createMap();
                map.putString("result", data.getStringExtra("data"));
                promise.resolve(map);
            } else if (resultCode == Activity.RESULT_CANCELED) {
                if (data != null) {
                    SessionStatus sessionStatus = data.getParcelableExtra("key_session_status");
                    assert sessionStatus != null;
                    sendError(promise, sessionStatus.getSessionStatusCode().name(), sessionStatus.getSessionStatusCode().getMessage(), sessionStatus.getSessionTask().name(), sessionStatus.getData());
                }
            }
        }
    }

    @Override
    public void onNewIntent(Intent intent) {

    }

    /**
     * Helper method to consolidate appearance mode logic on the UI thread.
     */
    private void handleAppearanceMode(JSONObject json) throws JSONException {
        if (json.has("appearanceMode")) {
            String mode = json.getString("appearanceMode");
            if ("LIGHT".equals(mode)) {
                AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
            } else if ("DARK".equals(mode)) {
                AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
            } else if ("SYSTEM".equals(mode)) {
                AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
            }
        } else {
            AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
        }
    }

}
