package be.iminds.ilabt.jfed.experimenter_gui.preferences.subsections;

import be.iminds.ilabt.jfed.experimenter_gui.preferences.AbstractPreferencesSubPane;
import be.iminds.ilabt.jfed.experimenter_gui.util.ui.TextFieldWithStatus;
import be.iminds.ilabt.jfed.fedmon.webapi.service.json.Testbed;
import be.iminds.ilabt.jfed.lowlevel.authority.JFedTestbedInfoSource;
import be.iminds.ilabt.jfed.lowlevel.authority.StoredTestbedInfo;
import be.iminds.ilabt.jfed.preferences.CorePreferenceKey;
import be.iminds.ilabt.jfed.preferences.JFedGuiPreferences;
import be.iminds.ilabt.jfed.ui.javafx.dialogs.JFDialogs;
import be.iminds.ilabt.jfed.util.TextUtil;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.RadioButton;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.FileChooser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * PuttyPane
 */
public class TestbedsPane extends AbstractPreferencesSubPane {
    private final static Logger LOG = LoggerFactory.getLogger(TestbedsPane.class);

    @FXML
    private TextFieldWithStatus testbedsXmlUrlTextField;

    @FXML
    private HBox testbedsXmlUrlTextBox;
    @FXML
    private HBox testbedsXmlLocalFileBox;
    @FXML
    private TextFieldWithStatus testbedsXmlLocalFileTextField;
    @FXML
    private Button testbedsXmlLocalFileBrowseButton;

    @FXML
    private RadioButton defaultTestbedsXml;
    @FXML
    private RadioButton onlineTestbedXml;
    @FXML
    private RadioButton localTestbedXml;
    @FXML
    private RadioButton builtinTestbedXml;

    @FXML
    private RadioButton autoForceExoSM;
    @FXML
    private RadioButton yesForceExoSM;
    @FXML
    private RadioButton noForceExoSM;
    @FXML
    private RadioButton bothRecoverExoGeni;
    @FXML
    private RadioButton singleRecoverExoGeni;
    @FXML
    private BorderPane root;

    @FXML
    private Label extraTestbedsLabel;

    @Override
    public Node getRoot() {
        return root;
    }

    private final JFedGuiPreferences jFedPreferences;

    @Inject
    TestbedsPane(JFedGuiPreferences jFedPreferences) {
        super("Testbed Info Source", true/*advanced*/);
        this.jFedPreferences = jFedPreferences;
    }

    @FXML
    private void initialize() {

        testbedsXmlLocalFileBox.managedProperty().bind(testbedsXmlLocalFileBox.visibleProperty());
        testbedsXmlLocalFileBox.visibleProperty().bind(localTestbedXml.selectedProperty());

        testbedsXmlUrlTextBox.managedProperty().bind(testbedsXmlUrlTextBox.visibleProperty());
        testbedsXmlUrlTextBox.visibleProperty().bind(onlineTestbedXml.selectedProperty());

        testbedsXmlUrlTextField.setStatus(TextFieldWithStatus.Status.ERROR);
        testbedsXmlLocalFileTextField.setStatus(TextFieldWithStatus.Status.ERROR);

        testbedsXmlLocalFileTextField.focusedProperty().addListener((observableValue, oldValue, newValue) -> {
            if (newValue) {
                testbedsXmlLocalFileTextField.setStatus(TextFieldWithStatus.Status.NONE);
            } else {
                testTestbedsXmlLocalFile();
            }
        });

        testbedsXmlUrlTextField.focusedProperty().addListener((observableValue, oldValue, newValue) -> {
            if (newValue) {
                testbedsXmlUrlTextField.setStatus(TextFieldWithStatus.Status.NONE);
            } else {
                testTestbedsXmlUrl();
            }
        });

        //load settings
        String testbedsSourcePref = jFedPreferences.getString(CorePreferenceKey.PREF_TESTBEDS_JSON_SOURCE);
        if (testbedsSourcePref.equalsIgnoreCase("AUTO") || testbedsSourcePref.equalsIgnoreCase("DEFAULT")) {
            defaultTestbedsXml.setSelected(true);
        } else {
            if (testbedsSourcePref.equalsIgnoreCase("BUILTIN")) {
                builtinTestbedXml.setSelected(true);
            } else {
                if (testbedsSourcePref.startsWith("file:")) {
                    localTestbedXml.setSelected(true);
                    testbedsXmlLocalFileTextField.setText(testbedsSourcePref.substring(5));
                    testTestbedsXmlLocalFile();
                } else {
                    //must be URL
                    onlineTestbedXml.setSelected(true);
                    testbedsXmlUrlTextField.setText(testbedsSourcePref);
                    testTestbedsXmlUrl();
                }
            }
        }

        String forceExoPref =
                jFedPreferences.getString(CorePreferenceKey.PREF_TESTBEDSPECIFIC_EXOGENI_FORCE_EXOSM);
        Boolean foreExoPrefBool = TextUtil.stringToBoolean(forceExoPref);
        if (forceExoPref.equalsIgnoreCase("AUTO")) {
            autoForceExoSM.setSelected(true);
        } else
            assert foreExoPrefBool != null :
                    "Invalid value for PREF_TESTBEDSPECIFIC_EXOGENI_FORCE_EXOSM: \"" + forceExoPref + "\"";
        if (foreExoPrefBool != null && foreExoPrefBool)
            yesForceExoSM.setSelected(true);
        if (foreExoPrefBool != null && !foreExoPrefBool)
            noForceExoSM.setSelected(true);

        String recoverExoPref =
                jFedPreferences.getString(CorePreferenceKey.PREF_TESTBEDSPECIFIC_EXOGENI_RECOVER_METHOD);
        assert recoverExoPref.equals("BOTH") || recoverExoPref.equals("SINGLE") :
                "invalid preference value: \"" + recoverExoPref + "\"";
        if (Objects.equals(recoverExoPref, "BOTH")) {
            bothRecoverExoGeni.setSelected(true);
        } else {
            singleRecoverExoGeni.setSelected(true);
        }

        updateExtraTestbedsLabels();
    }

    private void updateExtraTestbedsLabels() {
        List<File> extraFiles = JFedTestbedInfoSource.getExtraTestbedFiles();
        List<File> overwriteFiles = JFedTestbedInfoSource.getOverwriteTestbedFiles();
        extraTestbedsLabel.setText(
                "The files below will be scanned for additional testbed information:" +
                        (extraFiles.isEmpty() ? "" : "\n  -  ") +
                        extraFiles.stream().map(File::getAbsolutePath).collect(Collectors.joining("\n   - ")) +
                        (overwriteFiles.isEmpty() ? "" : "\n  - (*) ") +
                        overwriteFiles.stream().map(File::getAbsolutePath).collect(Collectors.joining("\n   - (*) ")) +
                        (overwriteFiles.isEmpty() ? ""
                                : "\n\nThe files marked with a (*) will overwrite information from other sources, the other files will never do this.")
        );
        extraTestbedsLabel.setVisible(!extraFiles.isEmpty() || !overwriteFiles.isEmpty());
    }

    @FXML
    private void onTestbedsXmlLocalFileBrowseButtonAction(ActionEvent actionEvent) {
        FileChooser dc = new FileChooser();
        dc.setTitle("Select the local Testbed Information File");

        File selectedFile = dc.showOpenDialog(testbedsXmlLocalFileTextField.getScene().getWindow());

        if (selectedFile != null) {
            testbedsXmlLocalFileTextField.setText(selectedFile.getAbsolutePath());
            testTestbedsXmlLocalFile();
        }
    }

    private boolean testTestbedsXmlUrl() {
        URL url;
        try {
            url = new URL(testbedsXmlUrlTextField.getText());
        } catch (MalformedURLException e) {
            testbedsXmlUrlTextField.setStatus(TextFieldWithStatus.Status.ERROR);
            testbedsXmlUrlTextField.setStatusDetailString("Not a valid URL");
            return false;
        }

        try {
            List<Testbed> testbeds = StoredTestbedInfo.load(url.openStream());

            if (testbeds != null && !testbeds.isEmpty()) {
                testbedsXmlUrlTextField.setStatus(TextFieldWithStatus.Status.OK);
                testbedsXmlUrlTextField.setStatusDetailString("");
            } else {
                testbedsXmlUrlTextField.setStatus(TextFieldWithStatus.Status.ERROR);
                testbedsXmlUrlTextField.setStatusDetailString("Content at URL is invalid");
                LOG.warn("Invalid content in remote testbeds.json \"" + url + "\"");
            }
            return true;
        } catch (IOException e) {
            testbedsXmlUrlTextField.setStatus(TextFieldWithStatus.Status.ERROR);
            testbedsXmlUrlTextField.setStatusDetailString("IOException opening file: " + e.getMessage());
            LOG.warn("Failed to open user remote testbeds.json \"" + url + "\"", e);
            return false;
        }
    }

    private boolean testTestbedsXmlLocalFile() {
        File file = new File(testbedsXmlLocalFileTextField.getText());

        if (!file.exists()) {
            testbedsXmlLocalFileTextField.setStatus(TextFieldWithStatus.Status.ERROR);
            testbedsXmlLocalFileTextField.setStatusDetailString("File does not exist");
            LOG.warn("local testbeds.json \"" + file.getAbsolutePath() + "\" does not exist");
            return false;
        }

        try {
            List<Testbed> testbeds = StoredTestbedInfo.load(file);

            if (testbeds != null && !testbeds.isEmpty()) {
                testbedsXmlLocalFileTextField.setStatus(TextFieldWithStatus.Status.OK);
                testbedsXmlLocalFileTextField.setStatusDetailString("");
            } else {
                testbedsXmlLocalFileTextField.setStatus(TextFieldWithStatus.Status.ERROR);
                testbedsXmlLocalFileTextField.setStatusDetailString("Invalid content in file");
                LOG.warn("Invalid content in user local testbeds.json \"" + file.getAbsolutePath() + "\"");
            }
            return true;
        } catch (IOException e) {
            testbedsXmlLocalFileTextField.setStatus(TextFieldWithStatus.Status.ERROR);
            testbedsXmlLocalFileTextField.setStatusDetailString("IOException opening or reading file: " + e.getMessage());
            LOG.warn("Failed to open or read user local testbeds.json \"" + file.getAbsolutePath() + "\"", e);
            return false;
        }
    }

//    private boolean testTestbedsXmlContent(String content) {
//        //TODO add a better check!
//        return content.contains("<authorities");
//    }

    @Override
    public boolean check() {
        if (localTestbedXml.isSelected() && testbedsXmlLocalFileTextField.getStatus() != TextFieldWithStatus.Status.OK) {
            JFDialogs.create()
                    .owner(root)
                    .message("Please provide a valid Testbed Information File before saving.")
                    .showError();
            return false;
        }

        if (onlineTestbedXml.isSelected() && testbedsXmlUrlTextField.getStatus() != TextFieldWithStatus.Status.OK) {
            JFDialogs.create()
                    .owner(root)
                    .message("Please provide a valid Testbed Information URL before saving.")
                    .showError();
            return false;
        }

        return true;
    }

    public boolean save() {
        if (!check()) return false;

        if (defaultTestbedsXml.isSelected())
            jFedPreferences.setString(CorePreferenceKey.PREF_TESTBEDS_JSON_SOURCE, "AUTO");
        if (builtinTestbedXml.isSelected())
            jFedPreferences.setString(CorePreferenceKey.PREF_TESTBEDS_JSON_SOURCE, "BUILTIN");
        if (localTestbedXml.isSelected())
            jFedPreferences.setString(CorePreferenceKey.PREF_TESTBEDS_JSON_SOURCE, "file:" + testbedsXmlLocalFileTextField.getText());
        if (onlineTestbedXml.isSelected())
            jFedPreferences.setString(CorePreferenceKey.PREF_TESTBEDS_JSON_SOURCE, testbedsXmlUrlTextField.getText());

        if (autoForceExoSM.isSelected())
            jFedPreferences.setString(CorePreferenceKey.PREF_TESTBEDSPECIFIC_EXOGENI_FORCE_EXOSM, "AUTO");
        if (yesForceExoSM.isSelected())
            jFedPreferences.setString(CorePreferenceKey.PREF_TESTBEDSPECIFIC_EXOGENI_FORCE_EXOSM, "YES");
        if (noForceExoSM.isSelected())
            jFedPreferences.setString(CorePreferenceKey.PREF_TESTBEDSPECIFIC_EXOGENI_FORCE_EXOSM, "NO");

        if (bothRecoverExoGeni.isSelected())
            jFedPreferences.setString(CorePreferenceKey.PREF_TESTBEDSPECIFIC_EXOGENI_RECOVER_METHOD, "BOTH");
        if (singleRecoverExoGeni.isSelected())
            jFedPreferences.setString(CorePreferenceKey.PREF_TESTBEDSPECIFIC_EXOGENI_RECOVER_METHOD, "SINGLE");

        return true;
    }
}
