package org.mte.numecoeval.referentiel.infrastructure.jpa;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.NullSource;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mte.numecoeval.referentiel.domain.exception.ReferentielException;
import org.mte.numecoeval.referentiel.domain.model.CorrespondanceRefEquipement;
import org.mte.numecoeval.referentiel.infrastructure.jpa.adapter.CorrespondanceRefEquipementJpaAdapter;
import org.mte.numecoeval.referentiel.infrastructure.jpa.entity.CorrespondanceRefEquipementEntity;
import org.mte.numecoeval.referentiel.infrastructure.jpa.repository.CorrespondanceRefEquipementRepository;
import org.mte.numecoeval.referentiel.infrastructure.mapper.CorrespondanceRefEquipementMapper;
import org.mte.numecoeval.referentiel.infrastructure.mapper.CorrespondanceRefEquipementMapperImpl;
import org.springframework.test.util.ReflectionTestUtils;

import java.util.Collections;
import java.util.Optional;

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

class CorrespondanceRefEquipementJpaAdapterTest {

    @InjectMocks
    private CorrespondanceRefEquipementJpaAdapter jpaAdapter;

    @Mock
    CorrespondanceRefEquipementRepository hypotheseRepository;

    CorrespondanceRefEquipementMapper mapper = new CorrespondanceRefEquipementMapperImpl();

    @BeforeEach
    void setup() {
        MockitoAnnotations.openMocks(this);
        ReflectionTestUtils.setField(jpaAdapter, "mapper", mapper);
    }

    @Test
    void get_shouldReturnDomain() {
        var expectedEntity = new CorrespondanceRefEquipementEntity()
                .setModeleEquipementSource("source")
                .setRefEquipementCible("cible");
        var wantedId = "source";

        Mockito.when(hypotheseRepository.findById(wantedId)).thenReturn(Optional.of(expectedEntity));

        var expectedDomain = assertDoesNotThrow( () -> jpaAdapter.get(wantedId) );

        assertEquals(expectedEntity.getModeleEquipementSource(), expectedDomain.getModeleEquipementSource());
        assertEquals(expectedEntity.getRefEquipementCible(), expectedDomain.getRefEquipementCible());
    }

    @Test
    void get_shouldThrowException() {
        var wantedId = "notExisting";

        Mockito.when(hypotheseRepository.findById(wantedId)).thenReturn(Optional.empty());

        ReferentielException expectedException = assertThrows(ReferentielException.class, () -> jpaAdapter.get(wantedId) );

        assertEquals("Correspondance au RefEquipement "+ wantedId +" non trouvé",expectedException.getMessage());
    }

    @Test
    void get_withNullValueShouldThrowException() {
        String wantedId = null;

        Mockito.when(hypotheseRepository.findById(wantedId)).thenReturn(Optional.empty());

        ReferentielException expectedException = assertThrows(ReferentielException.class, () -> jpaAdapter.get(wantedId) );

        assertEquals("Correspondance au RefEquipement (id null) non trouvé",expectedException.getMessage());
    }

    @Test
    void purge_shouldCallDeleteAll() {
        jpaAdapter.purge();

        Mockito.verify(hypotheseRepository, Mockito.times(1)).deleteAll();
    }

    @Test
    void getAll_shouldCallfindAll() {
        jpaAdapter.getAll();

        Mockito.verify(hypotheseRepository, Mockito.times(1)).findAll();
    }

    @Test
    void saveAll_shouldCallsaveAll() {
        var domainToSave = CorrespondanceRefEquipement.builder()
                .modeleEquipementSource("source")
                .refEquipementCible("cible")
                .build();
        var entityToSave = mapper.toEntity(domainToSave);

        assertDoesNotThrow(() -> jpaAdapter.saveAll(Collections.singletonList(domainToSave)));

        Mockito.verify(hypotheseRepository, Mockito.times(1)).saveAll(Collections.singletonList(entityToSave));
    }

    @Test
    void save_shouldSaveAndReturnDomain() {
        var wantedDomain = CorrespondanceRefEquipement.builder()
                .modeleEquipementSource("source")
                .refEquipementCible("cible")
                .build();
        var expectedEntity = mapper.toEntities(Collections.singletonList(wantedDomain)).get(0);

        Mockito.when(hypotheseRepository.save(expectedEntity)).thenReturn(expectedEntity);

        var expectedDomain = assertDoesNotThrow( () -> jpaAdapter.save(wantedDomain) );

        assertEquals(expectedEntity.getModeleEquipementSource(), expectedDomain.getModeleEquipementSource());
        assertEquals(expectedEntity.getRefEquipementCible(), expectedDomain.getRefEquipementCible());
    }

    @ParameterizedTest
    @NullSource
    void save_shouldSaveAndReturnNull(CorrespondanceRefEquipement nullValue) {
        var expectedDomain = assertDoesNotThrow( () -> jpaAdapter.save(nullValue) );

        assertNull(expectedDomain);
    }
}
