package ch.zhaw.gartenverwaltung.types;

import com.fasterxml.jackson.annotation.JsonIgnore;

import java.time.LocalDate;
import java.util.Optional;

/**
 * Represents a Task
 * May be created using the builder pattern.
 */
public class Task {
    private Long id;
    private String name;
    private String description;
    private LocalDate startDate;
    private Integer interval;
    private LocalDate endDate;
    private LocalDate nextExecution;
    private LocalDate nextNotification;
    private long cropId;

    /**
     * Default constructor
     * (used by Json deserializer)
     */
    public Task(){
        name= "";
        description= "";
        startDate = LocalDate.now();
        endDate = startDate;
        nextExecution = startDate;
    }

    /**
     * Constructor for a non-repeating task related to a {@link Crop}
     *
     * @param name The name of the task
     * @param description A description of the task
     * @param startDate The start date of the task
     * @param cropId The id of the crop to which the task belongs
     */
    public Task(String name, String description, LocalDate startDate, long cropId) {
        this.name = name;
        this.description = description;
        this.startDate = startDate;
        this.endDate = startDate;
        nextExecution = startDate;
        this.cropId = cropId;
    }

    /**
     * Constructor for weather events
     *
     * @param name The name of the task
     * @param description A description of the task
     * @param startDate The start date of the task
     * @param endDate The maximum date on which the task should be executed
     */
    public Task(String name, String description, LocalDate startDate, LocalDate endDate, long cropId) {
        this.name = name;
        this.description = description;
        this.startDate = startDate;
        nextExecution = startDate;
        this.endDate = endDate;
        this.cropId = cropId;
    }

    /**
     * Full constructor (without id)
     *
     * @param name The name of the task
     * @param description A description of the task
     * @param startDate The start date of the task
     * @param endDate The maximum date on which the task should be executed
     * @param interval The number of days between executions
     * @param cropId The id of the crop to which the task belongs
     */
    public Task(String name, String description, LocalDate startDate, LocalDate endDate, int interval, long cropId) {
        this.name = name;
        this.description = description;
        this.startDate = startDate;
        nextExecution = startDate;
        this.endDate = endDate;
        this.interval = interval;
        this.cropId = cropId;
    }

    // Builder-pattern-style setters
    public Task withId(long id) {
        this.id = id;
        return this;
    }
    public Task withInterval(int interval) {
        this.interval = interval;
        return this;
    }
    public Task withEndDate(LocalDate endDate) {
        this.endDate = endDate;
        return this;
    }

    /**
     * Checks if the Task is within a specific date range.
     *
     * @param searchStartDate The minimum date
     * @param searchEndDate The maximum date
     * @return Whether the Task is within the given range
     */
    public boolean isInTimePeriod(LocalDate searchStartDate, LocalDate searchEndDate) {
        return  (endDate.isAfter(searchStartDate) && startDate.isBefore(searchEndDate)) || ((nextExecution != null && nextExecution.isBefore(searchEndDate.plusDays(1)) && nextExecution.isAfter(searchStartDate.minusDays(1))));
    }

    /**
     * Marks a specific execution of a Task as done.
     */
    public void done(){
        if(interval != null && interval != 0 && !nextExecution.plusDays(interval).isAfter(endDate)){
            nextExecution = nextExecution.plusDays(interval);
            nextNotification = nextExecution;
        } else {
            nextExecution = null;
            nextNotification = null;
        }
    }

    @JsonIgnore
    public boolean isDone(){
        return nextExecution == null;
    }

    public void setNextExecution(LocalDate nextExecution) {
        this.nextExecution = nextExecution;
    }

    public void setNextNotification(LocalDate nextNotification) {
        this.nextNotification = nextNotification;
    }

    // Getters
    public LocalDate getNextNotification() { return nextNotification; }
    public LocalDate getNextExecution() { return nextExecution; }
    public Optional<Long> getId() { return Optional.ofNullable(id); }
    public String getName() { return name; }
    public String getDescription() { return description; }
    public LocalDate getStartDate() { return startDate; }
    public long getCropId() { return cropId; }

    public Optional<Integer> getInterval() {
        return Optional.ofNullable(interval);
    }
    public Optional<LocalDate> getEndDate() {
        return Optional.ofNullable(endDate);
    }

    /**
     * Updates the fields of this Task using the values of the given Task
     *
     * @param task The task whose fields to copy
     * @return This task with the fields already updated
     */
    public Task updateTask(Task task) {
        this.name = task.getName();
        this.description = task.getDescription();
        this.startDate = task.getStartDate();
        this.endDate = task.getEndDate().orElse(null);
        this.interval = task.getInterval().orElse(0);
        this.cropId = task.getCropId();
        return this;
    }
}
