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

import lombok.AllArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.mte.numecoeval.expositiondonneesentrees.domain.exception.ValidationException;
import org.mte.numecoeval.expositiondonneesentrees.domain.model.ResultatImport;
import org.mte.numecoeval.expositiondonneesentrees.domain.ports.input.ImportDonneesEntreePort;
import org.mte.numecoeval.expositiondonneesentrees.domain.ports.output.SaveDonneesEntreePort;
import org.mte.numecoeval.expositiondonneesentrees.generated.api.model.DonneesEntreeRest;
import org.mte.numecoeval.expositiondonneesentrees.generated.api.model.RapportImportRest;
import org.mte.numecoeval.expositiondonneesentrees.generated.api.server.ImportsApi;
import org.mte.numecoeval.expositiondonneesentrees.infrastructure.helper.CSVHelper;
import org.mte.numecoeval.expositiondonneesentrees.infrastructure.mapper.DonneesEntreeRestMapper;
import org.mte.numecoeval.expositiondonneesentrees.infrastructure.service.ErrorManagementPostSaveService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.server.ResponseStatusException;

import java.time.LocalDate;
import java.time.format.DateTimeParseException;
import java.util.List;

@RestController
@AllArgsConstructor
@SuppressWarnings("java:S107")
// Obligatoire à cause de la gestion des fichiers différents au niveau contrat d'interface
public class ImportCSVController implements ImportsApi {

    private static final Logger LOGGER = LoggerFactory.getLogger(ImportCSVController.class);

    DonneesEntreeRestMapper donneesEntreeMapper;

    ImportDonneesEntreePort importDonneesEntreePort;

    SaveDonneesEntreePort saveDonneesEntreePort;

    ErrorManagementPostSaveService errorManagementPostSaveService;

    @Override
    public ResponseEntity<List<RapportImportRest>> importCSV(String nomOrganisation, String nomLot, String dateLot, MultipartFile csvDataCenter, MultipartFile csvEquipementPhysique, MultipartFile csvEquipementVirtuel, MultipartFile csvApplication, MultipartFile csvOperationNonIT, MultipartFile csvMessagerie, MultipartFile csvEntite) {
        return importInterneCSV(nomOrganisation, nomLot, dateLot, csvDataCenter, csvEquipementPhysique, csvEquipementVirtuel, csvApplication, csvOperationNonIT, csvMessagerie, csvEntite);
    }

    public ResponseEntity<List<RapportImportRest>> importInterneCSV(String nomOrganisation, String nomLot, String dateLot, MultipartFile csvDataCenter, MultipartFile csvEquipementPhysique, MultipartFile csvEquipementVirtuel, MultipartFile csvApplication, MultipartFile csvOperationNonIT, MultipartFile csvMessagerie, MultipartFile csvEntite) {
        return ResponseEntity.ok(
                importDonneesFromCSV(
                        nomOrganisation, nomLot, dateLot,
                        csvDataCenter,
                        csvEquipementPhysique,
                        csvEquipementVirtuel,
                        csvApplication,
                        csvOperationNonIT,
                        csvMessagerie,
                        csvEntite
                )
        );
    }

    @Override
    public ResponseEntity<List<RapportImportRest>> importJson(DonneesEntreeRest donneesEntreeRest) {
        throw new ResponseStatusException(
                HttpStatus.NOT_IMPLEMENTED,
                "Cette méthode d'import n'a pas encore été implémenté."
        );
    }

    public List<RapportImportRest> importDonneesFromCSV(
            String nomOrganisation, String nomLot, String dateLot,
            MultipartFile csvDataCenter,
            MultipartFile csvEquipementPhysique,
            MultipartFile csvEquipementVirtuel,
            MultipartFile csvApplication,
            MultipartFile csvOperationNonIT,
            MultipartFile csvMessagerie,
            MultipartFile csvEntite) throws ValidationException {
        LOGGER.info("Reception de fichiers pour imports de données d'entrées :Nom Organisation : {}, Nom de Lot : {}, Date de lot : {}", nomOrganisation, nomLot, dateLot);

        validateRequestParametersForImportCSV(nomOrganisation, nomLot, dateLot, csvDataCenter, csvEquipementPhysique, csvEquipementVirtuel, csvApplication, csvOperationNonIT, csvMessagerie, csvEntite);

        return importAllCsv(nomOrganisation, nomLot, dateLot, csvDataCenter, csvEquipementPhysique, csvEquipementVirtuel, csvApplication, csvOperationNonIT, csvMessagerie, csvEntite);
    }

    /**
     * Validation des entrées lors d'un import d'un ou plusieurs CSV.
     *
     * @param nomLot                Nom de lot
     * @param dateLot               date du lot
     * @param csvDataCenter         Fichier CSV des data centers
     * @param csvEquipementPhysique Fichier CSV des équipements physiques
     * @param csvEquipementVirtuel  Fichier CSV des équipements virtuels
     * @param csvApplication        Fichier CSV des applications
     * @param csvOperationNonIT     Fichier CSV des opérations non IT
     * @param csvMessagerie         Fichier CSV de la messagerie
     * @param csvEntite             Fichier CSV des entités
     * @throws ResponseStatusException avec le statut 400 lorsque les entrées ne sont pas cohérentes.
     */
    private void validateRequestParametersForImportCSV(String nomOrganisation, String nomLot, String dateLot, MultipartFile csvDataCenter, MultipartFile csvEquipementPhysique, MultipartFile csvEquipementVirtuel, MultipartFile csvApplication, MultipartFile csvOperationNonIT, MultipartFile csvMessagerie, MultipartFile csvEntite) {
        if (CSVHelper.fileIsNullOrEmpty(csvDataCenter)
                && CSVHelper.fileIsNullOrEmpty(csvEquipementPhysique)
                && CSVHelper.fileIsNullOrEmpty(csvEquipementVirtuel)
                && CSVHelper.fileIsNullOrEmpty(csvApplication)
                && CSVHelper.fileIsNullOrEmpty(csvOperationNonIT)
                && CSVHelper.fileIsNullOrEmpty(csvMessagerie)
                && CSVHelper.fileIsNullOrEmpty(csvEntite)
        ) {
            LOGGER.error("Import: Erreur contrôle : Tous les fichiers ne peuvent être vides en même temps");
            throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Tous les fichiers ne peuvent être vides en même temps");
        }

        if (StringUtils.isBlank(nomLot)) {
            LOGGER.error("Import: Erreur contrôle : Le nom du Lot ne peut être pas vide");
            throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Le nom du Lot ne peut être pas vide");
        }
        if (StringUtils.isBlank(nomOrganisation)) {
            LOGGER.error("Import: Erreur contrôle : Le nom de l'organisation ne peut être pas vide");
            throw new ResponseStatusException(HttpStatus.BAD_REQUEST, " Le nom de l'organisation ne peut être pas vide");
        }

        if (dateLot != null && !"".equals(dateLot)) {
            try {
                LocalDate.parse(dateLot);
            } catch (DateTimeParseException e) {
                LOGGER.error("Import: Erreur contrôle : La date du lot doit avoir le format yyyy-MM-dd");
                throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "La date du lot doit avoir le format yyyy-MM-dd");
            }
        }

    }

    /**
     * Importe tous les fichiers CSV en paramètre.
     *
     * @param csvDataCenter         Fichier CSV des data centers
     * @param csvEquipementPhysique Fichier CSV des équipements physiques
     * @param csvEquipementVirtuel  Fichier CSV des équipements virtuels
     * @param csvApplication        Fichier CSV des applications
     * @param csvOperationNonIT     Fichier CSV des opérations non IT
     * @param csvMessagerie         Fichier CSV de la messagerie
     * @param csvEntite             Fichier CSV des entités
     * @param dateLot               Date du lot associée aux fichiers
     * @param nomOrganisation       Nom de l'organisation
     * @param nomLot                Nom du lot
     * @return {@link List} des {@link RapportImportRest} correspondant à l'import
     * @throws ValidationException en cas d'absence de donner à pousser dans le système.
     */
    private List<RapportImportRest> importAllCsv(String nomOrganisation, String nomLot, String dateLot, MultipartFile csvDataCenter, MultipartFile csvEquipementPhysique, MultipartFile csvEquipementVirtuel, MultipartFile csvApplication, MultipartFile csvOperationNonIT, MultipartFile csvMessagerie, MultipartFile csvEntite) throws ValidationException {
        // Lecture & conversion
        var resultatImport = importDonneesEntreePort.importCsv(nomOrganisation, nomLot, dateLot, csvDataCenter, csvEquipementPhysique, csvEquipementVirtuel, csvApplication, csvOperationNonIT, csvMessagerie, csvEntite);

        saveDonneesEntreePort.save(resultatImport.getDonneesEntree());

        // verifications apres sauvegarde en bdd
        updateResultatImport(resultatImport, "equipement_physique", errorManagementPostSaveService.checkEquipementPhysiques(nomLot));
        updateResultatImport(resultatImport, "equipement_virtuel", errorManagementPostSaveService.checkEquipementVirtuels(nomLot));
        updateResultatImport(resultatImport, "application", errorManagementPostSaveService.checkApplications(nomLot));
        updateResultatImport(resultatImport, "operation_non_it", errorManagementPostSaveService.checkOperationsNonIT(nomLot));

        return resultatImport.getRapports().stream()
                .map(donneesEntreeMapper::toRestDTO)
                .toList();
    }

    /**
     * Mets a jour l'objet ResultatImport en y ajoutant la liste d'erreurs si non vide
     *
     * @param resultatImport l'objet resultatImport
     * @param type           le type d'element
     * @param erreurs        la liste d'erreur
     */
    private void updateResultatImport(ResultatImport resultatImport, String type, List<String> erreurs) {
        if (erreurs.isEmpty()) return;
        resultatImport.getRapports().stream()
                .filter(res -> type.equals(res.getType()))
                .findFirst()
                .ifPresent(o -> o.getErreurs().addAll(erreurs));
    }

}
