package org.mte.numecoeval.expositiondonneesentrees.infrastructure.jpa.adapter;

import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.time.StopWatch;
import org.mte.numecoeval.expositiondonneesentrees.domain.model.*;
import org.mte.numecoeval.expositiondonneesentrees.domain.ports.output.EntreePersistencePort;
import org.mte.numecoeval.expositiondonneesentrees.domain.ports.output.SaveDonneesEntreePort;
import org.mte.numecoeval.expositiondonneesentrees.generated.api.model.StatutTraitement;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;

import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@AllArgsConstructor
@Service
public class SaveDonneesEntreeAdapter implements SaveDonneesEntreePort {

    private EntreePersistencePort<DonneesEntree> donneesEntreesEntreePersistencePort;
    private EntreePersistencePort<DataCenter> dataCenterEntreePersistencePort;
    private EntreePersistencePort<EquipementPhysique> equipementPhysiqueEntreePersistencePort;
    private EntreePersistencePort<EquipementVirtuel> equipementVirtuelEntreePersistencePort;
    private EntreePersistencePort<Application> applicationEntreePersistencePort;
    private EntreePersistencePort<Messagerie> messagerieEntreePersistencePort;
    private EntreePersistencePort<Entite> entiteEntreePersistencePort;

    @Override
    @CacheEvict(cacheNames = "correspondanceRefEquipement", allEntries = true)
    public void save(DonneesEntree donneesEntree) {

        if (donneesEntree == null) {
            log.warn("Données null reçue");
            return;
        }

        log.info("Données reçues  : Nom Lot : {}, Date de lot : {} - Nom Organisation : {}",
                donneesEntree.getNomLot(), donneesEntree.getDateLot(), donneesEntree.getNomOrganisation()
        );


        Set<String> equipementsPhysiquesImpactes = new HashSet<>();
        Set<String> csvEquipementPhysiques = new HashSet<>();

        StopWatch globalStopWatch = StopWatch.createStarted();

        StopWatch stopWatch = StopWatch.createStarted();
        donneesEntreesEntreePersistencePort.save(donneesEntree);
        stopWatch.stop();
        log.info("Fin du traitement de l'objet DonneesEntree reçue en {} secondes", stopWatch.getTime(TimeUnit.SECONDS));

        if (!CollectionUtils.isEmpty(donneesEntree.getDataCenters())) {
            stopWatch = StopWatch.createStarted();
            dataCenterEntreePersistencePort.saveAll(donneesEntree.getDataCenters());
            stopWatch.stop();
            log.info("Fin du traitement des {} objets DataCenter reçus en {} secondes",
                    donneesEntree.getDataCenters().size(),
                    stopWatch.getTime(TimeUnit.SECONDS)
            );
        }

        if (!CollectionUtils.isEmpty(donneesEntree.getEquipementsPhysiques())) {
            stopWatch = StopWatch.createStarted();
            equipementPhysiqueEntreePersistencePort.saveAll(donneesEntree.getEquipementsPhysiques());
            stopWatch.stop();
            log.info("Fin du traitement des {} objets EquipementPhysique reçus en {} secondes",
                    donneesEntree.getEquipementsPhysiques().size(),
                    stopWatch.getTime(TimeUnit.SECONDS)
            );
            csvEquipementPhysiques = donneesEntree.getEquipementsPhysiques().stream()
                    .map(EquipementPhysique::getNomEquipementPhysique)
                    .collect(Collectors.toSet());
        }

        if (!CollectionUtils.isEmpty(donneesEntree.getEquipementsVirtuels())) {
            stopWatch = StopWatch.createStarted();
            equipementVirtuelEntreePersistencePort.saveAll(donneesEntree.getEquipementsVirtuels());
            stopWatch.stop();
            log.info("Fin du traitement des {} objets EquipementVirtuel reçus en {} secondes",
                    donneesEntree.getEquipementsVirtuels().size(),
                    stopWatch.getTime(TimeUnit.SECONDS)
            );

            equipementsPhysiquesImpactes.addAll(donneesEntree.getEquipementsVirtuels().stream()
                    .map(EquipementVirtuel::getNomEquipementPhysique)
                    .collect(Collectors.toSet()));
        }

        if (!CollectionUtils.isEmpty(donneesEntree.getApplications())) {
            stopWatch = StopWatch.createStarted();
            applicationEntreePersistencePort.saveAll(donneesEntree.getApplications());
            stopWatch.stop();
            log.info("Fin du traitement des {} objets Applications reçus en {} secondes",
                    donneesEntree.getApplications().size(),
                    stopWatch.getTime(TimeUnit.SECONDS)
            );

            equipementsPhysiquesImpactes.addAll(donneesEntree.getApplications().stream()
                    .map(Application::getNomEquipementPhysique)
                    .collect(Collectors.toSet()));
        }

        if (!CollectionUtils.isEmpty(donneesEntree.getMessageries())) {
            stopWatch = StopWatch.createStarted();
            messagerieEntreePersistencePort.saveAll(donneesEntree.getMessageries());
            stopWatch.stop();
            log.info("Fin du traitement des {} objets Messagerie reçus en {} secondes",
                    donneesEntree.getMessageries().size(),
                    stopWatch.getTime(TimeUnit.SECONDS)
            );
        }

        if (!CollectionUtils.isEmpty(donneesEntree.getEntites())) {
            stopWatch = StopWatch.createStarted();
            entiteEntreePersistencePort.saveAll(donneesEntree.getEntites());
            stopWatch.stop();
            log.info("Fin du traitement des {} objets Entites reçus en {} secondes",
                    donneesEntree.getEntites(),
                    stopWatch.getTime(TimeUnit.SECONDS)
            );
        }


        equipementsPhysiquesImpactes.removeAll(csvEquipementPhysiques);

        if (!equipementsPhysiquesImpactes.isEmpty()) {
            // mise a jour du statut_traitement à EN_ATTENTE
            int nbUpdated = equipementPhysiqueEntreePersistencePort.updateStatutInList(StatutTraitement.EN_ATTENTE.getValue(), donneesEntree.getNomLot(), donneesEntree.getNomOrganisation(), equipementsPhysiquesImpactes);
            log.info("Traitement en mode delta: Nom de Lot : {} - Date de lot : {} - Nom Organisation : {}. Nombre d'équipements physiques à rejouer : {}, réellement rejoués: {}",
                    donneesEntree.getNomLot(), donneesEntree.getDateLot(), donneesEntree.getNomOrganisation(),
                    equipementsPhysiquesImpactes.size(), nbUpdated);
        }

        globalStopWatch.stop();
        log.info("Fin du traitement des données reçues : Nom de Lot : {} - Date de lot : {} - Nom Organisation : {}, en {} secondes",
                donneesEntree.getNomLot(), donneesEntree.getDateLot(), donneesEntree.getNomOrganisation(),
                globalStopWatch.getTime(TimeUnit.SECONDS)
        );

    }
}
