package com.fsck.k9.ui.messageview;


import android.app.Activity;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes;

import com.fsck.k9.Account;
import com.fsck.k9.K9;
import com.fsck.k9.R;
import com.fsck.k9.mailstore.CryptoResultAnnotation;
import com.fsck.k9.mailstore.MessageViewInfo;
import com.fsck.k9.view.MessageCryptoDisplayStatus;
import timber.log.Timber;


public class MessageCryptoPresenter implements OnCryptoClickListener {
    public static final int REQUEST_CODE_UNKNOWN_KEY = 123;
    public static final int REQUEST_CODE_SECURITY_WARNING = 124;


    // injected state
    private final MessageCryptoMvpView messageCryptoMvpView;


    // persistent state
    private boolean overrideCryptoWarning;


    // transient state
    private CryptoResultAnnotation cryptoResultAnnotation;
    private boolean reloadOnResumeWithoutRecreateFlag;


    public MessageCryptoPresenter(Bundle savedInstanceState, MessageCryptoMvpView messageCryptoMvpView) {
        this.messageCryptoMvpView = messageCryptoMvpView;

        if (savedInstanceState != null) {
            overrideCryptoWarning = savedInstanceState.getBoolean("overrideCryptoWarning");
        }
    }

    public void onSaveInstanceState(Bundle outState) {
        outState.putBoolean("overrideCryptoWarning", overrideCryptoWarning);
    }

    public void onResume() {
        if (reloadOnResumeWithoutRecreateFlag) {
            reloadOnResumeWithoutRecreateFlag = false;
            messageCryptoMvpView.restartMessageCryptoProcessing();
        }
    }

    public boolean maybeHandleShowMessage(MessageTopView messageView, Account account, MessageViewInfo messageViewInfo) {
        this.cryptoResultAnnotation = messageViewInfo.cryptoResultAnnotation;

        MessageCryptoDisplayStatus displayStatus =
                MessageCryptoDisplayStatus.fromResultAnnotation(messageViewInfo.cryptoResultAnnotation);
        if (displayStatus == MessageCryptoDisplayStatus.DISABLED) {
            return false;
        }

        boolean suppressSignOnlyMessages = !K9.getOpenPgpSupportSignOnly();
        if (suppressSignOnlyMessages && displayStatus.isUnencryptedSigned()) {
            return false;
        }

        if (cryptoResultAnnotation.isOverrideSecurityWarning()) {
            overrideCryptoWarning = true;
        }

        messageView.getMessageHeaderView().setCryptoStatus(displayStatus);

        switch (displayStatus) {
            case UNENCRYPTED_SIGN_REVOKED:
            case ENCRYPTED_SIGN_REVOKED: {
                showMessageCryptoWarning(messageView, account, messageViewInfo,
                        R.string.messageview_crypto_warning_revoked);
                break;
            }
            case UNENCRYPTED_SIGN_EXPIRED:
            case ENCRYPTED_SIGN_EXPIRED: {
                showMessageCryptoWarning(messageView, account, messageViewInfo,
                        R.string.messageview_crypto_warning_expired);
                break;
            }
            case UNENCRYPTED_SIGN_INSECURE:
            case ENCRYPTED_SIGN_INSECURE: {
                showMessageCryptoWarning(messageView, account, messageViewInfo,
                        R.string.messageview_crypto_warning_insecure);
                break;
            }
            case UNENCRYPTED_SIGN_ERROR:
            case ENCRYPTED_SIGN_ERROR: {
                showMessageCryptoWarning(messageView, account, messageViewInfo,
                        R.string.messageview_crypto_warning_error);
                break;
            }
            case ENCRYPTED_UNSIGNED: {
                showMessageCryptoWarning(messageView, account, messageViewInfo,
                        R.string.messageview_crypto_warning_unsigned);
                break;
            }

            case CANCELLED: {
                Drawable providerIcon = getOpenPgpApiProviderIcon(messageView.getContext());
                messageView.showMessageCryptoCancelledView(messageViewInfo, providerIcon);
                break;
            }

            case INCOMPLETE_ENCRYPTED: {
                Drawable providerIcon = getOpenPgpApiProviderIcon(messageView.getContext());
                messageView.showMessageEncryptedButIncomplete(messageViewInfo, providerIcon);
                break;
            }

            case ENCRYPTED_ERROR:
            case ENCRYPTED_INSECURE:
            case UNSUPPORTED_ENCRYPTED: {
                Drawable providerIcon = getOpenPgpApiProviderIcon(messageView.getContext());
                if (messageViewInfo.cryptoResultAnnotation.hasReplacementData()) {
                    showMessageCryptoWarning(messageView, account, messageViewInfo,
                            R.string.messageview_crypto_warning_insecure);
                } else {
                    messageView.showMessageCryptoErrorView(messageViewInfo, providerIcon);
                }
                break;
            }

            case ENCRYPTED_NO_PROVIDER: {
                messageView.showCryptoProviderNotConfigured(messageViewInfo);
                break;
            }

            case INCOMPLETE_SIGNED:
            case UNSUPPORTED_SIGNED:
            default: {
                messageView.showMessage(account, messageViewInfo);
                break;
            }

            case LOADING: {
                throw new IllegalStateException("Displaying message while in loading state!");
            }
        }

        return true;
    }

    private void showMessageCryptoWarning(MessageTopView messageView, Account account,
            MessageViewInfo messageViewInfo, @StringRes int warningStringRes) {
        if (overrideCryptoWarning) {
            messageView.showMessage(account, messageViewInfo);
            return;
        }
        Drawable providerIcon = getOpenPgpApiProviderIcon(messageView.getContext());
        boolean showDetailButton = cryptoResultAnnotation.hasOpenPgpInsecureWarningPendingIntent();
        messageView.showMessageCryptoWarning(messageViewInfo, providerIcon, warningStringRes, showDetailButton);
    }

    @Override
    public void onCryptoClick() {
        if (cryptoResultAnnotation == null) {
            return;
        }
        MessageCryptoDisplayStatus displayStatus =
                MessageCryptoDisplayStatus.fromResultAnnotation(cryptoResultAnnotation);
        switch (displayStatus) {
            case LOADING:
                // no need to do anything, there is a progress bar...
                break;
            case UNENCRYPTED_SIGN_UNKNOWN:
                launchPendingIntent(cryptoResultAnnotation);
                break;
            default:
                displayCryptoInfoDialog(displayStatus);
                break;
        }
    }

    @SuppressWarnings("UnusedParameters") // for consistency with Activity.onActivityResult
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == REQUEST_CODE_UNKNOWN_KEY) {
            if (resultCode != Activity.RESULT_OK) {
                return;
            }

            messageCryptoMvpView.restartMessageCryptoProcessing();
        } else if (requestCode == REQUEST_CODE_SECURITY_WARNING) {
            if (overrideCryptoWarning || resultCode != Activity.RESULT_OK) {
                return;
            }

            overrideCryptoWarning = true;
            messageCryptoMvpView.redisplayMessage();
        } else {
            throw new IllegalStateException("got an activity result that wasn't meant for us. this is a bug!");
        }
    }

    private void displayCryptoInfoDialog(MessageCryptoDisplayStatus displayStatus) {
        messageCryptoMvpView.showCryptoInfoDialog(
                displayStatus, cryptoResultAnnotation.hasOpenPgpInsecureWarningPendingIntent());
    }

    private void launchPendingIntent(CryptoResultAnnotation cryptoResultAnnotation) {
        try {
            PendingIntent pendingIntent = cryptoResultAnnotation.getOpenPgpPendingIntent();
            if (pendingIntent != null) {
                messageCryptoMvpView.startPendingIntentForCryptoPresenter(
                        pendingIntent.getIntentSender(), REQUEST_CODE_UNKNOWN_KEY, null, 0, 0, 0);
            }
        } catch (IntentSender.SendIntentException e) {
            Timber.e(e, "SendIntentException");
        }
    }

    public void onClickShowCryptoKey() {
        try {
            PendingIntent pendingIntent = cryptoResultAnnotation.getOpenPgpSigningKeyIntentIfAny();
            if (pendingIntent != null) {
                messageCryptoMvpView.startPendingIntentForCryptoPresenter(
                        pendingIntent.getIntentSender(), null, null, 0, 0, 0);
            }
        } catch (IntentSender.SendIntentException e) {
            Timber.e(e, "SendIntentException");
        }
    }

    public void onClickRetryCryptoOperation() {
        messageCryptoMvpView.restartMessageCryptoProcessing();
    }

    public void onClickShowMessageOverrideWarning() {
        overrideCryptoWarning = true;
        messageCryptoMvpView.redisplayMessage();
    }

    public void onClickShowCryptoWarningDetails() {
        try {
            PendingIntent pendingIntent = cryptoResultAnnotation.getOpenPgpInsecureWarningPendingIntent();
            if (pendingIntent != null) {
                messageCryptoMvpView.startPendingIntentForCryptoPresenter(
                        pendingIntent.getIntentSender(), REQUEST_CODE_SECURITY_WARNING, null, 0, 0, 0);
            }
        } catch (IntentSender.SendIntentException e) {
            Timber.e(e, "SendIntentException");
        }
    }

    public Parcelable getDecryptionResultForReply() {
        if (cryptoResultAnnotation != null && cryptoResultAnnotation.isOpenPgpResult()) {
            return cryptoResultAnnotation.getOpenPgpDecryptionResult();
        }
        return null;
    }

    @Nullable
    private static Drawable getOpenPgpApiProviderIcon(Context context) {
        try {
            String openPgpProvider = K9.getOpenPgpProvider();
            if (K9.NO_OPENPGP_PROVIDER.equals(openPgpProvider)) {
                return null;
            }
            return context.getPackageManager().getApplicationIcon(openPgpProvider);
        } catch (NameNotFoundException e) {
            return null;
        }
    }

    public void onClickConfigureProvider() {
        reloadOnResumeWithoutRecreateFlag = true;
        messageCryptoMvpView.showCryptoConfigDialog();
    }

    public interface MessageCryptoMvpView {
        void redisplayMessage();
        void restartMessageCryptoProcessing();

        void startPendingIntentForCryptoPresenter(IntentSender si, Integer requestCode, Intent fillIntent,
                int flagsMask, int flagValues, int extraFlags) throws IntentSender.SendIntentException;

        void showCryptoInfoDialog(MessageCryptoDisplayStatus displayStatus, boolean hasSecurityWarning);
        void showCryptoConfigDialog();
    }
}
