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

import be.iminds.ilabt.jfed.highlevel.controller.TaskThread;
import be.iminds.ilabt.jfed.highlevel.model.ParameterHistoryModel;
import be.iminds.ilabt.jfed.highlevel.model.SfaModel;
import be.iminds.ilabt.jfed.highlevel.tasks.HighLevelTaskFactory;
import be.iminds.ilabt.jfed.lowlevel.AbacCredential;
import be.iminds.ilabt.jfed.lowlevel.GeniUserProvider;
import be.iminds.ilabt.jfed.lowlevel.authority.AuthorityFinder;
import be.iminds.ilabt.jfed.lowlevel.connection.JFedConnectionProvider;
import be.iminds.ilabt.jfed.preferences.JFedPreferences;
import be.iminds.ilabt.jfed.util.IOUtils;
import be.iminds.ilabt.jfed.util.JavaFXLogger;
import be.iminds.ilabt.jfed.util.KeyUtil;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.stage.FileChooser;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import java.io.File;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.Date;

/**
 * CredentialCreatorPanel
 */
public class SpeaksForAbacCredentialCreatorPanelController extends CredentialCreatorPanel {
    private static final org.slf4j.Logger LOG =
            LoggerFactory.getLogger(SpeaksForAbacCredentialCreatorPanelController.class);

    @FXML
    private BorderPane root;
    @FXML
    private Label spokenForUrnLabel;
    @FXML
    private TextField spokenForUrnText;
    @FXML
    private Label spokenForUrnNoteLabel;

    @FXML
    private TextArea speakerCertContent;
    @FXML
    private Button loadCertButton;

    @FXML
    private Button generateButton;
    @FXML
    private Label generateNoteLabel;

    @FXML
    private TextArea resultText;
    @FXML
    private Button saveResultButton;

    @FXML
    private TextField expireText;

    private final ParameterHistoryModel parameterHistoryModel;

    @Inject
    SpeaksForAbacCredentialCreatorPanelController(JavaFXLogger logger, AuthorityFinder authorityFinder,
                                                  GeniUserProvider geniUserProvider, JFedConnectionProvider connectionProvider,
                                                  SfaModel sfaModel, HighLevelTaskFactory hltf,
                                                  TaskThread tt, JFedPreferences jFedPreferences,
                                                  ParameterHistoryModel parameterHistoryModel) {
        super(logger, authorityFinder, geniUserProvider, connectionProvider, sfaModel, hltf, tt, jFedPreferences);
        this.parameterHistoryModel = parameterHistoryModel;
    }


    @FXML
    private void initialize() {
        spokenForUrnLabel.managedProperty().bind(spokenForUrnLabel.visibleProperty());
        spokenForUrnText.managedProperty().bind(spokenForUrnText.visibleProperty());
        spokenForUrnNoteLabel.managedProperty().bind(spokenForUrnNoteLabel.visibleProperty());
        generateButton.managedProperty().bind(generateButton.visibleProperty());
        generateNoteLabel.managedProperty().bind(generateNoteLabel.visibleProperty());
        resultText.managedProperty().bind(resultText.visibleProperty());

        expireText.managedProperty().bind(expireText.visibleProperty());

        spokenForUrnLabel.visibleProperty().bind(spokenForUrnText.visibleProperty());
        spokenForUrnNoteLabel.visibleProperty().bind(spokenForUrnText.visibleProperty());
        generateNoteLabel.visibleProperty().bind(generateButton.visibleProperty());
        saveResultButton.visibleProperty().bind(resultText.visibleProperty());

        updateLoggedInUser();
    }

    @FXML
    private void loadCertFile() {
        FileChooser fileChooser = new FileChooser();
        File file = fileChooser.showOpenDialog(null);

        if (file != null) {
            String filename = file.getPath();
            try {
                String fileContent = IOUtils.fileToString(file);
                Platform.runLater(() -> speakerCertContent.setText(fileContent));
            } catch (Exception e) {
                LOG.error("Error reading user certificate file \"" + filename + "\": " + e.getMessage(), e);
                Platform.runLater(() -> speakerCertContent.setText("ERROR loading file \"" + filename + "\": " + e.getMessage()));
            }
        }
    }


    private X509Certificate spokenForCert;
    private PrivateKey spokenForPrivateKey;
    private X509Certificate speakerCert;

    private void updateLoggedInUser() {
        if (geniUserProvider.isUserLoggedIn()) {
            spokenForUrnText.setText(geniUserProvider.getLoggedInGeniUser().getUserUrnString());
            generateButton.setDisable(false);
            if (!geniUserProvider.getLoggedInGeniUser().getClientCertificateChain().isEmpty())
                spokenForCert = geniUserProvider.getLoggedInGeniUser().getClientCertificateChain().get(0);
            else
                spokenForCert = null;
            spokenForPrivateKey = geniUserProvider.getLoggedInGeniUser().getPrivateKey();
        } else {
            spokenForUrnText.setText("no user logged in");
            generateButton.setDisable(true);
            spokenForCert = null;
            spokenForPrivateKey = null;
        }
    }

    protected void setResult(final String text) {
        Platform.runLater(() -> resultText.setText(text));
    }

    public void generate() {
        speakerCert = KeyUtil.pemToX509Certificate(speakerCertContent.getText());
        if (speakerCert == null) {
            LOG.info("button clicked: generateDelegate not called since no PEM X509 certificate is provided");
            return;
        }

        LOG.debug("button clicked: generateSpeaksFor called");

        int expireInDaysHelper;
        try {
            expireInDaysHelper = Integer.parseInt(expireText.getText());
        } catch (NumberFormatException e) {
            LOG.debug("Error processing \"expire\": \"" + expireText.getText() + "\" is not a valid integer", e);
            setResult("Error processing \"expire\": \"" + expireText.getText() + "\" is not a valid integer");
            return;
        }
        final int expireInDays = expireInDaysHelper;


        try {
            Date expireDate =
                    new Date(System.currentTimeMillis() + (expireInDays * 24 * 60 * 60 * 1000L)); //expireInDays days from now

            LOG.trace("generateSpeaksFor_Helper() creating credential");
            AbacCredential speakForCredential = AbacCredential.createSpeaksFor(
                    expireDate,
                    speakerCert,
                    spokenForCert,
                    spokenForPrivateKey);

            LOG.trace("generateSpeaksFor_Helper() showing result");
            setResult(speakForCredential.getCredentialXml());

            parameterHistoryModel.addUserCredential(speakForCredential);
        } catch (AssertionError e) {
            LOG.error("AssertionError generating speaks for credential", e);
            setResult("AssertionError generating speaks for credential: " + e.getMessage());
        } catch (Exception e) {
            LOG.error("Exception generating speaks for credential", e);
            setResult("ERROR generating speaks for credential: " + e.getMessage());
        }
    }

    public void save() {
        FileChooser fileChooser = new FileChooser();
        fileChooser.setTitle("Save Credential File");
        fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Credential XML Files", "*.xml"));
        fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("All Files", "*.*"));
        File file = fileChooser.showSaveDialog(root.getScene().getWindow());
        if (file != null) {
            try {
                IOUtils.stringToFile(file, resultText.getText());
            } catch (Exception e) {
                LOG.error("Failed to save to file: " + file.getPath(), e);
            }
        }
    }
}
