package org.fuseri.modulelanguageschool.user;

import org.fuseri.model.dto.course.CourseDto;
import org.fuseri.model.dto.course.LanguageTypeDto;
import org.fuseri.model.dto.course.ProficiencyLevelDto;
import org.fuseri.model.dto.user.UserAddLanguageDto;
import org.fuseri.model.dto.user.UserCreateDto;
import org.fuseri.model.dto.user.UserDto;
import org.fuseri.modulelanguageschool.course.Course;
import org.fuseri.modulelanguageschool.course.CourseMapper;
import org.fuseri.modulelanguageschool.course.Language;
import org.fuseri.modulelanguageschool.course.ProficiencyLevel;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;


@SpringBootTest
@AutoConfigureMockMvc
final class UserFacadeTest {

    @Autowired
    private UserFacade userFacade;

    @MockBean
    private UserService userService;

    @MockBean
    private UserMapper userMapper;

    @MockBean
    private CourseMapper courseMapper;

    private static final LanguageTypeDto LANGUAGE_DTO = LanguageTypeDto.ENGLISH;
    private static final ProficiencyLevelDto PROFICIENCY_DTO = ProficiencyLevelDto.B2;
    private final static List<CourseDto> COURSE_DTO_LIST = List.of(new CourseDto( "AJ1", 10,
            LANGUAGE_DTO, PROFICIENCY_DTO));
    private static final List<Course> COURSE_LIST = List.of(new Course("AJ1", 10,
            Language.valueOf(LANGUAGE_DTO.name()), ProficiencyLevel.valueOf(PROFICIENCY_DTO.name())));
//    private static final Address ADDRESS = new Address(
//            "Czechia", "Brno", "Masarykova", "45", "90033");
    private final UserCreateDto USER_CREATE_DTO = new UserCreateDto(
            "xnovak", "xnovak@emample.com", "Peter", "Novak");
    private final UserDto USER_DTO = new UserDto("xnovak", "xnovak@emample.com", "Peter", "Novak",new HashMap<>());
    private final User USER = new User(
            "xnovak", "xnovak@emample.com", "Peter", "Novak", Set.of(), Map.of());

    @Test
    void find() {
        long id = 1L;
        when(userService.find(id)).thenReturn(USER);
        when(userMapper.toDto(USER)).thenReturn(USER_DTO);

        UserDto actualDto = userFacade.find(id);
        assertNotNull(actualDto);
        assertEquals(USER_DTO, actualDto);
    }

    @Test
    void create() {
        when(userMapper.fromCreateDto(USER_CREATE_DTO)).thenReturn(USER);
        when(userService.create(USER)).thenReturn(USER);
        when(userMapper.toDto(USER)).thenReturn(USER_DTO);

        UserDto actualDto = userFacade.create(USER_CREATE_DTO);
        assertEquals(USER_DTO, actualDto);
    }

    @Test
    void delete() {
        long id = 1L;
        userFacade.delete(id);
        verify(userService).delete(id);
    }

    @Test
    void update() {
        long id = 1L;
        when(userMapper.fromCreateDto(USER_CREATE_DTO)).thenReturn(USER);
        when(userService.update(id, USER)).thenReturn(USER);
        when(userMapper.toDto(USER)).thenReturn(USER_DTO);

        UserDto actualDto = userFacade.update(id, USER_CREATE_DTO);

        assertEquals(USER_DTO, actualDto);
    }

    @Test
    void findAll() {
        Pageable pageable = PageRequest.of(0, 10);
        Page<User> userPage = new PageImpl<>(List.of(USER), pageable, 1);
        Page<UserDto> expectedPageDto = new PageImpl<>(List.of(USER_DTO), pageable, 1);

        when(userService.findAll(pageable)).thenReturn(userPage);
        when(userMapper.toDtoPage(userPage)).thenReturn(expectedPageDto);

        Page<UserDto> actualPageDto = userFacade.findAll(0);

        assertEquals(expectedPageDto, actualPageDto);
    }

    @Test
    void addLanguage() {
        long id = 1L;
        UserAddLanguageDto dto = new UserAddLanguageDto(LANGUAGE_DTO, PROFICIENCY_DTO);
        when(userService.addLanguageProficiency(
                id, Language.valueOf(LANGUAGE_DTO.name()), ProficiencyLevel.valueOf(PROFICIENCY_DTO.name())))
                .thenReturn(USER);
        when(userMapper.toDto(USER)).thenReturn(USER_DTO);

        UserDto actualDto = userFacade.addLanguageProficiency(id, dto);

        assertNotNull(actualDto);
        assertEquals(USER_DTO, actualDto);
    }

    @Test
    void getEnrolled() {
        long id = 1L;
        when(courseMapper.mapToList(COURSE_LIST)).thenReturn(COURSE_DTO_LIST);
        when(userService.getEnrolled(id)).thenReturn(COURSE_LIST);
        List<CourseDto> actualDtos = userFacade.getEnrolled(id);

        assertNotNull(actualDtos);
        assertEquals(COURSE_DTO_LIST, actualDtos);
    }

    @Test
    void getFinished() {
        long id = 1L;
        when(courseMapper.mapToList(COURSE_LIST)).thenReturn(COURSE_DTO_LIST);
        when(userService.getFinished(id)).thenReturn(COURSE_LIST);
        List<CourseDto> actualDtos = userFacade.getFinished(id);

        assertNotNull(actualDtos);
        assertEquals(COURSE_DTO_LIST, actualDtos);
    }
}
