package org.mte.numecoeval.expositiondonneesentrees.domain.port.input;

import com.fasterxml.jackson.annotation.JsonInclude;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mapstruct.factory.Mappers;
import org.mockito.InjectMocks;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mte.numecoeval.expositiondonneesentrees.domain.exception.NotFoundException;
import org.mte.numecoeval.expositiondonneesentrees.domain.model.Volume;
import org.mte.numecoeval.expositiondonneesentrees.domain.ports.input.impl.StatutPourCalculPortImpl;
import org.mte.numecoeval.expositiondonneesentrees.generated.api.model.StatutCalculRest;
import org.mte.numecoeval.expositiondonneesentrees.generated.api.model.VolumeRest;
import org.mte.numecoeval.expositiondonneesentrees.infrastructure.mapper.VolumeMapper;
import org.springframework.test.util.ReflectionTestUtils;
import org.testcontainers.shaded.com.fasterxml.jackson.databind.ObjectMapper;
import org.testcontainers.shaded.com.fasterxml.jackson.databind.SerializationFeature;

import java.io.IOException;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

@ExtendWith(MockitoExtension.class)
class StatutPourCalculPortImplTest {

    @InjectMocks
    StatutPourCalculPortImpl statutPourCalculPort;

    private static final ObjectMapper mapper = new ObjectMapper()
            .setSerializationInclusion(JsonInclude.Include.NON_NULL)
            .configure(SerializationFeature.INDENT_OUTPUT, true);

    @BeforeEach
    public void setup() {
        VolumeMapper volumeMapper = Mappers.getMapper(VolumeMapper.class); // Initialization of the mapper
        ReflectionTestUtils.setField(statutPourCalculPort, "volumeMapper", volumeMapper);
    }

    @Test
    void testStatutDesCalculs_Nominal() throws IOException {

        /* INPUT VOLUME MOCKED */
        var volumeEqPhysique = new Volume(10L, 100L);
        var volumeOpNonIT = new Volume(100L, 10L);
        var volumeMessagerie = new Volume(50L, 5000L);

        /* EXECUTE : params does not matter in the test */
        var actual = statutPourCalculPort.statutCalculs(volumeEqPhysique, volumeOpNonIT, volumeMessagerie);

        var expected = StatutCalculRest.builder()
                .statut(StatutCalculRest.StatutEnum.EN_COURS)
                .etat("96%")
                .equipementPhysique(VolumeRest.builder()
                        .nbEnCours(10)
                        .nbTraite(100)
                        .build())
                .operationNonIT(VolumeRest.builder()
                        .nbEnCours(100)
                        .nbTraite(10)
                        .build())
                .messagerie(VolumeRest.builder()
                        .nbEnCours(50)
                        .nbTraite(5000)
                        .build())
                .build();

        /* ASSERT */
        assertEquals(mapper.writeValueAsString(expected), mapper.writeValueAsString(actual));

    }

    @Test
    void testStatutDesCalculs_Termine() throws IOException {

        /* INPUT VOLUME MOCKED */
        var volumeEqPhysique = new Volume(0L, 100L);
        var volumeOperationNonIT = new Volume(0L, 100L);
        var volumeMessagerie = new Volume(0L, 5000L);

        /* EXECUTE : params does not matter in the test */
        var actual = statutPourCalculPort.statutCalculs(volumeEqPhysique, volumeOperationNonIT, volumeMessagerie);

        var expected = StatutCalculRest.builder()
                .statut(StatutCalculRest.StatutEnum.TERMINE)
                .etat("100%")
                .equipementPhysique(VolumeRest.builder()
                        .nbEnCours(0)
                        .nbTraite(100)
                        .build())
                .operationNonIT(VolumeRest.builder()
                        .nbEnCours(0)
                        .nbTraite(100)
                        .build())
                .messagerie(VolumeRest.builder()
                        .nbEnCours(0)
                        .nbTraite(5000)
                        .build())
                .build();

        /* ASSERT */
        assertEquals(mapper.writeValueAsString(expected), mapper.writeValueAsString(actual));

    }

    @Test
    void testStatutDesCalculs_NotFound() {
        var volume0 = new Volume(0L, 0L);
        assertThrows(NotFoundException.class, () -> statutPourCalculPort.statutCalculs(volume0, volume0, volume0));
    }

}
