package com.rnbiometrics;
import java.nio.charset.StandardCharsets;
import android.app.KeyguardManager;
import android.content.Context;
import android.os.Build;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyPermanentlyInvalidatedException;
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.cert.Certificate;
import java.security.spec.RSAKeyGenParameterSpec;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
/**
 * 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;
    }
    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") : "";
    UiThreadUtil.runOnUiThread(() -> {
      try {
        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 result = new WritableNativeMap();
        if (e.getMessage() == "Key permanently invalidated") {
          result.putInt("code", -15);
          deleteBiometricKeyByKeytag(keytag);
        } else {
          result.putInt("code", -9);
        }
        result.putBoolean("success", false);
        result.putString("message", "Lỗi tạo csr: " + e.getMessage());

        promise.resolve(result);
      }
    });
  }

  @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 {
        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);
      }
    } catch (Exception e) {
      WritableMap result = new WritableNativeMap();
      result.putBoolean("success", false);
      result.putString(
          "message", "Error generating public private keys: " + e.getMessage());
      result.putInt("code", -9);
      promise.resolve(result);
    }
  }

  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);
      }
    }
  }

  // Cache trong session
  private static PrivateKey cachedPrivateKey = null;
  private static long lastAuthTime = 0;
  private static final long SESSION_TIMEOUT = 3 * 1000; // 30 giây

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

      UiThreadUtil.runOnUiThread(() -> {
        try {
          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;
          }

          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;
          }

          long now = System.currentTimeMillis();
          boolean needAuth = true;

          // Nếu đã xác thực trong session
          if (cachedPrivateKey != null
              && (now - lastAuthTime) < SESSION_TIMEOUT) {
            needAuth = false;
            privateKey = cachedPrivateKey;
          }

          if (needAuth) {
            // Gọi biometric để xác thực
            Signature signature = Signature.getInstance("SHA256withRSA");
            signature.initSign(privateKey);

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

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

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

            // Nếu auth thành công, trong CreateSignatureCallback bạn nhớ set:
            // cachedPrivateKey = privateKey;
            // lastAuthTime = System.currentTimeMillis();

          } else {
            // Không cần auth lại → ký trực tiếp
            Signature signature = Signature.getInstance("SHA256withRSA");
            signature.initSign(privateKey);

            byte[] dataToSign = (type == 1)
                ? Base64.decode(payload, Base64.DEFAULT)
                : payload.getBytes(StandardCharsets.UTF_8);

            signature.update(dataToSign);
            byte[] signedData = signature.sign();

            WritableMap result = new WritableNativeMap();
            result.putBoolean("success", true);
            result.putString(
                "signature", Base64.encodeToString(signedData, Base64.NO_WRAP));
            promise.resolve(result);
          }

        } catch (Exception e) {
          WritableMap result = new WritableNativeMap();
          if ("Key permanently invalidated".equals(e.getMessage())) {
            result.putInt("code", -15);
            deleteBiometricKeyByKeytag(keytag);
          } else {
            result.putInt("code", -9);
          }
          result.putBoolean("success", false);
          result.putString("message", "Lỗi tạo chữ ký: " + e.getMessage());
          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);
    }
  }
  public void setCachedKey(PrivateKey key, long timestamp) {
      this.cachedPrivateKey = key;
      this.lastAuthTime = timestamp;
  }
  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;
    }
  }
}
