package ch.threema.app.threemasafe;

import android.annotation.SuppressLint;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.text.Editable;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import com.google.android.material.textfield.TextInputLayout;

import org.koin.java.KoinJavaComponent;
import org.slf4j.Logger;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import androidx.appcompat.app.ActionBar;
import ch.threema.android.LifecycleAwareAsyncTask;
import ch.threema.app.R;
import ch.threema.app.di.DependencyContainer;
import ch.threema.app.home.HomeActivity;
import ch.threema.app.activities.ThreemaToolbarActivity;
import ch.threema.app.dialogs.GenericAlertDialog;
import ch.threema.app.dialogs.GenericProgressDialog;
import ch.threema.app.fragments.wizard.WizardFragment1;
import ch.threema.app.threemasafe.usecases.CheckBadPasswordUseCase;
import ch.threema.app.ui.InsetSides;
import ch.threema.android.textwatchers.SimpleTextWatcher;
import ch.threema.app.ui.ViewExtensionsKt;
import ch.threema.app.utils.ConfigUtils;
import ch.threema.app.utils.DialogUtil;
import ch.threema.base.ThreemaException;
import static ch.threema.base.utils.LoggingKt.getThreemaLogger;

import static ch.threema.app.threemasafe.ThreemaSafeServiceImpl.MIN_PW_LENGTH;
import static ch.threema.app.threemasafe.usecases.CheckBadPasswordUseCase.Result.BAD_PASSWORD;
import static ch.threema.app.utils.ActiveScreenLoggerKt.logScreenVisibility;
import static ch.threema.common.JavaCompat.isNullOrBlank;
import static ch.threema.common.JavaCompat.isNullOrEmpty;

public class ThreemaSafeConfigureActivity extends ThreemaToolbarActivity implements ThreemaSafeAdvancedDialog.WizardDialogCallback, GenericAlertDialog.DialogClickListener {
    private static final Logger logger = getThreemaLogger("ThreemaSafeConfigureActivity");

    private static final String DIALOG_TAG_PREPARING = "prep";
    private static final String DIALOG_TAG_ADVANCED = "adv";

    public static final String EXTRA_CHANGE_PASSWORD = "cp";
    public static final String EXTRA_WORK_FORCE_PASSWORD = "fp";
    public static final String EXTRA_OPEN_HOME_ACTIVITY = "oha";
    private static final String DIALOG_TAG_UNSAFE_PASSWORD = "unsafe";
    private static final String DIALOG_TAG_UNSAFE_PASSWORD_WORK = "unsafework";

    @NonNull
    private final DependencyContainer dependencies = KoinJavaComponent.get(DependencyContainer.class);
    private final CheckBadPasswordUseCase badPasswordUseCase = KoinJavaComponent.get(CheckBadPasswordUseCase.class);

    private EditText password1, password2;
    private String safePassword = null;
    private TextInputLayout password1layout, password2layout;
    private Button nextButton;
    private boolean updatePasswordOnly;
    private ThreemaSafeServerInfo serverInfo;
    private boolean openHomeActivity = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        logScreenVisibility(this, logger);
    }

    @Override
    protected void handleDeviceInsets() {
        super.handleDeviceInsets();
        ViewExtensionsKt.applyDeviceInsetsAsPadding(
            findViewById(R.id.content_container),
            InsetSides.lbr()
        );
    }

    @SuppressLint("SetTextI18n")
    @Override
    protected boolean initActivity(Bundle savedInstanceState) {
        if (!super.initActivity(savedInstanceState)) {
            return false;
        }

        ActionBar actionBar = getSupportActionBar();
        if (actionBar == null) {
            return false;
        }
        actionBar.setDisplayHomeAsUpEnabled(true);

        Intent intent = getIntent();

        this.password1 = findViewById(R.id.safe_password1);
        this.password2 = findViewById(R.id.safe_password2);
        this.password1layout = findViewById(R.id.password1layout);
        this.password2layout = findViewById(R.id.password2layout);

        this.password1.addTextChangedListener(new PasswordWatcher());
        this.password2.addTextChangedListener(new PasswordWatcher());

        Button advancedButton = findViewById(R.id.advanced_options);
        if ((intent != null && intent.getBooleanExtra(EXTRA_CHANGE_PASSWORD, false))) {
            updatePasswordOnly = true;
            actionBar.setTitle(R.string.safe_change_password);
            advancedButton.setVisibility(View.INVISIBLE);
        } else {
            updatePasswordOnly = false;
            actionBar.setTitle(R.string.safe_configure_choose_password_title);
            advancedButton.setVisibility(View.VISIBLE);
            advancedButton.setOnClickListener(v -> {
                ThreemaSafeAdvancedDialog dialog = ThreemaSafeAdvancedDialog.newInstance(serverInfo, true);
                dialog.show(getSupportFragmentManager(), DIALOG_TAG_ADVANCED);
            });

            if (ConfigUtils.isWorkRestricted() && intent != null && intent.getBooleanExtra(EXTRA_WORK_FORCE_PASSWORD, false)) {
                TextView explainText = findViewById(R.id.safe_enable_explain);
                explainText.setText(getString(R.string.work_safe_forced_explain) + "\n\n" + getString(R.string.safe_configure_choose_password));
            }
        }

        nextButton = findViewById(R.id.next);
        nextButton.setOnClickListener(v -> {
            // finish up
            saveChangesAndExit(safePassword);
        });
        nextButton.setEnabled(false);
        nextButton.setVisibility(View.VISIBLE);

        this.serverInfo = dependencies.getPreferenceService().getThreemaSafeServerInfo();

        if (ConfigUtils.isWorkRestricted()) {
            ThreemaSafeMDMConfig safeMDMConfig = ThreemaSafeMDMConfig.getInstance();
            if (safeMDMConfig.isBackupExpertSettingsDisabled()) {
                advancedButton.setVisibility(View.INVISIBLE);
                this.serverInfo = safeMDMConfig.getServerInfo();
            }
        }

        openHomeActivity = (intent != null && intent.getBooleanExtra(EXTRA_OPEN_HOME_ACTIVITY, false))
            || (savedInstanceState != null && savedInstanceState.getBoolean(EXTRA_OPEN_HOME_ACTIVITY, false));

        return true;
    }

    @Override
    public void onSaveInstanceState(@NonNull Bundle outState, @NonNull PersistableBundle outPersistentState) {
        super.onSaveInstanceState(outState, outPersistentState);

        outState.putBoolean(EXTRA_OPEN_HOME_ACTIVITY, openHomeActivity);
    }

    @Override
    public int getLayoutResource() {
        return R.layout.activity_threema_safe_configure;
    }

    @Override
    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
        if (item.getItemId() == android.R.id.home) {
            onBackPressed();
        }
        return false;
    }

    @SuppressLint("StaticFieldLeak")
    private void saveChangesAndExit(final String safePassword) {
        if (!isNullOrEmpty(safePassword)) {
            new LifecycleAwareAsyncTask<Void, Boolean>() {
                byte[] masterkey;

                @Override
                protected void onPreExecute() {
                    GenericProgressDialog.newInstance(R.string.preparing_threema_safe, R.string.please_wait).show(getSupportFragmentManager(), DIALOG_TAG_PREPARING);
                }

                @Override
                protected Boolean doInBackground(Void params) {
                    masterkey = dependencies.getThreemaSafeService().deriveMasterKey(safePassword, dependencies.getUserService().getIdentity());
                    if (badPasswordUseCase.call(safePassword) == BAD_PASSWORD) {
                        return false;
                    }
                    if (updatePasswordOnly) {
                        deleteExistingBackup();
                    }
                    return true;
                }

                @Override
                protected void onPostExecute(Boolean passwordOK) {
                    DialogUtil.dismissDialog(getSupportFragmentManager(), DIALOG_TAG_PREPARING, true);

                    if (masterkey != null) {
                        if (!passwordOK) {
                            if (dependencies.getAppRestrictions().getSafePasswordPattern() != null) {
                                GenericAlertDialog.newInstance(R.string.password_bad, dependencies.getAppRestrictions().getSafePasswordMessage(), R.string.try_again, 0, false).show(getSupportFragmentManager(), DIALOG_TAG_UNSAFE_PASSWORD_WORK);
                            } else {
                                GenericAlertDialog dialog = GenericAlertDialog.newInstance(R.string.password_bad, R.string.password_bad_explain, R.string.continue_anyway, R.string.try_again, false);
                                dialog.setData(masterkey);
                                dialog.show(getSupportFragmentManager(), DIALOG_TAG_UNSAFE_PASSWORD);
                            }
                        } else {
                            storeKeyAndFinish(masterkey);
                        }
                    } else {
                        Toast.makeText(ThreemaSafeConfigureActivity.this, R.string.safe_error_preparing, Toast.LENGTH_LONG).show();
                        finish();
                    }
                }
            }.execute(this, null);
        } else {
            dependencies.getThreemaSafeService().storeMasterKey(new byte[0]);
            finish();
        }
    }

    @WorkerThread
    private boolean deleteExistingBackup() {
        try {
            dependencies.getThreemaSafeService().deleteBackup();
            return true;
        } catch (ThreemaException e) {
            logger.error("Exception", e);
        }
        return false;
    }

    private void storeKeyAndFinish(byte[] masterkey) {
        var threemaSafeService = dependencies.getThreemaSafeService();
        threemaSafeService.storeMasterKey(masterkey);
        dependencies.getPreferenceService().setThreemaSafeServerInfo(serverInfo);
        threemaSafeService.setEnabled(true);
        threemaSafeService.uploadNow();

        if (updatePasswordOnly) {
            Toast.makeText(this, R.string.safe_password_updated, Toast.LENGTH_LONG).show();
        } else {
            Toast.makeText(this, R.string.safe_activated, Toast.LENGTH_LONG).show();
        }

        if (openHomeActivity) {
            startActivity(HomeActivity.createIntent(ThreemaSafeConfigureActivity.this));
        }

        finish();
    }

    private class PasswordWatcher extends SimpleTextWatcher {
        private PasswordWatcher() {
        }

        @Override
        public void afterTextChanged(@NonNull Editable editable) {
            boolean passwordOk = getPasswordOK(password1.getText().toString(), password2.getText().toString());

            if (passwordOk) {
                safePassword = editable.toString();
                nextButton.setEnabled(true);
            } else {
                safePassword = null;
                nextButton.setEnabled(false);
            }
        }
    }

    private boolean getPasswordOK(String password1Text, String password2Text) {
        var hasSafePasswordPattern = dependencies.getAppRestrictions().getSafePasswordPattern() != null;
        boolean lengthOk = WizardFragment1.getPasswordLengthOK(password1Text, hasSafePasswordPattern ? 1 : MIN_PW_LENGTH);
        boolean passwordsMatch = password1Text.equals(password2Text);

        if (!lengthOk && !password1Text.isEmpty()) {
            this.password1layout.setError(getString(R.string.password_too_short_generic));
            this.password2layout.setError(null);
        } else {
            this.password1layout.setError(null);
            if (!isNullOrBlank(this.password2.getText())) {
                this.password2layout.setError(passwordsMatch ? null : getString(R.string.passwords_dont_match));
            } else {
                this.password2layout.setError(null);
            }
        }

        return (lengthOk && passwordsMatch);
    }

    @Override
    public void onYes(String tag, @NonNull ThreemaSafeServerInfo serverInfo) {
        this.serverInfo = serverInfo;
    }

    @Override
    public void onNo(String tag) {
    }

    @SuppressLint("StaticFieldLeak")
    @Override
    public void onYes(@Nullable String tag, @Nullable Object data) {
        if (!DIALOG_TAG_UNSAFE_PASSWORD_WORK.equals(tag)) {
            if (updatePasswordOnly) {
                new LifecycleAwareAsyncTask<Void, Boolean>() {
                    @Override
                    protected Boolean doInBackground(Void params) {
                        return deleteExistingBackup();
                    }

                    @Override
                    protected void onPostExecute(Boolean success) {
                        storeKeyAndFinish((byte[]) data);
                    }
                }.execute(this, null);
            } else {
                storeKeyAndFinish((byte[]) data);
            }
        }
    }

    @Override
    public void onConfigurationChanged(@NonNull Configuration newConfig) {
        super.onConfigurationChanged(newConfig);

        ConfigUtils.adjustToolbar(this, getToolbar());
    }
}
