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.MixElectrique;
import org.mte.numecoeval.referentiel.domain.model.id.MixElectriqueId;
import org.mte.numecoeval.referentiel.infrastructure.jpa.adapter.MixElectriqueJpaAdapter;
import org.mte.numecoeval.referentiel.infrastructure.jpa.entity.MixElectriqueEntity;
import org.mte.numecoeval.referentiel.infrastructure.jpa.repository.MixElectriqueRepository;
import org.mte.numecoeval.referentiel.infrastructure.mapper.MixElectriqueMapper;
import org.mte.numecoeval.referentiel.infrastructure.mapper.MixElectriqueMapperImpl;
import org.springframework.test.util.ReflectionTestUtils;

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

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

class MixElectriqueJpaAdapterTest {

    @InjectMocks
    private MixElectriqueJpaAdapter jpaAdapter;

    @Mock
    MixElectriqueRepository repository;

    MixElectriqueMapper mapper = new MixElectriqueMapperImpl();

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

    @Test
    void get_shouldReturnDomain() {
        var expectedCritere = "Changement climatique";
        var expectedEntity = new MixElectriqueEntity()
                .setCritere(expectedCritere)
                .setPays("France")
                .setSource("Test")
                .setRaccourcisAnglais("FR")
                .setValeur(0.120);

        var wantedId = new MixElectriqueId()
                .setPays(expectedEntity.getPays())
                .setCritere(expectedCritere);
        var wantedEntityId = mapper.toEntityId(wantedId);
        Mockito.when(repository.findById(wantedEntityId)).thenReturn(Optional.of(expectedEntity));

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

        assertNotNull(expectedDomain.getCritere());
        Assertions.assertEquals(expectedEntity.getCritere(), expectedDomain.getCritere());
        Assertions.assertEquals(expectedEntity.getPays(), expectedDomain.getPays());
        Assertions.assertEquals(expectedEntity.getRaccourcisAnglais(), expectedDomain.getRaccourcisAnglais());
        Assertions.assertEquals(expectedEntity.getValeur(), expectedDomain.getValeur());
        Assertions.assertEquals(expectedEntity.getSource(), expectedDomain.getSource());
    }

    @Test
    void get_shouldThrowException() {
        var wantedId = new MixElectriqueId()
                .setPays("NonExistant")
                .setCritere("Inexistant");
        var wantedEntityId = mapper.toEntityId(wantedId);
        Mockito.when(repository.findById(wantedEntityId)).thenReturn(Optional.empty());

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

        assertEquals("Mix Electrique non trouvé pour l'id MixElectriqueId(pays=NonExistant, critere=Inexistant)", expectedException.getMessage());
    }

    @Test
    void get_whenNull_shouldThrowException() {
        ReferentielException expectedException = assertThrows(ReferentielException.class, () -> jpaAdapter.get(null));

        assertEquals("Mix Electrique non trouvé pour l'id null", expectedException.getMessage());
    }

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

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

    @Test
    void getAll_shouldCallfindAll() {
        var expectedCritere = "Changement climatique";
        var expectedEntity = new MixElectriqueEntity()
                .setCritere(expectedCritere)
                .setPays("France")
                .setSource("Test")
                .setRaccourcisAnglais("FR")
                .setValeur(0.120);
        Mockito.when(repository.findAll()).thenReturn(Collections.singletonList(expectedEntity));

        var result = jpaAdapter.getAll();

        Mockito.verify(repository, Mockito.times(1)).findAll();
        assertNotNull(result);
        assertEquals(1, result.size());

        var expectedDomain = result.get(0);
        assertNotNull(expectedDomain.getCritere());
        Assertions.assertEquals(expectedEntity.getCritere(), expectedDomain.getCritere());
        Assertions.assertEquals(expectedEntity.getPays(), expectedDomain.getPays());
        Assertions.assertEquals(expectedEntity.getRaccourcisAnglais(), expectedDomain.getRaccourcisAnglais());
        Assertions.assertEquals(expectedEntity.getValeur(), expectedDomain.getValeur());
        Assertions.assertEquals(expectedEntity.getSource(), expectedDomain.getSource());
    }

    @Test
    void saveAll_shouldCallsaveAll() {
        var wantedCritere = "Changement climatique";
        var domainToSave = new MixElectrique()
                .setCritere(wantedCritere)
                .setPays("France")
                .setSource("Test")
                .setRaccourcisAnglais("FR")
                .setValeur(0.120);

        var entityToSave = mapper.toEntity(domainToSave);

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

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

    @Test
    void save_shouldSaveAndReturnDomain() {
        var wantedCritere = "Changement climatique";
        var wantedDomain = new MixElectrique()
                .setCritere(wantedCritere)
                .setPays("France")
                .setSource("Test")
                .setRaccourcisAnglais("FR")
                .setValeur(0.120);
        var expectedEntity = mapper.toEntities(Collections.singletonList(wantedDomain)).get(0);

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

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

        assertNotNull(expectedDomain.getCritere());
        Assertions.assertEquals(wantedDomain.getCritere(), expectedDomain.getCritere());
        Assertions.assertEquals(wantedDomain.getPays(), expectedDomain.getPays());
        Assertions.assertEquals(wantedDomain.getRaccourcisAnglais(), expectedDomain.getRaccourcisAnglais());
        Assertions.assertEquals(wantedDomain.getValeur(), expectedDomain.getValeur());
        Assertions.assertEquals(wantedDomain.getSource(), expectedDomain.getSource());
    }

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

        assertNull(expectedDomain);
    }
}
