package com.kabouzeid.gramophone.ui.activities.bugreport;

import android.app.Activity;
import android.app.Dialog;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.StringDef;
import androidx.annotation.StringRes;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.textfield.TextInputEditText;
import com.google.android.material.textfield.TextInputLayout;
import androidx.appcompat.widget.Toolbar;
import android.text.TextUtils;
import android.view.MenuItem;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.widget.RadioButton;
import android.widget.TextView;
import android.widget.Toast;

import com.afollestad.materialdialogs.MaterialDialog;
import com.kabouzeid.appthemehelper.ThemeStore;
import com.kabouzeid.appthemehelper.util.TintHelper;
import com.kabouzeid.gramophone.R;
import com.kabouzeid.gramophone.misc.DialogAsyncTask;
import com.kabouzeid.gramophone.ui.activities.base.AbsThemeActivity;
import com.kabouzeid.gramophone.ui.activities.bugreport.model.DeviceInfo;
import com.kabouzeid.gramophone.ui.activities.bugreport.model.Report;
import com.kabouzeid.gramophone.ui.activities.bugreport.model.github.ExtraInfo;
import com.kabouzeid.gramophone.ui.activities.bugreport.model.github.GithubLogin;
import com.kabouzeid.gramophone.ui.activities.bugreport.model.github.GithubTarget;

import org.eclipse.egit.github.core.Issue;
import org.eclipse.egit.github.core.client.GitHubClient;
import org.eclipse.egit.github.core.client.RequestException;
import org.eclipse.egit.github.core.service.IssueService;

import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

import butterknife.BindView;
import butterknife.ButterKnife;

public class BugReportActivity extends AbsThemeActivity {

    private static final int STATUS_BAD_CREDENTIALS = 401;
    private static final int STATUS_ISSUES_NOT_ENABLED = 410;

    @StringDef({RESULT_OK, RESULT_BAD_CREDENTIALS, RESULT_INVALID_TOKEN, RESULT_ISSUES_NOT_ENABLED,
            RESULT_UNKNOWN})
    @Retention(RetentionPolicy.SOURCE)
    private @interface Result {
    }

    private static final String RESULT_OK = "RESULT_OK";
    private static final String RESULT_BAD_CREDENTIALS = "RESULT_BAD_CREDENTIALS";
    private static final String RESULT_INVALID_TOKEN = "RESULT_INVALID_TOKEN";
    private static final String RESULT_ISSUES_NOT_ENABLED = "RESULT_ISSUES_NOT_ENABLED";
    private static final String RESULT_UNKNOWN = "RESULT_UNKNOWN";

    private DeviceInfo deviceInfo;

    @BindView(R.id.toolbar)
    Toolbar toolbar;

    @BindView(R.id.input_layout_title)
    TextInputLayout inputLayoutTitle;
    @BindView(R.id.input_title)
    TextInputEditText inputTitle;
    @BindView(R.id.input_layout_description)
    TextInputLayout inputLayoutDescription;
    @BindView(R.id.input_description)
    TextInputEditText inputDescription;
    @BindView(R.id.air_textDeviceInfo)
    TextView textDeviceInfo;

    @BindView(R.id.input_layout_username)
    TextInputLayout inputLayoutUsername;
    @BindView(R.id.input_username)
    TextInputEditText inputUsername;
    @BindView(R.id.input_layout_password)
    TextInputLayout inputLayoutPassword;
    @BindView(R.id.input_password)
    TextInputEditText inputPassword;
    @BindView(R.id.option_use_account)
    RadioButton optionUseAccount;
    @BindView(R.id.option_anonymous)
    RadioButton optionManual;

    @BindView(R.id.button_send)
    FloatingActionButton sendFab;

    private static final String ISSUE_TRACKER_LINK = "https://github.com/kabouzeid/Phonograph/issues";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_bug_report);
        ButterKnife.bind(this);

        setStatusbarColorAuto();
        setNavigationbarColorAuto();
        setTaskDescriptionColorAuto();

        initViews();

        if (TextUtils.isEmpty(getTitle()))
            setTitle(R.string.report_an_issue);


        deviceInfo = new DeviceInfo(this);
        textDeviceInfo.setText(deviceInfo.toString());
    }

    private void initViews() {
        final int accentColor = ThemeStore.accentColor(this);
        final int primaryColor = ThemeStore.primaryColor(this);
        toolbar.setBackgroundColor(primaryColor);
        setSupportActionBar(toolbar);
        //noinspection ConstantConditions
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);

        TintHelper.setTintAuto(optionUseAccount, accentColor, false);
        optionUseAccount.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                inputTitle.setEnabled(true);
                inputDescription.setEnabled(true);
                inputUsername.setEnabled(true);
                inputPassword.setEnabled(true);

                optionManual.setChecked(false);
                sendFab.hide(new FloatingActionButton.OnVisibilityChangedListener() {
                    @Override
                    public void onHidden(FloatingActionButton fab) {
                        super.onHidden(fab);
                        sendFab.setImageResource(R.drawable.ic_send_white_24dp);
                        sendFab.show();
                    }
                });
            }
        });
        TintHelper.setTintAuto(optionManual, accentColor, false);
        optionManual.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                inputTitle.setEnabled(false);
                inputDescription.setEnabled(false);
                inputUsername.setEnabled(false);
                inputPassword.setEnabled(false);

                optionUseAccount.setChecked(false);
                sendFab.hide(new FloatingActionButton.OnVisibilityChangedListener() {
                    @Override
                    public void onHidden(FloatingActionButton fab) {
                        super.onHidden(fab);
                        sendFab.setImageResource(R.drawable.ic_open_in_browser_white_24dp);
                        sendFab.show();
                    }
                });
            }
        });

        inputPassword.setOnEditorActionListener((textView, actionId, event) -> {
            if (actionId == EditorInfo.IME_ACTION_SEND) {
                reportIssue();
                return true;
            }
            return false;
        });

        textDeviceInfo.setOnClickListener(v -> copyDeviceInfoToClipBoard());

        TintHelper.setTintAuto(sendFab, accentColor, true);
        sendFab.setOnClickListener(v -> reportIssue());

        TintHelper.setTintAuto(inputTitle, accentColor, false);
        TintHelper.setTintAuto(inputDescription, accentColor, false);
        TintHelper.setTintAuto(inputUsername, accentColor, false);
        TintHelper.setTintAuto(inputPassword, accentColor, false);
    }

    private void reportIssue() {
        if (optionUseAccount.isChecked()) {
            if (!validateInput()) return;
            String username = inputUsername.getText().toString();
            String password = inputPassword.getText().toString();
            sendBugReport(new GithubLogin(username, password));
        } else {
            copyDeviceInfoToClipBoard();

            Intent i = new Intent(Intent.ACTION_VIEW);
            i.setData(Uri.parse(ISSUE_TRACKER_LINK));
            i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            startActivity(i);
        }
    }

    private void copyDeviceInfoToClipBoard() {
        ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
        ClipData clip = ClipData.newPlainText(getString(R.string.device_info), deviceInfo.toMarkdown());
        clipboard.setPrimaryClip(clip);

        Toast.makeText(BugReportActivity.this, R.string.copied_device_info_to_clipboard, Toast.LENGTH_LONG).show();
    }

    private boolean validateInput() {
        boolean hasErrors = false;

        if (optionUseAccount.isChecked()) {
            if (TextUtils.isEmpty(inputUsername.getText())) {
                setError(inputLayoutUsername, R.string.bug_report_no_username);
                hasErrors = true;
            } else {
                removeError(inputLayoutUsername);
            }

            if (TextUtils.isEmpty(inputPassword.getText())) {
                setError(inputLayoutPassword, R.string.bug_report_no_password);
                hasErrors = true;
            } else {
                removeError(inputLayoutPassword);
            }
        }

        if (TextUtils.isEmpty(inputTitle.getText())) {
            setError(inputLayoutTitle, R.string.bug_report_no_title);
            hasErrors = true;
        } else {
            removeError(inputLayoutTitle);
        }

        if (TextUtils.isEmpty(inputDescription.getText())) {
            setError(inputLayoutDescription, R.string.bug_report_no_description);
            hasErrors = true;
        } else {
            removeError(inputLayoutDescription);
        }

        return !hasErrors;
    }

    private void setError(TextInputLayout editTextLayout, @StringRes int errorRes) {
        editTextLayout.setError(getString(errorRes));
    }

    private void removeError(TextInputLayout editTextLayout) {
        editTextLayout.setError(null);
    }

    private void sendBugReport(GithubLogin login) {
        if (!validateInput()) return;

        String bugTitle = inputTitle.getText().toString();
        String bugDescription = inputDescription.getText().toString();

        Report report = new Report(bugTitle, bugDescription, deviceInfo, new ExtraInfo());
        GithubTarget target = new GithubTarget("kabouzeid", "Phonograph");

        ReportIssueAsyncTask.report(this, report, target, login);
    }

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

    private static class ReportIssueAsyncTask extends DialogAsyncTask<Void, Void, String> {
        private final Report report;
        private final GithubTarget target;
        private final GithubLogin login;

        public static void report(Activity activity, Report report, GithubTarget target,
                                  GithubLogin login) {
            new ReportIssueAsyncTask(activity, report, target, login).execute();
        }

        private ReportIssueAsyncTask(Activity activity, Report report, GithubTarget target,
                                     GithubLogin login) {
            super(activity);
            this.report = report;
            this.target = target;
            this.login = login;
        }

        @Override
        protected Dialog createDialog(@NonNull Context context) {
            return new MaterialDialog.Builder(context)
                    .progress(true, 0)
                    .progressIndeterminateStyle(true)
                    .title(R.string.bug_report_uploading)
                    .show();
        }

        @Override
        @Result
        protected String doInBackground(Void... params) {
            GitHubClient client;
            if (login.shouldUseApiToken()) {
                client = new GitHubClient().setOAuth2Token(login.getApiToken());
            } else {
                client = new GitHubClient().setCredentials(login.getUsername(), login.getPassword());
            }

            Issue issue = new Issue().setTitle(report.getTitle()).setBody(report.getDescription());
            try {
                new IssueService(client).createIssue(target.getUsername(), target.getRepository(), issue);
                return RESULT_OK;
            } catch (RequestException e) {
                switch (e.getStatus()) {
                    case STATUS_BAD_CREDENTIALS:
                        if (login.shouldUseApiToken())
                            return RESULT_INVALID_TOKEN;
                        return RESULT_BAD_CREDENTIALS;
                    case STATUS_ISSUES_NOT_ENABLED:
                        return RESULT_ISSUES_NOT_ENABLED;
                    default:
                        e.printStackTrace();
                        return RESULT_UNKNOWN;
                }
            } catch (IOException e) {
                e.printStackTrace();
                return RESULT_UNKNOWN;
            }
        }

        @Override
        protected void onPostExecute(@Result String result) {
            super.onPostExecute(result);

            Context context = getContext();
            if (context == null) return;

            switch (result) {
                case RESULT_OK:
                    tryToFinishActivity();
                    break;
                case RESULT_BAD_CREDENTIALS:
                    new MaterialDialog.Builder(context)
                            .title(R.string.bug_report_failed)
                            .content(R.string.bug_report_failed_wrong_credentials)
                            .positiveText(android.R.string.ok)
                            .show();
                    break;
                case RESULT_INVALID_TOKEN:
                    new MaterialDialog.Builder(context)
                            .title(R.string.bug_report_failed)
                            .content(R.string.bug_report_failed_invalid_token)
                            .positiveText(android.R.string.ok)
                            .show();
                    break;
                case RESULT_ISSUES_NOT_ENABLED:
                    new MaterialDialog.Builder(context)
                            .title(R.string.bug_report_failed)
                            .content(R.string.bug_report_failed_issues_not_available)
                            .positiveText(android.R.string.ok)
                            .show();
                    break;
                default:
                    new MaterialDialog.Builder(context)
                            .title(R.string.bug_report_failed)
                            .content(R.string.bug_report_failed_unknown)
                            .positiveText(android.R.string.ok)
                            .onPositive((dialog, which) -> tryToFinishActivity())
                            .cancelListener(dialog -> tryToFinishActivity())
                            .show();
                    break;
            }
        }

        private void tryToFinishActivity() {
            Context context = getContext();
            if (context instanceof Activity && !((Activity) context).isFinishing()) {
                ((Activity) context).finish();
            }
        }
    }
}