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 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;

public final class R1_2_01M extends AbstractOpenAireProfile {

    private static final String[] NAME_IDENTIFIER_SCHEMES = {
            "ORCID", "ISNI", "ROR", "GRID"
    };

    private static final String[] CONTRIBUTOR_TYPES = {
            "ContactPerson", "DataCollector", "DataCurator", "DataManager", "Distributor",
            "Editor", "HostingInstitution", "Producer", "ProjectLeader", "ProjectManager", "ProjectMember",
            "RegistrationAgency", "RegistrationAuthority", "RelatedPerson", "Researcher", "ResearchGroup",
            "RightsHolder", "Sponsor", "Supervisor", "WorkPackageLeader", "Other"
    };

    private static final String[] DATE_TYPES = {
            "Accepted", "Available", "Copyrighted", "Collected", "Created", "Issued", "Submitted", "Updated", "Valid"
    };

    private static final String[] CONTRIBUTOR_NAME_IDENTIFIER_TYPES = {
            "Funder", "FundingProgramme", "ProjectID", "Jurisdiction", "ProjectName", "ProjectAcronym"
    };

    private static final String[] IDENTIFIER_TYPES = {
            "ARK", "arXiv", "bibcode", "DOI", "EAN13", "EISSN", "Handle", "IGSN", "ISBN",
            "ISSN", "ISTC", "LISSN", "LSID", "PISSN", "PMID", "PURL", "UPC", "URL", "URN", "WOS",
    };

    private static final String[] RELATION_TYPES_LIMITED = {
            "IsSupplementTo", "Describes", "IsMetadataFor", "IsPartOf"
    };

    private static final ElementSpec R1_2_01M_SPEC_1 = Builders.
        forMandatoryElement("creator", ONE_TO_N)
            .withSubElement(Builders.forMandatoryElement("creatorName", ONE))
            .withSubElement(Builders
                    .forRecommendedElement("nameIdentifier")
                    .withMandatoryAttribute("nameIdentifierScheme")
                    .withMandatoryAttribute("schemeURI")
            )
            .withSubElement(Builders.forRecommendedRepeatableElement("affiliation"))
            .build();

    private static final ElementSpec R1_2_01M_SPEC_2 = Builders.
            forMandatoryElement("contributor", ONE_TO_N).
            withMandatoryAttribute("contributorType", CONTRIBUTOR_TYPES).
            withSubElement(Builders
                    .forMandatoryIfApplicableElement("contributorName", ONE, AbstractOpenAireProfile.elementIsPresent("contributor"))
            ).
            withMandatoryAttribute("nameIdentifier", CONTRIBUTOR_NAME_IDENTIFIER_TYPES).
            withMandatoryAttribute("affiliation").
            build();

    private static final ElementSpec R1_2_01M_SPEC_3 = Builders.
            forMandatoryElement("date", ONE_TO_N).
            withMandatoryAttribute("dateType", DATE_TYPES).
            build();

    private static final ElementSpec R1_2_01M_SPEC_4 = Builders.
            forMandatoryElement("version", ONE).
            build();

    private static final ElementSpec R1_2_01M_SPEC_5 = Builders.
            forMandatoryElement("relatedIdentifier", ONE_TO_N).
            withMandatoryAttribute("relatedIdentifierType", IDENTIFIER_TYPES).
            withMandatoryAttribute("relationType", RELATION_TYPES_LIMITED).
            build();

    //TODO: weights for guidelines haven't been finalized. They've been given an arbitrary value of 1.
    public static SyntheticGuideline R1_2_01M_1 = SyntheticGuideline.of("Creator", 2, R1_2_01M_SPEC_1);
    public static SyntheticGuideline R1_2_01M_2 = SyntheticGuideline.of("Contributor", 2, R1_2_01M_SPEC_2);
    public static SyntheticGuideline R1_2_01M_3 = SyntheticGuideline.of("Date", 2, R1_2_01M_SPEC_3);
    public static SyntheticGuideline R1_2_01M_4 = SyntheticGuideline.of("Version", 2, R1_2_01M_SPEC_4);
    public static SyntheticGuideline R1_2_01M_5 = SyntheticGuideline.of("RelatedIdentifier", 2, R1_2_01M_SPEC_5);

    private static final List<Guideline<Document>> GUIDELINES = Collections.unmodifiableList(
            Arrays.asList(
                    R1_2_01M_1,
                    R1_2_01M_2,
                    R1_2_01M_3,
                    R1_2_01M_4,
                    R1_2_01M_5
            )
    );

    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 R1_2_01M() {
        super("Metadata includes provenance information according to a cross-community language");
    }

    @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;
    }
}