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

import org.apache.commons.csv.CSVRecord;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.mte.numecoeval.referentiel.domain.data.ResultatImport;
import org.mte.numecoeval.referentiel.domain.exception.ReferentielException;

import java.io.InputStream;
import java.util.Arrays;

public interface ImportCSVReferentielPort<T> {

    String CSV_SEPARATOR = ";";

    String MESSAGE_AVERTISSEMENT_LIGNE_INCONSISTENTE = "Fichier %s : La ligne n°%d n'est pas consistente avec les headers du fichier";

    String MESSAGE_LIGNE_INVALIDE = "La ligne n°%d est invalide : %s";

    void checkCSVRecord(CSVRecord csvRecord) throws ReferentielException;

    default void checkAllHeadersAreMapped(CSVRecord csvRecord, String[] headers)  throws ReferentielException{
        if(!Arrays.stream(headers).allMatch(csvRecord::isMapped)) {
            throw new ReferentielException(MESSAGE_LIGNE_INVALIDE.formatted(csvRecord.getRecordNumber()+1, "Entêtes incohérentes"));
        }
    }

    default void checkFieldIsMappedInCSVRecord(CSVRecord csvRecord, String field)  throws ReferentielException{
        if(!csvRecord.isMapped(field)) {
            throw new ReferentielException(MESSAGE_LIGNE_INVALIDE.formatted(csvRecord.getRecordNumber()+1, "La colonne "+field+" doit être présente"));
        }
    }

    default void checkFieldIsMappedAndNotBlankInCSVRecord(CSVRecord csvRecord, String field)  throws ReferentielException{
        checkFieldIsMappedInCSVRecord(csvRecord, field);
        if(StringUtils.isBlank(csvRecord.get(field))) {
            throw new ReferentielException(MESSAGE_LIGNE_INVALIDE.formatted(csvRecord.getRecordNumber()+1, "La colonne "+field+" ne peut être vide"));
        }
    }

    ResultatImport<T> importCSV(InputStream csvInputStream);
    default Double getDoubleValueFromRecord(CSVRecord csvRecord, String field, Double defaultValue) {
        if(!NumberUtils.isCreatable(StringUtils.trim(csvRecord.get(field)))) {
            return defaultValue;
        }
        return NumberUtils.toDouble(csvRecord.get(field));
    }

    /**
     * Renvoie la valeur de la colonne {@param mainName} dans le {@param csvRecord}.
     * Si le {@param mainName} n'est pas mappé dans le {@link CSVRecord}, la liste des noms alternatifs est utilisée.
     * Si aucune colonne n'est mappée ou que la liste alternative est vide, la valeur {@code null} est renvoyée.
     * @param csvRecord La ligne de CSV à traiter
     * @param mainName Le nom de la colonne souhaitée en 1er
     * @param alternativeNames Les noms de colonnes alternatifs à utiliser, peut être vide
     * @return La valeur de la 1er colonne correctement mappée sur la ligne de CSV, {@code null} en absence de mapping
     */
    default String getStringValueFromRecord(CSVRecord csvRecord, String mainName, String... alternativeNames) {
        if(csvRecord.isMapped(mainName)) {
            return StringUtils.trim(csvRecord.get(mainName));
        }

        for (String alternativeName : alternativeNames) {
            if(csvRecord.isMapped(alternativeName)) {
                return StringUtils.trim(csvRecord.get(alternativeName));
            }
        }
        return null;
    }
}
