package ch.zhaw.gartenverwaltung.taskList;

import ch.zhaw.gartenverwaltung.io.*;
import ch.zhaw.gartenverwaltung.types.HardinessZone;
import ch.zhaw.gartenverwaltung.types.Plant;
import ch.zhaw.gartenverwaltung.types.Task;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.time.LocalDate;
import java.util.*;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;

class TaskListModelTest {
    TaskDatabase taskDatabase;
    PlantDatabase plantDatabase;
    List<Task> exampleTaskList;
    Map<Long, Plant> examplePlantMap;
    TaskListModel model;

    @BeforeEach
    void setUp() throws IOException {
        createExampleTaskList();
        taskDatabase = mockTaskDatabase(exampleTaskList);
        plantDatabase = mockPlantDatabase(examplePlantMap);
        model = new TaskListModel(taskDatabase, plantDatabase);
    }

    private TaskDatabase mockTaskDatabase(List<Task> exampleTaskList) throws IOException {
        TaskDatabase taskDatabase = mock(JsonTaskDatabase.class);
        when(taskDatabase.getTaskList(LocalDate.MIN, LocalDate.MAX)).thenReturn(exampleTaskList);

        when(taskDatabase.getTaskList(LocalDate.now(), LocalDate.MAX)).thenReturn((exampleTaskList.subList(1, 4)));

        List<Task> pastTasks = new ArrayList<>();
        pastTasks.add(exampleTaskList.get(0));
        pastTasks.add(exampleTaskList.get(2));
        pastTasks.add(exampleTaskList.get(4));
        when(taskDatabase.getTaskList(LocalDate.MIN, LocalDate.now())).thenReturn(pastTasks);


        when(taskDatabase.getTaskList(LocalDate.now(), LocalDate.now())).thenReturn(List.of(exampleTaskList.get(2)));
        when(taskDatabase.getTaskList(LocalDate.now().plusDays(1L), LocalDate.now().plusDays(1L))).thenReturn(List.of(exampleTaskList.get(1)));
        when(taskDatabase.getTaskList(LocalDate.now().plusDays(2L), LocalDate.now().plusDays(2L))).thenReturn(List.of());
        when(taskDatabase.getTaskList(LocalDate.now().plusDays(3L), LocalDate.now().plusDays(3L))).thenReturn(List.of());
        when(taskDatabase.getTaskList(LocalDate.now().plusDays(4L), LocalDate.now().plusDays(4L))).thenReturn(List.of());
        when(taskDatabase.getTaskList(LocalDate.now().plusDays(5L), LocalDate.now().plusDays(5L))).thenReturn(List.of());
        when(taskDatabase.getTaskList(LocalDate.now().plusDays(6L), LocalDate.now().plusDays(6L))).thenReturn(List.of());

        return taskDatabase;
    }

    private PlantDatabase mockPlantDatabase(Map<Long, Plant> examplePlantMap) {
        return new PlantDatabase() {
            @Override
            public List<Plant> getPlantList(HardinessZone zone) {
                return null;
            }

            @Override
            public Optional<Plant> getPlantById(HardinessZone zone, long id) {
                return Optional.ofNullable(examplePlantMap.get(id));
            }
        };
    }

    void createExampleTaskList() {
        exampleTaskList = new ArrayList<>();
        exampleTaskList.add(new Task("name", "description", LocalDate.now().minusDays(1), 1L));
        exampleTaskList.add(new Task("name", "description", LocalDate.now().plusDays(1), 2L));
        exampleTaskList.add(new Task("name", "description", LocalDate.now(), 1L));
        exampleTaskList.add(new Task("name", "description", LocalDate.of(9019, 5, 5), 1L));
        exampleTaskList.add(new Task("name", "description", LocalDate.of(2019, 5, 5), 2L));
    }

    void createExamplePlantMap() {
        examplePlantMap = new HashMap<>();
    }



    @Test
    void addTask() throws IOException {
        Task taskToAdd = new Task("name", "description", LocalDate.now(), 1L);
        model.addTask(taskToAdd);
        verify(taskDatabase, times(1)).saveTask(taskToAdd);
    }

    @Test
    void removeTask() throws IOException {
        Task taskToRemove = new Task("name", "description", LocalDate.now(), 1L);
        model.removeTask(taskToRemove);
        verify(taskDatabase, times(1)).removeTask(taskToRemove);
    }

    @Test
    void getTaskList() throws IOException {
        List<Task> listToCheck = model.getTaskList();
        assertEquals(5, listToCheck.size());
        assertEquals(exampleTaskList.get(4), listToCheck.get(0));
        assertEquals(exampleTaskList.get(0), listToCheck.get(1));
        assertEquals(exampleTaskList.get(2), listToCheck.get(2));
        assertEquals(exampleTaskList.get(1), listToCheck.get(3));
        assertEquals(exampleTaskList.get(3), listToCheck.get(4));
    }

    @Test
    void getFutureTasks() throws IOException {
        List<Task> listToCheck = model.getFutureTasks();
        assertEquals(3, listToCheck.size());
        assertEquals(exampleTaskList.get(2), listToCheck.get(0));
        assertEquals(exampleTaskList.get(1), listToCheck.get(1));
        assertEquals(exampleTaskList.get(3), listToCheck.get(2));
    }

    @Test
    void getPastTasks() throws IOException {
        List<Task> listToCheck = model.getPastTasks();
        assertEquals(3, listToCheck.size());
        assertEquals(exampleTaskList.get(4), listToCheck.get(0));
        assertEquals(exampleTaskList.get(0), listToCheck.get(1));
        assertEquals(exampleTaskList.get(2), listToCheck.get(2));
    }

    @Test
    void getTasksUpcomingWeek() throws IOException {
        List<List<Task>> dayList = model.getTasksUpcomingWeek();
        assertEquals(7, dayList.size());

        //Check day 0
        assertEquals(1, dayList.get(0).size());
        assertEquals(exampleTaskList.get(2), dayList.get(0).get(0));

        //Check day 1
        assertEquals(1, dayList.get(1).size());
        assertEquals(exampleTaskList.get(1), dayList.get(1).get(0));

        //Check day 2
        assertEquals(0, dayList.get(2).size());
        //Check day 3
        assertEquals(0, dayList.get(3).size());
        //Check day 4
        assertEquals(0, dayList.get(4).size());
        //Check day 5
        assertEquals(0, dayList.get(5).size());
        //Check day 6
        assertEquals(0, dayList.get(6).size());
    }

    @Test
    void removeTasksForCrop() throws IOException {
        model.removeTasksForCrop(1L);
        verify(taskDatabase, times(1)).removeTasksForCrop(1L);
    }
}