package eu.dnetlib.validator2.validation.guideline.openaire;

import eu.dnetlib.validator2.validation.guideline.Builders;
import eu.dnetlib.validator2.validation.guideline.ElementSpec;
import eu.dnetlib.validator2.validation.guideline.Guideline;
import eu.dnetlib.validator2.validation.guideline.SyntheticGuideline;
import eu.dnetlib.validator2.validation.utils.RegexValuePredicate;
import org.w3c.dom.Document;

import java.util.*;
import java.util.stream.Collectors;

import static eu.dnetlib.validator2.validation.guideline.Cardinality.ONE;
import static eu.dnetlib.validator2.validation.guideline.Cardinality.ONE_TO_N;
import static eu.dnetlib.validator2.validation.utils.SupportedRegExs.*;

public final class F2_01M_LIT extends AbstractOpenAireProfile {

    private static final String[] TITLE_TYPES = {
            "AlternativeTitle", "Subtitle", "TranslatedTitle", "Other"
    };

    private static final String[] NAME_TYPES = {
            "Organizational", "Personal"
    };

    private static final ElementSpec F2_01M_LIT_SPEC_1 = Builders.
            forMandatoryElement("datacite:creator", ONE_TO_N).
            withSubElement(Builders.
                    forMandatoryElement("datacite:creatorName", ONE).
                    withRecommendedAttribute("nameType", NAME_TYPES)).
            withSubElement(Builders.
                    forRecommendedElement("datacite:givenName")).
            withSubElement(Builders.
                    forRecommendedElement("datacite:familyName")).
            withSubElement(Builders.
                    forRecommendedRepeatableElement("datacite:nameIdentifier").
                    withMandatoryAttribute("nameIdentifierScheme").
                    withRecommendedAttribute("schemeURI")).
            withSubElement(Builders.
                    forRecommendedRepeatableElement("datacite:affiliation")).
            build();

    private static final ElementSpec F2_01M_LIT_SPEC_2 = Builders.
            forMandatoryElement("datacite:title", ONE_TO_N).
            withOptionalAttribute("xml:lang", new RegexValuePredicate(COMPILED_BCP47_LANG_TAGS_REG_EX)).
            withOptionalAttribute("titleType", TITLE_TYPES).
            build();

    private static final ElementSpec F2_01M_LIT_SPEC_3 = Builders.
            forMandatoryElement("dc:publisher", ONE_TO_N).
            build();

    private static final ElementSpec F2_01M_LIT_SPEC_4 = Builders.
            forMandatoryElement("datacite:date", ONE).
            withMandatoryAttribute("dateType", "Issued").
            allowedValues(new RegexValuePredicate(COMPILED_PUBLICATION_DATE_REG_EX).or(new RegexValuePredicate(COMPILED_YYYY_MM_DD_RANGE_REGEX).or(new RegexValuePredicate(COMPILED_YEAR_YYYY_REG_EX)))).
            build();

    private static final ElementSpec F2_01M_LIT_SPEC_5 = Builders.
            forMandatoryElement("dc:description", ONE_TO_N).
            withMandatoryAttribute("descriptionType", "Abstract").
            withOptionalAttribute("xml:lang", new RegexValuePredicate(COMPILED_BCP47_LANG_TAGS_REG_EX)).
            build();

    private static final ElementSpec F2_01M_LIT_SPEC_6 = Builders.
            forMandatoryElement("datacite:subject", ONE_TO_N).
            withOptionalAttribute("subjectScheme").
            withOptionalAttribute("schemeURI").
            withOptionalAttribute("valueURI").
            build();

    //TODO: weights for guidelines haven't been finalized. They've been given an arbitrary value of 1.
    public static SyntheticGuideline F2_01M_LIT_1 = SyntheticGuideline.of("Creator", 2, F2_01M_LIT_SPEC_1);
    public static SyntheticGuideline F2_01M_LIT_2 = SyntheticGuideline.of("Title", 2, F2_01M_LIT_SPEC_2);
    public static SyntheticGuideline F2_01M_LIT_3 = SyntheticGuideline.of("Publisher", 2, F2_01M_LIT_SPEC_3);
    public static SyntheticGuideline F2_01M_LIT_4 = SyntheticGuideline.of("Date", 2, F2_01M_LIT_SPEC_4);
    public static SyntheticGuideline F2_01M_LIT_5 = SyntheticGuideline.of("Summary (Description with descriptionType)", 2, F2_01M_LIT_SPEC_5);
    public static SyntheticGuideline F2_01M_LIT_6 = SyntheticGuideline.of("Keywords (Subjects)", 2, F2_01M_LIT_SPEC_6);

    private static final List<Guideline<Document>> GUIDELINES = Collections.unmodifiableList(
            Arrays.asList(
                    F2_01M_LIT_1,
                    F2_01M_LIT_2,
                    F2_01M_LIT_3,
                    F2_01M_LIT_4,
                    F2_01M_LIT_5,
                    F2_01M_LIT_6
            )
    );

    private static final Map<String, Guideline> GUIDELINE_MAP = GUIDELINES.
            stream().
            collect(Collectors.toMap(Guideline::getName, (guideline) -> guideline));

    private static final int MAX_SCORE = GUIDELINES.stream().map(Guideline::getWeight).reduce(0, Integer::sum);

    public F2_01M_LIT() {
        super("Rich metadata is provided to allow discovery");
    }

    @Override
    public Collection<? extends Guideline<Document>> guidelines() {
        return GUIDELINES;
    }

    /**
     *
     * @param guidelineName
     * @return
     */
    @Override
    public Guideline guideline(String guidelineName) {
        return GUIDELINE_MAP.get(guidelineName);
    }

    @Override
    public int maxScore() {
        return MAX_SCORE;
    }
}