package io.trustbuilder.mfa.android.demoapp.screen;

import android.os.Bundle;
import android.security.keystore.KeyPermanentlyInvalidatedException;
import android.security.keystore.KeyProperties;
import android.util.Log;
import android.widget.EditText;

import androidx.biometric.BiometricPrompt;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;

import io.trustbuilder.mfa.android.demoapp.R;
import io.trustbuilder.mfa.android.demoapp.security.KeystoreService;
import io.trustbuilder.mfa.lib.definition.CredTypeEnum;
import io.trustbuilder.mfa.lib.generator.crypto.CipherTools;
import io.trustbuilder.mfa.lib.usecase.IUseCase;
import io.trustbuilder.mfa.lib.usecase.definition.CredValue;
import io.trustbuilder.mfa.lib.usecase.definition.CredentialProvider;
import io.trustbuilder.mfa.lib.usecase.definition.biokeyregistration.BiokeyRegistration;
import io.trustbuilder.mfa.lib.usecase.definition.biokeyregistration.BiokeyRegistrationParams;
import io.trustbuilder.mfa.lib.usecase.definition.biokeyregistration.BiokeyRegistrationSetupResult;

public class RegisterBiokeyActivity extends TrustBuilderAuthenticationActivity {

    private EditText disclaimer;
    private byte[] bioChallenge;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "onCreate: RegistrationBiokeyUseCase");
        setTitle(this.getClass().getSimpleName());
        setContentView(R.layout.activity_register_biokey);
        this.tknAlias = this.findViewById(R.id.tknAlias_RegisterBioKey);
        this.disclaimer = this.findViewById(R.id.disclaimer_RegisterBioKey);
        this.proceed = this.findViewById(R.id.proceed_RegisterBiokey);
        this.result = this.findViewById(R.id.result_RegisterBiokey);
    }

    @Override
    protected void onStart() {
        super.onStart();
        Log.d(TAG, "onStart: RegistrationBiokeyUseCase");
        this.proceed.setOnClickListener(v -> {
            if (checkMandatoryField(tknAlias, "tknAlias")) return;
            BiokeyRegistrationParams request = new BiokeyRegistrationParams(tknAlias.getText().toString());
            try {
                BiokeyRegistration biokeyRegistration = mfaLib.startBiokeyRegistration(request);
                BiokeyRegistrationSetupResult setupResult = biokeyRegistration.setup();
                if (setupResult.isError()) {
                    displayJsonResult(setupResult.getError());
                } else {
                    bioChallenge = setupResult.getResponse().getBioChallenge();
                    CredentialProvider credentialProvider = setupResult.getResponse().getCredentialProviders().get(0);
                    displayCredentials(credentialProvider.getCredType(), credentialProvider.getCredMode(), bioChallenge, biokeyRegistration);
                }
            } catch (Exception e) {
                displayException(e);
            }
        });
    }

    @Override
    protected <T extends IUseCase> void executeUseCase(CredValue cred, T usecase) throws Exception {
        registerBioKey(bioChallenge, cred.getCredType(), cred.getValue(), disclaimer.getText().toString().getBytes(StandardCharsets.UTF_8), usecase);
    }

    private <T extends IUseCase> void registerBioKey(byte[] bioChallenge, CredTypeEnum credType, byte[] credValue, byte[] disclaimer, T usecase) {
        BiometricPrompt.PromptInfo promptInfo = new BiometricPrompt.PromptInfo.Builder()
                .setTitle("Register your biometric for my app")
                .setSubtitle("Register your biometric credential")
                .setNegativeButtonText(CANCEL_BUTTON)
                .build();
        CipherTools cipherTools = new CipherTools(KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_CBC + "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7);
        BiometricPrompt.AuthenticationCallback registerBioCallback = createAuthenticationCallback(true, credType, credValue, usecase, bioChallenge, disclaimer, cipherTools);

        initializeCrypto(bioChallenge);
        KeystoreService keystoreService = new KeystoreService();
        SecretKey secretKey = createKey(keystoreService);

        if (initCipherForEncrypt(keystoreService, secretKey, cipherTools)) {
            authenticateWithBiometric(promptInfo, registerBioCallback, cipherTools);
        } else {
            displayJsonResult("Unable to encrypt biometrics object");
        }
    }

    private static SecretKey createKey(KeystoreService keystoreService) {
        SecretKey secretKey = null;
        // The enrolling flow for fingerprint. This is where you ask the user to set up fingerprint
        // for your flow. Use of keys is necessary if you need to know if the set of
        // enrolled fingerprints has changed.
        try {
            // Set the alias of the entry in Android KeyStore where the key will appear
            // and the constrains (purposes) in the constructor of the Builder
            secretKey = keystoreService.generateKey(ANDROID_KEY_STORE, CLIENT_ID);
        } catch (GeneralSecurityException | IOException e) {
            Log.e(TAG, "Error creating secret key in key store", e);
        }
        return secretKey;
    }

    private boolean initCipherForEncrypt(KeystoreService keystoreService, SecretKey secretKey, CipherTools cipherTools) {
        boolean initCipher = false;
        try {
            cipherTools.initCipher(Cipher.ENCRYPT_MODE, secretKey, null);

            initCipher = true;
        } catch (KeyPermanentlyInvalidatedException e) {
            Log.e(TAG, "Error initiating cipher to encrypt: key has been invalidated", e);
            removePermanentlyInvalidatedKey(keystoreService);
        } catch (GeneralSecurityException e) {
            Log.e(TAG, "Error initiating cipher to decrypt", e);
        }
        return initCipher;
    }
}