package org.mte.numecoeval.calculs.domain.port.input.service.impl;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.mte.numecoeval.calculs.domain.data.demande.DemandeCalculImpactMessagerie;
import org.mte.numecoeval.calculs.domain.data.erreur.TypeErreurCalcul;
import org.mte.numecoeval.calculs.domain.data.indicateurs.ImpactMessagerie;
import org.mte.numecoeval.calculs.domain.data.referentiel.ReferentielImpactMessagerie;
import org.mte.numecoeval.calculs.domain.exception.CalculImpactException;
import org.mte.numecoeval.calculs.domain.port.input.service.CalculImpactMessagerieService;
import org.mte.numecoeval.calculs.domain.traceur.TraceCalculImpactMessagerieUtils;
import org.mte.numecoeval.calculs.domain.traceur.TraceUtils;

@Slf4j
@AllArgsConstructor
public class CalculImpactMessagerieServiceImpl implements CalculImpactMessagerieService {
    private static final String VERSION_CALCUL = "1.0";
    private final ObjectMapper objectMapper;

    @Override
    public ImpactMessagerie calculerImpactMessagerie(DemandeCalculImpactMessagerie demandeCalcul) {
        ImpactMessagerie impactErreur;
        try {
            return calculerImpact(demandeCalcul);
        } catch (CalculImpactException e) {
            log.debug("Erreur de calcul d'impact de messagerie : Type : {}, Cause: {}, Critere: {}, Mois-Années: {}",
                    e.getErrorType(), e.getMessage(),
                    demandeCalcul.getCritere().getNomCritere(),
                    demandeCalcul.getMessagerie().getMoisAnnee()
            );

            impactErreur = buildImpactForError(demandeCalcul, e);
        } catch (Exception e) {
            log.debug("Erreur générale de calcul d'impact de messagerie : Type : {}, Cause: {}, Critere: {}, Mois-Années: {}",
                    TypeErreurCalcul.ERREUR_TECHNIQUE.getCode(), e.getMessage(),
                    demandeCalcul.getCritere().getNomCritere(),
                    demandeCalcul.getMessagerie().getMoisAnnee()
            );
            impactErreur = buildImpactForError(demandeCalcul, new CalculImpactException(TypeErreurCalcul.ERREUR_TECHNIQUE.getCode(), e.getMessage()));
        }

        return impactErreur;
    }

    private ImpactMessagerie calculerImpact(DemandeCalculImpactMessagerie demandeCalcul) throws CalculImpactException {
        Double nombreMailEmis = demandeCalcul.getMessagerie().getNombreMailEmis();
        var referentielImpactMessagerie = demandeCalcul.getImpactsMessagerie()
                .stream()
                .filter(refImpactMessagerie -> demandeCalcul.getCritere().getNomCritere().equals(refImpactMessagerie.getCritere()))
                .findFirst()
                .orElseThrow(() -> new CalculImpactException(
                        TypeErreurCalcul.ERREUR_FONCTIONNELLE.getCode(), "Référentiel ImpactMessagerie indisponible pour le critère " + demandeCalcul.getCritere().getNomCritere())
                );
        if (nombreMailEmis > 0) {
            var poidsMoyenMail = demandeCalcul.getMessagerie().getVolumeTotalMailEmis() / nombreMailEmis;
            Double impactMensuel = (referentielImpactMessagerie.getConstanteCoefficientDirecteur() * poidsMoyenMail + referentielImpactMessagerie.getConstanteOrdonneeOrigine()) * demandeCalcul.getMessagerie().getNombreMailEmisXDestinataires();
            return buildImpactMessagerie(demandeCalcul, referentielImpactMessagerie, impactMensuel);
        }
        throw new CalculImpactException(
                TypeErreurCalcul.ERREUR_FONCTIONNELLE.getCode(),
                "Calcul d'impact messagerie : Critère : " + demandeCalcul.getCritere().getNomCritere() + ", Mois Années : " + demandeCalcul.getMessagerie().getMoisAnnee() + ", nombreMailEmis " + demandeCalcul.getMessagerie().getNombreMailEmis() + "=< 0.0"
        );
    }

    private ImpactMessagerie buildImpactMessagerie(DemandeCalculImpactMessagerie demandeCalcul, ReferentielImpactMessagerie referentielImpactMessagerie, Double impact) {
        return ImpactMessagerie.builder()
                .dateCalcul(demandeCalcul.getDateCalcul())
                .critere(demandeCalcul.getCritere().getNomCritere())
                .unite(demandeCalcul.getCritere().getUnite())
                .moisAnnee(demandeCalcul.getMessagerie().getMoisAnnee())
                .nombreMailEmis(demandeCalcul.getMessagerie().getNombreMailEmis())
                .volumeTotalMailEmis(demandeCalcul.getMessagerie().getVolumeTotalMailEmis())
                .impactMensuel(impact)
                .statutIndicateur("OK")
                .nomLot(demandeCalcul.getMessagerie().getNomLot())
                .nomSourceDonnee(demandeCalcul.getMessagerie().getNomSourceDonnee())
                .dateLot(demandeCalcul.getMessagerie().getDateLot())
                .nomEntite(demandeCalcul.getMessagerie().getNomEntite())
                .nomOrganisation(demandeCalcul.getMessagerie().getNomOrganisation())
                .versionCalcul(VERSION_CALCUL)
                .trace(TraceUtils.getTraceFromTraceur(objectMapper, TraceCalculImpactMessagerieUtils.buildTrace(demandeCalcul, referentielImpactMessagerie)))
                .build();
    }

    private ImpactMessagerie buildImpactForError(DemandeCalculImpactMessagerie demandeCalcul, CalculImpactException exception) {
        return ImpactMessagerie.builder()
                .dateCalcul(demandeCalcul.getDateCalcul())
                .critere(demandeCalcul.getCritere().getNomCritere())
                .unite(demandeCalcul.getCritere().getUnite())
                .moisAnnee(demandeCalcul.getMessagerie().getMoisAnnee())
                .impactMensuel(null)
                .nombreMailEmis(demandeCalcul.getMessagerie().getNombreMailEmis())
                .volumeTotalMailEmis(demandeCalcul.getMessagerie().getVolumeTotalMailEmis())
                .statutIndicateur("ERREUR")
                .nomLot(demandeCalcul.getMessagerie().getNomLot())
                .nomSourceDonnee(demandeCalcul.getMessagerie().getNomSourceDonnee())
                .dateLot(demandeCalcul.getMessagerie().getDateLot())
                .nomEntite(demandeCalcul.getMessagerie().getNomEntite())
                .nomOrganisation(demandeCalcul.getMessagerie().getNomOrganisation())
                .versionCalcul(VERSION_CALCUL)
                .trace(TraceUtils.getTraceFromTraceur(objectMapper, TraceCalculImpactMessagerieUtils.buildTraceError(exception)))
                .build();
    }

}
