package ch.zhaw.gartenverwaltung.backgroundtasks.weather;

import ch.zhaw.gartenverwaltung.io.CropList;
import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException;
import ch.zhaw.gartenverwaltung.io.PlantList;
import ch.zhaw.gartenverwaltung.io.TaskList;
import ch.zhaw.gartenverwaltung.models.PlantNotFoundException;
import ch.zhaw.gartenverwaltung.types.*;

import java.io.IOException;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import static java.util.stream.Collectors.toList;

/**
 * The WeatherGardenTaskPlanner creates Tasks based on weather events and the rain amount from the last days
 */
public class WeatherGradenTaskPlanner {
    private final TaskList taskList;
    private final PlantList plantList;
    private final CropList cropList;
    WeatherService weatherService;
    private final LocalDate dateSevereWeather = LocalDate.now();

    public WeatherGradenTaskPlanner(TaskList taskList, PlantList plantList, CropList cropList) {
        this.taskList = taskList;
        this.plantList = plantList;
        this.cropList = cropList;
        weatherService = new WeatherService();
    }

    /**
     * Method to refresh watering tasks and severe weather tasks
     * @throws IOException If the database cannot be accessed
     * @throws HardinessZoneNotSetException If the hardiness zone is not available
     * @throws PlantNotFoundException if the plant is not available for the watering task
     */
    public void refreshTasks() throws IOException, HardinessZoneNotSetException, PlantNotFoundException {
        getSevereWeatherEvents();
        getRainAmount();
    }

    private void getSevereWeatherEvents() throws IOException {
        SevereWeather actualWeather = weatherService.causeSevereWeather(1);
        if (SevereWeather.HAIL.equals(actualWeather)) {
            createPreHailTask();
        } else if (SevereWeather.FROST.equals(actualWeather)) {
            createPreFrostTask();
        } else if (SevereWeather.SNOW.equals(actualWeather)) {
            createPreSnowTask();
        }
    }

    private void getRainAmount() throws IOException, HardinessZoneNotSetException, PlantNotFoundException {
        int rainAmount = weatherService.causeRainAmount(3);
        adjustWateringTask(rainAmount);
    }

    /**
     * Method to create a PreHailTask
     * @throws IOException If the database cannot be accessed
     */
    private void createPreHailTask() throws IOException {

        List<Crop> actualCrops = cropList.getCrops();

        for (Crop crop : actualCrops) {
            Task preHailTask = new Task("Hail",
                    "During a summer Thunderstorm it could hail heavily. THe Hail could damage the crops. To prevent damage cover the plants with a strong tarpaulin",
                    dateSevereWeather,dateSevereWeather.plusDays(1L),crop.getCropId().orElse(-1L));

            List<Task> actualCropTaskList = taskList.getTaskForCrop(crop.getCropId().orElse(-1L));
            List<Task> hailTasklist = actualCropTaskList.stream().filter(task -> task.getName().equals("Hail")).toList();

            List<Task> hailTaskListAtDate = new ArrayList<>();
            for (Task task : hailTasklist) {
                if (task.getStartDate() == (preHailTask.getStartDate())) {
                    hailTaskListAtDate.add(task);
                }
            }

            if(hailTaskListAtDate.isEmpty() && hailTasklist.isEmpty()){
                taskList.saveTask(preHailTask);
            }
        }
    }

    /**
     * Method to create a PreFrosttask
     * @throws IOException If the database cannot be accessed
     */
    private void createPreFrostTask() throws IOException {
        List<Crop> actualCrops = cropList.getCrops();
        for (Crop crop : actualCrops) {

            Task preFrostTask = new Task("Frost",
                    "The temperatur falls below zero degrees, cover especially the root with wool",
                    dateSevereWeather,dateSevereWeather.plusDays(1L),crop.getCropId().orElse(-1L));

            List<Task> actualCropTaskList = taskList.getTaskForCrop(crop.getCropId().orElse(-1L));
            List<Task> frostTasklist = actualCropTaskList.stream().filter(task -> task.getName().equals("Frost")).toList();

            List<Task> frostTaskListAtDate = new ArrayList<>();
            for (Task task : frostTasklist) {
                if (task.getStartDate() == preFrostTask.getStartDate()) {
                    frostTaskListAtDate.add(task);
                }
            }
            if(frostTaskListAtDate.isEmpty() && frostTasklist.isEmpty()){
                taskList.saveTask(preFrostTask);
            }
        }
    }

    /**
     * Method to create a PreSnowTask
     * @throws IOException If the database cannot be accessed
     */
    private void createPreSnowTask() throws IOException {
        List<Crop> actualCrops = cropList.getCrops();
        for (Crop crop : actualCrops) {

            Task preSnowTask = new Task("Snow",
                    "The weather brings little snowfall. Cover your crops",
                    dateSevereWeather, dateSevereWeather.plusDays(1L), crop.getCropId().orElse(-1L));

            List<Task> actualCropTaskList = taskList.getTaskForCrop(crop.getCropId().orElse(-1L));
            List<Task> snowTasklist = actualCropTaskList.stream().filter(task -> task.getName().equals("Snow")).toList();

            List<Task> snowTaskListAtDate = new ArrayList<>();
            for (Task task : snowTasklist) {
                if (task.getStartDate() == preSnowTask.getStartDate()) {
                    snowTaskListAtDate.add(task);
                }
            }
            if(snowTaskListAtDate .isEmpty() && snowTasklist.isEmpty()){
                taskList.saveTask(preSnowTask);
            }

        }
    }

    /**
     * Method to adjust the water plant tasks
     * @param rainAmount Amount of rain from the last 7 days
     * @throws IOException If the database cannot be accessed
     * @throws HardinessZoneNotSetException If the hardiness zone is not available
     * @throws PlantNotFoundException if the plant is not available for the watering task
     */
    private void adjustWateringTask(int rainAmount) throws HardinessZoneNotSetException, IOException, PlantNotFoundException {

        for (Crop crop : cropList.getCrops()) {
            Plant plant = plantList.getPlantById(HardinessZone.ZONE_8A,crop.getPlantId()).orElseThrow(PlantNotFoundException::new);

            if(plant.wateringCycle().litersPerSqM() < rainAmount){
                adjustNextExecutionOfWateringTasks(taskList.getTaskList(LocalDate.now(), LocalDate.now().plusDays(7)));
            }
        }

    }

    /**
     * Method to set next execution date of the water plant tasks
     * @param cropTaskList List with tasks from crops
     */
    private void adjustNextExecutionOfWateringTasks(List<Task> cropTaskList){
        for(Task task : cropTaskList){
            if(task.getName().equals("water plant")){
                task.setNextExecution(task.getNextExecution().plusDays(task.getInterval().orElse(1)));
            }
        }
    }
}
