package com.rnbiometrics;

import android.os.Build;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import android.util.Base64;

import androidx.biometric.BiometricManager;
import androidx.biometric.BiometricPrompt;
import androidx.biometric.BiometricPrompt.AuthenticationCallback;
import androidx.biometric.BiometricPrompt.PromptInfo;
import androidx.fragment.app.FragmentActivity;

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.ReadableMap;
import com.facebook.react.bridge.UiThreadUtil;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.WritableNativeMap;

import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.RSAKeyGenParameterSpec;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import android.security.keystore.KeyPermanentlyInvalidatedException;
import java.security.cert.Certificate;
import android.app.KeyguardManager;
import android.content.Context;
/**
 * Created by brandon on 4/5/18.
 */

public class ReactNativeBiometrics extends ReactContextBaseJavaModule {

    protected String biometricKeyAlias = "biometric_key";

    public ReactNativeBiometrics(ReactApplicationContext reactContext) {
        super(reactContext);
    }

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

    @ReactMethod
public void isSensorAvailable(final ReadableMap params, final Promise promise) {
    try {
        if (isCurrentSDKMarshmallowOrLater()) {
            ReactApplicationContext context = getReactApplicationContext();
            KeyguardManager keyguardManager = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);

            WritableMap resultMap = new WritableNativeMap();

            if (keyguardManager != null && keyguardManager.isDeviceSecure()) {
                resultMap.putBoolean("available", true);
                resultMap.putString("biometryType", "Passcode");
                promise.resolve(resultMap);
            } else {
                resultMap.putBoolean("available", false);
                resultMap.putInt("code", -16);
                resultMap.putString("message", "Thiết bị chưa đặt mật mã (PIN/Pattern/Password).");
                promise.resolve(resultMap);
            }
        } else {
            WritableMap resultMap = new WritableNativeMap();
            resultMap.putBoolean("available", false);
            resultMap.putInt("code", -4);
            resultMap.putString("message", "Phiên bản Android không được hỗ trợ.");
            promise.resolve(resultMap);
        }
    } catch (Exception e) {
        promise.reject("Error", "Error checking device passcode: " + e.getMessage());
    }
}

    @ReactMethod
        public void createCsr(final ReadableMap params, final Promise promise) {
            if (!isCurrentSDKMarshmallowOrLater()) {
                WritableMap resultMap = new WritableNativeMap();
                resultMap.putBoolean("success", false);
                resultMap.putString("message", "Android version < 6.0 not supported");
                resultMap.putInt("code", -4);
                promise.resolve(resultMap);
                return;
            }

            UiThreadUtil.runOnUiThread(() -> {
                try {
                    String promptMessage = params.getString("promptMessage");
                    String cancelButtonText = params.getString("cancelButtonText");
                    String keytag = params.getString("keytag");

                    String cn = params.hasKey("commonName") ? params.getString("commonName") : "";
                    String ou = params.hasKey("organizationalUnit") ? params.getString("organizationalUnit") : "";
                    String o = params.hasKey("organization") ? params.getString("organization") : "";
                    String l = params.hasKey("locality") ? params.getString("locality") : "";
                    String st = params.hasKey("state") ? params.getString("state") : "";
                    String c = params.hasKey("country") ? params.getString("country") : "";

                    if (keytag == null || keytag.isEmpty()) {
                        WritableMap resultMap = new WritableNativeMap();
                        resultMap.putBoolean("success", false);
                        resultMap.putString("message", "keytag is empty");
                        promise.resolve(resultMap);
                        return;
                    }

                    KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
                    keyStore.load(null);
                    Certificate cert = keyStore.getCertificate(keytag);
                    if (cert == null) {
                        WritableMap resultMap = new WritableNativeMap();
                        resultMap.putBoolean("success", false);
                        resultMap.putString("message", "No certificate found with keytag: " + keytag);
                        promise.resolve(resultMap);
                        return;
                    }

                    PublicKey publicKey = cert.getPublicKey();
                    PrivateKey privateKey = (PrivateKey) keyStore.getKey(keytag, null);
                    Signature signature = Signature.getInstance("SHA256withRSA");

                    signature.initSign(privateKey); // ✅ PHẢI làm trước

                    BiometricPrompt.CryptoObject cryptoObject = new BiometricPrompt.CryptoObject(signature);

                    FragmentActivity fragmentActivity = (FragmentActivity) getCurrentActivity();
                    Executor executor = Executors.newSingleThreadExecutor();

                    BiometricPrompt biometricPrompt = new BiometricPrompt(fragmentActivity, executor,
                        new CreateCsrCallback(promise, publicKey,signature, cn, ou, o, l, st, c));

                    biometricPrompt.authenticate(getPromptInfo(promptMessage, cancelButtonText, true), cryptoObject);


                } catch (Exception e) {
                    WritableMap resultMap = new WritableNativeMap();
                    resultMap.putBoolean("success", false);
                    resultMap.putString("message", "Exception: " + e.getMessage());
                    promise.resolve(resultMap);
                }
            });
        }


    @ReactMethod
    public void getPublicKey(final String keytag, Promise promise) {
        if(keytag.isEmpty()){
            promise.reject("keytag is empty", "keytag is empty");
        }else{
            try{
                KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
                keyStore.load(null);
                PublicKey publicKey = keyStore.getCertificate(keytag).getPublicKey();
                byte[] encodedPublicKey = publicKey.getEncoded();
                String publicKeyString = Base64.encodeToString(encodedPublicKey, Base64.DEFAULT);
                publicKeyString = publicKeyString.replaceAll("\r", "").replaceAll("\n", "");

                WritableMap resultMap = new WritableNativeMap();
                resultMap.putString("publicKey", publicKeyString);
                promise.resolve(resultMap);
            }catch (Exception ex){
                promise.reject("Get publickey error", "Get publickey error");
            }
        }
    }

    @ReactMethod
    public void createKeys(final ReadableMap params, Promise promise) {
        try {
            if (isCurrentSDKMarshmallowOrLater()) {
                String keytag = params.getString("keytag");
                int keytype = params.getInt("keytype");
                if(keytype == 0){
                    int keysize = 2048;
                    if(keytag.isEmpty()){
                        promise.reject("keytag is empty", "keytag is empty");
                    }else{
                        deleteBiometricKeyByKeytag(keytag);
                        int size = params.getInt("keysize");
                        if(size > 0){
                            keysize = size;
                        }
                        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore");
                        KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(keytag, KeyProperties.PURPOSE_SIGN)
                        .setDigests(KeyProperties.DIGEST_SHA256)
                        .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
                        .setAlgorithmParameterSpec(new RSAKeyGenParameterSpec(keysize, RSAKeyGenParameterSpec.F4))
                        .setUserAuthenticationRequired(false)
                        .setUserAuthenticationValidityDurationSeconds(-1)
                        .setInvalidatedByBiometricEnrollment(false)
                        .build();
                        keyPairGenerator.initialize(spec);

                        KeyPair keyPair = keyPairGenerator.generateKeyPair();
                        PublicKey publicKey = keyPair.getPublic();
                        byte[] encodedPublicKey = publicKey.getEncoded();
                        String publicKeyString = Base64.encodeToString(encodedPublicKey, Base64.DEFAULT);
                        publicKeyString = publicKeyString.replaceAll("\r", "").replaceAll("\n", "");

                        WritableMap resultMap = new WritableNativeMap();
                        resultMap.putString("publicKey", publicKeyString);
                        promise.resolve(resultMap);
                    }
                }else{
                    promise.reject("Chỉ hỗ trợ genkey rsa (keytype = 0)", "Chỉ hỗ trợ genkey rsa (keytype = 0)");
                }
            } else {
                promise.reject("Cannot generate keys on android versions below 6.0", "Cannot generate keys on android versions below 6.0");
            }
        } catch (Exception e) {
            promise.reject("Error generating public private keys: " + e.getMessage(), "Error generating public private keys");
        }
    }

    private boolean isCurrentSDKMarshmallowOrLater() {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
    }

    @ReactMethod
    public void deleteKeys(final String keytag, Promise promise) {
        if(keytag.isEmpty()){
            promise.reject("keytag is empty", "keytag is empty");
        }else{
            if (doesBiometricKeyExist(keytag)) {
                boolean deletionSuccessful = deleteBiometricKeyByKeytag(keytag);

                if (deletionSuccessful) {
                    WritableMap resultMap = new WritableNativeMap();
                    resultMap.putBoolean("keysDeleted", true);
                    promise.resolve(resultMap);
                } else {
                    promise.reject("Error deleting biometric key from keystore", "Error deleting biometric key from keystore");
                }
            } else {
                WritableMap resultMap = new WritableNativeMap();
                resultMap.putBoolean("keysDeleted", false);
                promise.resolve(resultMap);
            }
        }
    }

    @ReactMethod
    public void createSignature(final ReadableMap params, final Promise promise) {
        if (isCurrentSDKMarshmallowOrLater()) {
            UiThreadUtil.runOnUiThread(() -> {
                try {
                    String promptMessage = params.getString("promptMessage");
                    String payload = params.getString("payload");
                    String keytag = params.getString("keytag");
                    int type = params.getInt("type");

                    if (keytag.isEmpty()) {
                        WritableMap result = new WritableNativeMap();
                        result.putBoolean("success", false);
                        result.putString("message", "Giá trị 'keytag' không được để trống.");
                        result.putInt("code", -1);
                        promise.resolve(result);
                        return;
                    }

                    String cancelButtonText = params.getString("cancelButtonText");
                    boolean allowDeviceCredentials = params.getBoolean("allowDeviceCredentials");

                    Signature signature = Signature.getInstance("SHA256withRSA");
                    KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
                    keyStore.load(null);

                    PrivateKey privateKey = (PrivateKey) keyStore.getKey(keytag, null);
                    if (privateKey == null) {
                        WritableMap result = new WritableNativeMap();
                        result.putBoolean("success", false);
                        result.putString("message", "Không tìm thấy khóa bí mật.");
                        result.putInt("code", -6);
                        promise.resolve(result);
                        return;
                    }

                    signature.initSign(privateKey);

                    BiometricPrompt.CryptoObject cryptoObject = new BiometricPrompt.CryptoObject(signature);
                    AuthenticationCallback authCallback = new CreateSignatureCallback(promise, payload, type);

                    FragmentActivity fragmentActivity = (FragmentActivity) getCurrentActivity();
                    Executor executor = Executors.newSingleThreadExecutor();
                    BiometricPrompt biometricPrompt = new BiometricPrompt(fragmentActivity, executor, authCallback);

                    biometricPrompt.authenticate(
                        getPromptInfo(promptMessage, cancelButtonText, true),
                        cryptoObject
                    );
                } catch (Exception e) {
                    WritableMap result = new WritableNativeMap();
                    result.putBoolean("success", false);
                    result.putString("message", "Lỗi tạo chữ ký: " + e.getMessage());
                    result.putInt("code", -999);
                    promise.resolve(result);
                }
            });
        } else {
            WritableMap result = new WritableNativeMap();
            result.putBoolean("success", false);
            result.putString("message", "Không hỗ trợ Android dưới 6.0");
            result.putInt("code", -4);
            promise.resolve(result);
        }
    }

    private PromptInfo getPromptInfo(String promptMessage, String cancelButtonText, boolean allowDeviceCredentials) {
        PromptInfo.Builder builder = new PromptInfo.Builder()
            .setTitle(promptMessage);

        if (allowDeviceCredentials && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { // Android 11+
            builder.setAllowedAuthenticators(
                BiometricManager.Authenticators.BIOMETRIC_STRONG | BiometricManager.Authenticators.DEVICE_CREDENTIAL
            );
        } else {
            builder.setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG);
            builder.setNegativeButtonText(cancelButtonText); // bắt buộc nếu không có DEVICE_CREDENTIAL
        }

        return builder.build();
    }


    private int getAllowedAuthenticators(boolean allowDeviceCredentials) {
        if (allowDeviceCredentials && !isCurrentSDK29OrEarlier()) {
            return BiometricManager.Authenticators.BIOMETRIC_STRONG | BiometricManager.Authenticators.DEVICE_CREDENTIAL;
        }
        return BiometricManager.Authenticators.BIOMETRIC_STRONG;
    }

    private boolean isCurrentSDK29OrEarlier() {
        return Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q;
    }

    @ReactMethod
    public void simplePrompt(final ReadableMap params, final Promise promise) {
        if (isCurrentSDKMarshmallowOrLater()) {
            UiThreadUtil.runOnUiThread(
                    new Runnable() {
                        @Override
                        public void run() {
                            try {
                                String promptMessage = params.getString("promptMessage");
                                String cancelButtonText = params.getString("cancelButtonText");
                                boolean allowDeviceCredentials = params.getBoolean("allowDeviceCredentials");

                                AuthenticationCallback authCallback = new SimplePromptCallback(promise);
                                FragmentActivity fragmentActivity = (FragmentActivity) getCurrentActivity();
                                Executor executor = Executors.newSingleThreadExecutor();
                                BiometricPrompt biometricPrompt = new BiometricPrompt(fragmentActivity, executor, authCallback);

                                biometricPrompt.authenticate(getPromptInfo(promptMessage, cancelButtonText, allowDeviceCredentials));
                            } catch (Exception e) {
                                promise.reject("Error displaying local biometric prompt: " + e.getMessage(), "Error displaying local biometric prompt: " + e.getMessage());
                            }
                        }
                    });
        } else {
            promise.reject("Cannot display biometric prompt on android versions below 6.0", "Cannot display biometric prompt on android versions below 6.0");
        }
    }

    @ReactMethod
    public void biometricKeysExist(final String keytag, Promise promise) {
        if(keytag.isEmpty()){
            promise.reject("keytag is empty", "keytag is empty");
        }else{
            try {
                boolean doesBiometricKeyExist = doesBiometricKeyExist(keytag);
                WritableMap resultMap = new WritableNativeMap();
                resultMap.putBoolean("keysExist", doesBiometricKeyExist);
                promise.resolve(resultMap);
            } catch (Exception e) {
                promise.reject("Error checking if biometric key exists: " + e.getMessage(), "Error checking if biometric key exists: " + e.getMessage());
            }
        }
    }

    protected boolean doesBiometricKeyExist(String keytag) {
        try {
            KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
            keyStore.load(null);

            return keyStore.containsAlias(keytag);
        } catch (Exception e) {
            return false;
        }
    }

    protected boolean deleteBiometricKey() {
        try {
            KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
            keyStore.load(null);

            keyStore.deleteEntry(biometricKeyAlias);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    protected boolean deleteBiometricKeyByKeytag(String key) {
        try {
            KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
            keyStore.load(null);

            keyStore.deleteEntry(key);
            return true;
        } catch (Exception e) {
            return false;
        }
    }
}
