package org.fuseri.modulelanguageschool.lecture;

import org.fuseri.modulelanguageschool.course.Course;
import org.fuseri.modulelanguageschool.course.Language;
import org.fuseri.modulelanguageschool.course.ProficiencyLevel;
import org.fuseri.modulelanguageschool.user.User;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.web.server.ResponseStatusException;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;

import static org.mockito.Mockito.*;

@SpringBootTest
final class LectureServiceTest {

    @MockBean
    private LectureRepository lectureRepository;

    @Autowired
    private LectureService lectureService;

    private final Course course = new Course("AJ1", 10,
            Language.ENGLISH, ProficiencyLevel.A1);
    private final User student = new User("novakovat", "novakova@gmail.com", "Tereza",
            "Nováková", new HashSet<>(), new HashMap<>());
    private final User lecturer = new User("dolezelt", "dolezel@gmail.com", "Tomáš",
            "Doležel", new HashSet<>(), new HashMap<>());
    private final Lecture lecture = new Lecture(
            LocalDateTime.now().plusDays(2),
            LocalDateTime.now().plusDays(2).plusHours(2),
            "Learning how to spell deprecated",
            10,
            course, lecturer, new ArrayList<>(List.of()));

    private final User user = new User("novakovat",
            "novakova@gamil.com", "Tereza",
            "Nováková", new HashSet<>(), new HashMap<>());

    private final Lecture lectureWithEnrolledStudent = new Lecture(
            LocalDateTime.now().plusDays(2),
            LocalDateTime.now().plusDays(2).plusHours(2),
            "Learning how to spell deprecated",
            10,
            course, lecturer, new ArrayList<>(List.of(student)));
    private final List<Lecture> lectures = new ArrayList<>(List.of(lecture, lecture));

    @Test
    void save() {
        when(lectureRepository.save(lecture)).thenReturn(lecture);

        Lecture result = lectureService.save(lecture);

        Assertions.assertEquals(lecture, result);
        verify(lectureRepository).save(lecture);
    }

    @Test
    void notFoundById() {
        when(lectureRepository.findById(anyLong())).thenReturn(Optional.empty());

        Assertions.assertThrows(ResponseStatusException.class, () -> lectureService.findById(anyLong()));

    }

    @Test
    void findById() {
        when(lectureRepository.findById(anyLong())).thenReturn(Optional.of(lecture));

        Lecture result = lectureService.findById(anyLong());

        Assertions.assertEquals(lecture, result);
        verify(lectureRepository).findById(anyLong());
    }

    @Test
    void update() {
        Long id = 1L;
        when(lectureRepository.findById(anyLong())).thenReturn(Optional.of(lecture));
        when(lectureRepository.save(lecture)).thenReturn(lecture);

        Lecture result = lectureService.update(id, lecture);

        Assertions.assertEquals(lecture, result);
        verify(lectureRepository).findById(anyLong());
        verify(lectureRepository).save(lecture);
    }

    @Test
    void updateIdDoesNotExist() {
        Long id = 1L;
        when(lectureRepository.findById(anyLong())).thenReturn(Optional.empty());

        Assertions.assertThrows(ResponseStatusException.class, () -> lectureService.update(id, lecture));
    }

    @Test
    void delete() {
        Long id = 1L;
        lectureService.delete(id);

        verify(lectureRepository).deleteById(id);
    }

    @Test
    void findAllByLanguage() {
        Language language = Language.ENGLISH;
        when(lectureRepository.findAllByLang(any(Language.class))).thenReturn(lectures);

        List<Lecture> result = lectureService.findAll(language);

        Assertions.assertEquals(lectures, result);
        verify(lectureRepository).findAllByLang(language);
    }

    @Test
    void findAllByLanguageAndProf() {
        Language language = Language.ENGLISH;
        ProficiencyLevel proficiencyLevel = ProficiencyLevel.B1;

        when(lectureRepository.findAllByLangProf(any(Language.class), any(ProficiencyLevel.class))).thenReturn(lectures);

        List<Lecture> result = lectureService.findAll(language, proficiencyLevel);

        Assertions.assertEquals(lectures, result);
        verify(lectureRepository).findAllByLangProf(language, proficiencyLevel);
    }

    @Test
    void enrol() {
        Long id = 1L;
        when(lectureRepository.findById(anyLong())).thenReturn(Optional.of(lecture));
        when(lectureRepository.save(any(Lecture.class))).thenReturn(lectureWithEnrolledStudent);

        Lecture result = lectureService.enrol(id, user);

        Assertions.assertEquals(lectureWithEnrolledStudent, result);
        verify(lectureRepository).findById(id);
        verify(lectureRepository).save(any(Lecture.class));
    }

    @Test
    void enrolIdDoesNotExist() {
        Long id = 1L;
        when(lectureRepository.findById(anyLong())).thenReturn(Optional.empty());

        Assertions.assertThrows(ResponseStatusException.class, () -> lectureService.enrol(id, user));
    }

    @Test
    void expel() {
        Long id = 1L;
        when(lectureRepository.findById(anyLong())).thenReturn(Optional.of(lectureWithEnrolledStudent));
        when(lectureRepository.save(any(Lecture.class))).thenReturn(lecture);

        Lecture result = lectureService.expel(id, user);

        Assertions.assertEquals(lecture, result);
        verify(lectureRepository).findById(id);
        verify(lectureRepository).save(any(Lecture.class));
    }

    @Test
    void expelIdDoesNotExist() {
        Long id = 1L;
        when(lectureRepository.findById(anyLong())).thenReturn(Optional.empty());

        Assertions.assertThrows(ResponseStatusException.class, () -> lectureService.expel(id, user));
    }
}
