package fr.mines_stetienne.ci.saref.checkers;

import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;

import fr.mines_stetienne.ci.saref.SAREF;
import fr.mines_stetienne.ci.saref.SAREFPipeline;
import fr.mines_stetienne.ci.saref.SAREFPipelineException;
import fr.mines_stetienne.ci.saref.SAREFRepositoryErrorLogger;
import fr.mines_stetienne.ci.saref.entities.SAREFRepository;
import fr.mines_stetienne.ci.saref.entities.SAREFTerm;
import fr.mines_stetienne.ci.saref.entities.SAREFVersion;

public class TermsChecker extends SAREFRepositoryErrorLogger {

	private static final String TERMS = "terms";

	public TermsChecker(SAREFPipeline pipeline, SAREFRepository repository) {
		super(pipeline, repository, pipeline.getLogger(SAREF.getMessage(TERMS, repository.getProject())));
	}

	private static enum MESSAGE {
		used_not_defined, defined_not_exemplified, exemplified_not_defined
	}

	public void check() throws SAREFPipelineException {
		for (SAREFVersion version : repository.getVersions().values()) {
			checkUsedNotDefined(version);
			checkLocalExemplifiedNotDefined(version);
			checkSourceExemplifiedNotDefined(version);
			checkDefinedNotExemplified(version);
		}
	}

	private void checkUsedNotDefined(SAREFVersion version) {
		String msg = version.getUsedTerms().stream().filter(term -> !term.isDefined()).map(SAREFTerm::getPrefixedName)
				.collect(Collectors.joining("\n"));
		if (!msg.isEmpty()) {
			logWarning(getMessage(MESSAGE.used_not_defined, project.getName(), version.getVersionName(), msg));
		}
	}

	private void checkLocalExemplifiedNotDefined(SAREFVersion version) {
		Set<SAREFTerm> localExemplifiedNotDefined = new HashSet<>();
		version.getExamples().values().forEach(example -> {
			example.getExemplifiedTerms().stream()
					.filter(term -> term.getRepository().equals(repository)
							&& !term.getIsDefinedBy().stream().anyMatch(v -> v.equals(version)))
					.forEach(localExemplifiedNotDefined::add);
		});
		if (!localExemplifiedNotDefined.isEmpty()) {
			String msg = localExemplifiedNotDefined.stream().map(SAREFTerm::getPrefixedName)
					.collect(Collectors.joining(", "));
			logError(getMessage(MESSAGE.exemplified_not_defined, project.getName(), version.getVersionName(), msg));
		}
	}

	private void checkSourceExemplifiedNotDefined(SAREFVersion version) {
		Set<SAREFTerm> localSourceNotDefined = new HashSet<>();
		version.getExamples().values().forEach(example -> {
			example.getExemplifiedTerms().stream()
					.filter(term -> !term.getRepository().equals(repository) && !term.isDefined())
					.forEach(localSourceNotDefined::add);
		});
		if (!localSourceNotDefined.isEmpty()) {
			String msg = localSourceNotDefined.stream().map(SAREFTerm::getPrefixedName)
					.collect(Collectors.joining(", "));
			logWarning(getMessage(MESSAGE.exemplified_not_defined, project.getName(), version.getVersionName(), msg));
		}
	}

	private void checkDefinedNotExemplified(SAREFVersion version) {
		String msg = version.getDefinedTerms().stream().filter(
				term -> !term.getIsExemplifiedBy().stream().anyMatch(example -> example.getVersion().equals(version)))
				.map(SAREFTerm::getPrefixedName).collect(Collectors.joining(", "));
		if (!msg.isEmpty()) {
			logWarning(getMessage(MESSAGE.defined_not_exemplified, project.getName(), version.getVersionName(), msg));
		}
	}

}
