package be.iminds.ilabt.jfed.ui.javafx.probe_gui.command_arguments;


import be.iminds.ilabt.jfed.highlevel.model.SfaModel;
import be.iminds.ilabt.jfed.lowlevel.GeniUserProvider;
import be.iminds.ilabt.jfed.lowlevel.api.user_spec.UserSpec;
import be.iminds.ilabt.jfed.util.GeniUrn;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.*;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;

/**
 * StringArgumentChooser
 */
public class UserSpecListArgumentChooser extends CommandArgumentChooser<List<UserSpec>> {
    private static final org.slf4j.Logger LOG = org.slf4j.LoggerFactory.getLogger(UserSpecListArgumentChooser.class);

    private static final boolean startWithLoggedInUserAdded = true;
    private static final boolean startWithFirstUserSelected = false;
    private static final boolean saveClearsSelection = false;
    private static final boolean removeSelectsNext = true;
    private static final boolean showSaveOnlyIfDirty = true;
    private static final boolean addSelectsNew = false;
    private static final boolean autoSave = false; //Note: autosave currently has bugs! Don't activate...

    final ObservableList<UserSpec> userSpecs = FXCollections.observableArrayList();
    @FXML
    private ListView<UserSpec> userSpecListView;
    @FXML
    private TextField userUrnTextField;
    //    @FXML private CheckBox sshCheckBox;
    @FXML
    private TextArea sshTextField;
    @FXML
    private Label userUrnLabel;
    @FXML
    private Label sshLabel;
    @FXML
    private Button saveButton;
    @FXML
    private Button addButton;
    @FXML
    private Button removeButton;
    private final ObjectProperty<List<UserSpec>> valueProperty = new SimpleObjectProperty<>();


    private final GeniUserProvider geniUserProvider;
    private final SfaModel sfaModel;

    public UserSpecListArgumentChooser(GeniUserProvider geniUserProvider, SfaModel sfaModel) {
        this.geniUserProvider = geniUserProvider;
        this.sfaModel = sfaModel;


        if (startWithLoggedInUserAdded)
            if (this.geniUserProvider.isUserLoggedIn()) {
                String userUrn = this.geniUserProvider.getLoggedInGeniUser().getUserUrnString();
                assert userUrn != null;
                Collection<String> userKeys = this.sfaModel.getUserKeys();
                if (userKeys == null) userKeys = new ArrayList<>();
                userSpecs.add(new MyUserSpec(userUrn, userKeys));
            }

        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("UserSpecListArgumentChooser.fxml"));
        fxmlLoader.setRoot(this);
        fxmlLoader.setController(this);

        try {
            fxmlLoader.load();

            assert userSpecListView != null;
            userSpecListView.setItems(this.userSpecs);
            userSpecListView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);

            if (!userSpecListView.getItems().isEmpty())
                userSpecListView.getSelectionModel().selectLast();

            final ObservableList<UserSpec> selectedItems = userSpecListView.getSelectionModel().getSelectedItems();
            selectedItems.addListener((ListChangeListener<UserSpec>) change -> valueProperty.set(new ArrayList<>(selectedItems)));
            //init
            valueProperty.set(new ArrayList<>(selectedItems));
            value = valueProperty;
            assert value != null;

//            sshTextField.disableProperty().bind(sshCheckBox.selectedProperty().not());

//            addButton.managedProperty().bind(addButton.visibleProperty());
//            saveButton.managedProperty().bind(saveButton.visibleProperty());
//            removeButton.managedProperty().bind(removeButton.visibleProperty());
//            userUrnTextField.managedProperty().bind(userUrnTextField.visibleProperty());
//            sshTextField.managedProperty().bind(sshTextField.visibleProperty());
//            userUrnLabel.managedProperty().bind(userUrnLabel.visibleProperty());
//            sshLabel.managedProperty().bind(sshLabel.visibleProperty());

            ChangeListener<UserSpec> setEditItemChangeListener = (observableValue, oldUserSpec, newUserSpec) -> {
                if (autoSave && oldUserSpec != null)
                    save(oldUserSpec, userSpecs.indexOf(oldUserSpec));

                if (newUserSpec != null) {
                    if (saveButton != null && !autoSave)
                        saveButton.setVisible(true);
                    removeButton.setVisible(true);
                    userUrnTextField.setVisible(true);
                    sshTextField.setVisible(true);
                    userUrnLabel.setVisible(true);
                    sshLabel.setVisible(true);

                    userUrnTextField.setText(newUserSpec.getUrn());
                    if (newUserSpec.getSshKey().isEmpty()) {
//                            sshCheckBox.setSelected(false);
                        sshTextField.setText("");
                    } else {
//                            sshCheckBox.setSelected(true);
                        String keys = "";
                        for (String key : newUserSpec.getSshKey())
                            keys += key + "\n";
                        sshTextField.setText(keys);
                    }
                } else {
                    if (saveButton != null && !autoSave)
                        saveButton.setVisible(false);
                    removeButton.setVisible(false);
                    userUrnTextField.setVisible(false);
                    sshTextField.setVisible(false);
                    userUrnLabel.setVisible(false);
                    sshLabel.setVisible(false);
                }
            };

            removeButton.setVisible(false);
            if (saveButton != null)
                saveButton.setVisible(false);

//            userSpecListView.getFocusModel().focusedItemProperty()
            userSpecListView.getSelectionModel().selectedItemProperty().addListener(setEditItemChangeListener);
            if (!this.userSpecs.isEmpty()) {
                if (startWithFirstUserSelected) {
                    userSpecListView.getSelectionModel().clearSelection();
                    userSpecListView.getSelectionModel().selectFirst();
//                    setEditItemChangeListener.changed(null, null, userSpecs.get(0));
                } else {
                    userSpecListView.getSelectionModel().clearSelection();
                    setEditItemChangeListener.changed(null, null, null);
                }
            } else
                setEditItemChangeListener.changed(null, null, null);

            userUrnTextField.focusedProperty().addListener((observableValue, wasFocused, isfocused) -> {
                if (autoSave && wasFocused && !isfocused)
                    save();
            });
            sshTextField.focusedProperty().addListener((observableValue, wasFocused, isfocused) -> {
                if (autoSave && wasFocused && !isfocused)
                    save();
            });

            if (showSaveOnlyIfDirty) {
                ChangeListener<String> dirtyListener = (observableValue, oldText, newText) -> {
                    if (userUrnTextField.disableProperty().get())
                        return;

                    UserSpec selectedUserSpec = selectedUserSpec();
                    if (selectedUserSpec == null && !autoSave) {
                        LOG.warn("strange situation: no userspec selected, yet userUrnTextField enabled and edited!");
                        saveButton.setVisible(true);
                        return;
                    }
                    MyUserSpec currentEditUserSpec = currentEditUserSpec();

                    if (!autoSave)
                        saveButton.setVisible(!Objects.equals(currentEditUserSpec, selectedUserSpec));
                };
                userUrnTextField.textProperty().addListener(dirtyListener);
                sshTextField.textProperty().addListener(dirtyListener);
            }
        } catch (IOException exception) {
            throw new RuntimeException(exception);
        }
    }

    private boolean isUrnInList(String userUrn) {
        for (UserSpec userSpec : userSpecs)
            if (Objects.equals(userSpec.getUrn(), userUrn))
                return true;
        return false;
    }

    public UserSpec selectedUserSpec() {
        return userSpecListView.getSelectionModel().getSelectedItem();
    }

    //    public void remove() {
//        String userUrn = userUrnTextField.getText();
//        List<UserSpec> toRemove = new ArrayList<UserSpec>();
//        for (UserSpec userSpec : userSpecs)
//            if (userSpec.getUrn().equals(userUrn))
//                toRemove.add(userSpec);
//        userSpecs.removeAll(toRemove);
//    }
//    public void add() {
//        String userUrn = userUrnTextField.getText();
//        List<String> userKeys = new ArrayList<String>();
//        if (sshCheckBox.isSelected()) {
//            String keysText = sshTextField.getText();
//            String [] keys = keysText.split("\n");
//            for (String key : keys)
//                if (!key.isEmpty())
//                    userKeys.add(key);
//        }
//
//        //if already exists, remove, so we replace it.
//        List<UserSpec> toRemove = new ArrayList<UserSpec>();
//        for (UserSpec userSpec : userSpecs)
//            if (userSpec.getUrn().equals(userUrn))
//                toRemove.add(userSpec);
//        userSpecs.removeAll(toRemove);
//
//        userSpecs.add(new MyUserSpec(userUrn, userKeys));
//    }
    public void remove() {
        UserSpec userSpec = selectedUserSpec();
        if (userSpec == null) return;
        int oldPos = userSpecListView.getSelectionModel().getSelectedIndex();
        userSpecs.remove(userSpec);
        if (removeSelectsNext) {
            if (userSpecs.size() > 0) {
                if (oldPos >= userSpecs.size())
                    oldPos = userSpecs.size() - 1;
                if (oldPos < 0) //should not happen
                    oldPos = 0;
                userSpecListView.getSelectionModel().select(oldPos);
            } else
                userSpecListView.getSelectionModel().clearSelection();
        } else {
            userSpecListView.getSelectionModel().clearSelection();
        }
    }

    public void add() {
        if (autoSave)
            save();

        MyUserSpec newUserSpec = null;
        if (geniUserProvider.isUserLoggedIn()) {
            String userUrn = geniUserProvider.getLoggedInGeniUser().getUserUrnString();
            assert userUrn != null;
            Collection<String> userKeys = sfaModel.getUserKeys();
            if (userKeys == null) userKeys = new ArrayList<>();
            if (!isUrnInList(userUrn))
                newUserSpec = new MyUserSpec(userUrn, userKeys);
        }

        if (newUserSpec == null) {
            String authPart;
            if (geniUserProvider.isUserLoggedIn())
                authPart = geniUserProvider.getLoggedInGeniUser().getUserAuthorityServer().getUrnTld();
            else
                authPart = "authority";
            int usernameCount = 1;
            String userUrn = GeniUrn.createGeniUrnFromEncodedParts(authPart, "user", "user" + usernameCount).toString();
            while (isUrnInList(userUrn)) {
                usernameCount++;
                userUrn = GeniUrn.createGeniUrnFromEncodedParts(authPart, "user", "user" + usernameCount).toString();
            }
            List<String> userKeys = new ArrayList<>();

            newUserSpec = new MyUserSpec(userUrn, userKeys);
        }
        userSpecs.add(newUserSpec);
        if (addSelectsNew) {
            userSpecListView.getSelectionModel().clearSelection();
            userSpecListView.getSelectionModel().select(newUserSpec);
        }
    }

    public void save() {
        UserSpec oldUserSpec = selectedUserSpec();
        if (oldUserSpec == null) return;
        int oldPos = userSpecListView.getSelectionModel().getSelectedIndex();
        save(oldUserSpec, oldPos);
        userSpecListView.getSelectionModel().clearSelection();
        if (!saveClearsSelection)
            userSpecListView.getSelectionModel().select(oldPos);
        if (showSaveOnlyIfDirty)
            saveButton.setVisible(false);
    }

    public void save(UserSpec oldUserSpec, int oldPos) {
        MyUserSpec newUserSpec = currentEditUserSpec();

//        userSpecs.remove(oldUserSpec);
//        userSpecs.add(newUserSpec);

        userSpecs.set(oldPos, newUserSpec);
    }

    public MyUserSpec currentEditUserSpec() {
        String userUrn = userUrnTextField.getText();
        List<String> userKeys = new ArrayList<>();

//        if (sshCheckBox.isSelected()) {
        String keysText = sshTextField.getText();
        String[] keys = keysText.split("\n");
        for (String key : keys)
            if (!key.trim().isEmpty())
                userKeys.add(key);
//        }

        return new MyUserSpec(userUrn, userKeys);
    }

    private class MyUserSpec extends UserSpec {
        public MyUserSpec(String urn, Collection<String> sshKey) {
            super(urn, sshKey);
        }

        public MyUserSpec(String urn) {
            super(urn);
        }

        @Override
        public String toString() {
            String usernameString = urn;
            GeniUrn geniUrn = GeniUrn.parse(urn);
            if (geniUrn != null)
                usernameString = geniUrn.getEncodedResourceName();

            if (!sshKey.isEmpty()) {
//                String keysString = "";
//                //                      keysString += "[ ";
//                boolean first = true;
//                for (String key : sshKey) {
//                    if (!first)
//                        keysString += ", ";
//                    if (key.length() < 10)
//                        keysString += "\""+key+"\"";
//                    else
//                        keysString += "\""+key.substring(0, 10)+"...\"";
//                    first = false;
//                }
//                //                        keysString += " ]";
//
//                if (sshKey.size() == 1)
//                    return usernameString + " with 1 key " + keysString;
//                else
//                    return usernameString + " with "+sshKey.size()+" keys: " + keysString;

                if (sshKey.size() == 1)
                    return usernameString + " with 1 key";
                else
                    return usernameString + " with " + sshKey.size() + " keys";
            } else
                return usernameString;
        }
    }
}
