package org.fuseri.modulecertificate;

import org.fuseri.model.dto.certificate.CertificateCreateDto;
import org.fuseri.model.dto.certificate.CertificateGenerateDto;
import org.fuseri.model.dto.certificate.CertificateSimpleDto;
import org.fuseri.model.dto.course.CourseCertificateDto;
import org.fuseri.model.dto.course.LanguageTypeDto;
import org.fuseri.model.dto.course.ProficiencyLevelDto;
import org.fuseri.model.dto.user.UserDto;
import org.fuseri.modulecertificate.certificate.Certificate;
import org.fuseri.modulecertificate.certificate.CertificateFacade;
import org.fuseri.modulecertificate.certificate.CertificateMapper;
import org.fuseri.modulecertificate.certificate.CertificateService;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
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.io.IOException;
import java.time.Instant;
import java.util.HashMap;
import java.util.List;

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

@SpringBootTest
@AutoConfigureMockMvc
final class CertificateFacadeTests {
    private final UserDto USER = new UserDto("novakovat",
            "novakova@gamil.com", "Tereza", "Nováková",new HashMap<>());
    private final CourseCertificateDto COURSE = new CourseCertificateDto("AJ1", 10,
            LanguageTypeDto.ENGLISH, ProficiencyLevelDto.A1);
    private final CertificateCreateDto certificateCreateDto = new CertificateCreateDto(USER, COURSE);
    private final CertificateSimpleDto certificateDto = new CertificateSimpleDto(0L, USER.getId(),
            Instant.now(), COURSE.getId(), "");
    private final CertificateGenerateDto certificateGenerateDto = new CertificateGenerateDto(0L,USER.getId(),
            Instant.now(), COURSE.getId(), "");

    private final Certificate certificate = new Certificate();
    private final List<Certificate> certificateList = List.of(certificate);

    @Autowired
    private CertificateFacade certificateFacade;

    @MockBean
    private CertificateService certificateService;

    @MockBean
    private CertificateMapper certificateMapper;

    @Test
    void generateCertificate() throws IOException {
        when(certificateMapper.mapToCertificate(any(), any(), any())).thenReturn(certificate);
        when(certificateService.save(any())).thenReturn(certificate);
        Mockito.doNothing().when(certificateService).generateCertificateFile(any(), any(), any());
        when(certificateMapper.mapToCertificateGenerateDto(any())).thenReturn(certificateGenerateDto);

        var actualDto = certificateFacade.generate(certificateCreateDto, null);

        assertEquals(certificateGenerateDto, actualDto);
    }

    @Test
    public void testFindById() {
        Long id = 0L;
        when(certificateService.findById(id)).thenReturn(certificate);
        when(certificateMapper.mapToSimpleDto(certificate)).thenReturn(certificateDto);

        CertificateSimpleDto actualDto = certificateFacade.findById(id);

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

    @Test
    public void testFindForUser() {
        List<CertificateSimpleDto> expectedDtos = List.of(certificateDto);

        when(certificateService.findByUserId(USER.getId())).thenReturn(certificateList);
        when(certificateMapper.mapToList(certificateList)).thenReturn(expectedDtos);

        List<CertificateSimpleDto> actualDtos = certificateFacade.findByUserId(USER.getId());

        assertEquals(expectedDtos, actualDtos);
    }

    @Test
    public void testFindForUserAndCourse() {
        List<CertificateSimpleDto> expectedDtos = List.of(certificateDto);

        when(certificateService.findByUserIdAndCourseId(USER.getId(), COURSE.getId())).thenReturn(certificateList);
        when(certificateMapper.mapToList(certificateList)).thenReturn(expectedDtos);

        List<CertificateSimpleDto> actualDtos = certificateFacade.findByUserIdAndCourseId(USER.getId(), COURSE.getId());

        assertEquals(expectedDtos, actualDtos);
    }

    @Test
    public void testDelete() {
        Long certificateId = 1L;
        certificateFacade.deleteCertificate(certificateId);
        verify(certificateService).delete(certificateId);
    }

    @Test
    public void testFindAll() {
        Pageable pageable = PageRequest.of(0, 10);
        Page<Certificate> certificatePage = new PageImpl<>(List.of(certificate), pageable, 0);
        Page<CertificateSimpleDto> expectedPageDto = new PageImpl<>(List.of(certificateDto), pageable, 0);

        when(certificateService.findAll(pageable)).thenReturn(certificatePage);
        when(certificateMapper.mapToPageDto(certificatePage)).thenReturn(expectedPageDto);

        Page<CertificateSimpleDto> actualPageDto = certificateFacade.findAll(pageable);

        assertEquals(expectedPageDto, actualPageDto);
    }
}
