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

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.mte.numecoeval.calculs.TestJsonUtils;
import org.mte.numecoeval.calculs.domain.data.demande.DemandeCalculImpactOperationNonIT;
import org.mte.numecoeval.calculs.domain.data.indicateurs.ImpactOperationNonIT;
import org.mte.numecoeval.calculs.domain.data.referentiel.ReferentielFacteurCaracterisation;
import org.mte.numecoeval.calculs.domain.data.referentiel.ReferentielHypothese;
import org.mte.numecoeval.calculs.domain.data.referentiel.ReferentielTypeItem;
import org.mte.numecoeval.calculs.domain.port.input.service.CalculImpactOperationNonITService;
import org.mte.numecoeval.calculs.domain.port.input.service.impl.CalculImpactOperationNonITServiceImpl;

import java.time.LocalDateTime;
import java.util.List;

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


class CalculImpactOperationNonITServiceTest {

    CalculImpactOperationNonITService calculImpactOperationNonITService = new CalculImpactOperationNonITServiceImpl();

    private static final ObjectMapper objectMapper = new ObjectMapper();

    private DemandeCalculImpactOperationNonIT createDemande() {
        try {
            var res = objectMapper.readValue("""
                       {
                         "operationNonIT": {
                           "nomItemNonIT": "nomItem",
                           "quantite": 1.0,
                           "localisation": "France",
                           "nomEntite": "nomEntite"
                         },
                         "etape": {
                           "code": "FABRICATION"
                         },
                         "critere": {
                           "nomCritere": "Changement Climatique",
                           "unite": "kg CO_{2} eq"
                         },
                         "typeItem": {},
                         "hypotheses": [],
                         "facteurCaracterisations": []
                       }
                    """, DemandeCalculImpactOperationNonIT.class);
            res.setDateCalcul(LocalDateTime.parse("2024-01-01T00:00:00"));
            return res;
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Tests de la fonction getFormuleDefaut()
     */
    @Test
    void shouldCalculerImpactOperationNonIT_reseau_mobile_fabrication() throws JsonProcessingException {
        // Given
        DemandeCalculImpactOperationNonIT demandeCalcul = createDemande();
        demandeCalcul.getOperationNonIT().setType("Reseau Mobile");
        demandeCalcul.setTypeItem(objectMapper.readValue("""
                {
                   "type": "Reseau Mobile",
                   "categorie": "RESEAU_MOBILE",
                   "refItemParDefaut": "Reseau Mobile",
                   "dureeVieDefaut": 10.0
                 }
                """, ReferentielTypeItem.class));
        demandeCalcul.setFacteurCaracterisations(List.of(objectMapper.readValue("""
                  [
                     {
                       "nom": "Reseau Mobile",
                       "etape": "FABRICATION",
                       "critere": "Changement Climatique",
                       "valeur": 10,
                       "source": "SSG"
                     }
                  ]
                """, ReferentielFacteurCaracterisation[].class)));

        // When
        var actual = calculImpactOperationNonITService.calculerImpactOperationNonIT(demandeCalcul);

        // Then
        assertContentIndicateur(demandeCalcul, actual);
        assertEquals("OK", actual.getStatutIndicateur());
        assertEquals(10.0, actual.getImpactUnitaire(), 0.0001d);
        assertEquals(TestJsonUtils.format("""
                        {
                          "formule" : "ImpactOperationNonIT = (Quantité(1.0) * ReferentielFacteurCaracterisation(10.0))",
                          "sourceReferentielFacteurCaracterisation" : "SSG",
                          "valeurReferentielFacteurCaracterisation" : 10.0
                        }
                        """),
                TestJsonUtils.format(actual.getTrace()));
    }

    /**
     * Tests de la fonction getFormuleUtilisationDefaut()
     */
    @Test
    void shouldCalculerImpactOperationNonIT_reseau_mobile_utilisation_consoElecFromItemNonIT() throws JsonProcessingException {
        // Given
        DemandeCalculImpactOperationNonIT demandeCalcul = createDemande();
        demandeCalcul.getEtape().setCode("UTILISATION");
        demandeCalcul.getOperationNonIT().setType("Reseau Mobile");
        demandeCalcul.getOperationNonIT().setConsoElecAnnuelle(100.0);
        demandeCalcul.setTypeItem(objectMapper.readValue("""
                {
                   "type": "Reseau Mobile",
                   "categorie": "RESEAU_MOBILE",
                   "refItemParDefaut": "Reseau Mobile",
                   "dureeVieDefaut": 10.0
                 }
                """, ReferentielTypeItem.class));
        demandeCalcul.setFacteurCaracterisations(List.of(objectMapper.readValue("""
                [
                   {
                     "nom": "Reseau Mobile",
                     "etape": "UTILISATION",
                     "critere": "Changement Climatique",
                     "valeur": 10,
                     "source": "SSG"
                   },
                   {
                      "nom": "Electricty Mix FR",
                      "etape": "FABRICATION",
                      "critere": "Changement Climatique",
                      "categorie" : "electricity-mix",
                      "localisation": "France",
                      "valeur" : 0.01
                   }
                ]
                  """, ReferentielFacteurCaracterisation[].class)));

        // When
        var actual = calculImpactOperationNonITService.calculerImpactOperationNonIT(demandeCalcul);

        // Then
        assertContentIndicateur(demandeCalcul, actual);
        assertEquals("OK", actual.getStatutIndicateur());
        assertEquals(1.0, actual.getImpactUnitaire(), 0.0001d);
        assertEquals(demandeCalcul.getOperationNonIT().getConsoElecAnnuelle(), actual.getConsoElecMoyenne());
        assertEquals(TestJsonUtils.format("""
                        {
                           "consoElecAnMoyenne" : {
                             "valeur" : 100.0,
                             "valeurItemConsoElecAnnuelle" : 100.0
                           },
                           "formule" : "ImpactOperationNonIT = (Quantité(1.0) * ConsoElecAnMoyenne(100.0) * MixElectrique(0.01))",
                           "mixElectrique" : {
                             "serveur" : false,
                             "valeur" : 0.01,
                             "valeurReferentielMixElectrique" : 0.01
                           }
                        }
                        """),
                TestJsonUtils.format(actual.getTrace()));
    }

    @Test
    void shouldCalculerImpactOperationNonIT_reseau_mobile_utilisation_consoElecFromFacteurCaracterisation() throws JsonProcessingException {
        // Given
        DemandeCalculImpactOperationNonIT demandeCalcul = createDemande();
        demandeCalcul.getEtape().setCode("UTILISATION");
        demandeCalcul.getOperationNonIT().setType("Reseau Mobile");
        demandeCalcul.setTypeItem(objectMapper.readValue("""
                {
                   "type": "Reseau Mobile",
                   "categorie": "RESEAU_MOBILE",
                   "refItemParDefaut": "Reseau Mobile",
                   "dureeVieDefaut": 10.0
                 }
                """, ReferentielTypeItem.class));
        demandeCalcul.setFacteurCaracterisations(List.of(objectMapper.readValue("""
                [
                   {
                     "nom": "Reseau Mobile",
                     "etape": "UTILISATION",
                     "critere": "Changement Climatique",
                     "consoElecMoyenne": 100.0,
                     "valeur": 10,
                     "source": "SSG"
                   },
                   {
                     "nom": "Electricty Mix FR",
                     "etape": "FABRICATION",
                     "critere": "Changement Climatique",
                     "categorie" : "electricity-mix",
                     "localisation": "France",
                     "valeur" : 0.01
                   }
                ]
                         """, ReferentielFacteurCaracterisation[].class)));

        // When
        var actual = calculImpactOperationNonITService.calculerImpactOperationNonIT(demandeCalcul);

        // Then
        assertContentIndicateur(demandeCalcul, actual);
        assertEquals("OK", actual.getStatutIndicateur());
        assertEquals(1.0, actual.getImpactUnitaire(), 0.0001d);
        assertEquals(TestJsonUtils.format("""
                        {
                          "consoElecAnMoyenne" : {
                            "sourceReferentielFacteurCaracterisation" : "SSG",
                            "valeur" : 100.0,
                            "valeurReferentielConsoElecMoyenne" : 100.0
                          },
                          "formule" : "ImpactOperationNonIT = (Quantité(1.0) * ConsoElecAnMoyenne(100.0) * MixElectrique(0.01))",
                          "mixElectrique" : {
                            "serveur" : false,
                            "valeur" : 0.01,
                            "valeurReferentielMixElectrique" : 0.01
                          }
                        }
                        """),
                TestJsonUtils.format(actual.getTrace()));
    }

    @Test
    void when_CalculerImpactOperationNonIT_reseau_mobile_Item_NotInFacteurCaracterisation_shouldReturnIndicateurWithError() {
        // Given
        DemandeCalculImpactOperationNonIT demandeCalcul = createDemande();
        demandeCalcul.setTypeItem(ReferentielTypeItem.builder()
                .categorie("RESEAU_MOBILE")
                .build());
        demandeCalcul.setFacteurCaracterisations(null);

        // When
        var actual = calculImpactOperationNonITService.calculerImpactOperationNonIT(demandeCalcul);

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

    @Test
    void when_CalculerImpactOperationNonIT_reseau_mobile_utilisation_And_NoConsoElec_shouldReturnIndicateurWithError() throws JsonProcessingException {
        // Given
        DemandeCalculImpactOperationNonIT demandeCalcul = createDemande();
        demandeCalcul.getEtape().setCode("UTILISATION");
        demandeCalcul.getOperationNonIT().setType("Reseau Mobile");
        demandeCalcul.setTypeItem(objectMapper.readValue("""
                {
                   "type": "Reseau Mobile",
                   "categorie": "RESEAU_MOBILE",
                   "refItemParDefaut": "Reseau Mobile",
                   "dureeVieDefaut": 10.0
                 }
                """, ReferentielTypeItem.class));
        demandeCalcul.setFacteurCaracterisations(List.of(objectMapper.readValue("""
                [
                   {
                     "nom": "Reseau Mobile",
                     "etape": "UTILISATION",
                     "critere": "Changement Climatique",
                     "valeur": 10,
                     "source": "SSG"
                   },
                   {
                     "nom": "Electricty Mix FR",
                     "etape": "FABRICATION",
                     "critere": "Changement Climatique",
                     "categorie" : "electricity-mix",
                     "localisation": "France",
                     "valeur" : 0.01
                   }
                ]
                         """, ReferentielFacteurCaracterisation[].class)));

        // When
        var actual = calculImpactOperationNonITService.calculerImpactOperationNonIT(demandeCalcul);

        // Then
        assertContentIndicateur(demandeCalcul, actual);
        assertNull(actual.getImpactUnitaire());
        assertEquals("ERREUR", actual.getStatutIndicateur());
        assertEquals(TestJsonUtils.format("""
                        {
                            "erreur" : "ErrCalcFonc : Donnée de consommation electrique manquante : operationNonIT : nomItem, RefItemParDefaut : Reseau Mobile"
                        }
                        """),
                TestJsonUtils.format(actual.getTrace()));
    }

    @Test
    void when_CalculerImpactOperationNonIT_reseau_mobile_utilisation_And_MixElecNotInReferentiel_shouldReturnIndicateurWithError() throws JsonProcessingException {
        // Given
        DemandeCalculImpactOperationNonIT demandeCalcul = createDemande();
        demandeCalcul.getEtape().setCode("UTILISATION");
        demandeCalcul.getOperationNonIT().setConsoElecAnnuelle(0.09);
        demandeCalcul.getOperationNonIT().setType("Reseau Mobile");
        demandeCalcul.setTypeItem(objectMapper.readValue("""
                {
                   "type": "Reseau Mobile",
                   "categorie": "RESEAU_MOBILE",
                   "refItemParDefaut": "Reseau Mobile",
                   "dureeVieDefaut": 10.0
                 }
                """, ReferentielTypeItem.class));
        demandeCalcul.setFacteurCaracterisations(List.of(objectMapper.readValue("""
                [
                   {
                     "nom": "Reseau Mobile",
                     "etape": "UTILISATION",
                     "critere": "Changement Climatique",
                     "valeur": 10,
                     "source": "SSG"
                   },
                   {
                     "nom": "Electricty Mix FR",
                     "etape": "FABRICATION",
                     "critere": "Changement Climatique",
                     "categorie" : "electricity-mix",
                     "localisation": "!!! PAYS INCONNU !!!",
                     "valeur" : 0.01
                   }
                ]
                         """, ReferentielFacteurCaracterisation[].class)));

        // When
        var actual = calculImpactOperationNonITService.calculerImpactOperationNonIT(demandeCalcul);

        // Then
        assertContentIndicateur(demandeCalcul, actual);
        assertNull(actual.getImpactUnitaire());
        assertEquals("ERREUR", actual.getStatutIndicateur());
        assertEquals(TestJsonUtils.format("""
                        {
                          "erreur" : "ErrCalcFonc : Il n'existe pas de Mix électrique pour cet item non it : critere: Changement Climatique - operation non it: nomItem - Localisation: France"
                        }
                        """),
                TestJsonUtils.format(actual.getTrace()));

    }

    @Test
    void shouldCalculerImpactOperationNonIT_maintenance_fabrication() throws JsonProcessingException {
        // Given
        DemandeCalculImpactOperationNonIT demandeCalcul = createDemande();
        demandeCalcul.getOperationNonIT().setType("Maintenance sur facture");
        demandeCalcul.setTypeItem(objectMapper.readValue("""
                {
                   "type": "Maintenance sur facture",
                   "categorie": "MAINTENANCE",
                   "refItemParDefaut": "maintenance-1",
                   "source": "sourceDefault"
                 }
                """, ReferentielTypeItem.class));
        demandeCalcul.setFacteurCaracterisations(List.of(objectMapper.readValue("""
                [
                   {
                     "nom": "maintenance-1",
                     "etape": "FABRICATION",
                     "critere": "Changement Climatique",
                     "valeur": 10,
                     "source": "sourceDefault"
                   }
                ]
                         """, ReferentielFacteurCaracterisation[].class)));

        // When
        var actual = calculImpactOperationNonITService.calculerImpactOperationNonIT(demandeCalcul);

        // Then
        assertContentIndicateur(demandeCalcul, actual);
        assertEquals("OK", actual.getStatutIndicateur());
        assertEquals(10.0, actual.getImpactUnitaire(), 0.0001d);
        assertEquals(TestJsonUtils.format(
                        """
                                {
                                   "formule" : "ImpactOperationNonIT = (Quantité(1.0) * ReferentielFacteurCaracterisation(10.0))",
                                   "sourceReferentielFacteurCaracterisation" : "sourceDefault",
                                   "valeurReferentielFacteurCaracterisation" : 10.0
                                }
                                """),
                TestJsonUtils.format(actual.getTrace()));

    }

    @Test
    void shouldCalculerImpactOperationNonIT_maintenance_utilisation_consoElecFromItemNonIT() throws JsonProcessingException {
        // Given
        DemandeCalculImpactOperationNonIT demandeCalcul = createDemande();
        demandeCalcul.getEtape().setCode("UTILISATION");
        demandeCalcul.getOperationNonIT().setConsoElecAnnuelle(100.0);
        demandeCalcul.getOperationNonIT().setType("Maintenance sur facture");
        demandeCalcul.setTypeItem(objectMapper.readValue("""
                {
                   "type": "Maintenance sur facture",
                   "categorie": "MAINTENANCE",
                   "refItemParDefaut": "maintenance-1",
                   "source": "sourceDefault"
                 }
                """, ReferentielTypeItem.class));
        demandeCalcul.setFacteurCaracterisations(List.of(objectMapper.readValue("""
                [
                   {
                     "nom": "maintenance-1",
                     "etape": "UTILISATION",
                     "critere": "Changement Climatique",
                     "consoElecMoyenne": 100.0,
                     "valeur": 10,
                     "source": "sourceDefault"
                   },
                   {
                     "nom": "Electricty Mix FR",
                     "etape": "FABRICATION",
                     "critere": "Changement Climatique",
                     "categorie" : "electricity-mix",
                     "localisation": "France",
                     "valeur" : 0.01
                   }
                ]
                         """, ReferentielFacteurCaracterisation[].class)));
        // When
        var actual = calculImpactOperationNonITService.calculerImpactOperationNonIT(demandeCalcul);

        // Then
        assertContentIndicateur(demandeCalcul, actual);
        assertEquals("OK", actual.getStatutIndicateur());
        assertEquals(1.0, actual.getImpactUnitaire(), 0.0001d);
        assertEquals(TestJsonUtils.format("""
                        {
                           "consoElecAnMoyenne" : {
                             "valeur" : 100.0,
                             "valeurItemConsoElecAnnuelle" : 100.0
                           },
                           "formule" : "ImpactOperationNonIT = (Quantité(1.0) * ConsoElecAnMoyenne(100.0) * MixElectrique(0.01))",
                           "mixElectrique" : {
                             "serveur" : false,
                             "valeur" : 0.01,
                             "valeurReferentielMixElectrique" : 0.01
                           }
                        }
                        """),
                TestJsonUtils.format(actual.getTrace()));
    }

    /**
     * Tests de la fonction getFormuleReseauFixe()
     */
    @Test
    void shouldCalculerImpactOperationNonIT_reseau_fixe_fabrication() throws JsonProcessingException {
        // Given
        DemandeCalculImpactOperationNonIT demandeCalcul = createDemande();
        demandeCalcul.getOperationNonIT().setType("Fixed Line FR");
        demandeCalcul.setTypeItem(objectMapper.readValue("""
                {
                   "type": "Fixed Line FR",
                   "categorie": "RESEAU_FIXE",
                   "refHypothese": "CAPACITE_LIGNE_FIXE_FR",
                   "refItemParDefaut": "fixed-line-network-1",
                   "source": "sourceDefault"
                 }
                """, ReferentielTypeItem.class));

        demandeCalcul.setFacteurCaracterisations(List.of(objectMapper.readValue("""
                [
                   {
                     "nom": "fixed-line-network-1",
                     "etape": "FABRICATION",
                     "critere": "Changement Climatique",
                     "valeur" : 0.01
                   }
                ]
                         """, ReferentielFacteurCaracterisation[].class)));

        demandeCalcul.setHypotheses(List.of(objectMapper.readValue("""
                [
                  {
                    "code": "CAPACITE_LIGNE_FIXE_FR",
                    "valeur": "10.0",
                    "source": "sourceDefault"
                  }
                ]
                """, ReferentielHypothese[].class)));

        // When
        var actual = calculImpactOperationNonITService.calculerImpactOperationNonIT(demandeCalcul);

        // Then
        assertContentIndicateur(demandeCalcul, actual);
        assertEquals("OK", actual.getStatutIndicateur());
        assertEquals(0.001, actual.getImpactUnitaire(), 0.0001d);
        assertEquals(TestJsonUtils.format("""
                        {
                           "formule" : "ImpactOperationNonIT = (Quantité(1.0) * ReferentielFacteurCaracterisation(0.01)) / CapaciteLigneFixe(10.0)",
                           "hypothese" : {
                             "nom_hypothese" : "CAPACITE_LIGNE_FIXE_FR",
                             "source" : "sourceDefault",
                             "valeur" : 10.0
                           },
                           "valeurReferentielFacteurCaracterisation" : 0.01
                        }
                        """),
                TestJsonUtils.format(actual.getTrace()));
    }

    @Test
    void shouldCalculerImpactOperationNonIT_reseau_fixe_utilisation_consoElecFromFacteurCaracterisation() throws JsonProcessingException {
        // Given
        DemandeCalculImpactOperationNonIT demandeCalcul = createDemande();
        demandeCalcul.getEtape().setCode("UTILISATION");
        demandeCalcul.getOperationNonIT().setType("Fixed Line FR");
        demandeCalcul.setTypeItem(objectMapper.readValue("""
                {
                   "type": "Fixed Line FR",
                   "categorie": "RESEAU_FIXE",
                   "refHypothese": "CAPACITE_LIGNE_FIXE_FR",
                   "refItemParDefaut": "fixed-line-network-1",
                   "source": "sourceDefault"
                 }
                """, ReferentielTypeItem.class));

        demandeCalcul.setFacteurCaracterisations(List.of(objectMapper.readValue("""
                [
                   {
                     "nom": "fixed-line-network-1",
                     "etape": "UTILISATION",
                     "critere": "Changement Climatique",
                     "consoElecMoyenne": 100.0,
                     "valeur" : 0.01
                   },
                   {
                    "nom": "Electricty Mix FR",
                    "etape": "FABRICATION",
                    "critere": "Changement Climatique",
                    "categorie" : "electricity-mix",
                    "localisation": "France",
                    "valeur" : 0.01
                   }
                ]
                         """, ReferentielFacteurCaracterisation[].class)));

        demandeCalcul.setHypotheses(List.of(objectMapper.readValue("""
                [
                  {
                    "code": "CAPACITE_LIGNE_FIXE_FR",
                    "valeur": "10.0",
                    "source": "sourceDefault"
                  }
                ]
                """, ReferentielHypothese[].class)));

        // When
        var actual = calculImpactOperationNonITService.calculerImpactOperationNonIT(demandeCalcul);

        // Then
        assertContentIndicateur(demandeCalcul, actual);
        assertEquals("OK", actual.getStatutIndicateur());
        assertEquals(0.1, actual.getImpactUnitaire(), 0.0001d);
        assertEquals(TestJsonUtils.format(
                        """
                                {
                                   "consoElecAnMoyenne" : {
                                     "valeur" : 100.0,
                                     "valeurReferentielConsoElecMoyenne" : 100.0
                                   },
                                   "formule" : "ImpactOperationNonIT = (Quantité(1.0) * ConsoElecAnMoyenne(100.0) * MixElectrique(0.01)) / CapaciteLigneFixe(10.0)",
                                   "hypothese" : {
                                     "nom_hypothese" : "CAPACITE_LIGNE_FIXE_FR",
                                     "source" : "sourceDefault",
                                     "valeur" : 10.0
                                   },
                                   "mixElectrique" : {
                                     "serveur" : false,
                                     "valeur" : 0.01,
                                     "valeurReferentielMixElectrique" : 0.01
                                   }
                                 }
                                 """),
                TestJsonUtils.format(actual.getTrace())
        );
    }

    /**
     * Tests de la fonction getFormuleBatiment()
     */
    @Test
    void shouldCalculerImpactOperationNonIT_batiment_fabrication_DureeDeVieFromOperationNonIT() throws JsonProcessingException {
        // Given
        DemandeCalculImpactOperationNonIT demandeCalcul = createDemande();
        demandeCalcul.getOperationNonIT().setType("batiment-1");
        demandeCalcul.getOperationNonIT().setDureeDeVie(10.0);
        demandeCalcul.setTypeItem(objectMapper.readValue("""
                {
                   "type": "batiment-1",
                   "categorie": "BATIMENT",
                   "refItemParDefaut": "batiment-1",
                   "source": "sourceDefault"
                 }
                """, ReferentielTypeItem.class));

        demandeCalcul.setFacteurCaracterisations(List.of(objectMapper.readValue("""
                [
                   {
                     "nom": "batiment-1",
                     "etape": "FABRICATION",
                     "critere": "Changement Climatique",
                     "valeur" : 0.1
                   }
                ]
                         """, ReferentielFacteurCaracterisation[].class)));

        // When
        var actual = calculImpactOperationNonITService.calculerImpactOperationNonIT(
                demandeCalcul
        );

        // Then
        assertContentIndicateur(demandeCalcul, actual);
        assertEquals("OK", actual.getStatutIndicateur());
        assertEquals(0.01, actual.getImpactUnitaire(), 0.0001d);
        assertEquals(TestJsonUtils.format("""
                        {
                           "dureeDeVie" : {
                             "valeurRetenue" : 10.0
                           },
                           "formule" : "ImpactOperationNonIT = (Quantité(1.0) * ReferentielFacteurCaracterisation(0.1)) / DureeDeVie(10.0)",
                           "valeurReferentielFacteurCaracterisation" : 0.1
                        }
                         """),
                TestJsonUtils.format(actual.getTrace()));
    }

    @Test
    void shouldCalculerImpactOperationNonIT_batiment_fabrication_DureeDeVieFromTypeItem() throws JsonProcessingException {
        // Given
        DemandeCalculImpactOperationNonIT demandeCalcul = createDemande();
        demandeCalcul.getOperationNonIT().setType("batiment-1");
        demandeCalcul.setTypeItem(objectMapper.readValue("""
                {
                   "type": "batiment-1",
                   "categorie": "BATIMENT",
                   "refItemParDefaut": "batiment-1",
                   "dureeVieDefaut": 30.0,
                   "source": "sourceDefault"
                 }
                """, ReferentielTypeItem.class));

        demandeCalcul.setFacteurCaracterisations(List.of(objectMapper.readValue("""
                [
                   {
                     "nom": "batiment-1",
                     "etape": "FABRICATION",
                     "critere": "Changement Climatique",
                     "valeur" : 0.1
                   }
                ]
                         """, ReferentielFacteurCaracterisation[].class)));

        // When
        var actual = calculImpactOperationNonITService.calculerImpactOperationNonIT(
                demandeCalcul
        );

        // Then
        assertContentIndicateur(demandeCalcul, actual);
        assertEquals("OK", actual.getStatutIndicateur());
        assertEquals(0.0033333333333333335, actual.getImpactUnitaire(), 0.0001d);
        assertEquals(TestJsonUtils.format("""
                        {
                           "dureeDeVie" : {
                             "dureeDeVieParDefaut" : {
                               "sourceTypeItemDureeVieDefaut" : "sourceDefault",
                               "valeur" : 30.0,
                               "valeurTypeItemDureeVieDefaut" : 30.0
                             }
                           },
                           "formule" : "ImpactOperationNonIT = (Quantité(1.0) * ReferentielFacteurCaracterisation(0.1)) / DureeDeVie(30.0)",
                           "valeurReferentielFacteurCaracterisation" : 0.1
                        }
                        """),
                TestJsonUtils.format(actual.getTrace()));
    }

    @Test
    void shouldCalculerImpactOperationNonIT_batiment_fabrication_DureeDeVieFromHypothese() throws JsonProcessingException {
        // Given
        DemandeCalculImpactOperationNonIT demandeCalcul = createDemande();
        demandeCalcul.getOperationNonIT().setType("batiment-1");
        demandeCalcul.setTypeItem(objectMapper.readValue("""
                {
                   "type": "batiment-1",
                   "categorie": "BATIMENT",
                   "refItemParDefaut": "batiment-1",
                   "source": "sourceDefault"
                 }
                """, ReferentielTypeItem.class));

        demandeCalcul.setFacteurCaracterisations(List.of(objectMapper.readValue("""
                [
                   {
                     "nom": "batiment-1",
                     "etape": "FABRICATION",
                     "critere": "Changement Climatique",
                     "valeur" : 0.1
                   }
                ]
                         """, ReferentielFacteurCaracterisation[].class)));

        demandeCalcul.setHypotheses(List.of(objectMapper.readValue("""
                [
                  {
                    "code": "dureeVieBatimentParDefaut",
                    "valeur": "50.0",
                    "source": "sourceDefault"
                  }
                ]
                """, ReferentielHypothese[].class)));

        // When
        var actual = calculImpactOperationNonITService.calculerImpactOperationNonIT(demandeCalcul);

        // Then
        assertContentIndicateur(demandeCalcul, actual);
        assertEquals("OK", actual.getStatutIndicateur());
        assertEquals(0.002, actual.getImpactUnitaire(), 0.0001d);
        assertEquals(TestJsonUtils.format("""
                        {
                           "dureeDeVie" : {
                             "dureeDeVieParDefaut" : {
                               "sourceReferentielHypothese" : "sourceDefault",
                               "valeur" : 50.0,
                               "valeurReferentielHypothese" : 50.0
                             }
                           },
                           "formule" : "ImpactOperationNonIT = (Quantité(1.0) * ReferentielFacteurCaracterisation(0.1)) / DureeDeVie(50.0)",
                           "valeurReferentielFacteurCaracterisation" : 0.1
                        }
                        """),
                TestJsonUtils.format(actual.getTrace()));
    }

    @Test
    void calculImpactOperationNonIT_batiment_utilisation_shouldReturnNull() throws JsonProcessingException {
        // Given
        DemandeCalculImpactOperationNonIT demandeCalcul = createDemande();
        demandeCalcul.getEtape().setCode("UTILISATION");
        demandeCalcul.getOperationNonIT().setType("batiment-1");
        demandeCalcul.setTypeItem(objectMapper.readValue("""
                {
                   "type": "batiment-1",
                   "categorie": "BATIMENT",
                   "refItemParDefaut": "batiment-1",
                   "source": "sourceDefault"
                 }
                """, ReferentielTypeItem.class));

        // When
        var actual = calculImpactOperationNonITService.calculerImpactOperationNonIT(demandeCalcul);

        // Then
        assertNull(actual);
    }

    /**
     * Tests de la fonction getFormuleDeplacement()
     */
    @Test
    void shouldCalculerImpactOperationNonIT_deplacementEssence_utilisation() throws JsonProcessingException {
        // Given
        DemandeCalculImpactOperationNonIT demandeCalcul = createDemande();
        demandeCalcul.getEtape().setCode("UTILISATION");
        demandeCalcul.getOperationNonIT().setType("deplacement-voiture-essence");
        demandeCalcul.setTypeItem(objectMapper.readValue("""
                {
                   "type": "deplacement-voiture-essence",
                   "categorie": "DEPLACEMENT_ESSENCE",
                   "refHypothese": "CONSO_MOYENNE_VOITURE_ESSENCE",
                   "refItemParDefaut": "fuel",
                   "source": "sourceDefault"
                 }
                """, ReferentielTypeItem.class));

        demandeCalcul.setFacteurCaracterisations(List.of(objectMapper.readValue("""
                [
                   {
                     "nom": "fuel",
                     "etape": "FABRICATION",
                     "critere": "Changement Climatique",
                     "valeur": 0.1
                   }
                ]
                         """, ReferentielFacteurCaracterisation[].class)));

        demandeCalcul.setHypotheses(List.of(objectMapper.readValue("""
                [
                  {
                    "code": "CONSO_MOYENNE_VOITURE_ESSENCE",
                    "valeur": "6.5",
                    "source": "sourceDefault"
                  }
                ]
                """, ReferentielHypothese[].class)));

        // When
        var actual = calculImpactOperationNonITService.calculerImpactOperationNonIT(
                demandeCalcul
        );

        // Then
        assertContentIndicateur(demandeCalcul, actual);
        assertEquals("OK", actual.getStatutIndicateur());
        assertEquals(0.65, actual.getImpactUnitaire(), 0.0001d);
        assertEquals(TestJsonUtils.format("""
                        {
                           "formule" : "ImpactOperationNonIT = (ConsoMoyenne(6.5) * NbKmParcourus(1.0) * ReferentielFacteurCaracterisation(0.1))",
                           "hypothese" : {
                             "nom_hypothese" : "CONSO_MOYENNE_VOITURE_ESSENCE",
                             "source" : "sourceDefault",
                             "valeur" : 6.5
                           },
                           "valeurReferentielFacteurCaracterisation" : 0.1
                        }
                        """),
                TestJsonUtils.format(actual.getTrace()));
    }

    @Test
    void shouldCalculerImpactOperationNonIT_deplacementEssence_utilisation_withoutFacteurCaracterisation_shouldReturnIndicateurWithError() throws JsonProcessingException {
        // Given
        DemandeCalculImpactOperationNonIT demandeCalcul = createDemande();
        demandeCalcul.getEtape().setCode("UTILISATION");
        demandeCalcul.getOperationNonIT().setType("deplacement-voiture-essence");
        demandeCalcul.setTypeItem(objectMapper.readValue("""
                {
                   "type": "deplacement-voiture-essence",
                   "categorie": "DEPLACEMENT_ESSENCE",
                   "refHypothese": "CONSO_MOYENNE_VOITURE_ESSENCE",
                   "refItemParDefaut": "fuel",
                   "source": "sourceDefault"
                 }
                """, ReferentielTypeItem.class));

        demandeCalcul.setHypotheses(List.of(objectMapper.readValue("""
                [
                  {
                    "code": "CONSO_MOYENNE_VOITURE_ESSENCE",
                    "valeur": "6.5",
                    "source": "sourceDefault"
                  }
                ]
                """, ReferentielHypothese[].class)));

        // When
        var actual = calculImpactOperationNonITService.calculerImpactOperationNonIT(
                demandeCalcul
        );

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

    @Test
    void shouldCalculerImpactOperationNonIT_deplacementEssence_utilisation_withoutRefHypotheses_shouldReturnIndicateurWithError() throws JsonProcessingException {
        // Given
        DemandeCalculImpactOperationNonIT demandeCalcul = createDemande();
        demandeCalcul.getEtape().setCode("UTILISATION");
        demandeCalcul.getOperationNonIT().setType("deplacement-voiture-essence");
        demandeCalcul.setTypeItem(objectMapper.readValue("""
                {
                   "type": "deplacement-voiture-essence",
                   "categorie": "DEPLACEMENT_ESSENCE",
                   "refHypothese": "CONSO_MOYENNE_VOITURE_ESSENCE",
                   "refItemParDefaut": "fuel",
                   "source": "sourceDefault"
                 }
                """, ReferentielTypeItem.class));

        demandeCalcul.setFacteurCaracterisations(List.of(objectMapper.readValue("""
                [
                   {
                     "nom": "fuel",
                     "etape": "FABRICATION",
                     "critere": "Changement Climatique",
                     "valeur" : 0.1
                   }
                ]
                         """, ReferentielFacteurCaracterisation[].class)));


        // When
        var actual = calculImpactOperationNonITService.calculerImpactOperationNonIT(
                demandeCalcul
        );

        // Then
        assertContentIndicateur(demandeCalcul, actual);
        assertNull(actual.getImpactUnitaire());
        assertEquals("ERREUR", actual.getStatutIndicateur());
        assertEquals(TestJsonUtils.format("""
                        {
                           "erreur": "ErrCalcFonc : Il n'y a pas d'hypothese pour l'item pour la refHypothese 'CONSO_MOYENNE_VOITURE_ESSENCE' de l'item nomItem"
                        }
                        """),
                TestJsonUtils.format(actual.getTrace()));
    }

    @Test
    void shouldCalculerImpactOperationNonIT_deplacementHybride_utilisation() throws JsonProcessingException {
        // Given
        DemandeCalculImpactOperationNonIT demandeCalcul = createDemande();
        demandeCalcul.getEtape().setCode("UTILISATION");
        demandeCalcul.getOperationNonIT().setType("deplacement-voiture-hybride");
        demandeCalcul.setTypeItem(objectMapper.readValue("""
                {
                   "type": "deplacement-voiture-hybride",
                   "categorie": "DEPLACEMENT_HYBRIDE",
                   "refHypothese": "CONSO_MOYENNE_VOITURE_ESSENCE",
                   "refItemParDefaut": "fuel",
                   "source": "sourceDefault"
                 }
                """, ReferentielTypeItem.class));

        demandeCalcul.setFacteurCaracterisations(List.of(objectMapper.readValue("""
                [
                   {
                     "nom": "fuel",
                     "etape": "FABRICATION",
                     "critere": "Changement Climatique",
                     "valeur" : 0.1
                   }
                ]
                         """, ReferentielFacteurCaracterisation[].class)));

        demandeCalcul.setHypotheses(List.of(objectMapper.readValue("""
                [
                  {
                    "code": "CONSO_MOYENNE_VOITURE_ESSENCE",
                    "valeur": "6.5",
                    "source": "sourceDefault"
                  },
                  {
                    "code": "TAUX_VEHICULE_HYBRIDE",
                    "valeur": "0.85",
                    "source": "NosGestesClimat.com"
                  }
                ]
                """, ReferentielHypothese[].class)));

        // When
        var actual = calculImpactOperationNonITService.calculerImpactOperationNonIT(demandeCalcul);

        // Then
        assertContentIndicateur(demandeCalcul, actual);
        assertEquals("OK", actual.getStatutIndicateur());
        assertEquals(0.5525, actual.getImpactUnitaire(), 0.0001d);
        assertEquals(TestJsonUtils.format("""
                        {
                            "formule" : "ImpactOperationNonIT = (ConsoMoyenne(6.5) * NbKmParcourus(1.0) * ReferentielFacteurCaracterisation(0.1) * TauxHybride(0.85))",
                            "hypothese" : {
                              "nom_hypothese" : "CONSO_MOYENNE_VOITURE_ESSENCE",
                              "source" : "sourceDefault",
                              "valeur" : 6.5
                            },
                            "valeurReferentielFacteurCaracterisation" : 0.1
                        }
                        """),
                TestJsonUtils.format(actual.getTrace()));
    }

    @Test
    void shouldCalculerImpactOperationNonIT_deplacementHybride_utilisation_whithoutHypotheseTaux_shouldReturnIndicateurWithError() throws JsonProcessingException {
        // Given
        DemandeCalculImpactOperationNonIT demandeCalcul = createDemande();
        demandeCalcul.getEtape().setCode("UTILISATION");
        demandeCalcul.getOperationNonIT().setType("deplacement-voiture-hybride");
        demandeCalcul.setTypeItem(objectMapper.readValue("""
                {
                   "type": "deplacement-voiture-hybride",
                   "categorie": "DEPLACEMENT_HYBRIDE",
                   "refHypothese": "CONSO_MOYENNE_VOITURE_ESSENCE",
                   "refItemParDefaut": "fuel",
                   "source": "sourceDefault"
                 }
                """, ReferentielTypeItem.class));

        demandeCalcul.setFacteurCaracterisations(List.of(objectMapper.readValue("""
                [
                   {
                     "nom": "fuel",
                     "etape": "FABRICATION",
                     "critere": "Changement Climatique",
                     "valeur" : 0.1
                   }
                ]
                         """, ReferentielFacteurCaracterisation[].class)));

        demandeCalcul.setHypotheses(List.of(objectMapper.readValue("""
                [
                  {
                    "code": "CONSO_MOYENNE_VOITURE_ESSENCE",
                    "valeur": "6.5",
                    "source": "sourceDefault"
                  }
                ]
                """, ReferentielHypothese[].class)));

        // When
        var actual = calculImpactOperationNonITService.calculerImpactOperationNonIT(demandeCalcul);

        // Then
        assertContentIndicateur(demandeCalcul, actual);
        assertNull(actual.getImpactUnitaire());
        assertEquals("ERREUR", actual.getStatutIndicateur());
        assertEquals(TestJsonUtils.format("""
                        {
                            "erreur": "ErrCalcFonc : Il n'y a pas de taux renseigné pour les véhicules hybrides dans la table des références"
                        }
                        """),
                TestJsonUtils.format(actual.getTrace()));
    }

    @Test
    void shouldCalculerImpactOperationNonIT_deplacementElectrique_utilisation() throws JsonProcessingException {
        // Given
        DemandeCalculImpactOperationNonIT demandeCalcul = createDemande();
        demandeCalcul.getEtape().setCode("UTILISATION");
        demandeCalcul.getOperationNonIT().setType("deplacement-trot-elec");
        demandeCalcul.setTypeItem(objectMapper.readValue("""
                {
                   "type": "deplacement-trot-elec",
                   "categorie": "DEPLACEMENT_ELECTRIQUE",
                   "refHypothese": "CONSO_MOYENNE_TROTINETTE_ELECTRIQUE",
                   "source": "sourceDefault"
                 }
                """, ReferentielTypeItem.class));

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

        demandeCalcul.setHypotheses(List.of(objectMapper.readValue("""
                [
                  {
                    "code": "CONSO_MOYENNE_TROTINETTE_ELECTRIQUE",
                    "valeur": "1.5",
                    "source": "sourceDefault"
                  }
                ]
                """, ReferentielHypothese[].class)));

        // When
        var actual = calculImpactOperationNonITService.calculerImpactOperationNonIT(demandeCalcul);

        // Then
        assertContentIndicateur(demandeCalcul, actual);
        assertEquals("OK", actual.getStatutIndicateur());
        assertEquals(0.15000, actual.getImpactUnitaire(), 0.0001d);
        assertEquals(TestJsonUtils.format("""
                        {
                           "formule" : "ImpactOperationNonIT = (ConsoMoyenne(1.5) * NbKmParcourus(1.0) * MixElectrique(0.1))",
                           "mixElectrique" : {
                             "serveur" : false,
                             "valeur" : 0.1,
                             "valeurReferentielMixElectrique" : 0.1
                           }
                        }
                        """),
                TestJsonUtils.format(actual.getTrace()));
    }

    @Test
    void shouldCalculerImpactOperationNonIT_deplacementElectrique_utilisation_whithoutMixElec_shouldReturnIndicateurWithError() throws JsonProcessingException {
        // Given
        DemandeCalculImpactOperationNonIT demandeCalcul = createDemande();
        demandeCalcul.getEtape().setCode("UTILISATION");
        demandeCalcul.getOperationNonIT().setType("deplacement-trot-elec");
        demandeCalcul.setTypeItem(objectMapper.readValue("""
                {
                   "type": "deplacement-trot-elec",
                   "categorie": "DEPLACEMENT_ELECTRIQUE",
                   "refHypothese": "CONSO_MOYENNE_TROTINETTE_ELECTRIQUE",
                   "source": "sourceDefault"
                 }
                """, ReferentielTypeItem.class));

        demandeCalcul.setHypotheses(List.of(objectMapper.readValue("""
                [
                  {
                    "code": "CONSO_MOYENNE_TROTINETTE_ELECTRIQUE",
                    "valeur": "1.5",
                    "source": "sourceDefault"
                  }
                ]
                """, ReferentielHypothese[].class)));

        // When
        var actual = calculImpactOperationNonITService.calculerImpactOperationNonIT(demandeCalcul);

        // Then
        assertContentIndicateur(demandeCalcul, actual);
        assertNull(actual.getImpactUnitaire());
        assertEquals("ERREUR", actual.getStatutIndicateur());
        assertEquals(TestJsonUtils.format("""
                        {
                          "erreur" : "ErrCalcFonc : Il n'existe pas de Mix électrique pour cet item non it : critere: Changement Climatique - operation non it: nomItem - Localisation: France"
                        }
                        """),
                TestJsonUtils.format(actual.getTrace()));
    }

    @Test
    void shouldCalculerImpactOperationNonIT_deplacementElectrique_utilisation_whithoutRefConso_shouldReturnIndicateurWithError() throws JsonProcessingException {
        // Given
        DemandeCalculImpactOperationNonIT demandeCalcul = createDemande();
        demandeCalcul.getEtape().setCode("UTILISATION");
        demandeCalcul.getOperationNonIT().setType("deplacement-trot-elec");
        demandeCalcul.setTypeItem(objectMapper.readValue("""
                {
                   "type": "deplacement-trot-elec",
                   "categorie": "DEPLACEMENT_ELECTRIQUE",
                   "refHypothese": "CONSO_MOYENNE_TROTINETTE_ELECTRIQUE",
                   "source": "sourceDefault"
                 }
                """, ReferentielTypeItem.class));

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

        // When
        var actual = calculImpactOperationNonITService.calculerImpactOperationNonIT(demandeCalcul);

        // Then
        assertContentIndicateur(demandeCalcul, actual);
        assertNull(actual.getImpactUnitaire());
        assertEquals("ERREUR", actual.getStatutIndicateur());
        assertEquals(TestJsonUtils.format("""
                        {
                          "erreur" : "ErrCalcFonc : Il n'y a pas d'hypothese pour l'item pour la refHypothese 'CONSO_MOYENNE_TROTINETTE_ELECTRIQUE' de l'item nomItem"
                        }
                        """),
                TestJsonUtils.format(actual.getTrace()));
    }

    private void assertContentIndicateur(DemandeCalculImpactOperationNonIT source, ImpactOperationNonIT result) {
        assertNotNull(result);
        assertEquals(source.getOperationNonIT().getNomItemNonIT(), result.getNomItemNonIT());
        assertEquals(source.getOperationNonIT().getType(), result.getTypeItem());
        assertEquals(source.getOperationNonIT().getQuantite(), result.getQuantite());


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

        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());
    }
}