package com.taxonic.carml.engine;

import org.eclipse.rdf4j.model.IRI;
import org.eclipse.rdf4j.model.Model;
import org.eclipse.rdf4j.model.Resource;
import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
import org.eclipse.rdf4j.model.util.ModelBuilder;
import org.eclipse.rdf4j.model.vocabulary.RDF;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;

import java.util.Set;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.taxonic.carml.rdf_mapper.util.ImmutableCollectors;

public class SubjectMapperTest {

	@Rule
	public MockitoRule mockitoRule = MockitoJUnit.rule();

	@Mock
	TermGenerator<Resource> subjectGenerator;

	@Test
	public void map_withPredicateObjectMappers_callsEveryPredicateObjectMapper() throws Exception {

		EvaluateExpression evaluator = null;

		SimpleValueFactory f = SimpleValueFactory.getInstance();
		IRI subjectIRI = f.createIRI("http://foo.bar/subjectIRI");

		when(subjectGenerator.apply(evaluator))
				.thenReturn(ImmutableList.of(subjectIRI));

		PredicateObjectMapper mockMapper1 = mock(PredicateObjectMapper.class);
		PredicateObjectMapper mockMapper2 = mock(PredicateObjectMapper.class);
		PredicateObjectMapper mockMapper3 = mock(PredicateObjectMapper.class);
		PredicateObjectMapper mockMapper4 = mock(PredicateObjectMapper.class);
		Set<PredicateObjectMapper> predObjMappers = 
			ImmutableSet.of(mockMapper1, mockMapper2, mockMapper3, mockMapper4);

		Model model = new ModelBuilder().build();
		SubjectMapper s = 
			new SubjectMapper(subjectGenerator, ImmutableSet.of(), ImmutableSet.of(), predObjMappers);
		
		s.map(model, evaluator);

		predObjMappers.forEach(
			mapper -> 
				verify(mapper)
				.map(model, evaluator, subjectIRI, ImmutableSet.of()));
	}

	@Test
	public void map_withClasses_appliesEveryClassToGeneratedSubject() throws Exception {

		EvaluateExpression evaluator = null;
		SimpleValueFactory f = SimpleValueFactory.getInstance();
		IRI subjectIRI = f.createIRI("http://foo.bar/subjectIRI");

		when(subjectGenerator.apply(evaluator))
				.thenReturn(ImmutableList.of(subjectIRI));

		Set<IRI> expectedClasses = 
			ImmutableSet.of(
				f.createIRI("http://www.none.invalid/foo"),
				f.createIRI("http://www.none.invalid/bar"),
				f.createIRI("http://www.none.invalid/baz")
			);

		Model model = new ModelBuilder().build();
		SubjectMapper s = new SubjectMapper(subjectGenerator, ImmutableSet.of(), expectedClasses, ImmutableSet.of());
		s.map(model, evaluator);

		expectedClasses.forEach(iri -> Assert.assertTrue(model.contains(subjectIRI, RDF.TYPE, iri)));
	}


	@Test
	public void map_withGraphsAndClasses_appliesGraphsToEveryClass() throws Exception {

		EvaluateExpression evaluator = null;

		SimpleValueFactory f = SimpleValueFactory.getInstance();
		IRI subjectIRI = f.createIRI("http://foo.bar/subjectIRI");

		when(subjectGenerator.apply(evaluator))
				.thenReturn(ImmutableList.of(subjectIRI));


		Set<IRI> graphs = 
			ImmutableSet.of(
				f.createIRI("http://www.none.invalid/graph1"),
				f.createIRI("http://www.none.invalid/graph2"),
				f.createIRI("http://www.none.invalid/graph3"),
				f.createIRI("http://www.none.invalid/graph4")
			);
		Set<TermGenerator<IRI>> graphGenerators = 
			graphs.stream()
				.map(graphIri -> {
					@SuppressWarnings("unchecked")
					TermGenerator<IRI> generator = (TermGenerator<IRI>) mock(TermGenerator.class);
					when(generator.apply(evaluator)).thenReturn(ImmutableList.of(graphIri));
					return generator;
				})
				.collect(ImmutableCollectors.toImmutableSet());


		Set<IRI> expectedClasses = 
			ImmutableSet.of(
				f.createIRI("http://www.none.invalid/foo"),
				f.createIRI("http://www.none.invalid/bar"),
				f.createIRI("http://www.none.invalid/baz")
			);


		Model model = new ModelBuilder().build();
		SubjectMapper s = new SubjectMapper(subjectGenerator, graphGenerators, expectedClasses, ImmutableSet.of());
		s.map(model, evaluator);

		expectedClasses.forEach(iri ->
			graphs.forEach(graph ->
					Assert.assertTrue(model.contains(subjectIRI, RDF.TYPE, iri, graph)))
		);
	}


	@Test
	public void map_withGraphsAndClasses_appliesAllGraphsToEveryPredicateObjectMapper() throws Exception {
		SimpleValueFactory f = SimpleValueFactory.getInstance();
		IRI subjectIRI = f.createIRI("http://foo.bar/subjectIRI");

		EvaluateExpression evaluator = null;

		Set<IRI> graphs = 
			ImmutableSet.of(
				f.createIRI("http://www.none.invalid/graph1"),
				f.createIRI("http://www.none.invalid/graph2"),
				f.createIRI("http://www.none.invalid/graph3"),
				f.createIRI("http://www.none.invalid/graph4")
			);
		Set<TermGenerator<IRI>> graphGenerators =
			graphs.stream()
				.map(graphIri -> {
					@SuppressWarnings("unchecked")
					TermGenerator<IRI> generator = (TermGenerator<IRI>) mock(TermGenerator.class);
					when(generator.apply(evaluator)).thenReturn(ImmutableList.of(graphIri));
					return generator;
				})
				.collect(ImmutableCollectors.toImmutableSet());


		when(subjectGenerator.apply(evaluator))
				.thenReturn(ImmutableList.of(subjectIRI));

		PredicateObjectMapper mockMapper1 = mock(PredicateObjectMapper.class);
		PredicateObjectMapper mockMapper2 = mock(PredicateObjectMapper.class);
		PredicateObjectMapper mockMapper3 = mock(PredicateObjectMapper.class);
		PredicateObjectMapper mockMapper4 = mock(PredicateObjectMapper.class);
		Set<PredicateObjectMapper> predObjMappers = 
			ImmutableSet.of(mockMapper1, mockMapper2, mockMapper3, mockMapper4);

		Model m = new ModelBuilder().build();
		SubjectMapper s = new SubjectMapper(subjectGenerator, graphGenerators, ImmutableSet.of(), predObjMappers);
		s.map(m, evaluator);

		predObjMappers.forEach(mapper -> verify(mapper).map(m, evaluator, subjectIRI, graphs));
	}

}