package org.mte.numecoeval.calculs.domain.service;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mte.numecoeval.calculs.domain.data.demande.DemandeCalculImpactEquipementPhysique;
import org.mte.numecoeval.calculs.domain.data.demande.OptionsCalcul;
import org.mte.numecoeval.calculs.domain.data.entree.DataCenter;
import org.mte.numecoeval.calculs.domain.data.indicateurs.ImpactEquipementPhysique;
import org.mte.numecoeval.calculs.domain.data.referentiel.*;
import org.mte.numecoeval.calculs.domain.data.trace.ConsoElecAnMoyenne;
import org.mte.numecoeval.calculs.domain.data.trace.DureeDeVie;
import org.mte.numecoeval.calculs.domain.data.trace.MixElectrique;
import org.mte.numecoeval.calculs.domain.data.trace.TraceCalculImpactEquipementPhysique;
import org.mte.numecoeval.calculs.domain.exception.CalculImpactException;
import org.mte.numecoeval.calculs.domain.port.input.service.CalculImpactEquipementPhysiqueService;
import org.mte.numecoeval.calculs.domain.port.input.service.impl.CalculImpactEquipementPhysiqueServiceImpl;
import org.mte.numecoeval.calculs.domain.port.input.service.impl.DureeDeVieEquipementPhysiqueServiceImpl;
import org.mte.numecoeval.calculs.domain.traceur.TraceCalculImpactEquipementPhysiqueUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.List;

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


@ExtendWith(MockitoExtension.class)
class CalculImpactEquipementPhysiqueServiceTest {

    private CalculImpactEquipementPhysiqueService calculImpactEquipementPhysiqueService;

    @Mock
    private DureeDeVieEquipementPhysiqueServiceImpl dureeDeVieEquipementPhysiqueService;

    private static final ObjectMapper objectMapper = new ObjectMapper();

    private DemandeCalculImpactEquipementPhysique createDemande() {
        try {
            var res = objectMapper.readValue("""
                       {
                         "equipementPhysique": {
                           "nomEquipementPhysique" : "Apple Iphone 11",
                           "nomOrganisation": "Test",
                           "nomLot": "Test|20220101",
                           "type" : "telephone",
                           "modele": "iphone-11",
                           "paysDUtilisation": "France",
                           "quantite": 1.0                         },
                         "etape": {
                           "code": "UTILISATION"
                         },
                         "critere": {
                           "nomCritere": "Changement Climatique",
                           "unite": "kg CO_{2} eq"
                         },
                         "correspondanceRefEquipement":{
                           "refEquipementCible": "iphone",
                           "modeleEquipementSource" : "iphone-11"
                         },
                         "facteurCaracterisations":[
                           {
                             "nom": "Electricty Mix FR",
                             "etape": "FABRICATION",
                             "critere": "Changement Climatique",
                             "categorie" : "electricity-mix",
                             "niveau": "0-Base data",
                             "localisation": "France",
                             "source": "CAF1",
                             "valeur" : 0.08
                           },
                           {
                             "nom": "iphone",
                             "niveau" : "2-Equipement",
                             "etape": "UTILISATION",
                             "critere": "Changement Climatique",
                             "consoElecMoyenne":10
                           }],
                         "hypotheses": []
                       }
                    """, DemandeCalculImpactEquipementPhysique.class);
            res.setDateCalcul(LocalDateTime.parse("2024-01-01T00:00:00"));
            return res;
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    @BeforeEach
    public void init() {
        MockitoAnnotations.openMocks(this);
        calculImpactEquipementPhysiqueService = new CalculImpactEquipementPhysiqueServiceImpl(dureeDeVieEquipementPhysiqueService);
    }


    @Test
    void shouldCalculerImpactEquipementPhysique_CAF1() throws JsonProcessingException {
        //Given
        DemandeCalculImpactEquipementPhysique demandeCalcul = createDemande();
        demandeCalcul.setFacteurCaracterisations(List.of(objectMapper.readValue("""
                [
                   {
                     "nom": "Electricty Mix FR",
                     "etape": "FABRICATION",
                     "critere": "Changement Climatique",
                     "categorie": "electricity-mix",
                     "niveau": "0-Base data",
                     "localisation": "France",
                     "source": "CAF1",
                     "valeur": 0.08
                   },
                   {
                     "nom": "iphone",
                     "niveau": "2-Equipement",
                     "etape": "UTILISATION",
                     "critere": "Changement Climatique",
                     "consoElecMoyenne": 10
                   }
                 ]
                  """, ReferentielFacteurCaracterisation[].class)));

        //When
        var actual = calculImpactEquipementPhysiqueService.calculerImpactEquipementPhysique(demandeCalcul);

        //Then
        assertContentIndicateur(demandeCalcul, actual);
        assertEquals("OK", actual.getStatutIndicateur());
        assertEquals(LocalDateTime.parse("2024-01-01T00:00:00"), actual.getDateCalcul());
        assertNotNull(actual.getImpactUnitaire());
        double actualImpactUnitaireLimited = BigDecimal.valueOf(actual.getImpactUnitaire()).setScale(4, RoundingMode.DOWN).doubleValue();
        assertEquals(0.8, actualImpactUnitaireLimited);
        assertEquals(10, actual.getConsoElecMoyenne());
    }

    /**
     * même cas que le CAF1 mais avec une consoElecAnnuelle à 0 au lieu de null.
     * Conformément au Taiga 895 : le 0 est bien appliqué et ramène le résultat à 0
     */
    @Test
    void taiga895_shouldApplyConsoElecAnnuelWithConsoElecAnnuelleAt0_CAF1() {

        //Given
        DemandeCalculImpactEquipementPhysique demandeCalcul = createDemande();
        demandeCalcul.getEquipementPhysique().setConsoElecAnnuelle(0.0);

        //When
        var actual = calculImpactEquipementPhysiqueService.calculerImpactEquipementPhysique(demandeCalcul);

        //Then
        assertContentIndicateur(demandeCalcul, actual);
        assertEquals("OK", actual.getStatutIndicateur());
        assertNotNull(actual.getImpactUnitaire());
        double actualImpactUnitaireLimited = BigDecimal.valueOf(actual.getImpactUnitaire()).setScale(4, RoundingMode.DOWN).doubleValue();
        assertEquals(0, actualImpactUnitaireLimited);
        assertEquals(0, actual.getConsoElecMoyenne());
    }

    @Test
    void shouldCalculerImpactEquipementPhysique_CAF2_neededCorrection() throws JsonProcessingException {

        //Given
        DemandeCalculImpactEquipementPhysique demandeCalcul = createDemande();
        demandeCalcul.setFacteurCaracterisations(List.of(objectMapper.readValue("""
                [
                   {
                     "nom": "Electricty Mix FR",
                     "etape": "FABRICATION",
                     "critere": "Changement Climatique",
                     "categorie": "electricity-mix",
                     "niveau": "0-Base data",
                     "localisation": "France",
                     "source": "CAF1",
                     "valeur": 0.0813225
                   },
                   {
                     "nom": "iphone",
                     "niveau": "2-Equipement",
                     "etape": "UTILISATION",
                     "critere": "Changement Climatique",
                     "consoElecMoyenne": 70.0
                   }
                 ]
                  """, ReferentielFacteurCaracterisation[].class)));
        //When
        var actual = calculImpactEquipementPhysiqueService.calculerImpactEquipementPhysique(demandeCalcul);

        //Then
        assertContentIndicateur(demandeCalcul, actual);
        assertEquals("OK", actual.getStatutIndicateur());
        assertNotNull(actual.getImpactUnitaire());
        double actualImpactUnitaireLimited = BigDecimal.valueOf(actual.getImpactUnitaire()).setScale(3, RoundingMode.DOWN).doubleValue();
        assertEquals(5.692, actualImpactUnitaireLimited);
        assertEquals(70.0, actual.getConsoElecMoyenne());
    }

    @Test
    void shouldCalculerImpactEquipementPhysique_CAF3_decimalFixing() throws JsonProcessingException {

        //Given
        DemandeCalculImpactEquipementPhysique demandeCalcul = createDemande();
        demandeCalcul.setFacteurCaracterisations(List.of(objectMapper.readValue("""
                [
                   {
                     "nom": "Electricty Mix FR",
                     "etape": "FABRICATION",
                     "critere": "Changement Climatique",
                     "categorie": "electricity-mix",
                     "niveau": "0-Base data",
                     "localisation": "France",
                     "source": "CAF1",
                     "valeur": 0.648118
                   },
                   {
                     "nom": "iphone",
                     "niveau": "2-Equipement",
                     "etape": "UTILISATION",
                     "critere": "Changement Climatique",
                     "consoElecMoyenne": 3504.0
                   }
                 ]
                  """, ReferentielFacteurCaracterisation[].class)));

        //When
        var actual = calculImpactEquipementPhysiqueService.calculerImpactEquipementPhysique(demandeCalcul);

        //Then
        assertContentIndicateur(demandeCalcul, actual);
        assertEquals("OK", actual.getStatutIndicateur());
        assertNotNull(actual.getImpactUnitaire());
        double actualImpactUnitaireLimited = BigDecimal.valueOf(actual.getImpactUnitaire()).setScale(4, RoundingMode.DOWN).doubleValue();
        // quantité * refEquipementPhysique.consoElecMoyenne * MixElec
        assertEquals(BigDecimal.valueOf(1.0 * 3504.0 * 0.648118).setScale(4, RoundingMode.DOWN).doubleValue(), actualImpactUnitaireLimited);
        assertEquals(3504.0, actual.getConsoElecMoyenne());
    }

    @Test
    void taiga918_DataCenterReferencedAndUsedForPUE_CAF1() throws JsonProcessingException {

        //Given
        DemandeCalculImpactEquipementPhysique demandeCalcul = createDemande();
        DataCenter dataCenter = DataCenter.builder()
                .nomCourtDatacenter("sequoia2")
                .nomLongDatacenter("sequoia2")
                .localisation("France")
                .pue(1.43)
                .build();
        demandeCalcul.getEquipementPhysique().setConsoElecAnnuelle(500.0);
        demandeCalcul.getEquipementPhysique().setDataCenter(dataCenter);
        demandeCalcul.getEquipementPhysique().setQuantite(2.0);

        demandeCalcul.setFacteurCaracterisations(List.of(objectMapper.readValue("""
                [
                   {
                     "nom": "Electricty Mix FR",
                     "etape": "FABRICATION",
                     "critere": "Changement Climatique",
                     "categorie": "electricity-mix",
                     "localisation": "France",
                     "valeur": 0.08
                   }
                 ]
                  """, ReferentielFacteurCaracterisation[].class)));

        //When
        var actual = calculImpactEquipementPhysiqueService.calculerImpactEquipementPhysique(demandeCalcul);

        //Then
        assertContentIndicateur(demandeCalcul, actual);
        assertEquals("OK", actual.getStatutIndicateur());
        assertNotNull(actual.getImpactUnitaire());
        double actualImpactUnitaireLimited = BigDecimal.valueOf(actual.getImpactUnitaire()).setScale(4, RoundingMode.DOWN).doubleValue();
        // quantité * equipementPhysique.consoElecAnnuelle * DataCenter.pue * MixElec(France)
        assertEquals(BigDecimal.valueOf(2.0 * 500.0 * 1.43 * 0.08).setScale(4, RoundingMode.DOWN).doubleValue(), actualImpactUnitaireLimited);
    }

    @Test
    void taiga918_DataCenterWithoutPUEAndDefaultPUEUsed_CAF2() throws JsonProcessingException {
        //Given
        DemandeCalculImpactEquipementPhysique demandeCalcul = createDemande();
        DataCenter dataCenter = DataCenter.builder()
                .nomCourtDatacenter("sequoia3")
                .nomLongDatacenter("sequoia3")
                .localisation("France")
                .pue(null)
                .build();
        demandeCalcul.getEquipementPhysique().setConsoElecAnnuelle(500.0);
        demandeCalcul.getEquipementPhysique().setDataCenter(dataCenter);

        demandeCalcul.setFacteurCaracterisations(List.of(objectMapper.readValue("""
                [
                   {
                     "nom": "Electricty Mix FR",
                     "etape": "FABRICATION",
                     "critere": "Changement Climatique",
                     "categorie": "electricity-mix",
                     "localisation": "France",
                     "valeur": 0.08
                   }
                 ]
                  """, ReferentielFacteurCaracterisation[].class)));
        demandeCalcul.setHypotheses(List.of(objectMapper.readValue("""
                [
                  {
                    "code": "PUEParDefaut",
                    "valeur": "2.0",
                    "source": "sourceDefault"
                  }
                ]
                """, ReferentielHypothese[].class)));

        //When
        var actual = calculImpactEquipementPhysiqueService.calculerImpactEquipementPhysique(demandeCalcul);

        //Then
        assertContentIndicateur(demandeCalcul, actual);
        assertEquals("OK", actual.getStatutIndicateur());
        assertNotNull(actual.getImpactUnitaire());
        double actualImpactUnitaireLimited = BigDecimal.valueOf(actual.getImpactUnitaire()).setScale(4, RoundingMode.DOWN).doubleValue();
        // quantité * equipementPhysique.consoElecAnnuelle * ref_Hypothese(PUEParDefaut).valeur * MixElec France
        assertEquals(BigDecimal.valueOf(1.0 * 500.0 * 2.0 * 0.08).setScale(4, RoundingMode.DOWN).doubleValue(), actualImpactUnitaireLimited);
    }

    @Test
    void taiga918_ConsoElecMoyenneUsedFromRef_CAF3() throws JsonProcessingException {

        //Given
        DemandeCalculImpactEquipementPhysique demandeCalcul = createDemande();
        DataCenter dataCenter = DataCenter.builder()
                .nomCourtDatacenter("sequoia2")
                .nomLongDatacenter("sequoia2")
                .localisation("France")
                .pue(1.43)
                .build();
        demandeCalcul.getEquipementPhysique().setDataCenter(dataCenter);

        demandeCalcul.setFacteurCaracterisations(List.of(objectMapper.readValue("""
                [
                   {
                     "nom": "Electricty Mix FR",
                     "etape": "FABRICATION",
                     "critere": "Changement Climatique",
                     "categorie": "electricity-mix",
                     "localisation": "France",
                     "valeur": 0.08
                   },
                   {
                     "nom": "iphone",
                     "niveau": "2-Equipement",
                     "etape": "UTILISATION",
                     "critere": "Changement Climatique",
                     "consoElecMoyenne": 10512.0
                   }
                 ]
                  """, ReferentielFacteurCaracterisation[].class)));

        //When
        var actual = calculImpactEquipementPhysiqueService.calculerImpactEquipementPhysique(demandeCalcul);

        //Then
        assertContentIndicateur(demandeCalcul, actual);
        assertEquals("OK", actual.getStatutIndicateur());
        assertNotNull(actual.getImpactUnitaire());
        double actualImpactUnitaireLimited = BigDecimal.valueOf(actual.getImpactUnitaire()).setScale(4, RoundingMode.DOWN).doubleValue();
        // quantité * refEquipementPhysique.consoElecMoyenne * DataCEnter.pue * MixElec France
        assertEquals(BigDecimal.valueOf(1.0 * 10512.0 * 1.43 * 0.08).setScale(4, RoundingMode.DOWN).doubleValue(), actualImpactUnitaireLimited);
        assertEquals(10512.0, actual.getConsoElecMoyenne());
    }

    @Test
    void taiga918_CAF4_whenDonneesConsoElecAbsente_shouldReturnErreurCalcul() throws JsonProcessingException {

        //Given
        DemandeCalculImpactEquipementPhysique demandeCalcul = createDemande();
        demandeCalcul.getEquipementPhysique().setConsoElecAnnuelle(null);

        demandeCalcul.setTypeItem(objectMapper.readValue("""
                {
                   "type": "telephone",
                   "categorie": "EquipementPhysique",
                   "refItemParDefaut": "iphone"
                 }
                """, ReferentielTypeItem.class));

        demandeCalcul.setFacteurCaracterisations(List.of(objectMapper.readValue("""
                [
                   {
                     "nom": "Electricty Mix FR",
                     "etape": "FABRICATION",
                     "critere": "Changement Climatique",
                     "categorie": "electricity-mix",
                     "localisation": "France",
                     "valeur": 0.08
                   },
                   {
                     "nom": "iphone",
                     "niveau": "2-Equipement",
                     "etape": "UTILISATION",
                     "critere": "Changement Climatique",
                     "consoElecMoyenne": null
                   }
                 ]
                  """, ReferentielFacteurCaracterisation[].class)));

        //When
        var actual = calculImpactEquipementPhysiqueService.calculerImpactEquipementPhysique(demandeCalcul);

        //Then
        assertContentIndicateur(demandeCalcul, actual);
        assertNull(actual.getImpactUnitaire());
        assertEquals("ERREUR", actual.getStatutIndicateur());
        assertEquals(
                "{\"erreur\":\"ErrCalcFonc : Donnée de consommation electrique manquante : equipementPhysique : Apple Iphone 11, RefEquipementCible : iphone, RefItemParDefaut : iphone\"}",
                actual.getTrace()
        );
    }

    @Test
    void taiga918_ecranCAF5() throws JsonProcessingException {
        //Given
        DemandeCalculImpactEquipementPhysique demandeCalcul = createDemande();
        demandeCalcul.getEquipementPhysique().setModele("ECRAN DELL");
        demandeCalcul.getEquipementPhysique().setConsoElecAnnuelle(100.0);

        demandeCalcul.setCorrespondanceRefEquipement(objectMapper.readValue("""
                {
                   "modeleEquipementSource":"ECRAN DELL",
                   "refEquipementCible": "ref-Ecran"
                 }
                """, ReferentielCorrespondanceRefEquipement.class));

        demandeCalcul.setFacteurCaracterisations(List.of(objectMapper.readValue("""
                [
                   {
                     "nom": "Electricty Mix FR",
                     "etape": "FABRICATION",
                     "critere": "Changement Climatique",
                     "categorie": "electricity-mix",
                     "localisation": "France",
                     "valeur": 0.08
                   },
                   {
                     "nom": "iphone",
                     "niveau": "2-Equipement",
                     "etape": "UTILISATION",
                     "critere": "Changement Climatique",
                     "consoElecMoyenne": null
                   }
                 ]
                  """, ReferentielFacteurCaracterisation[].class)));

        //When
        var actual = calculImpactEquipementPhysiqueService.calculerImpactEquipementPhysique(demandeCalcul);

        //Then
        assertContentIndicateur(demandeCalcul, actual);
        assertEquals("OK", actual.getStatutIndicateur());
        assertNotNull(actual.getImpactUnitaire());
        double actualImpactUnitaireLimited = BigDecimal.valueOf(actual.getImpactUnitaire()).setScale(4, RoundingMode.DOWN).doubleValue();
        // quantité * equipementPhysique.consoElecAnnuelle * MixElec France
        assertEquals(BigDecimal.valueOf(1.0 * 100.0 * 0.08).setScale(4, RoundingMode.DOWN).doubleValue(), actualImpactUnitaireLimited);
    }

    @Test
    void shouldCalculerImpactEquipementPhysique_CAF4() throws Exception {
        //Given
        DemandeCalculImpactEquipementPhysique demandeCalcul = createDemande();
        demandeCalcul.setEtape(objectMapper.readValue("""
                {"code": "FABRICATION"}
                """, ReferentielEtapeACV.class));

        demandeCalcul.getEquipementPhysique().setModele("ECRAN DELL");

        demandeCalcul.setCorrespondanceRefEquipement(objectMapper.readValue("""
                {
                   "modeleEquipementSource":"ECRAN DELL",
                   "refEquipementCible": "refEcran"
                 }
                """, ReferentielCorrespondanceRefEquipement.class));

        demandeCalcul.setFacteurCaracterisations(List.of(objectMapper.readValue("""
                [
                   {
                     "nom": "Electricty Mix FR",
                     "etape": "FABRICATION",
                     "critere": "Changement Climatique",
                     "categorie": "electricity-mix",
                     "localisation": "France",
                     "valeur": 0.08
                   },
                   {
                     "nom": "refEcran",
                     "niveau": "2-Equipement",
                     "etape": "FABRICATION",
                     "critere": "Changement Climatique",
                     "valeur": 142.0
                   }
                 ]
                  """, ReferentielFacteurCaracterisation[].class)));

        when(dureeDeVieEquipementPhysiqueService.calculerDureeVie(any(DemandeCalculImpactEquipementPhysique.class))).thenReturn(DureeDeVie.builder().valeurRetenue(5.0d).build());

        //When
        var actual = calculImpactEquipementPhysiqueService.calculerImpactEquipementPhysique(demandeCalcul);

        //Then
        assertContentIndicateur(demandeCalcul, actual);
        assertEquals("OK", actual.getStatutIndicateur());
        assertNotNull(actual.getImpactUnitaire());
        double actualImpactUnitaireLimited = BigDecimal.valueOf(actual.getImpactUnitaire()).setScale(4, RoundingMode.DOWN).doubleValue();
        // quantité * facteurCaracterisation / dureeDeVie
        assertEquals(BigDecimal.valueOf(1.0 * 142.0 / 5.0).setScale(4, RoundingMode.DOWN).doubleValue(), actualImpactUnitaireLimited);
        assertEquals(28.4, actualImpactUnitaireLimited);
        verify(dureeDeVieEquipementPhysiqueService, times(1)).calculerDureeVie(demandeCalcul);
        assertNull(actual.getConsoElecMoyenne());
    }

    @Test
    void shouldCalculerImpactEquipementPhysique_CAF5() throws Exception {
        //Given
        DemandeCalculImpactEquipementPhysique demandeCalcul = createDemande();
        demandeCalcul.setEtape(objectMapper.readValue("""
                {"code": "FIN_DE_VIE"}
                """, ReferentielEtapeACV.class));

        demandeCalcul.getEquipementPhysique().setModele("ECRAN DELL");

        demandeCalcul.setCorrespondanceRefEquipement(objectMapper.readValue("""
                {
                   "modeleEquipementSource":"ECRAN DELL",
                   "refEquipementCible": "ref-Ecran"
                 }
                """, ReferentielCorrespondanceRefEquipement.class));

        demandeCalcul.setFacteurCaracterisations(List.of(objectMapper.readValue("""
                [
                   {
                     "nom": "Electricty Mix FR",
                     "etape": "FABRICATION",
                     "critere": "Changement Climatique",
                     "categorie": "electricity-mix",
                     "localisation": "France",
                     "valeur": 0.08
                   },
                   {
                     "nom": "ref-Ecran",
                     "niveau": "2-Equipement",
                     "etape": "FIN_DE_VIE",
                     "critere": "Changement Climatique",
                     "valeur": 17.3
                   }
                 ]
                  """, ReferentielFacteurCaracterisation[].class)));

        when(dureeDeVieEquipementPhysiqueService.calculerDureeVie(any(DemandeCalculImpactEquipementPhysique.class))).thenReturn(DureeDeVie.builder().valeurRetenue(6.0).build());

        //When
        var actual = calculImpactEquipementPhysiqueService.calculerImpactEquipementPhysique(demandeCalcul);

        //Then
        assertContentIndicateur(demandeCalcul, actual);
        assertEquals("OK", actual.getStatutIndicateur());
        assertNotNull(actual.getImpactUnitaire());
        double actualImpactUnitaireLimited = BigDecimal.valueOf(actual.getImpactUnitaire()).setScale(4, RoundingMode.DOWN).doubleValue();
        // quantité * facteurCaracterisation / dureeDeVie
        assertEquals(BigDecimal.valueOf(1.0 * 17.3 / 6.0).setScale(4, RoundingMode.DOWN).doubleValue(), actualImpactUnitaireLimited);
        verify(dureeDeVieEquipementPhysiqueService, times(1)).calculerDureeVie(demandeCalcul);
        assertNull(actual.getConsoElecMoyenne());
    }

    @Test
    void shouldCalculerImpactEquipementPhysique_CAF6() throws Exception {
        //Given
        DemandeCalculImpactEquipementPhysique demandeCalcul = createDemande();
        demandeCalcul.setEtape(objectMapper.readValue("""
                {"code": "DISTRIBUTION"}
                """, ReferentielEtapeACV.class));

        demandeCalcul.getEquipementPhysique().setModele("ECRAN DELL");

        demandeCalcul.setCorrespondanceRefEquipement(objectMapper.readValue("""
                {
                   "modeleEquipementSource":"ECRAN DELL",
                   "refEquipementCible": "ref-Ecran"
                 }
                """, ReferentielCorrespondanceRefEquipement.class));

        demandeCalcul.setFacteurCaracterisations(List.of(objectMapper.readValue("""
                [
                   {
                     "nom": "Electricty Mix FR",
                     "etape": "FABRICATION",
                     "critere": "Changement Climatique",
                     "categorie": "electricity-mix",
                     "localisation": "France",
                     "valeur": 0.08
                   },
                   {
                     "nom": "ref-Ecran",
                     "niveau": "2-Equipement",
                     "etape": "DISTRIBUTION",
                     "critere": "Changement Climatique",
                     "valeur": 1.273
                   }
                 ]
                  """, ReferentielFacteurCaracterisation[].class)));

        when(dureeDeVieEquipementPhysiqueService.calculerDureeVie(any(DemandeCalculImpactEquipementPhysique.class))).thenReturn(DureeDeVie.builder().valeurRetenue(7.0).build());

        //When
        var actual = calculImpactEquipementPhysiqueService.calculerImpactEquipementPhysique(demandeCalcul);

        //Then
        assertContentIndicateur(demandeCalcul, actual);
        assertEquals("OK", actual.getStatutIndicateur());
        assertNotNull(actual.getImpactUnitaire());
        double actualImpactUnitaireLimited = BigDecimal.valueOf(actual.getImpactUnitaire()).setScale(4, RoundingMode.DOWN).doubleValue();
        // quantité * facteurCaracterisation / dureeDeVie
        assertEquals(BigDecimal.valueOf(1.0 * 1.273 / 7.0).setScale(4, RoundingMode.DOWN).doubleValue(), actualImpactUnitaireLimited);
        verify(dureeDeVieEquipementPhysiqueService, times(1)).calculerDureeVie(demandeCalcul);
        assertNull(actual.getConsoElecMoyenne());
    }

    @Test
    void shouldBuildTraceWithFirstFormulaScenario1() throws JsonProcessingException {

        //Given
        DemandeCalculImpactEquipementPhysique demandeCalcul = createDemande();
        demandeCalcul.getEquipementPhysique().setQuantite(3d);
        demandeCalcul.getEquipementPhysique().setConsoElecAnnuelle(200d);
        demandeCalcul.getEquipementPhysique().setModele("ECRAN DELL");

        demandeCalcul.setFacteurCaracterisations(List.of(objectMapper.readValue("""
                [
                   {
                     "nom": "Electricty Mix FR",
                     "etape": "FABRICATION",
                     "critere": "Changement Climatique",
                     "categorie": "electricity-mix",
                     "localisation": "France",
                     "valeur": 150,
                     "source": "Source-Mix"
                   }
                 ]
                  """, ReferentielFacteurCaracterisation[].class)));

        var formule = TraceCalculImpactEquipementPhysiqueUtils.getFormulePremierScenario(3d, 200d, 150d, 1.0);
        var consoElecAnMoyenne = ConsoElecAnMoyenne.builder().valeurItemConsoElecAnnuelle(200d).valeur(200d).build();
        var mixElectrique = MixElectrique.builder().valeur(150d).valeurReferentielMixElectrique(150d).sourceReferentielMixElectrique("Source-Mix").build();
        TraceCalculImpactEquipementPhysique trace = TraceCalculImpactEquipementPhysique.builder()
                .formule(formule)
                .consoElecAnMoyenne(consoElecAnMoyenne)
                .mixElectrique(mixElectrique)
                .build();

        var expected = new ObjectMapper().writeValueAsString(trace);

        //WHEN
        var actual = calculImpactEquipementPhysiqueService.calculerImpactEquipementPhysique(demandeCalcul);
        //THEN
        assertEquals(expected, actual.getTrace());
    }

    @Test
    void shouldBuildTraceWithFirstFormulaScenario2() throws JsonProcessingException {
        //Given
        DemandeCalculImpactEquipementPhysique demandeCalcul = createDemande();

        DataCenter dataCenter = DataCenter.builder()
                .localisation("Spain")
                .pue(5d)
                .build();
        demandeCalcul.getEquipementPhysique().setDataCenter(dataCenter);

        demandeCalcul.getEquipementPhysique().setQuantite(6d);
        demandeCalcul.getEquipementPhysique().setDataCenter(dataCenter);
        demandeCalcul.getEquipementPhysique().setServeur(true);
        demandeCalcul.getEquipementPhysique().setModele("ECRAN DELL");

        demandeCalcul.setCorrespondanceRefEquipement(objectMapper.readValue("""
                {
                   "modeleEquipementSource":"ECRAN DELL",
                   "refEquipementCible": "ref-Ecran"
                 }
                """, ReferentielCorrespondanceRefEquipement.class));

        demandeCalcul.setFacteurCaracterisations(List.of(objectMapper.readValue("""
                [
                   {
                     "nom": "Electricty Mix FR",
                     "etape": "FABRICATION",
                     "critere": "Changement Climatique",
                     "categorie": "electricity-mix",
                     "localisation": "Spain",
                     "valeur": 25,
                     "source":"Source-Mix"
                   },
                   {
                     "nom": "ref-Ecran",
                     "niveau": "2-Equipement",
                     "etape": "UTILISATION",
                     "critere": "Changement Climatique",
                     "consoElecMoyenne":130,
                     "source": "source-RefEquipement"
                   }
                 ]
                  """, ReferentielFacteurCaracterisation[].class)));

        var formule = TraceCalculImpactEquipementPhysiqueUtils.getFormulePremierScenario(6d, 130d, 5d * 25d, 1.0);

        var consoElecAnMoyenne = ConsoElecAnMoyenne.builder()
                .valeur(130d)
                .valeurReferentielConsoElecMoyenne(130d)
                .sourceReferentielFacteurCaracterisation("source-RefEquipement")
                .build();
        var mixElectrique = MixElectrique.builder()
                .valeur(125d)
                .dataCenterPue(5d)
                .valeurReferentielMixElectrique(25d)
                .sourceReferentielMixElectrique("Source-Mix")
                .isServeur(true)
                .build();
        TraceCalculImpactEquipementPhysique trace = TraceCalculImpactEquipementPhysique.builder()
                .formule(formule)
                .consoElecAnMoyenne(consoElecAnMoyenne)
                .mixElectrique(mixElectrique)
                .build();
        var expected = new ObjectMapper().writeValueAsString(trace);
        //WHEN
        var actual = calculImpactEquipementPhysiqueService.calculerImpactEquipementPhysique(demandeCalcul);
        //THEN
        assertEquals(expected, actual.getTrace());
    }

    @Test
    void shouldBuildTraceWithSecondFormula() throws JsonProcessingException, CalculImpactException {

        //Given
        LocalDate dateAchat = LocalDate.of(2020, 1, 22);
        LocalDate dateRetrait = LocalDate.of(2021, 5, 12);

        DemandeCalculImpactEquipementPhysique demandeCalcul = createDemande();
        demandeCalcul.getEquipementPhysique().setQuantite(6d);
        demandeCalcul.getEquipementPhysique().setDateAchat(dateAchat);
        demandeCalcul.getEquipementPhysique().setDateRetrait(dateRetrait);
        demandeCalcul.setOptionsCalcul(new OptionsCalcul("REEL"));
        demandeCalcul.getEquipementPhysique().setModele("ECRAN DELL");

        demandeCalcul.setEtape(objectMapper.readValue("""
                {"code": "FIN DE VIE"}
                """, ReferentielEtapeACV.class));

        demandeCalcul.setCorrespondanceRefEquipement(objectMapper.readValue("""
                {
                   "modeleEquipementSource":"ECRAN DELL",
                   "refEquipementCible": "ref-Ecran"
                 }
                """, ReferentielCorrespondanceRefEquipement.class));

        demandeCalcul.setFacteurCaracterisations(List.of(objectMapper.readValue("""
                [
                   {
                     "nom": "ref-Ecran",
                     "niveau": "2-Equipement",
                     "etape": "FIN DE VIE",
                     "critere": "Changement Climatique",
                     "valeur":100,
                     "source": "sourceReferentielFacteurCaracterisation"
                   }
                 ]
                  """, ReferentielFacteurCaracterisation[].class)));

        when(dureeDeVieEquipementPhysiqueService.calculerDureeVie(demandeCalcul)).thenCallRealMethod();

        var valeurDureeVie = ChronoUnit.DAYS.between(dateAchat, dateRetrait) / 365d;
        DureeDeVie dureeDeVie = DureeDeVie.builder()
                .valeurRetenue(valeurDureeVie)
                .methodeDureeUsage("REEL")
                .dureeUsageAmont(0.0)
                .dureeUsageAval(0.0)
                .dateAchat(dateAchat.format(DateTimeFormatter.ISO_DATE))
                .dateRetrait(dateRetrait.format(DateTimeFormatter.ISO_DATE))
                .build();

        var formule = TraceCalculImpactEquipementPhysiqueUtils.getFormuleSecondScenario(6d, 100d, valeurDureeVie, 1.0);

        TraceCalculImpactEquipementPhysique trace = TraceCalculImpactEquipementPhysique.builder()
                .formule(formule)
                .dureeDeVie(dureeDeVie)
                .valeurReferentielFacteurCaracterisation(100d)
                .sourceReferentielFacteurCaracterisation("sourceReferentielFacteurCaracterisation")
                .build();
        var expected = new ObjectMapper().writeValueAsString(trace);
        //WHEN
        var actual = calculImpactEquipementPhysiqueService.calculerImpactEquipementPhysique(demandeCalcul);

        //THEN
        assertEquals(expected, actual.getTrace());
    }

    //EN DOUBLE
    @Test
    void taiga862_CAF1_calculImpactEquipementPhysiqueWithoutCorrespondanceShouldUseRefItemParDefaut() throws Exception {
        //Given
        DemandeCalculImpactEquipementPhysique demandeCalcul = createDemande();
        demandeCalcul.getEquipementPhysique().setModele("ECRAN DELL");

        demandeCalcul.setEtape(objectMapper.readValue("""
                {"code": "DISTRIBUTION"}
                """, ReferentielEtapeACV.class));

        demandeCalcul.setCorrespondanceRefEquipement(objectMapper.readValue("""
                {
                   "modeleEquipementSource":"ECRAN DELL",
                   "refEquipementCible": "ref-Ecran"
                 }
                """, ReferentielCorrespondanceRefEquipement.class));

        demandeCalcul.setFacteurCaracterisations(List.of(objectMapper.readValue("""
                [
                   {
                     "nom": "ref-Ecran",
                     "niveau": "2-Equipement",
                     "etape": "DISTRIBUTION",
                     "critere": "Changement Climatique",
                     "valeur":1.273,
                     "consoElecMoyenne": 123,
                     "source": "sourceReferentielFacteurCaracterisation"
                   }
                 ]
                  """, ReferentielFacteurCaracterisation[].class)));

        when(dureeDeVieEquipementPhysiqueService.calculerDureeVie(any(DemandeCalculImpactEquipementPhysique.class))).thenReturn(DureeDeVie.builder().valeurRetenue(7.0).build());

        //When
        var actual = calculImpactEquipementPhysiqueService.calculerImpactEquipementPhysique(demandeCalcul);

        //Then
        assertContentIndicateur(demandeCalcul, actual);
        assertEquals("OK", actual.getStatutIndicateur());
        assertNotNull(actual.getImpactUnitaire());
        double actualImpactUnitaireLimited = BigDecimal.valueOf(actual.getImpactUnitaire()).setScale(4, RoundingMode.DOWN).doubleValue();
        assertEquals(0.1818, actualImpactUnitaireLimited);
        verify(dureeDeVieEquipementPhysiqueService, times(1)).calculerDureeVie(demandeCalcul);
        assertNull(actual.getConsoElecMoyenne());
    }

    @Test
    void taiga862_CAF1_calculImpactEquipementPhysiqueWithoutImpactForCorrespondanceShouldUseRefItemParDefaut() throws Exception {
        //Given
        DemandeCalculImpactEquipementPhysique demandeCalcul = createDemande();
        demandeCalcul.getEquipementPhysique().setModele("ECRAN DELL");

        demandeCalcul.setEtape(objectMapper.readValue("""
                {"code": "DISTRIBUTION"}
                """, ReferentielEtapeACV.class));

        demandeCalcul.setCorrespondanceRefEquipement(objectMapper.readValue("""
                {
                   "modeleEquipementSource":"ECRAN DELL",
                   "refEquipementCible": "ref-Cible"
                 }
                """, ReferentielCorrespondanceRefEquipement.class));

        demandeCalcul.setTypeItem(objectMapper.readValue("""
                {
                   "type": "telephone",
                   "categorie": "EquipementPhysique",
                   "refItemParDefaut": "iphone"
                 }
                """, ReferentielTypeItem.class));
        demandeCalcul.setFacteurCaracterisations(List.of(objectMapper.readValue("""
                [
                   {
                     "nom": "iphone",
                     "niveau": "2-Equipement",
                     "etape": "DISTRIBUTION",
                     "critere": "Changement Climatique",
                     "valeur":1.273,
                     "consoElecMoyenne": 123,
                     "source": "sourceReferentielFacteurCaracterisation"
                   }
                 ]
                  """, ReferentielFacteurCaracterisation[].class)));

        when(dureeDeVieEquipementPhysiqueService.calculerDureeVie(any(DemandeCalculImpactEquipementPhysique.class))).thenReturn(DureeDeVie.builder().valeurRetenue(7.0).build());

        //When
        var actual = calculImpactEquipementPhysiqueService.calculerImpactEquipementPhysique(demandeCalcul);

        //Then
        assertContentIndicateur(demandeCalcul, actual);
        assertEquals("OK", actual.getStatutIndicateur());
        assertNotNull(actual.getImpactUnitaire());
        double actualImpactUnitaireLimited = BigDecimal.valueOf(actual.getImpactUnitaire()).setScale(4, RoundingMode.DOWN).doubleValue();
        assertEquals(0.1818, actualImpactUnitaireLimited);
        verify(dureeDeVieEquipementPhysiqueService, times(1)).calculerDureeVie(demandeCalcul);
        assertNull(actual.getConsoElecMoyenne());
    }

    @Test
    void tech_whenDureeDeVieUnknown_shouldReturnIndicateurWithError() throws JsonProcessingException {
        //Given
        DemandeCalculImpactEquipementPhysique demandeCalcul = createDemande();
        demandeCalcul.getEquipementPhysique().setModele("ECRAN DELL");

        demandeCalcul.setEtape(objectMapper.readValue("""
                {"code": "DISTRIBUTION"}
                """, ReferentielEtapeACV.class));

        demandeCalcul.setCorrespondanceRefEquipement(objectMapper.readValue("""
                {
                   "modeleEquipementSource":"ECRAN DELL",
                   "refEquipementCible": "ref-Cible"
                 }
                """, ReferentielCorrespondanceRefEquipement.class));

        demandeCalcul.setTypeItem(objectMapper.readValue("""
                {
                   "type": "telephone",
                   "categorie": "EquipementPhysique",
                   "refItemParDefaut": "iphone"
                 }
                """, ReferentielTypeItem.class));
        demandeCalcul.setFacteurCaracterisations(List.of(objectMapper.readValue("""
                [
                   {
                     "nom": "iphone",
                     "niveau": "2-Equipement",
                     "etape": "DISTRIBUTION",
                     "critere": "Changement Climatique",
                     "valeur":1.273,
                     "consoElecMoyenne": 123,
                     "source": "sourceReferentielFacteurCaracterisation"
                   }
                 ]
                  """, ReferentielFacteurCaracterisation[].class)));
        //When
        var actual = calculImpactEquipementPhysiqueService.calculerImpactEquipementPhysique(demandeCalcul);

        //Then
        assertContentIndicateur(demandeCalcul, actual);
        assertNull(actual.getImpactUnitaire());
        assertEquals("ERREUR", actual.getStatutIndicateur());
        assertEquals(
                "{\"erreur\":\"ErrCalcFonc : Durée de vie de l'équipement inconnue\"}",
                actual.getTrace()
        );
    }

    @Test
    void verifyErrorwhenNoReferentielImpactEquipement_shouldReturnIndicateurWithError() throws JsonProcessingException {
        //Given
        DemandeCalculImpactEquipementPhysique demandeCalcul = createDemande();
        demandeCalcul.getEquipementPhysique().setModele("ECRAN DELL");

        demandeCalcul.setCorrespondanceRefEquipement(objectMapper.readValue("""
                {
                   "modeleEquipementSource":"ECRAN DELL",
                   "refEquipementCible": "Apple Iphone 11"
                 }
                """, ReferentielCorrespondanceRefEquipement.class));

        demandeCalcul.setTypeItem(objectMapper.readValue("""
                {
                   "type": "telephone",
                   "categorie": "EquipementPhysique",
                   "refItemParDefaut": "iphone"
                 }
                """, ReferentielTypeItem.class));
        demandeCalcul.setFacteurCaracterisations(List.of(objectMapper.readValue("""
                [
                {
                     "nom": "Electricty Mix FR",
                     "etape": "FABRICATION",
                     "critere": "Changement Climatique",
                     "categorie": "electricity-mix",
                     "localisation": "France",
                     "valeur": 0.08,
                     "source":"Source-Mix"
                   }                   
                 ]
                  """, ReferentielFacteurCaracterisation[].class)));

        //When
        var actual = calculImpactEquipementPhysiqueService.calculerImpactEquipementPhysique(demandeCalcul);

        //Then
        assertContentIndicateur(demandeCalcul, actual);
        assertNull(actual.getImpactUnitaire());
        assertEquals("ERREUR", actual.getStatutIndicateur());
        assertEquals(
                "{\"erreur\":\"ErrCalcFonc : Référentiel Facteur Caracterisation inconnu\"}",
                actual.getTrace()
        );
    }


    @Test
    void tech_whenMixElectriqueNotFound_shouldReturnIndicateurWithError() throws Exception {
        //Given
        DemandeCalculImpactEquipementPhysique demandeCalcul = createDemande();
        demandeCalcul.getEquipementPhysique().setModele("ECRAN DELL");

        demandeCalcul.setCorrespondanceRefEquipement(objectMapper.readValue("""
                {
                   "modeleEquipementSource":"ECRAN DELL",
                   "refEquipementCible": "ref-Cible"
                 }
                """, ReferentielCorrespondanceRefEquipement.class));

        demandeCalcul.setTypeItem(objectMapper.readValue("""
                {
                   "type": "telephone",
                   "categorie": "EquipementPhysique",
                   "refItemParDefaut": "iphone"
                 }
                """, ReferentielTypeItem.class));
        demandeCalcul.setFacteurCaracterisations(List.of(objectMapper.readValue("""
                [
                   {
                     "nom": "iphone",
                     "niveau": "2-Equipement",
                     "etape": "UTILISATION",
                     "critere": "Changement Climatique",
                     "valeur":1.273,
                     "consoElecMoyenne": 123,
                     "source": "sourceReferentielFacteurCaracterisation"
                   }
                 ]
                  """, ReferentielFacteurCaracterisation[].class)));
        //When
        var actual = calculImpactEquipementPhysiqueService.calculerImpactEquipementPhysique(demandeCalcul);

        //Then
        assertContentIndicateur(demandeCalcul, actual);
        assertNull(actual.getImpactUnitaire());
        assertEquals("ERREUR", actual.getStatutIndicateur());
        assertEquals(
                "{\"erreur\":\"ErrCalcFonc : Il n'existe pas de Mix électrique pour cet équipement : critere: Changement Climatique - equipement: Apple Iphone 11 - Localisation: France - NomCourtDatacenter: null\"}",
                actual.getTrace()
        );
    }

    @Test
    void tech_whenMixElectriqueNotFoundWithDataCenter_shouldReturnIndicateurWithError() throws Exception {
        //Given
        DemandeCalculImpactEquipementPhysique demandeCalcul = createDemande();
        demandeCalcul.getEquipementPhysique().setModele("ECRAN DELL");
        demandeCalcul.getEquipementPhysique().setNomCourtDatacenter("TanaMana 2");
        DataCenter dataCenter = DataCenter.builder()
                .localisation("Spain")
                .pue(5d)
                .nomCourtDatacenter("TanaMana 2")
                .build();
        demandeCalcul.getEquipementPhysique().setDataCenter(dataCenter);

        demandeCalcul.setCorrespondanceRefEquipement(objectMapper.readValue("""
                {
                   "modeleEquipementSource":"ECRAN DELL",
                   "refEquipementCible": "ref-Cible"
                 }
                """, ReferentielCorrespondanceRefEquipement.class));

        demandeCalcul.setTypeItem(objectMapper.readValue("""
                {
                   "type": "telephone",
                   "categorie": "EquipementPhysique",
                   "refItemParDefaut": "iphone"
                 }
                """, ReferentielTypeItem.class));
        demandeCalcul.setFacteurCaracterisations(List.of(objectMapper.readValue("""
                [
                   {
                     "nom": "iphone",
                     "niveau": "2-Equipement",
                     "etape": "UTILISATION",
                     "critere": "Changement Climatique",
                     "valeur":1.273,
                     "consoElecMoyenne": 123,
                     "source": "sourceReferentielFacteurCaracterisation"
                   }
                 ]
                  """, ReferentielFacteurCaracterisation[].class)));
        //When
        var actual = calculImpactEquipementPhysiqueService.calculerImpactEquipementPhysique(demandeCalcul);

        //Then
        assertContentIndicateur(demandeCalcul, actual);
        assertNull(actual.getImpactUnitaire());
        assertEquals("ERREUR", actual.getStatutIndicateur());
        assertEquals(
                "{\"erreur\":\"ErrCalcFonc : Il n'existe pas de Mix électrique pour cet équipement : critere: Changement Climatique - equipement: Apple Iphone 11 - Localisation: France - NomCourtDatacenter: TanaMana 2\"}",
                actual.getTrace()
        );
    }

    @Test
    void tech_whenPUEUknown_shouldReturnIndicateurWithError() throws Exception {
        //Given
        DemandeCalculImpactEquipementPhysique demandeCalcul = createDemande();
        demandeCalcul.getEquipementPhysique().setModele("ECRAN DELL");
        demandeCalcul.getEquipementPhysique().setNomCourtDatacenter("TanaMana 2");
        demandeCalcul.getEquipementPhysique().setServeur(true);
        DataCenter dataCenter = DataCenter.builder()
                .localisation("France")
                .pue(null)
                .nomCourtDatacenter("TanaMana 2")
                .build();
        demandeCalcul.getEquipementPhysique().setDataCenter(dataCenter);

        demandeCalcul.setCorrespondanceRefEquipement(objectMapper.readValue("""
                {
                   "modeleEquipementSource":"ECRAN DELL",
                   "refEquipementCible": "ref-Cible"
                 }
                """, ReferentielCorrespondanceRefEquipement.class));

        demandeCalcul.setTypeItem(objectMapper.readValue("""
                {
                   "type": "telephone",
                   "categorie": "EquipementPhysique",
                   "refItemParDefaut": "iphone"
                 }
                """, ReferentielTypeItem.class));
        demandeCalcul.setFacteurCaracterisations(List.of(objectMapper.readValue("""
                [
                    {
                       "nom": "Electricty Mix FR",
                       "etape": "FABRICATION",
                       "critere": "Changement Climatique",
                       "categorie" : "electricity-mix",
                       "niveau": "0-Base data",
                       "localisation": "France",
                       "source": "CAF1",
                       "valeur" : 0.08
                    },
                   {
                     "nom": "iphone",
                     "niveau": "2-Equipement",
                     "etape": "UTILISATION",
                     "critere": "Changement Climatique",
                     "valeur":1.273,
                     "consoElecMoyenne": 123,
                     "source": "sourceReferentielFacteurCaracterisation"
                   }
                 ]
                  """, ReferentielFacteurCaracterisation[].class)));

        //When
        var actual = calculImpactEquipementPhysiqueService.calculerImpactEquipementPhysique(demandeCalcul);

        //Then
        assertContentIndicateur(demandeCalcul, actual);
        assertNull(actual.getImpactUnitaire());
        assertEquals("ERREUR", actual.getStatutIndicateur());
        assertEquals(
                "{\"erreur\":\"ErrCalcFonc : Le PUE est manquant et ne permet le calcul de l'impact à l'usage de l'équipement : equipement: Apple Iphone 11 - RefEquipementCible: ref-Cible - refItemParDefaut: iphone - NomCourtDatacenter: TanaMana 2\"}",
                actual.getTrace()
        );
    }


    @Test
    void verifyFormulaForModeUtilisationCOPE() throws JsonProcessingException {
        //Given
        DemandeCalculImpactEquipementPhysique demandeCalcul = createDemande();
        demandeCalcul.getEquipementPhysique().setModeUtilisation("COPE");
        demandeCalcul.setHypotheses(List.of(objectMapper.readValue("""
                [
                  {
                    "code": "taux_utilisation_COPE",
                    "valeur": "0.8",
                    "source": "source"
                  }
                ]
                """, ReferentielHypothese[].class)));
        demandeCalcul.setFacteurCaracterisations(List.of(objectMapper.readValue("""
                [
                    {
                       "nom": "Electricty Mix FR",
                       "etape": "FABRICATION",
                       "critere": "Changement Climatique",
                       "categorie" : "electricity-mix",
                       "niveau": "0-Base data",
                       "localisation": "France",
                       "source": "CAF1",
                       "valeur" : 0.0813225
                    },
                   {
                     "nom": "iphone",
                     "niveau": "2-Equipement",
                     "etape": "UTILISATION",
                     "critere": "Changement Climatique",
                     "consoElecMoyenne": 0.09,
                     "source": "sourceReferentielFacteurCaracterisation"
                   }
                 ]
                  """, ReferentielFacteurCaracterisation[].class)));
        //When
        var actual = calculImpactEquipementPhysiqueService.calculerImpactEquipementPhysique(demandeCalcul);

        //Then
        assertContentIndicateur(demandeCalcul, actual);
        assertEquals("OK", actual.getStatutIndicateur());
        assertNotNull(actual.getImpactUnitaire());
        double actualImpactUnitaireLimited = BigDecimal.valueOf(actual.getImpactUnitaire()).setScale(4, RoundingMode.DOWN).doubleValue();
        // quantité * consoElecMoyenne *tauxUtilisation
        assertEquals(BigDecimal.valueOf(0.0813225 * 0.09 * 0.8).setScale(4, RoundingMode.DOWN).doubleValue(), actualImpactUnitaireLimited);
    }

    @Test
    void verifyFormulaForModeUtilisationBYOD() throws JsonProcessingException {
        //Given
        DemandeCalculImpactEquipementPhysique demandeCalcul = createDemande();
        demandeCalcul.getEquipementPhysique().setModeUtilisation("BYOD");
        demandeCalcul.setHypotheses(List.of(objectMapper.readValue("""
                [
                  {
                    "code": "taux_utilisation_BYOD",
                    "valeur": "0.57",
                    "source": "source"
                  }
                ]
                """, ReferentielHypothese[].class)));
        demandeCalcul.setFacteurCaracterisations(List.of(objectMapper.readValue("""
                [
                    {
                       "nom": "Electricty Mix FR",
                       "etape": "FABRICATION",
                       "critere": "Changement Climatique",
                       "categorie" : "electricity-mix",
                       "niveau": "0-Base data",
                       "localisation": "France",
                       "source": "CAF1",
                       "valeur" : 0.0813225
                    },
                   {
                     "nom": "iphone",
                     "niveau": "2-Equipement",
                     "etape": "UTILISATION",
                     "critere": "Changement Climatique",
                     "consoElecMoyenne": 0.09,
                     "source": "sourceReferentielFacteurCaracterisation"
                   }
                 ]
                  """, ReferentielFacteurCaracterisation[].class)));
        //When
        var actual = calculImpactEquipementPhysiqueService.calculerImpactEquipementPhysique(demandeCalcul);

        //Then
        assertContentIndicateur(demandeCalcul, actual);
        assertEquals("OK", actual.getStatutIndicateur());
        assertNotNull(actual.getImpactUnitaire());
        double actualImpactUnitaireLimited = BigDecimal.valueOf(actual.getImpactUnitaire()).setScale(4, RoundingMode.DOWN).doubleValue();
        // quantité * consoElecMoyenne *tauxUtilisation
        assertEquals(BigDecimal.valueOf(0.0813225 * 0.09 * 0.57).setScale(4, RoundingMode.DOWN).doubleValue(), actualImpactUnitaireLimited);
    }

    @Test
    void verifyFormulaForModeUtilisationModeUtilisationNull() throws JsonProcessingException {
        //Given
        DemandeCalculImpactEquipementPhysique demandeCalcul = createDemande();
        demandeCalcul.getEquipementPhysique().setModeUtilisation(null);
        demandeCalcul.setFacteurCaracterisations(List.of(objectMapper.readValue("""
                [
                    {
                       "nom": "Electricty Mix FR",
                       "etape": "FABRICATION",
                       "critere": "Changement Climatique",
                       "categorie" : "electricity-mix",
                       "niveau": "0-Base data",
                       "localisation": "France",
                       "source": "CAF1",
                       "valeur" : 0.0813225
                    },
                   {
                     "nom": "iphone",
                     "niveau": "2-Equipement",
                     "etape": "UTILISATION",
                     "critere": "Changement Climatique",
                     "consoElecMoyenne": 0.09,
                     "source": "sourceReferentielFacteurCaracterisation"
                   }
                 ]
                  """, ReferentielFacteurCaracterisation[].class)));
        //When
        var actual = calculImpactEquipementPhysiqueService.calculerImpactEquipementPhysique(demandeCalcul);

        //Then
        assertContentIndicateur(demandeCalcul, actual);
        assertEquals("OK", actual.getStatutIndicateur());
        assertNotNull(actual.getImpactUnitaire());
        double actualImpactUnitaireLimited = BigDecimal.valueOf(actual.getImpactUnitaire()).setScale(4, RoundingMode.DOWN).doubleValue();
        // quantité * consoElecMoyenne * tauxUtilisation
        assertEquals(BigDecimal.valueOf(0.0813225 * 0.09 * 1.0).setScale(4, RoundingMode.DOWN).doubleValue(), actualImpactUnitaireLimited);
    }

    @Test
    void verifyFormulaForModeUtilisationUnknown() throws JsonProcessingException {
        DemandeCalculImpactEquipementPhysique demandeCalcul = createDemande();
        demandeCalcul.getEquipementPhysique().setModeUtilisation("COB");
        demandeCalcul.setHypotheses(List.of(objectMapper.readValue("""
                [
                  {
                    "code": "taux_utilisation_BYOD",
                    "valeur": "0.57",
                    "source": "source"
                  }
                ]
                """, ReferentielHypothese[].class)));
        demandeCalcul.setFacteurCaracterisations(List.of(objectMapper.readValue("""
                [
                    {
                       "nom": "Electricty Mix FR",
                       "etape": "FABRICATION",
                       "critere": "Changement Climatique",
                       "categorie" : "electricity-mix",
                       "niveau": "0-Base data",
                       "localisation": "France",
                       "source": "CAF1",
                       "valeur" : 0.0813225
                    }
                 ]
                  """, ReferentielFacteurCaracterisation[].class)));
        //When
        var actual = calculImpactEquipementPhysiqueService.calculerImpactEquipementPhysique(demandeCalcul);
        //Then
        assertContentIndicateur(demandeCalcul, actual);
        assertEquals("ERREUR", actual.getStatutIndicateur());
        assertEquals("""
                {"erreur":"ErrCalcFonc : Le mode d'utilisation doit avoir les valeurs BYOD"}
                """.trim(), actual.getTrace());
    }

    @Test
    void verifyFormulaForModeUtilisationModeUtilisationCOPEButNotInRefHypotheses() throws JsonProcessingException {
        DemandeCalculImpactEquipementPhysique demandeCalcul = createDemande();
        demandeCalcul.getEquipementPhysique().setModeUtilisation("COPE");

        demandeCalcul.setFacteurCaracterisations(List.of(objectMapper.readValue("""
                [
                    {
                       "nom": "Electricty Mix FR",
                       "etape": "FABRICATION",
                       "critere": "Changement Climatique",
                       "categorie" : "electricity-mix",
                       "niveau": "0-Base data",
                       "localisation": "France",
                       "source": "CAF1",
                       "valeur" : 0.0813225
                    }
                 ]
                  """, ReferentielFacteurCaracterisation[].class)));

        //When
        var actual = calculImpactEquipementPhysiqueService.calculerImpactEquipementPhysique(demandeCalcul);

        //Then
        assertContentIndicateur(demandeCalcul, actual);
        assertEquals("ERREUR", actual.getStatutIndicateur());
        assertEquals("""
                {"erreur":"ErrCalcFonc : Il n'y a pas d'hypotheses pour le taux d'utilisation"}
                """.trim(), actual.getTrace());
    }

    @Test
    void verifyFormulaForNotTUILISATIONModeUtilisationCOPE() throws Exception {
        DemandeCalculImpactEquipementPhysique demandeCalcul = createDemande();
        demandeCalcul.setEtape(objectMapper.readValue("""
                {"code": "DISTRIBUTION"}
                """, ReferentielEtapeACV.class));

        demandeCalcul.getEquipementPhysique().setModele("ECRAN DELL");
        demandeCalcul.getEquipementPhysique().setModeUtilisation("COPE");
        demandeCalcul.setHypotheses(List.of(objectMapper.readValue("""
                [
                  {
                    "code": "taux_utilisation_COPE",
                    "valeur": "0.85",
                    "source": "source"
                  }
                ]
                """, ReferentielHypothese[].class)));
        demandeCalcul.setCorrespondanceRefEquipement(objectMapper.readValue("""
                {
                   "modeleEquipementSource":"ECRAN DELL",
                   "refEquipementCible": "ref-Ecran"
                 }
                """, ReferentielCorrespondanceRefEquipement.class));

        demandeCalcul.setFacteurCaracterisations(List.of(objectMapper.readValue("""
                [
                   {
                     "nom": "ref-Ecran",
                     "niveau": "2-Equipement",
                     "etape": "DISTRIBUTION",
                     "critere": "Changement Climatique",
                     "valeur": 142
                   }
                 ]
                  """, ReferentielFacteurCaracterisation[].class)));

        when(dureeDeVieEquipementPhysiqueService.calculerDureeVie(any(DemandeCalculImpactEquipementPhysique.class)))
                .thenReturn(DureeDeVie.builder().valeurRetenue(5.0d).build());

        //When
        var actual = calculImpactEquipementPhysiqueService.calculerImpactEquipementPhysique(demandeCalcul);

        //Then
        assertContentIndicateur(demandeCalcul, actual);
        assertEquals("OK", actual.getStatutIndicateur());
        assertNotNull(actual.getImpactUnitaire());
        double actualImpactUnitaireLimited = BigDecimal.valueOf(actual.getImpactUnitaire()).setScale(4, RoundingMode.DOWN).doubleValue();
        // quantité / facteurCaracterisation * tauxUtilisation
        assertEquals(BigDecimal.valueOf(1.0 * 142 * 0.85 / 5.0).setScale(4, RoundingMode.DOWN).doubleValue(), actualImpactUnitaireLimited);
        verify(dureeDeVieEquipementPhysiqueService, times(1)).calculerDureeVie(demandeCalcul);
        assertNull(actual.getConsoElecMoyenne());
    }

    @Test
    void verifyFormulaForUTILISATIONTauxUtilisationFilled() throws Exception {

        DemandeCalculImpactEquipementPhysique demandeCalcul = createDemande();

        demandeCalcul.getEquipementPhysique().setModele("ECRAN DELL");
        demandeCalcul.getEquipementPhysique().setConsoElecAnnuelle(56.0);
        demandeCalcul.getEquipementPhysique().setTauxUtilisation(0.1);

        demandeCalcul.setCorrespondanceRefEquipement(objectMapper.readValue("""
                {
                   "modeleEquipementSource":"ECRAN DELL",
                   "refEquipementCible": "ref-Ecran"
                 }
                """, ReferentielCorrespondanceRefEquipement.class));

        demandeCalcul.setFacteurCaracterisations(List.of(objectMapper.readValue("""
                [
                   {
                     "nom": "Electricty Mix FR",
                     "etape": "FABRICATION",
                     "critere": "Changement Climatique",
                     "categorie" : "electricity-mix",
                     "niveau": "0-Base data",
                     "localisation": "France",
                     "source": "CAF1",
                     "valeur" : 0.08
                   }
                 ]
                  """, ReferentielFacteurCaracterisation[].class)));

        //When
        var actual = calculImpactEquipementPhysiqueService.calculerImpactEquipementPhysique(demandeCalcul);
        //Then
        assertContentIndicateur(demandeCalcul, actual);
        assertEquals("OK", actual.getStatutIndicateur());
        assertNotNull(actual.getImpactUnitaire());
        double actualImpactUnitaireLimited = BigDecimal.valueOf(actual.getImpactUnitaire()).setScale(4, RoundingMode.DOWN).doubleValue();
        // quantité * consoElecMoyenne * MixElectrique * TauxUtilisation
        assertEquals(BigDecimal.valueOf(1.0 * 56 * 0.08 * 0.1).setScale(4, RoundingMode.DOWN).doubleValue(), actualImpactUnitaireLimited);
    }

    private void assertContentIndicateur(DemandeCalculImpactEquipementPhysique source, ImpactEquipementPhysique result) {
        assertNotNull(result);
        assertEquals(source.getEquipementPhysique().getNomEquipementPhysique(), result.getNomEquipement());
        assertEquals(source.getEquipementPhysique().getType(), result.getTypeEquipement());
        assertEquals(source.getEquipementPhysique().getStatut(), result.getStatutEquipementPhysique());
        assertEquals(source.getEquipementPhysique().getQuantite(), result.getQuantite());

        assertEquals(source.getEquipementPhysique().getNomLot(), result.getNomLot());
        assertEquals(source.getEquipementPhysique().getDateLot(), result.getDateLot());
        assertEquals(source.getEquipementPhysique().getNomOrganisation(), result.getNomOrganisation());
        assertEquals(source.getEquipementPhysique().getNomEntite(), result.getNomEntite());
        assertEquals(source.getEquipementPhysique().getNomSourceDonnee(), result.getNomSourceDonnee());

        assertEquals(source.getDateCalcul(), result.getDateCalcul());

        assertEquals(source.getCritere().getNomCritere(), result.getCritere());
        assertEquals(source.getEtape().getCode(), result.getEtapeACV());
        assertEquals(source.getCritere().getUnite(), result.getUnite());

        assertEquals("1.0", result.getVersionCalcul());
    }
}