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

import be.iminds.ilabt.jfed.experimenter_gui.preferences.AbstractPreferencesSubPane;
import be.iminds.ilabt.jfed.lowlevel.GeniUser;
import be.iminds.ilabt.jfed.lowlevel.GeniUserProvider;
import be.iminds.ilabt.jfed.lowlevel.TestbedInfoSource;
import be.iminds.ilabt.jfed.preferences.JFedGuiPreferences;
import be.iminds.ilabt.jfed.preferences.JFedPreferences;
import be.iminds.ilabt.jfed.ui.javafx.GlyphUtils;
import be.iminds.ilabt.jfed.ui.javafx.util.ExpandableHelp;
import be.iminds.ilabt.jfed.util.JFedPasswordManager;
import javafx.application.Platform;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.control.*;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Window;
import javafx.util.Callback;
import org.controlsfx.glyphfont.FontAwesome;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import java.io.IOException;
import java.util.Optional;

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

    private final GeniUserProvider geniUserProvider;
    private final TestbedInfoSource testbedInfoSource;
    private final JFedGuiPreferences jFedPreferences;

    private final JFedPasswordManager passwordManager;

    ObservableList<JFedPasswordManager.LoginInfo> loginInfos;

    @FXML
    protected TableView<JFedPasswordManager.LoginInfo> tableView;
    @FXML
    protected TableColumn<JFedPasswordManager.LoginInfo, String> passColumn;
    @FXML
    protected TableColumn<JFedPasswordManager.LoginInfo, Boolean> removeButtonColumn;

    @FXML
    private BorderPane root;

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

    @Inject
    LoginInfoPane(GeniUserProvider geniUserProvider, TestbedInfoSource testbedInfoSource, JFedGuiPreferences jFedPreferences) {
        super("Additional Accounts", false);
        this.geniUserProvider = geniUserProvider;
        this.testbedInfoSource = testbedInfoSource;
        this.jFedPreferences = jFedPreferences;
        this.passwordManager = new JFedPasswordManager();
        try {
            passwordManager.load(jFedPreferences, geniUserProvider.getLoggedInGeniUser());
        } catch (Exception | AssertionError t) {
            LOG.error("Unexpected Exception recovering passwords", t);
        }
    }

    @FXML
    private void initialize() {
        loginInfos = FXCollections.observableArrayList(passwordManager.getLogins());
        tableView.setItems(loginInfos);
        tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);

        assert passColumn != null;
        passColumn.setCellValueFactory(features -> {
            String pass = features.getValue().getPassword();
            if (pass == null || pass.isEmpty())
                return new ReadOnlyStringWrapper("");
            String hiddenPass = "";
            for (int i = 0; i < pass.length(); i++)
                hiddenPass += "x";
            return new ReadOnlyStringWrapper(hiddenPass);
        });

        assert removeButtonColumn != null;


        removeButtonColumn.setCellValueFactory(p -> new SimpleObjectProperty<>(p.getValue() != null));
        removeButtonColumn.setCellFactory(new Callback<TableColumn<JFedPasswordManager.LoginInfo, Boolean>, TableCell<JFedPasswordManager.LoginInfo, Boolean>>() {
            @Override
            public TableCell<JFedPasswordManager.LoginInfo, Boolean> call(TableColumn<JFedPasswordManager.LoginInfo, Boolean> btnCol) {
                return new TableCell<JFedPasswordManager.LoginInfo, Boolean>() {
                    final Button button = new Button();

                    {
                        button.setGraphic(GlyphUtils.createSmallGlyph(FontAwesome.Glyph.TIMES, Color.RED));
                        button.setText("");
                        button.setTooltip(new Tooltip("Remove Account Info"));
                    }

                    @Override
                    public void updateItem(final Boolean login, boolean empty) {
                        super.updateItem(login, empty);
                        if (login != null) {
                            setGraphic(button);
                            button.setOnAction(event -> {
                                passwordManager.removeLogin((JFedPasswordManager.LoginInfo)getTableRow().getItem());
                                Platform.runLater(() -> {
                                    try {
                                        passwordManager.save(jFedPreferences, geniUserProvider.getLoggedInGeniUser());
                                        loginInfos.setAll(passwordManager.getLogins());
                                    } catch (IOException e) {
                                        LOG.error("Failed to remove password");
                                    }
                                });
                            });
                        } else {
                            setGraphic(null);
                        }
                    }
                };
            }
        });
    }

    @Override
    public boolean check() {
        //nothing to save
        return true;
    }

    @Override
    public boolean save() {
        //nothing to save
        return true;
    }

    public void removeAll(ActionEvent actionEvent) {
        passwordManager.clear();
        try {
            passwordManager.save(jFedPreferences, geniUserProvider.getLoggedInGeniUser());
            loginInfos.setAll(passwordManager.getLogins());
        } catch (IOException e) {
            LOG.error("Failed to save cleared passwords");
        }
    }


    public void addGts(ActionEvent actionEvent) {
        addAccount("GTS");
    }

    public void addAccount(String service) {
        showAddAccountDialog(service, getRoot().getScene().getWindow(), passwordManager, jFedPreferences, geniUserProvider.getLoggedInGeniUser());
        loginInfos.setAll(passwordManager.getLogins());
    }

    public static void showAddAccountDialog(String service, Window window, JFedPasswordManager passwordManager, JFedPreferences jFedPreferences, GeniUser user) {
        JFedPasswordManager.LoginInfo loginInfo = showAddDialog(service, window);
        if (loginInfo == null)
            return;
        passwordManager.addLogin(loginInfo);
        try {
            passwordManager.save(jFedPreferences, user);
        } catch (IOException e) {
            LOG.error("Failed to save login for " + loginInfo.getService() + " user " + loginInfo.getUsername());
        }
    }

    private static JFedPasswordManager.LoginInfo showAddDialog(String service, Window window) {
        HBox hboxUserName = new HBox(10);
        //TODO: do not rely on ExpandableHelp to exist or be in right package
        hboxUserName.getChildren().add(new ImageView(ExpandableHelp.class.getResource("images/user.png").toExternalForm()));
        final TextField txUserName = new TextField();
        txUserName.setPromptText("User Name");
        hboxUserName.getChildren().add(txUserName);


        HBox hboxPassword = new HBox(10);
        hboxPassword.getChildren().add(new ImageView(ExpandableHelp.class.getResource("images/lock.png").toExternalForm()));
        final PasswordField txPassword = new PasswordField();
        txPassword.setPromptText("Password");
        hboxPassword.getChildren().add(txPassword);


        final VBox content = new VBox(10);

        final Label lbMessage =
                new Label("Please enter your " + service + " account information:");
        // lbMessage.getStyleClass().addAll("message-banner");
        content.getChildren().add(lbMessage);
        content.getChildren().add(hboxUserName);
        content.getChildren().add(hboxPassword);

        Dialog<ButtonType> dlg = new Dialog<>();
        dlg.initOwner(window);
        dlg.setTitle(service + " Login");
        DialogPane dlgPane = new DialogPane();

        dlgPane.setContent(content);
        dlgPane.setGraphic(new ImageView(ExpandableHelp.class.getResource("images/login.png").toExternalForm()));
        ButtonType loginButtonType = new ButtonType("Login");
        ButtonType noAccountButtonType =
                new ButtonType("I don't have an account");
        dlgPane.getButtonTypes().setAll(loginButtonType, noAccountButtonType);
        dlg.setDialogPane(dlgPane);

        Platform.runLater(txUserName::requestFocus);
        Optional<ButtonType> response = dlg.showAndWait();


        return response.isPresent() && response.get() == loginButtonType ?
                new JFedPasswordManager.LoginInfo(service, txUserName.getText(), txPassword.getText()) :
                null;
    }
}
