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

import be.iminds.ilabt.jfed.experimenter_gui.config.JFedGuiConfig;
import be.iminds.ilabt.jfed.experimenter_gui.util.DateTimeUtils;
import be.iminds.ilabt.jfed.fedmon.webapi.service.json.MaintenanceInfo;
import be.iminds.ilabt.jfed.fedmon.webapi.service.json.ServerGlimpse;
import be.iminds.ilabt.jfed.ui.javafx.FXMLUtil;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.fxml.FXML;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import org.controlsfx.glyphfont.Glyph;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.*;

/**
 * User: twalcari
 * Date: 9/16/2015
 * Time: 14:52
 */
public class HealthStatusPane extends StackPane {
    private static final Comparator<? super MaintenanceInfo> MAINTENANCE_MESSAGE_COMPARATOR =
            Comparator.nullsLast(Comparator.comparing(MaintenanceInfo::getStart))
                    .thenComparing(Comparator.nullsLast(Comparator.comparing(MaintenanceInfo::getEnd)))
                    .thenComparing(MaintenanceInfo::getMessage);

    @FXML
    private Label noInfoLabel;
    @FXML
    private GridPane infoGridPane;

    @FXML
    private Label healthKeyLabel;
    @FXML
    private Label healthLabel;
    @FXML
    private Label reputationLabel;
    @FXML
    private Label freeLabel;
    @FXML
    private Label freeInfoLabel;
    @FXML
    private Label ipFreeLabel;
    @FXML
    private Label ipFreeInfoLabel;
    @FXML
    private Label maintenanceLabel;

    @FXML
    private Glyph healthGlyph;
    @FXML
    private Glyph reputationGlyph;
    @FXML
    private AvailabilityMeter availabiltyMeter;
    @FXML
    private AvailabilityMeter ipAvailabiltyMeter;
    @FXML
    private Glyph maintenanceGlyph;

    @FXML
    private Button maintenanceInfoButton;

    private final ObjectProperty<JFedGuiConfig.ResourceType> type = new SimpleObjectProperty<>(null);
    private final ObjectProperty<ServerGlimpse> serverGlimpse = new SimpleObjectProperty<>(null);

    public HealthStatusPane() {
        FXMLUtil.injectFXML(this);

        availabiltyMeter.useVmCalculationMethodProperty().bind(type.isEqualTo(JFedGuiConfig.ResourceType.VM));
    }


    public void setConfig(JFedGuiConfig config) {
        reputationGlyph.setVisible(config.isShowReputationEnabled());
    }

    public void setServerGlimpse(@Nullable ServerGlimpse glimpse) {
        serverGlimpse.setValue(glimpse);

        if (glimpse == null) {
            noInfoLabel.setVisible(true);
            infoGridPane.setVisible(false);
            return;
        }
        noInfoLabel.setVisible(false);
        infoGridPane.setVisible(true);

        Tooltip healthTooltip;
        if (glimpse.getHealthInfo().getHealthExplanationFull() != null)
            healthTooltip = new Tooltip(glimpse.getHealthInfo().getHealthExplanationFull());
        else
            healthTooltip = new Tooltip("Health explanation unknown");

        healthLabel.setText(glimpse.getHealthInfo().getHealth() != null ? glimpse.getHealthInfo().getHealth() + "%" : "-");
        setGlyphColor(healthGlyph, glimpse.getHealthInfo().getHealth());

        healthLabel.setTooltip(healthTooltip);
        healthKeyLabel.setTooltip(healthTooltip);
        healthGlyph.setTooltip(healthTooltip);

        reputationLabel.setText(glimpse.getReputation() != null ? glimpse.getReputation() + "%" : "-");
        setGlyphColor(reputationGlyph, glimpse.getReputation());

        ServerGlimpse.AdvertisementInfo advInf = glimpse.getAdvertisementInfo();
        if (advInf != null) {
            if (type.get() != null) {
                Integer countAvail;
                Integer countTotal;
                switch (type.get()) {
                    case RAW_PC: {
                        countAvail = advInf.getCount(ServerGlimpse.AdvertisementInfo.CountedObject.RAW_PC, ServerGlimpse.AdvertisementInfo.CountScope.AVAILABLE);
                        countTotal = advInf.getCount(ServerGlimpse.AdvertisementInfo.CountedObject.RAW_PC, ServerGlimpse.AdvertisementInfo.CountScope.ALL);
                        break;
                    }
                    case VM: {
                        countAvail = advInf.getCount(ServerGlimpse.AdvertisementInfo.CountedObject.VM, ServerGlimpse.AdvertisementInfo.CountScope.AVAILABLE);
                        countTotal = advInf.getCount(ServerGlimpse.AdvertisementInfo.CountedObject.VM, ServerGlimpse.AdvertisementInfo.CountScope.ALL);
                        break;
                    }
                    default: {
                        countAvail = advInf.getCount(ServerGlimpse.AdvertisementInfo.CountedObject.ANY, ServerGlimpse.AdvertisementInfo.CountScope.AVAILABLE);
                        countTotal = advInf.getCount(ServerGlimpse.AdvertisementInfo.CountedObject.ANY, ServerGlimpse.AdvertisementInfo.CountScope.ALL);
                        break;
                    }
                }
                if (countAvail != null && countTotal != null && countTotal != 0) {
                    freeLabel.setText("Free " + type.get().getFriendlyName() + ":");

                    availabiltyMeter.setAvailable(countAvail);
                    availabiltyMeter.setTotal(countTotal);
                    availabiltyMeter.setUndetermined(false);

                    freeInfoLabel.setText(countAvail + " of " + countTotal);
                } else {
                    freeLabel.setText("Free resources:");
                    availabiltyMeter.setAvailable(0);
                    availabiltyMeter.setTotal(0);
                    availabiltyMeter.setUndetermined(true);
                    freeInfoLabel.setText("-");
                }
            }

            Integer countIpv4Avail = advInf.getCount(ServerGlimpse.AdvertisementInfo.CountedObject.IPV4, ServerGlimpse.AdvertisementInfo.CountScope.AVAILABLE);
            Integer countIpv4Total = advInf.getCount(ServerGlimpse.AdvertisementInfo.CountedObject.IPV4, ServerGlimpse.AdvertisementInfo.CountScope.ALL);

            if (countIpv4Total != null && countIpv4Total > 0) {
                ipAvailabiltyMeter.setAvailable(countIpv4Avail);
                ipAvailabiltyMeter.setTotal(countIpv4Total);
                ipAvailabiltyMeter.setUndetermined(false);
                ipFreeInfoLabel.setText(countIpv4Avail + " of " + countIpv4Total);
            } else {
                ipAvailabiltyMeter.setAvailable(0);
                ipAvailabiltyMeter.setTotal(0);
                ipAvailabiltyMeter.setUndetermined(true);
                ipFreeInfoLabel.setText("-");
            }
        }

//        //no maintenance:
//        maintenanceInfoButton.setVisible(false);
//        maintenanceLabel.setVisible(false);
//        maintenanceGlyph.setVisible(false);

        Optional<MaintenanceInfo> activeMaintenance;
        Optional<MaintenanceInfo> upcomingMaintenance;
        if (glimpse.getMaintenance() != null && !glimpse.getMaintenance().isEmpty()) {
            activeMaintenance =
                    glimpse.getMaintenance().stream()
                            .filter(mm -> mm.getStartTime() == null || mm.getStartTime().isBefore(ZonedDateTime.now()))
                            .filter(mm -> mm.getEndTime() == null || mm.getEndTime().isAfter(ZonedDateTime.now()))
                            .sorted(MAINTENANCE_MESSAGE_COMPARATOR)
                            .findFirst();

            upcomingMaintenance =
                    glimpse.getMaintenance().stream()
                            .filter(mm -> mm.getStartTime() != null && mm.getStartTime().isAfter(ZonedDateTime.now()))
                            .sorted(MAINTENANCE_MESSAGE_COMPARATOR)
                            .findFirst();
        } else {
            activeMaintenance = Optional.empty();
            upcomingMaintenance = Optional.empty();
        }
        if (activeMaintenance.isPresent()) {
            StringBuilder maintenanceMsg = new StringBuilder("Currently in maintenance ");

            if (activeMaintenance.get().getStartTime() != null)
                maintenanceMsg.append("from ")
                        .append(DateTimeUtils.DATE_HOUR_MINUTES_FORMATTER.format(activeMaintenance.get().getStartTime().withZoneSameInstant(ZoneId.systemDefault())))
                        .append(" ");
            if (activeMaintenance.get().getEndTime() != null)
                maintenanceMsg.append("until ")
                        .append(DateTimeUtils.DATE_HOUR_MINUTES_FORMATTER.format(activeMaintenance.get().getEndTime().withZoneSameInstant(ZoneId.systemDefault())));

            maintenanceLabel.setText(maintenanceMsg.toString());
            maintenanceGlyph.setTextFill(Color.RED);
        } else if (upcomingMaintenance.isPresent()) {
            StringBuilder maintenanceMsg = new StringBuilder("Upcoming maintenance ");

            if (upcomingMaintenance.get().getStartTime() != null)
                maintenanceMsg.append("from ")
                        .append(DateTimeUtils.DATE_HOUR_MINUTES_FORMATTER.format(upcomingMaintenance.get().getStartTime().withZoneSameInstant(ZoneId.systemDefault())))
                        .append(" ");
            if (upcomingMaintenance.get().getEndTime() != null)
                maintenanceMsg.append("until ")
                        .append(DateTimeUtils.DATE_HOUR_MINUTES_FORMATTER.format(upcomingMaintenance.get().getEndTime().withZoneSameInstant(ZoneId.systemDefault())));

            maintenanceLabel.setText(maintenanceMsg.toString());
            maintenanceGlyph.setTextFill(Color.ORANGE);
        } else {
            maintenanceLabel.setText("None planned");
            maintenanceGlyph.setTextFill(Color.GRAY);
        }

        maintenanceInfoButton.setDisable(!activeMaintenance.isPresent() && !upcomingMaintenance.isPresent());
    }

    @FXML
    private void showMaintenanceInfo() {
        //TODO restore maintenance
        assert serverGlimpse.get() != null;
        assert serverGlimpse.get().getMaintenance() != null;
        assert !serverGlimpse.get().getMaintenance().isEmpty();

        List<MaintenanceInfo> maintenances = new ArrayList<>(serverGlimpse.get().getMaintenance());
        maintenances.sort(MAINTENANCE_MESSAGE_COMPARATOR);


        Alert infoAlert = new Alert(Alert.AlertType.INFORMATION);
        infoAlert.setHeaderText("Planned maintenance");
        infoAlert.setContentText(formatMaintenanceInfos(maintenances));
        infoAlert.showAndWait();
    }

    private static void setGlyphColor(@Nonnull Glyph glyph, @Nullable Integer value) {
        if (value == null) {
            glyph.setTextFill(Color.GRAY);
        } else if (value > 95) {
            glyph.setTextFill(Color.GREEN);
        } else if (value > 66) {
            glyph.setTextFill(Color.YELLOWGREEN);
        } else if (value > 33) {
            glyph.setTextFill(Color.ORANGE);
        } else {
            glyph.setTextFill(Color.RED);
        }
    }

    public JFedGuiConfig.ResourceType getType() {
        return type.get();
    }

    public ObjectProperty<JFedGuiConfig.ResourceType> typeProperty() {
        return type;
    }

    public void setType(JFedGuiConfig.ResourceType type) {
        this.type.set(type);
    }



    @Nonnull
    public static String formatMaintenanceInfos(List<MaintenanceInfo> maintenanceInfos) {
        StringBuilder maintenanceMsg = new StringBuilder();

        for (MaintenanceInfo activeMaintenance : maintenanceInfos) {
            if (activeMaintenance.getStartTime() != null && activeMaintenance.getEndTime() != null) {
                maintenanceMsg.append("From ")
                        .append(DateTimeUtils.DATE_HOUR_MINUTES_FORMATTER.format(activeMaintenance.getStartTime().withZoneSameInstant(ZoneId.systemDefault())))
                        .append(" until ")
                        .append(DateTimeUtils.DATE_HOUR_MINUTES_FORMATTER.format(activeMaintenance.getEndTime().withZoneSameInstant(ZoneId.systemDefault())))
                        .append(": ");
            } else if (activeMaintenance.getStartTime() != null) {
                maintenanceMsg.append("From ")
                        .append(DateTimeUtils.DATE_HOUR_MINUTES_FORMATTER.format(activeMaintenance.getStartTime().withZoneSameInstant(ZoneId.systemDefault())))
                        .append(": ");
            } else if (activeMaintenance.getEndTime() != null) {
                maintenanceMsg
                        .append("Until ")
                        .append(DateTimeUtils.DATE_HOUR_MINUTES_FORMATTER.format(activeMaintenance.getEndTime().withZoneSameInstant(ZoneId.systemDefault())))
                        .append(": ");
            }

            maintenanceMsg.append(activeMaintenance.getMessage()).append("\n\n");
        }
        return maintenanceMsg.toString().trim();
    }
}
