package org.mte.numecoeval.referentiel.domain.ports.input.impl;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVRecord;
import org.mte.numecoeval.referentiel.domain.data.ResultatImport;
import org.mte.numecoeval.referentiel.domain.exception.ReferentielException;
import org.mte.numecoeval.referentiel.domain.ports.input.ImportCSVReferentielPort;
import org.mte.numecoeval.referentiel.infrastructure.restapi.dto.FacteurCaracterisationDTO;

import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

@Slf4j

public class ImportFacteurCaracterisationPortImpl implements ImportCSVReferentielPort<FacteurCaracterisationDTO> {
    private static final String HEADER_NOM = "nom";
    private static final String HEADER_ETAPEACV = "etapeacv";
    private static final String HEADER_CRITERE = "critere";
    private static final String HEADER_DESCRIPTION = "description";
    private static final String HEADER_NIVEAU = "niveau";
    private static final String HEADER_TIERS = "tiers";
    private static final String HEADER_CATEGORIE = "categorie";
    private static final String HEADER_CONSO_ELEC_MOYENNE = "consoElecMoyenne";
    private static final String HEADER_LOCALISATION = "localisation";
    private static final String HEADER_VALEUR = "valeur";
    private static final String HEADER_UNITE = "unite";
    private static final String HEADER_SOURCE = "source";
    private static final String[] HEADERS = new String[]{HEADER_NOM, HEADER_ETAPEACV, HEADER_CRITERE,
            HEADER_DESCRIPTION, HEADER_NIVEAU, HEADER_TIERS, HEADER_CATEGORIE, HEADER_CONSO_ELEC_MOYENNE, HEADER_LOCALISATION, HEADER_VALEUR,
            HEADER_UNITE, HEADER_SOURCE};
    private static final String[] MANDATORY_HEADERS = new String[]{HEADER_NOM, HEADER_ETAPEACV, HEADER_CRITERE
            , HEADER_NIVEAU, HEADER_TIERS, HEADER_CATEGORIE, HEADER_CONSO_ELEC_MOYENNE, HEADER_LOCALISATION, HEADER_VALEUR,
            HEADER_UNITE, HEADER_SOURCE};


    public static String[] getHeaders() {
        return HEADERS;
    }

    public void checkCSVRecord(CSVRecord csvRecord) throws ReferentielException {
        checkAllHeadersAreMapped(csvRecord, MANDATORY_HEADERS);
        checkFieldIsMappedAndNotBlankInCSVRecord(csvRecord, HEADER_NOM);
        checkFieldIsMappedAndNotBlankInCSVRecord(csvRecord, HEADER_ETAPEACV);
        checkFieldIsMappedAndNotBlankInCSVRecord(csvRecord, HEADER_CRITERE);
    }

    @Override
    public ResultatImport<FacteurCaracterisationDTO> importCSV(InputStream csvInputStream) {
        ResultatImport<FacteurCaracterisationDTO> resultatImport = new ResultatImport<>();
        List<FacteurCaracterisationDTO> dtos = new ArrayList<>();

        try (Reader reader = new InputStreamReader(csvInputStream)) {
            Iterable<CSVRecord> records = CSVFormat.DEFAULT.builder()
                    .setHeader()
                    .setDelimiter(CSV_SEPARATOR)
                    .setTrim(true)
                    .setAllowMissingColumnNames(true)
                    .setSkipHeaderRecord(true)
                    .build().parse(reader);
            records.forEach(csvRecord -> {
                try {
                    checkCSVRecord(csvRecord);
                    dtos.add(FacteurCaracterisationDTO.builder()
                            .nom(csvRecord.get(HEADER_NOM).trim())
                            .etape(csvRecord.get(HEADER_ETAPEACV).trim())
                            .critere(csvRecord.get(HEADER_CRITERE).trim())
                            .description(getStringValueFromRecord(csvRecord, "description"))
                            .niveau(getStringValueFromRecord(csvRecord, "niveau"))
                            .tiers(getStringValueFromRecord(csvRecord, "tiers"))
                            .categorie(getStringValueFromRecord(csvRecord, "categorie"))
                            .consoElecMoyenne(getDoubleValueFromRecord(csvRecord, HEADER_CONSO_ELEC_MOYENNE, null))
                            .localisation(getStringValueFromRecord(csvRecord, "localisation"))
                            .valeur(getDoubleValueFromRecord(csvRecord, HEADER_VALEUR, null))
                            .unite(getStringValueFromRecord(csvRecord, "unite"))
                            .source(getStringValueFromRecord(csvRecord, "source"))
                            .build());
                } catch (Exception e) {
                    log.error("Erreur prévue lors de la lecture de la ligne {} : {}", csvRecord.getRecordNumber() + 1, e.getMessage());
                    resultatImport.getErreurs().add(e.getMessage());
                }
            });

        } catch (Exception e) {
            log.error("Erreur de traitement du fichier", e);

            resultatImport.setErreurs(Collections.singletonList("Le fichier CSV n'a pas pu être lu."));
            resultatImport.setNbrLignesImportees(0);
            resultatImport.setObjects(null);
            return resultatImport;
        }

        resultatImport.setObjects(dtos);
        resultatImport.setNbrLignesImportees(dtos.size());

        return resultatImport;
    }

}
