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

import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.mte.numecoeval.calculs.domain.data.entree.Application;
import org.mte.numecoeval.calculs.domain.data.entree.EquipementVirtuel;
import org.mte.numecoeval.calculs.domain.data.indicateurs.ImpactApplication;
import org.mte.numecoeval.calculs.domain.data.indicateurs.ImpactEquipementPhysique;
import org.mte.numecoeval.calculs.domain.data.indicateurs.ImpactEquipementVirtuel;
import org.mte.numecoeval.calculs.domain.model.CalculEquipementPhysique;
import org.mte.numecoeval.calculs.domain.model.CalculSizes;
import org.mte.numecoeval.calculs.infrastructure.repository.ApplicationRepository;
import org.mte.numecoeval.calculs.infrastructure.repository.EquipementPhysiqueRepository;
import org.mte.numecoeval.calculs.infrastructure.repository.EquipementVirtuelRepository;
import org.mte.numecoeval.calculs.infrastructure.repository.IndicateurRepository;
import org.mte.numecoeval.topic.data.EquipementPhysiqueDTO;
import org.springframework.stereotype.Service;

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

@Slf4j
@Service
@AllArgsConstructor
public class IntegrationCalculEquipementsService {

    IndicateurRepository indicateurRepository;

    EquipementPhysiqueRepository equipementPhysiqueRepository;
    EquipementVirtuelRepository equipementVirtuelRepository;
    ApplicationRepository applicationRepository;
    CalculEquipementPhysiqueService calculEquipementPhysiqueService;
    CalculEquipementVirtuelService calculEquipementVirtuelService;
    CalculApplicationService calculApplicationService;

    /**
     * Calcul les impacts d'un équipement physique enrichi, liste des impacts:
     * - equipement physique
     * - ses équipements virtuels
     * - les applications sur ses équipements virtuels
     * - réseaux
     *
     * @param calculEquipementPhysique l'équipement physique enrichi
     * @return le nombre d'elements traites sous forme CalculSizes
     */
    public CalculSizes calculImpactEquipementPhysique(CalculEquipementPhysique calculEquipementPhysique) {

        if (calculEquipementPhysique == null) return null;

        var ttStart = System.currentTimeMillis();

        EquipementPhysiqueDTO equipementPhysiqueDTO = calculEquipementPhysique.getEquipementPhysique();
        String nomLot = equipementPhysiqueDTO.getNomLot();
        String nomOrganisation = equipementPhysiqueDTO.getNomOrganisation();
        String nomEquipementPhysique = equipementPhysiqueDTO.getNomEquipementPhysique();

        // delete existing indicateurs
        indicateurRepository.clearIndicateursEquipement(nomEquipementPhysique, nomLot);

        var ttAfterClear = System.currentTimeMillis();

        List<ImpactEquipementPhysique> impactEquipementPhysiqueList = calculEquipementPhysiqueService.calculImpactEquipementPhysique(calculEquipementPhysique);

        // get equipements virtuels
        List<EquipementVirtuel> equipementVirtuelList = equipementVirtuelRepository.findEquipementVirtuels(nomOrganisation, nomLot, nomEquipementPhysique);
        var ttAfterFindEqV = System.currentTimeMillis();

        List<ImpactEquipementVirtuel> allImpactVirtuel = new ArrayList<>();
        List<ImpactApplication> allImpactApplication = new ArrayList<>();
        var totalNbApplication = 0;

        var totalVCPU = calculEquipementVirtuelService.getTotalVCPU(equipementVirtuelList);
        var totalStockage = calculEquipementVirtuelService.getTotalStockage(equipementVirtuelList);

        for (EquipementVirtuel equipementVirtuel : equipementVirtuelList) {
            List<ImpactEquipementVirtuel> impactEquipementVirtuelList = calculEquipementVirtuelService.calculImpactEquipementVirtuel(
                    impactEquipementPhysiqueList, equipementVirtuel,
                    equipementVirtuelList.size(), totalVCPU, totalStockage
            );

            allImpactVirtuel.addAll(impactEquipementVirtuelList);

            List<Application> applicationList = applicationRepository.findApplications(nomOrganisation, nomLot, nomEquipementPhysique, equipementVirtuel.getNomEquipementVirtuel());

            totalNbApplication += applicationList.size();
            for (Application application : applicationList) {
                List<ImpactApplication> impactApplicationList = calculApplicationService.calculImpactApplication(impactEquipementVirtuelList, application, applicationList.size());
                allImpactApplication.addAll(impactApplicationList);
            }
        }

        var ttAfterEqVAndApp = System.currentTimeMillis();

        // save impacts into db
        indicateurRepository.saveIndicateursEquipements(impactEquipementPhysiqueList, allImpactVirtuel, allImpactApplication);

        var ttAfterSave = System.currentTimeMillis();

        // set statut of equipement physique to TRAITE
        equipementPhysiqueRepository.setStatutToTraite(equipementPhysiqueDTO.getId());

        var ttAfterSaveStatus = System.currentTimeMillis();

        log.info("{} - {} - {} : Calcul impacts equipement physique: {} [{}, {}]. Temps (ms): total={}, clear={}, eqV={} finEqVApp={}, saveInd={}, saveStatus={}",
                nomOrganisation, nomLot, equipementPhysiqueDTO.getDateLot(),
                nomEquipementPhysique,
                equipementVirtuelList.size(),
                totalNbApplication,
                ttAfterSaveStatus - ttStart,
                ttAfterClear - ttStart,
                ttAfterFindEqV - ttAfterClear,
                ttAfterEqVAndApp - ttAfterFindEqV,
                ttAfterSave - ttAfterEqVAndApp,
                ttAfterSaveStatus - ttAfterSave
        );

        CalculSizes result = new CalculSizes();
        result.setNbEquipementPhysique(1);
        result.setNbEquipementVirtuel(equipementVirtuelList.size());
        result.setNbApplication(totalNbApplication);
        result.setNbIndicateurEquipementPhysique(impactEquipementPhysiqueList.size());
        result.setNbIndicateurEquipementVirtuel(allImpactVirtuel.size());
        result.setNbIndicateurApplication(allImpactApplication.size());
        return result;
    }
}