package org.mte.numecoeval.expositiondonneesentrees.infrastructure.service;

import lombok.AllArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.mte.numecoeval.expositiondonneesentrees.domain.model.DataCenter;
import org.mte.numecoeval.expositiondonneesentrees.domain.model.EquipementPhysique;
import org.mte.numecoeval.expositiondonneesentrees.domain.ports.output.ReferentielServicePort;
import org.mte.numecoeval.expositiondonneesentrees.infrastructure.config.MessageProperties;
import org.mte.numecoeval.expositiondonneesentrees.referentiels.generated.api.model.CorrespondanceRefEquipementDTO;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;


@Service
@AllArgsConstructor
public class ErrorManagementService {

    final MessageProperties messageProperties;

    final ReferentielServicePort referentielServicePort;

    @Value("#{'${constraints.mode-utilisation}'.split(',')}")
    private List<String> modeUtilisationList;

    /**
     * Vérifie si la localisation du datacenter existe dans la table ref_MixElec.pays
     *
     * @param dataCenter le datacenter
     * @return la liste d'erreur
     */
    public List<String> checkDataCenter(DataCenter dataCenter) {
        var result = new ArrayList<String>();

        // CA 1.3
        if (!referentielServicePort.hasMixElec(dataCenter.getLocalisation())) {
            result.add(messageProperties.getMessages().get("DATACENTER_MIX_ELEC_LOCALISATION_INCONNUE").formatted(dataCenter.getLocalisation(), dataCenter.getNomLongDatacenter()));
        }

        return result;
    }

    /**
     * Vérifie un equipement physique
     *
     * @param equipementPhysique     l'equipement physique
     * @param refEquipementParDefaut la reference d'equipement par default
     * @return la paire (erreurs, avertissements)
     */
    public Pair<List<String>, List<String>> checkEquipementPhysique(EquipementPhysique equipementPhysique, String refEquipementParDefaut) {
        var erreurs = new ArrayList<String>();

        // CA 1.2
        // L'ajout d'un équipement physique dont le pays d'utilisation n'existe pas dans la table ref_MixElec.pays sort l'erreur suivante dans le rapport de contrôle
        if (StringUtils.isNotBlank(equipementPhysique.getPaysDUtilisation()) &&
                !referentielServicePort.hasMixElec(equipementPhysique.getPaysDUtilisation())) {

            erreurs.add(messageProperties.getMessages().get("EQUIPEMENT_MIX_ELEC_LOCALISATION_INCONNUE").formatted(equipementPhysique.getPaysDUtilisation(), equipementPhysique.getNomEquipementPhysique()));
        }

        String refEquipement = refEquipementParDefaut;

        // CA 1.5
        // L'ajout d'un équipement physique dont le type d'équipement ne possède pas de refEquipementParDefaut et dont le Modele n'est pas présent dans la table des ref_CorrespondanceRefEqP sort l'erreur suivante dans le rapport de contrôle
        if (StringUtils.isBlank(refEquipementParDefaut)) {
            CorrespondanceRefEquipementDTO refCorrespondance = referentielServicePort.getCorrespondance(equipementPhysique.getModele());
            if (refCorrespondance == null) {
                erreurs.add(messageProperties.getMessages().get("EQUIPEMENT_CORRESPONDANCE_INCONNUE").formatted(equipementPhysique.getNomEquipementPhysique(), equipementPhysique.getType()));
            } else {
                refEquipement = refCorrespondance.getRefEquipementCible();
            }
        }

        // CA 2.1
        // L'ajout d'un équipement physique dont la référence d'impact (déterminée à partir de la table de correspondance ou à partir de la table type équipement) est nulle sort un warning
        var avertissements = new ArrayList<String>();

        var etapes = referentielServicePort.getAllEtapes();
        var criteres = referentielServicePort.getAllCriteres();
        for (var critere : criteres) {
            for (var etape : etapes) {
                var impact = referentielServicePort.getImpactEquipement(refEquipement, critere.getNomCritere(), etape.getCode());
                if (impact == null) {
                    avertissements.add(messageProperties.getMessages().get("EQUIPEMENT_IMPACT_INCONNU").formatted(refEquipement, etape.getCode(), critere.getNomCritere()));
                }
            }
        }

        // CA 3.1
        // L'ajout d'un équipement dont le date de retrait (equipementPhysique.DateRetrait) précède la date d'achat (equipementPhysique.DateAchat)
        if (equipementPhysique.getDateAchat() != null && equipementPhysique.getDateRetrait() != null &&
                equipementPhysique.getDateAchat().isAfter(equipementPhysique.getDateRetrait())) {

            erreurs.add(messageProperties.getMessages().get("EQUIPEMENT_DATE_INCOHERENTE").formatted(equipementPhysique.getNomEquipementPhysique()));
        }
        //CA 4.1
        //L'ajout d'un équipement dont le mode d'utilisation est autre que COPE, BYOD ou null
        if (equipementPhysique.getModeUtilisation() != null && !modeUtilisationList.contains(equipementPhysique.getModeUtilisation())) {
            avertissements.add(messageProperties.getMessages().get("EQUIPEMENT_MODE_UTILISATION_INCONNU").formatted(equipementPhysique.getModeUtilisation()));
        }
        //CA 5.1
        //L'ajout d'un équipement dont le taux d'utilisation n'est pas compris entre 0 et 1
        Double taux = equipementPhysique.getTauxUtilisation();
        if (taux != null && (taux < 0 || taux > 1)) {
            avertissements.add(messageProperties.getMessages().get("EQUIPEMENT_TAUX_UTILISATION_INVALIDE").formatted(taux));
        }

        return Pair.of(erreurs, avertissements);
    }

}
