package org.mte.numecoeval.referentiel.infrastructure.restapi.facade;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.*;
import org.mte.numecoeval.referentiel.domain.exception.ReferentielException;
import org.mte.numecoeval.referentiel.domain.model.FacteurCaracterisation;
import org.mte.numecoeval.referentiel.domain.model.id.FacteurCaracterisationId;
import org.mte.numecoeval.referentiel.domain.ports.output.ReferentielPersistencePort;
import org.mte.numecoeval.referentiel.factory.TestDataFactory;
import org.mte.numecoeval.referentiel.infrastructure.mapper.FacteurCaracterisationMapper;
import org.mte.numecoeval.referentiel.infrastructure.mapper.FacteurCaracterisationMapperImpl;
import org.mte.numecoeval.referentiel.utils.Constants;
import org.springframework.test.util.ReflectionTestUtils;

import java.util.Arrays;
import java.util.Collection;

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

public class FacteurCaracterisationFacadeTest {
    @InjectMocks
    FacteurCaracterisationFacade facadeToTest;

    @Mock
    private ReferentielPersistencePort<FacteurCaracterisation, FacteurCaracterisationId> persistencePort;

    private FacteurCaracterisationMapper mapper = new FacteurCaracterisationMapperImpl();

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

    @Test
    void get_shouldReturnMatchingDTO() throws ReferentielException {
        var wantedDTOId = TestDataFactory.FacteurCaracterisationFactory.idDTO(
                "Ecran 27 pouces",
                TestDataFactory.DEFAULT_ETAPE,
                TestDataFactory.DEFAULT_CRITERE,
                "ADEO"
        );
        var expectedDomainID = TestDataFactory.FacteurCaracterisationFactory.idDomain(
                "Ecran 27 pouces",
                TestDataFactory.DEFAULT_ETAPE,
                TestDataFactory.DEFAULT_CRITERE,
                "ADEO"
        );
        var expectedDomain = TestDataFactory.FacteurCaracterisationFactory.domain("Ecran 27 pouces",
                TestDataFactory.EtapeFactory.domain(TestDataFactory.DEFAULT_ETAPE, "Test"),
                TestDataFactory.CritereFactory.domain(TestDataFactory.DEFAULT_CRITERE, TestDataFactory.DEFAULT_UNITE, TestDataFactory.DEFAULT_CRITERE),
                "", Constants.EQUIPEMENT_NIVEAU, "", "", 0.01, "France", 0.02, "", "NegaOctet", "ADEO"
        );
        Mockito.when(persistencePort.get(expectedDomainID)).thenReturn(expectedDomain);

        var dto = assertDoesNotThrow(() -> facadeToTest.get(wantedDTOId));
        assertEquals(expectedDomain.getNom(), dto.getNom());
        assertEquals(expectedDomain.getEtape(), dto.getEtape());
        assertEquals(expectedDomain.getCritere(), dto.getCritere());
        assertEquals(expectedDomain.getNiveau(), dto.getNiveau());
        assertEquals(expectedDomain.getValeur(), dto.getValeur());
    }

    @Test
    void purgeByOrganisationAndAddAll_ShouldCallPurgeThenSaveAll() throws ReferentielException {
        var dtosToSave = Arrays.asList(
                TestDataFactory.FacteurCaracterisationFactory.dto(
                        "Ecran 27 pouces",
                        TestDataFactory.DEFAULT_ETAPE,
                        TestDataFactory.DEFAULT_CRITERE,
                        "", Constants.EQUIPEMENT_NIVEAU, "", "", 0.01, "France", 0.02, "",
                        "NegaOctet", "ADEO"),
                TestDataFactory.FacteurCaracterisationFactory.dto(
                        "Ecran 27 pouces",
                        "FIN_DE_VIE",
                        "Acidification",
                        "", Constants.EQUIPEMENT_NIVEAU, "", "", 0.01, "France", 0.02, "",
                        "NegaOctet", "ADEO")
        );

        ArgumentCaptor<Collection<FacteurCaracterisation>> valueCapture = ArgumentCaptor.forClass(Collection.class);

        assertDoesNotThrow(() -> facadeToTest.purgeByOrganisationAndAddAll(dtosToSave, "ADEO"));

        Mockito.verify(persistencePort, Mockito.times(1)).purgeByOrganisation("ADEO");
        Mockito.verify(persistencePort, Mockito.times(1)).saveAll(valueCapture.capture());

        var expectedDomains = valueCapture.getValue();
        assertNotNull(expectedDomains);
        assertEquals(dtosToSave.size(), expectedDomains.size());
        dtosToSave.forEach(dto -> {
            var matchingDomain = expectedDomains.stream()
                    .filter(domain ->
                            dto.getEtape().equals(domain.getEtape())
                                    && dto.getCritere().equals(domain.getCritere())
                                    && dto.getNom().equals(domain.getNom()))
                    .findAny();
            assertTrue(matchingDomain.isPresent());
            var domain = matchingDomain.get();
            assertEquals(dto.getNom(), domain.getNom());
            assertEquals(dto.getEtape(), domain.getEtape());
            assertEquals(dto.getCritere(), domain.getCritere());
            assertEquals(dto.getNiveau(), domain.getNiveau());
            assertEquals(dto.getSource(), domain.getSource());
            assertEquals(dto.getValeur(), domain.getValeur());
            assertEquals(dto.getConsoElecMoyenne(), domain.getConsoElecMoyenne());
        });
    }
}
