package be.iminds.ilabt.jfed.experimenter_gui.ui;

import be.iminds.ilabt.jfed.JFedWebApiClientModule;
import be.iminds.ilabt.jfed.bugreport.BugReport;
import be.iminds.ilabt.jfed.experimenter_gui.bugreporting.BugReportComposer;
import be.iminds.ilabt.jfed.experimenter_gui.bugreporting.BugReportToServerTask;
import be.iminds.ilabt.jfed.experimenter_gui.ui.progressbar.TaskService;
import be.iminds.ilabt.jfed.fedmon.webapi.client.FedmonWebApiClientConfig;
import be.iminds.ilabt.jfed.ui.javafx.dialogs.JFDialogs;
import com.google.common.base.Throwables;
import com.google.inject.name.Named;
import javafx.application.Platform;
import javafx.scene.control.ButtonType;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.stage.Stage;
import org.controlsfx.dialog.ExceptionDialog;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import javax.inject.Provider;
import java.util.Objects;
import java.util.Optional;

/**
 * User: twalcari
 * Date: 6/25/2015
 * Time: 13:48
 */
public class UncaughtGuiExceptionToBugReport implements Thread.UncaughtExceptionHandler {
    private static final Logger LOG = LoggerFactory.getLogger(UncaughtGuiExceptionToBugReport.class);

    private final Provider<BugReportComposer> bugReportComposerProvider;
    private final Provider<FedmonWebApiClientConfig> fedmonWebApiClientConfigProvider;
    private final Provider<TaskService> taskServiceProvider;
    private Stage stage;

    @Inject
    public UncaughtGuiExceptionToBugReport(Provider<BugReportComposer> bugReportComposerProvider,
                                           @Named(JFedWebApiClientModule.NAMED__CLIENT_AUTH_WEBAPI_CONFIG) Provider<FedmonWebApiClientConfig> fedmonWebApiClientConfigProvider,
                                           Provider<TaskService> taskServiceProvider) {
        this.bugReportComposerProvider = bugReportComposerProvider;
        this.fedmonWebApiClientConfigProvider = fedmonWebApiClientConfigProvider;
        this.taskServiceProvider = taskServiceProvider;
    }

    public void setStage(Stage stage) {
        this.stage = stage;
    }

    @Override
    public void uncaughtException(Thread t, Throwable uncaughtThrowable) {
        LOG.error("UNCAUGHT ERROR IN THREAD {}", t.getName(), uncaughtThrowable);

        Platform.runLater(() -> {
            if (uncaughtThrowable instanceof OutOfMemoryError) {
                if (stage != null) {
                    try {
                        Optional<ButtonType> reply =
                                JFDialogs.create()
                                        .owner(stage)
                                        .message("jFed has run out of memory. This will cause bugs and instabilities.\n" +
                                                "It is advised to close jFed now and restart it.\n\n" +
                                                "You can choose to continue. You might still be able to save your work.\n\n" +
                                                "Continue running jFed?")
                                        .masthead("Out of Memory!")
                                        .buttonTypes(ButtonType.YES, ButtonType.NO, ButtonType.CANCEL)
                                        .showWarning();

                        if (reply.isPresent() && Objects.equals(reply.get(), ButtonType.YES)) {
                            LOG.warn("User chose to continue after out of memory.");
                        } else {
                            LOG.warn("User chose to exit after out of memory.");
                            System.exit(-1);
                        }
                    } catch (Throwable throwable) {
                        System.err.println("jFed encountered an exception trying to handle OutOfMemoryError dialog. Will exit.");
                        LOG.error("jFed encountered an exception trying to handle OutOfMemoryError dialog. Will exit.", throwable);
                        System.exit(-1);
                    }
                } else {
                    System.err.println("jFed ran out of memory and will exit.");
                    LOG.warn("jFed ran out of memory and will exit.", uncaughtThrowable);
                    System.exit(-1);
                }
            } else {
                try {

                    ExceptionDialog exceptionDialog = new ExceptionDialog(uncaughtThrowable);
                    if (stage != null)
                        exceptionDialog.initOwner(stage);
                    exceptionDialog.setHeaderText("Unexpected error");

                    VBox contentPane = new VBox(10);
                    contentPane.setPrefWidth(600);
                    Label sorryLabel = new Label("We're sorry");
                    sorryLabel.setFont(Font.font(Font.getDefault().getFamily(), FontWeight.BOLD, Font.getDefault().getSize()));

                    Label infoLabel = new Label("jFed detected an unexpected error, which can likely cause bugs and instabilities. If you experience these, we recommend restarting jFed.");
                    infoLabel.setWrapText(true);
                    CheckBox sendBugreportCheckbox = new CheckBox("Tell jFed developers about this error, so they can fix it");
                    sendBugreportCheckbox.setWrapText(true);
                    sendBugreportCheckbox.setSelected(true);

                    TextArea descriptionTextArea = new TextArea();
                    descriptionTextArea.setPromptText("Optional: add extra info");
                    descriptionTextArea.setStyle("-fx-margin: 0 0 0 30px");

                    contentPane.getChildren().addAll(sorryLabel, infoLabel, sendBugreportCheckbox, descriptionTextArea);
                    exceptionDialog.getDialogPane().setContent(contentPane);

                    Platform.runLater(() -> {
                        if (exceptionDialog.showAndWait().isPresent() && sendBugreportCheckbox.isSelected()) {
                            BugReportComposer bugReportComposer = bugReportComposerProvider.get();

                            bugReportComposer.setPostOnPublicList(false);
                            bugReportComposer.setReportTarget(BugReport.ReportTarget.JFED);
                            bugReportComposer.setSubject("Uncaught Exception: " + uncaughtThrowable.getMessage());

                            StringBuilder description = new StringBuilder();
                            description.append(descriptionTextArea.getText().trim());

                            if (description.length() > 0) {
                                description.append("\n---\n\n");
                            }

                            description
                                    .append("Exception:").append(Throwables.getStackTraceAsString(uncaughtThrowable))
                                    .append("\nThread: ").append(t.getName());

                            bugReportComposer.setDescription(description.toString());

                            taskServiceProvider.get().submitTask(new BugReportToServerTask(bugReportComposer, fedmonWebApiClientConfigProvider.get()));
                        }
                    });


                } catch (Throwable throwable) {
                    System.err.println("jFed encountered an exception trying to handle Error dialog.");
                    LOG.error("jFed encountered an exception trying to handle Error dialog.", throwable);
                }

            }
        });
    }
}
