package ch.zhaw.gartenverwaltung;

import ch.zhaw.gartenverwaltung.bootstrap.AppLoader;
import ch.zhaw.gartenverwaltung.bootstrap.Inject;
import ch.zhaw.gartenverwaltung.io.PlantList;
import ch.zhaw.gartenverwaltung.io.TaskList;
import ch.zhaw.gartenverwaltung.models.Garden;
import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException;
import ch.zhaw.gartenverwaltung.models.GardenSchedule;
import ch.zhaw.gartenverwaltung.models.PlantNotFoundException;
import ch.zhaw.gartenverwaltung.types.Crop;
import ch.zhaw.gartenverwaltung.types.Pest;
import ch.zhaw.gartenverwaltung.types.Plant;
import ch.zhaw.gartenverwaltung.types.Task;
import javafx.application.Platform;
import javafx.beans.property.ListProperty;
import javafx.beans.property.SimpleListProperty;
import javafx.collections.FXCollections;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.geometry.Pos;
import javafx.scene.control.*;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.stage.Stage;

import java.io.IOException;
import java.util.List;
import java.util.Objects;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Controller class for the CropDetail.fxml file
 */
public class CropDetailController {
    private Crop crop;

    @Inject
    private PlantList plantList;
    @Inject
    private GardenSchedule gardenSchedule;
    @Inject
    private Garden garden;

    @Inject
    AppLoader appLoader;

    private static final Logger LOG = Logger.getLogger(CropDetailController.class.getName());
    private final ListProperty<Task> taskListProperty = new SimpleListProperty<>(FXCollections.observableArrayList());
    private final ListProperty<Pest> pestListProperty = new SimpleListProperty<>(FXCollections.observableArrayList());

    @FXML
    private ImageView imageView;

    @FXML
    private Button area_button;

    @FXML
    private Label area_label;

    @FXML
    private Label cropName_label;

    @FXML
    private Label description_label;

    @FXML
    private Label light_label;

    @FXML
    private Label soil_label;

    @FXML
    private Label spacing_label;

    @FXML
    private Button addTask_button;

    @FXML
    private ListView<Task> taskList_listView;

    @FXML
    private ListView<Pest> pests_listView;

    @FXML
    void addTask() throws IOException, HardinessZoneNotSetException {
        createTaskDialog(true, null);
    }

    /**
     * close Window
     */
    @FXML
    void goBack() {
        Stage stage = (Stage) imageView.getScene().getWindow();
        stage.close();
    }

    /**
     * open dialog to set area
     */
    @FXML
    void setArea() throws IOException {
        openTextFieldDialog();
    }

    /**
     * set labels and image from selected {@link Crop}
     * set icons for buttons
     * @param crop {@link Crop} which will be displayed
     * @throws PlantNotFoundException exception
     */
    public void setPlantFromCrop(Crop crop) throws PlantNotFoundException {
        this.crop = crop;
        try {
            Plant plant = plantList.getPlantById(Settings.getInstance().getCurrentHardinessZone(), crop.getPlantId())
                    .orElseThrow(PlantNotFoundException::new);

            cropName_label.setText(plant.name());
            description_label.setText(plant.description());
            light_label.setText(String.valueOf(plant.light()));
            soil_label.setText(plant.soil());
            spacing_label.setText(plant.spacing());
            if (plant.image() != null) {
                imageView.setImage(plant.image());
            }
            area_label.setText(String.valueOf(crop.getArea()));

            initializeTaskListProperty(crop);

            TaskList.TaskListObserver taskListObserver = newTaskList -> {
                Platform.runLater(() -> {
                    taskListProperty.clear();
                    try {
                        taskListProperty.addAll(gardenSchedule.getTaskListForCrop(crop.getCropId().get()));
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                });
            };
            gardenSchedule.setTaskListObserver(taskListObserver);

            taskList_listView.itemsProperty().bind(taskListProperty);

            pestListProperty.addAll(plant.pests());
            pests_listView.itemsProperty().bind(pestListProperty);


        } catch (HardinessZoneNotSetException | IOException e) {
            throw new PlantNotFoundException();
        }
        setIconToButton(addTask_button, "addIcon.png");
        setIconToButton(area_button, "areaIcon.png");
        setCellFactoryPests();
        setCellFactoryTasks();
    }

    /**
     * cell Factory for TaskListView
     */
    private void setCellFactoryTasks() {
        taskList_listView.setCellFactory(param -> new ListCell<>() {
            @Override
            protected void updateItem(Task task, boolean empty) {
                super.updateItem(task, empty);

                if (empty || task == null) {
                    setText(null);
                    setGraphic(null);
                } else {
                    setText("");
                    setGraphic(createTaskHBox(task));
                }
            }
        });
    }

    /**
     * cell Factory for PestListView
     */
    private void setCellFactoryPests() {
        pests_listView.setCellFactory(param -> new ListCell<>() {
            @Override
            protected void updateItem(Pest pest, boolean empty) {
                super.updateItem(pest, empty);

                if (empty || pest == null) {
                    setText(null);
                    setGraphic(null);
                } else {
                    setText("");
                    setGraphic(createPestHBox(pest));
                }
            }
        });
    }

    /**
     * initialize task list
     * @param crop {@link Crop} that is selected
     */
    private void initializeTaskListProperty(Crop crop) {
        crop.getCropId().ifPresent(id -> {
            List<Task> taskList;
            try {
                taskList = gardenSchedule.getTaskListForCrop(id);
                taskListProperty.clear();
                taskListProperty.addAll(taskList);
            } catch (IOException e) {
                // TODO: Alert
                LOG.log(Level.SEVERE, "Could not get task list for crop", e.getCause());
            }
        });
    }

    /**
     * Creates a {@link HBox} for the given {@link Task}.
     * @param task {@link Task} which is selected
     * @return {@link HBox} that was created
     */
    private HBox createTaskHBox(Task task) {
        HBox hBox = new HBox(10);
        Label taskName = new Label(task.getName()+": ");
        taskName.setMinWidth(100);
        taskName.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
        taskName.setStyle("-fx-font-weight: bold");
        Label taskDescription = new Label(task.getDescription());
        taskDescription.setWrapText(true);
        taskDescription.setMaxSize(600, Double.MAX_VALUE);
        Pane puffer = new Pane();
        HBox.setHgrow(puffer, Priority.ALWAYS);


        Button edit = new Button();
        Button delete = new Button();
        edit.getStyleClass().add("button-class");
        delete.getStyleClass().add("button-class");
        HBox.setHgrow(edit, Priority.NEVER);
        HBox.setHgrow(delete, Priority.NEVER);
        setIconToButton(edit, "editIcon.png");
        setIconToButton(delete, "deleteIcon.png");
        edit.setOnAction(getEditTaskEvent(task));
        delete.setOnAction(deleteTask(task));

        hBox.getChildren().addAll(taskName, taskDescription, puffer, edit, delete);
        return hBox;
    }

    /**
     * Creates a {@link HBox} for the given {@link Pest}.
     * @param pest {@link Pest} which is selected
     * @return {@link HBox} that was created
     */
    private HBox createPestHBox(Pest pest) {
        Label label = new Label(pest.name() + ":    ");
        label.setStyle("-fx-font-weight: bold");
        HBox hBox = new HBox();
        hBox.fillHeightProperty();
        Label description = new Label(pest.description());
        description.setAlignment(Pos.TOP_LEFT);
        description.setWrapText(true);
        description.setMaxWidth(800);
        hBox.getChildren().addAll(label, description);
        return hBox;
    }

    /**
     * adds icon to button
     * @param button the button which get the icon
     * @param iconFileName file name of icon
     */
    private void setIconToButton(Button button, String iconFileName) {
        Image img = new Image(String.valueOf(getClass().getResource("icons/" + iconFileName)));
        ImageView imageView = new ImageView(img);
        imageView.setFitHeight(20);
        imageView.setPreserveRatio(true);
        button.setGraphic(imageView);
    }

    /**
     * opens dialog of {@link Task} edit.
     * @param task {@link Task}
     * @return {@link EventHandler} for the case of editing a {@link Task}
     */
    private EventHandler<ActionEvent> getEditTaskEvent(Task task) {
        return (event) -> {
            try {
                createTaskDialog(false, task);
            } catch (IOException | HardinessZoneNotSetException e) {
                e.printStackTrace();
            }
        };
    }

    /**
     * opens alert of {@link Task} deletion.
     * @param task {@link Task}
     * @return {@link EventHandler} for the case of deleting a {@link Task}
     */
    private EventHandler<ActionEvent> deleteTask(Task task) {
        return (event) -> {
            showDeleteTask(task);
        };
    }

    /**
     * opens a dialog to create a new Task or edit the given Task
     * @param newTask boolean if it is a new Task
     * @param givenTask {@link Task} which was selected
     * @throws IOException Exception
     * @throws HardinessZoneNotSetException Exception
     */
    private void createTaskDialog(boolean newTask, Task givenTask) throws IOException, HardinessZoneNotSetException {
        Dialog<Task> dialog = new Dialog<>();
        dialog.setTitle("Set Task");
        dialog.setHeaderText("Add/Edit Task:");
        dialog.setResizable(false);

        DialogPane dialogPane = dialog.getDialogPane();

        dialogPane.getStylesheets().add(
                Objects.requireNonNull(getClass().getResource("bootstrap/dialogStyle.css")).toExternalForm());
        dialogPane.getStyleClass().add("myDialog");

        ButtonType saveTask;
        if(newTask) {
            saveTask = new ButtonType("Add", ButtonBar.ButtonData.OK_DONE);
        } else {
            saveTask = new ButtonType("Save", ButtonBar.ButtonData.OK_DONE);
        }
        dialogPane.getButtonTypes().addAll(saveTask, ButtonType.CANCEL);

        if (appLoader.loadPaneToDialog("TaskFormular.fxml", dialogPane) instanceof TaskFormularController controller) {
            controller.setCorp(this.crop);
            controller.initSaveButton((Button) dialogPane.lookupButton(saveTask));
            if (!newTask) {
                controller.setTaskValue(givenTask);
            }
            dialog.setResultConverter(button -> button.equals(saveTask) ? controller.returnResult(this.crop) : null);

            dialog.showAndWait()
                    .ifPresent(task -> {
                        if (newTask) {
                            try {
                                gardenSchedule.addTask(task);
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        } else {
                            try {
                                gardenSchedule.addTask(givenTask.updateTask(task));
                            } catch (IOException e) {
                                throw new RuntimeException(e);
                            }
                        }
                    });
        }

    }

    /**
     * opens TextField Dialog to enter the plant area.
     * @throws IOException Exception
     */
    private void openTextFieldDialog() throws IOException {
        Dialog<String> dialog = new Dialog<>();
        dialog.setTitle("set Text Area");
        dialog.setHeaderText("set Text Area");
        dialog.setResizable(false);

        DialogPane dialogPane = dialog.getDialogPane();

        dialogPane.getStylesheets().add(
                Objects.requireNonNull(getClass().getResource("bootstrap/dialogStyle.css")).toExternalForm());
        dialogPane.getStyleClass().add("myDialog");

        ButtonType save = new ButtonType("Save", ButtonBar.ButtonData.OK_DONE);
        dialogPane.getButtonTypes().addAll(save, ButtonType.CANCEL);

        if (appLoader.loadPaneToDialog("TextFieldFormular.fxml", dialogPane) instanceof TextFieldFormularController controller) {
            controller.setDescription_label("Text Area");
            controller.setValueTextArea(area_label.getText());
            controller.initSaveButton((Button) dialogPane.lookupButton(save));

            dialog.setResultConverter(button -> button.equals(save) ? controller.getValue() : null);

            dialog.showAndWait()
                    .ifPresent(string -> {
                        try {
                            garden.updateCrop(this.crop.withArea(Double.parseDouble(string)));
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                        area_label.setText(string);
                    });
        }
    }

    /**
     * Alert to delete Task.
     * @param task {@link Task} which is being deleted
     */
    private void showDeleteTask(Task task) {
        Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
        alert.setTitle("Delete " + task.getName());
        alert.setHeaderText("Are you sure want to delete this Task?");

        DialogPane dialogPane = alert.getDialogPane();

        dialogPane.getStylesheets().add(
                Objects.requireNonNull(getClass().getResource("bootstrap/dialogStyle.css")).toExternalForm());
        dialogPane.getStyleClass().add("myDialog");

        alert.showAndWait()
                .ifPresent(buttonType -> {
                    if (buttonType == ButtonType.OK) {
                        try {
                            gardenSchedule.removeTask(task);
                            //setTaskListProperty(this.crop);
                        } catch (IOException e) {
                            // TODO: Show error alert
                            LOG.log(Level.SEVERE, "Could not remove crop.", e);
                        }
                    }
                });
    }
}
