package eu.siacs.conversations.ui;

import android.os.Build;
import android.view.ContextMenu;
import android.view.MenuItem;
import android.view.View;
import android.widget.CompoundButton;
import android.widget.LinearLayout;
import android.widget.Toast;
import androidx.databinding.DataBindingUtil;
import com.google.android.material.color.MaterialColors;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import de.gultsch.common.MiniUri;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.axolotl.FingerprintStatus;
import eu.siacs.conversations.crypto.axolotl.XmppAxolotlSession;
import eu.siacs.conversations.databinding.ItemDeviceFingerprintBinding;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.utils.CryptoHelper;

public abstract class OmemoActivity extends QrCodeScanningActivity {

    private Account mSelectedAccount;
    protected String mSelectedFingerprint;

    protected MiniUri.Xmpp mPendingFingerprintVerificationUri = null;

    @Override
    public void onCreateContextMenu(
            ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
        super.onCreateContextMenu(menu, v, menuInfo);
        Object account = v.getTag(R.id.TAG_ACCOUNT);
        Object fingerprint = v.getTag(R.id.TAG_FINGERPRINT);
        Object fingerprintStatus = v.getTag(R.id.TAG_FINGERPRINT_STATUS);
        if (account instanceof Account
                && fingerprint instanceof String
                && fingerprintStatus instanceof FingerprintStatus) {
            getMenuInflater().inflate(R.menu.omemo_key_context, menu);
            MenuItem distrust = menu.findItem(R.id.distrust_key);
            MenuItem verifyScan = menu.findItem(R.id.verify_scan);
            if (this instanceof TrustKeysActivity) {
                distrust.setVisible(false);
                verifyScan.setVisible(false);
            } else {
                FingerprintStatus status = (FingerprintStatus) fingerprintStatus;
                if (!status.isActive() || status.isVerified()) {
                    verifyScan.setVisible(false);
                }
                distrust.setVisible(
                        status.isVerified() || (!status.isActive() && status.isTrusted()));
            }
            // TODO can we rework this into using Intents?
            this.mSelectedAccount = (Account) account;
            this.mSelectedFingerprint = (String) fingerprint;
        }
    }

    @Override
    public boolean onContextItemSelected(final MenuItem item) {
        final var itemId = item.getItemId();
        if (itemId == R.id.distrust_key) {
            showPurgeKeyDialog(mSelectedAccount, mSelectedFingerprint);
            return true;
        } else if (itemId == R.id.copy_omemo_key) {
            copyOmemoFingerprint(mSelectedFingerprint);
            return true;
        } else if (itemId == R.id.verify_scan) {
            requestPermissionAndScanQrCode();
            return true;
        } else {
            return super.onContextItemSelected(item);
        }
    }

    protected void onQrCodeScanned(final String code) {
        final MiniUri miniUri;
        try {
            miniUri = MiniUri.tryParse(code.trim());
        } catch (final IllegalArgumentException e) {
            Toast.makeText(this, R.string.invalid_barcode, Toast.LENGTH_LONG).show();
            return;
        }
        if (miniUri instanceof MiniUri.Xmpp xmpp && xmpp.isAddress()) {
            if (xmppConnectionServiceBound) {
                processFingerprintVerification(xmpp);
            } else {
                this.mPendingFingerprintVerificationUri = xmpp;
            }
        } else if (miniUri instanceof MiniUri.Transformable transformable
                && transformable.transform() instanceof MiniUri.Xmpp xmpp
                && xmpp.isAddress()) {
            if (xmppConnectionServiceBound) {
                processFingerprintVerification(xmpp);
            } else {
                this.mPendingFingerprintVerificationUri = xmpp;
            }
        } else {
            Toast.makeText(this, R.string.invalid_barcode, Toast.LENGTH_LONG).show();
        }
    }

    protected abstract void processFingerprintVerification(MiniUri.Xmpp uri);

    protected void copyOmemoFingerprint(String fingerprint) {
        if (copyTextToClipboard(
                        CryptoHelper.prettifyFingerprint(fingerprint.substring(2)),
                        R.string.omemo_fingerprint)
                && Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
            Toast.makeText(this, R.string.toast_message_omemo_fingerprint, Toast.LENGTH_SHORT)
                    .show();
        }
    }

    protected void addFingerprintRow(
            LinearLayout keys, final XmppAxolotlSession session, boolean highlight) {
        final Account account = session.getAccount();
        final String fingerprint = session.getFingerprint();
        addFingerprintRowWithListeners(
                keys,
                session.getAccount(),
                fingerprint,
                highlight,
                session.getTrust(),
                true,
                true,
                (buttonView, isChecked) ->
                        account.getAxolotlService()
                                .setFingerprintTrust(
                                        fingerprint, FingerprintStatus.createActive(isChecked)));
    }

    protected void addFingerprintRowWithListeners(
            LinearLayout keys,
            final Account account,
            final String fingerprint,
            boolean highlight,
            FingerprintStatus status,
            boolean showTag,
            boolean undecidedNeedEnablement,
            CompoundButton.OnCheckedChangeListener onCheckedChangeListener) {
        ItemDeviceFingerprintBinding binding =
                DataBindingUtil.inflate(
                        getLayoutInflater(), R.layout.item_device_fingerprint, keys, true);
        binding.tglTrust.setVisibility(View.VISIBLE);
        registerForContextMenu(binding.getRoot());
        binding.getRoot().setTag(R.id.TAG_ACCOUNT, account);
        binding.getRoot().setTag(R.id.TAG_FINGERPRINT, fingerprint);
        binding.getRoot().setTag(R.id.TAG_FINGERPRINT_STATUS, status);
        boolean x509 =
                Config.X509_VERIFICATION
                        && status.getTrust() == FingerprintStatus.Trust.VERIFIED_X509;
        final View.OnClickListener toast;
        binding.tglTrust.setChecked(status.isTrusted());
        binding.tglTrust.jumpDrawablesToCurrentState();

        if (status.isActive()) {
            binding.key.setTextColor(
                    MaterialColors.getColor(
                            binding.key, com.google.android.material.R.attr.colorOnSurface));
            binding.keyType.setTextColor(
                    MaterialColors.getColor(
                            binding.keyType, com.google.android.material.R.attr.colorOnSurface));
            if (status.isVerified()) {
                binding.verifiedFingerprint.setVisibility(View.VISIBLE);
                binding.verifiedFingerprint.setAlpha(1.0f);
                binding.tglTrust.setVisibility(View.GONE);
                binding.verifiedFingerprint.setOnClickListener(
                        v ->
                                replaceToast(
                                        getString(R.string.this_device_has_been_verified), false));
                toast = null;
            } else {
                binding.verifiedFingerprint.setVisibility(View.GONE);
                binding.tglTrust.setVisibility(View.VISIBLE);
                binding.tglTrust.setOnCheckedChangeListener(onCheckedChangeListener);
                if (status.getTrust() == FingerprintStatus.Trust.UNDECIDED
                        && undecidedNeedEnablement) {
                    binding.buttonEnableDevice.setVisibility(View.VISIBLE);
                    binding.buttonEnableDevice.setOnClickListener(
                            v -> {
                                account.getAxolotlService()
                                        .setFingerprintTrust(
                                                fingerprint, FingerprintStatus.createActive(false));
                                binding.buttonEnableDevice.setVisibility(View.GONE);
                                binding.tglTrust.setVisibility(View.VISIBLE);
                            });
                    binding.tglTrust.setVisibility(View.GONE);
                } else {
                    binding.tglTrust.setOnClickListener(null);
                    binding.tglTrust.setEnabled(true);
                }
                toast = v -> hideToast();
            }
        } else {
            binding.key.setTextColor(
                    MaterialColors.getColor(
                            binding.key, com.google.android.material.R.attr.colorOnSurfaceVariant));
            binding.keyType.setTextColor(
                    MaterialColors.getColor(
                            binding.keyType,
                            com.google.android.material.R.attr.colorOnSurfaceVariant));
            toast = v -> replaceToast(getString(R.string.this_device_is_no_longer_in_use), false);
            if (status.isVerified()) {
                binding.tglTrust.setVisibility(View.GONE);
                binding.verifiedFingerprint.setVisibility(View.VISIBLE);
                binding.verifiedFingerprint.setAlpha(0.4368f);
                binding.verifiedFingerprint.setOnClickListener(toast);
            } else {
                binding.tglTrust.setVisibility(View.VISIBLE);
                binding.verifiedFingerprint.setVisibility(View.GONE);
                binding.tglTrust.setEnabled(false);
            }
        }

        binding.getRoot().setOnClickListener(toast);
        binding.key.setOnClickListener(toast);
        binding.keyType.setOnClickListener(toast);
        if (showTag) {
            binding.keyType.setText(
                    getString(x509 ? R.string.omemo_fingerprint_x509 : R.string.omemo_fingerprint));
        } else {
            binding.keyType.setVisibility(View.GONE);
        }
        if (highlight) {
            binding.keyType.setTextColor(
                    MaterialColors.getColor(
                            binding.keyType,
                            com.google.android.material.R.attr.colorPrimaryVariant));
            binding.keyType.setText(
                    getString(
                            x509
                                    ? R.string.omemo_fingerprint_x509_selected_message
                                    : R.string.omemo_fingerprint_selected_message));
        } else {
            binding.keyType.setText(
                    getString(x509 ? R.string.omemo_fingerprint_x509 : R.string.omemo_fingerprint));
        }

        binding.key.setText(CryptoHelper.prettifyFingerprint(fingerprint.substring(2)));
    }

    public void showPurgeKeyDialog(final Account account, final String fingerprint) {
        final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this);
        builder.setTitle(R.string.distrust_omemo_key);
        builder.setMessage(R.string.distrust_omemo_key_text);
        builder.setNegativeButton(getString(R.string.cancel), null);
        builder.setPositiveButton(
                R.string.confirm,
                (dialog, which) -> {
                    account.getAxolotlService().distrustFingerprint(fingerprint);
                    refreshUi();
                });
        builder.create().show();
    }
}
