package org.mte.numecoeval.calculs.infrastructure.service.calcul;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mte.numecoeval.calculs.infrastructure.client.ReferentielClient;
import org.mte.numecoeval.calculs.infrastructure.service.enrichissement.EnrichissementEquipementPhysiqueService;
import org.mte.numecoeval.calculs.referentiels.generated.api.model.*;
import org.mte.numecoeval.topic.data.EquipementPhysiqueDTO;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.ConfigDataApplicationContextInitializer;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.util.ReflectionTestUtils;

import java.util.Arrays;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;

@ExtendWith({MockitoExtension.class, SpringExtension.class})
@ContextConfiguration(initializers = ConfigDataApplicationContextInitializer.class)
class EnrichissementEquipementPhysiqueServiceTest {

    private static final ObjectMapper mapper = new ObjectMapper().registerModule(new JavaTimeModule());

    @InjectMocks
    EnrichissementEquipementPhysiqueService enrichissementEquipementPhysiqueService;

    @Mock
    ReferentielClient referentielClient;

    @Value("${numecoeval.hypotheses.equipementPhysique}")
    private String hypothesesEquipementPhysique;

    EquipementPhysiqueDTO equipementPhysiqueDTO = mapper.readValue("""
            {
              "id": 43702,
              "nomEquipementPhysique": "physical-eq-srv-1001",
              "modele": "blade-server--28",
              "type": "Server",
              "statut": "In use",
              "paysDUtilisation": null,
              "dateAchat": "2016-06-17",
              "dateRetrait": "2023-06-16",
              "nomCourtDatacenter": "default",
              "nbJourUtiliseAn": null,
              "goTelecharge": 0.0,
              "quantite": 7.0,
              "consoElecAnnuelle": null,
              "serveur": true,
              "nomLot": "lot1",
              "dateLot": "2023-10-26",
              "nomOrganisation": "org",
              "nbEquipementsVirtuels": 1,
              "nbTotalVCPU": 4,
              "dataCenter": {
                "id": 5026,
                "nomCourtDatacenter": "default",
                "nomLongDatacenter": "Default",
                "pue": 1.75,
                "localisation": "France",
                "dateLot": "2023-10-26",
                "nomOrganisation": "org"
              },
              "qualite":"HAUTE"
            }
            """, EquipementPhysiqueDTO.class);

    EnrichissementEquipementPhysiqueServiceTest() throws JsonProcessingException {
    }

    @BeforeEach
    void initMocksReferentiel() throws JsonProcessingException {
        ReflectionTestUtils.setField(enrichissementEquipementPhysiqueService, "hypothesesEquipementPhysique", hypothesesEquipementPhysique);

        /* MOCK REFERENTIEL : Etapes */
        Mockito.lenient().when(referentielClient.getEtapes()).thenReturn(Arrays.asList(mapper.readValue("""
                [{ "code": "UTILISATION", "libelle": "Using" }]
                """, EtapeDTO[].class)));

        /* MOCK REFERENTIEL : Criteres */
        Mockito.lenient().when(referentielClient.getCriteres()).thenReturn(Arrays.asList(mapper.readValue("""
                [{
                  "nomCritere": "Climate change",
                  "unite": "kg CO2 eq",
                  "description": "Greenhouse gases (GHG)"
                }]
                """, CritereDTO[].class)));

        /* MOCK REFERENTIEL : Hypothese */
        Mockito.lenient().when(referentielClient.getHypothese(any())).thenReturn(mapper.readValue("""
                {
                  "code": "hyp code",
                  "valeur": "val",
                  "source": "test"
                }
                """, HypotheseDTO.class));

        /* MOCK REFERENTIEL : ImpactReseau */
        Mockito.lenient().when(referentielClient.getImpactReseau(any(), any(), any())).thenReturn(new ImpactReseauDTO());

        /* MOCK REFERENTIEL : MixElectrique */
        var mixFr = new MixElectriqueDTO();
        mixFr.setPays("FR");
        Mockito.lenient().when(referentielClient.getMixElectrique("France", "Climate change")).thenReturn(mixFr);
        Mockito.lenient().when(referentielClient.getMixElectrique(null, "Climate change")).thenReturn(null);
    }

    @Test
    void testServiceEnrichissementEquipementPhysqique_null() {
        Assertions.assertNull(enrichissementEquipementPhysiqueService.serviceEnrichissementEquipementPhysique(null));
    }

    @Test
    void testServiceEnrichissementEquipementPhysqique_correspondanceRefEquipement() throws JsonProcessingException {

        /* MOCK REFERENTIEL : CorrespondanceRefEquipement */
        Mockito.when(referentielClient.getCorrespondanceRefEquipement("blade-server--28")).thenReturn(mapper.readValue("""
                {
                  "modeleEquipementSource": "srv",
                  "refEquipementCible": "cible"
                }
                """, CorrespondanceRefEquipementDTO.class));

        /* MOCK REFERENTIEL : ImpactEquipement */
        var impactEquipementDTO = new ImpactEquipementDTO();
        impactEquipementDTO.setValeur(1.1);

        Mockito.when(referentielClient.getImpactEquipement(eq("cible"), any(), any())).thenReturn(impactEquipementDTO);

        var actual = enrichissementEquipementPhysiqueService.serviceEnrichissementEquipementPhysique(equipementPhysiqueDTO);

        Assertions.assertEquals(1, actual.getEtapes().size());
        Assertions.assertEquals(1, actual.getCriteres().size());
        Assertions.assertEquals(4, actual.getHypotheses().size());
        Assertions.assertEquals(1, actual.getMixElectriques().size());
        Assertions.assertEquals("FR", actual.getMixElectriques().get(0).getPays());
        Assertions.assertEquals(1.1, actual.getImpactsEquipement().get(0).getValeur());
        Assertions.assertEquals("HAUTE", actual.getEquipementPhysique().getQualite());
    }


    @Test
    void testServiceEnrichissementEquipementPhysqique_typeEquipement() throws JsonProcessingException {

        /* MOCK REFERENTIEL : CorrespondanceRefEquipement */
        Mockito.when(referentielClient.getTypeEquipement("Server")).thenReturn(mapper.readValue("""
                {
                  "refEquipementParDefaut": "default-ref-server"
                }
                """, TypeEquipementDTO.class));

        /* MOCK REFERENTIEL : ImpactEquipement */
        var impactEquipementDTO = new ImpactEquipementDTO();
        impactEquipementDTO.setValeur(2.2);

        Mockito.when(referentielClient.getImpactEquipement(eq("default-ref-server"), any(), any())).thenReturn(impactEquipementDTO);

        var actual = enrichissementEquipementPhysiqueService.serviceEnrichissementEquipementPhysique(equipementPhysiqueDTO);

        Assertions.assertEquals(1, actual.getEtapes().size());
        Assertions.assertEquals(1, actual.getCriteres().size());
        Assertions.assertEquals(4, actual.getHypotheses().size());
        Assertions.assertEquals(1, actual.getMixElectriques().size());
        Assertions.assertEquals("FR", actual.getMixElectriques().get(0).getPays());
        Assertions.assertEquals(2.2, actual.getImpactsEquipement().get(0).getValeur());
        Assertions.assertEquals("HAUTE", actual.getEquipementPhysique().getQualite());
    }
}
