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

import org.junit.jupiter.api.Assertions;
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.Hypothese;
import org.mte.numecoeval.referentiel.domain.model.id.HypotheseId;
import org.mte.numecoeval.referentiel.infrastructure.jpa.adapter.HypotheseJpaAdapter;
import org.mte.numecoeval.referentiel.infrastructure.jpa.entity.HypotheseEntity;
import org.mte.numecoeval.referentiel.infrastructure.jpa.repository.HypotheseRepository;
import org.mte.numecoeval.referentiel.infrastructure.mapper.HypotheseMapper;
import org.mte.numecoeval.referentiel.infrastructure.mapper.HypotheseMapperImpl;
import org.springframework.test.util.ReflectionTestUtils;

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

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

class HypotheseJpaAdapterTest {

    @InjectMocks
    private HypotheseJpaAdapter hypotheseJpaAdapter;

    @Mock
    HypotheseRepository hypotheseRepository;

    HypotheseMapper hypotheseMapper = new HypotheseMapperImpl();

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

    @Test
    void get_shouldReturnDomain() {
        var expectedEntity = new HypotheseEntity()
                .setCode("code")
                .setValeur("valeur")
                .setSource("test");
        var wantedId = new HypotheseId()
                .setCode(expectedEntity.getCode());
        var wantedEntityId = hypotheseMapper.toEntityId(wantedId);

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

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

        Assertions.assertEquals(expectedEntity.getCode(), expectedDomain.getCode());
        Assertions.assertEquals(expectedEntity.getValeur(), expectedDomain.getValeur());
        Assertions.assertEquals(expectedEntity.getSource(), expectedDomain.getSource());
    }

    @Test
    void get_shouldThrowException() {
        var expectedEntity = new HypotheseEntity()
                .setCode("code")
                .setValeur("valeur")
                .setSource("test");
        var wantedId = new HypotheseId()
                .setCode(expectedEntity.getCode());
        var wantedEntityId = hypotheseMapper.toEntityId(wantedId);

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

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

        assertEquals("Hypothèse non trouvée", expectedException.getMessage());
    }

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

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

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

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

    @Test
    void saveAll_shouldCallsaveAll() {
        var domainToSave = new Hypothese()
                .setCode("code")
                .setValeur("valeur")
                .setSource("test");
        var entityToSave = hypotheseMapper.toEntity(domainToSave);

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

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

    @Test
    void save_shouldSaveAndReturnDomain() {
        var wantedDomain = new Hypothese()
                .setCode("code")
                .setValeur("valeur")
                .setSource("test");
        var expectedEntity = hypotheseMapper.toEntities(Collections.singletonList(wantedDomain)).get(0);

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

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

        Assertions.assertEquals(wantedDomain.getCode(), expectedDomain.getCode());
        Assertions.assertEquals(wantedDomain.getValeur(), expectedDomain.getValeur());
        Assertions.assertEquals(wantedDomain.getSource(), expectedDomain.getSource());
    }

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

        assertNull(expectedDomain);
    }
}
