package it.unibz.inf.ontop.spec.impl;

import com.google.common.collect.ImmutableList;
import com.google.inject.Guice;
import it.unibz.inf.ontop.dbschema.*;
import it.unibz.inf.ontop.dbschema.impl.OfflineMetadataProviderBuilder;
import it.unibz.inf.ontop.exception.MetadataExtractionException;
import it.unibz.inf.ontop.model.type.DBTermType;
import it.unibz.inf.ontop.spec.dbschema.ImplicitDBConstraintsProviderFactory;
import it.unibz.inf.ontop.dbschema.MetadataProvider;
import it.unibz.inf.ontop.spec.dbschema.impl.ImplicitDBConstraintsProviderFactoryImpl;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.File;
import java.util.List;
import java.util.Optional;

import static it.unibz.inf.ontop.utils.MappingTestingTools.*;
import static org.junit.jupiter.api.Assertions.*;

public class ImplicitDBConstraintsTest {

	private static final String DIR = "src/test/resources/userconstraints/";

	private static final ImplicitDBConstraintsProviderFactory CONSTRAINT_EXTRACTOR = Guice.createInjector()
			.getInstance(ImplicitDBConstraintsProviderFactoryImpl.class);

	private MetadataProvider md;

	@BeforeEach
	void setup() {
		OfflineMetadataProviderBuilder builder = createMetadataProviderBuilder();
		DBTermType stringDBType = builder.getDBTypeFactory().getDBStringType();

		builder.createDatabaseRelation("TABLENAME",
			"KEYNAME", stringDBType, false);

		builder.createDatabaseRelation( "TABLE2",
			"KEY1", stringDBType, false,
			"KEY2", stringDBType, false);

		md = builder.build();
	}
	
	@Test
	public void testEmptyUserConstraints() throws MetadataExtractionException {
		MetadataProvider uc = CONSTRAINT_EXTRACTOR.extract(Optional.of(new File(DIR + "empty_constraints.lst")), md);
		List<RelationID> refs = uc.getRelationIDs();
		NamedRelationDefinition rel1 = uc.getRelation(refs.get(0));
		NamedRelationDefinition rel2 = uc.getRelation(refs.get(1));
		assertAll(
				() -> assertEquals(ImmutableList.of(), rel1.getUniqueConstraints()),
				() -> assertEquals(ImmutableList.of(), rel2.getUniqueConstraints()),
				() -> assertEquals(ImmutableList.of(), rel1.getForeignKeys()),
				() -> assertEquals(ImmutableList.of(), rel2.getForeignKeys()));
	}

	@Test
	public void testPKeys() throws MetadataExtractionException {
		MetadataProvider uc = CONSTRAINT_EXTRACTOR.extract(Optional.of(new File(DIR + "pkeys.lst")), md);
		List<RelationID> refs = uc.getRelationIDs();
		NamedRelationDefinition rel1 = uc.getRelation(refs.get(0));
		NamedRelationDefinition rel2 = uc.getRelation(refs.get(1));
		assertAll(
				() -> assertEquals(ImmutableList.of(), rel1.getUniqueConstraints()),
				() -> assertEquals(ImmutableList.of(), rel2.getUniqueConstraints()),
				() -> assertEquals(ImmutableList.of(), rel1.getForeignKeys()),
				() -> assertEquals(ImmutableList.of(), rel2.getForeignKeys()));
	}

	@Test
	public void testAddPrimaryKeys() throws MetadataExtractionException {
		MetadataProvider uc = CONSTRAINT_EXTRACTOR.extract(Optional.of(new File(DIR + "pkeys.lst")), md);
		NamedRelationDefinition table = uc.getRelation(uc.getQuotedIDFactory().createRelationID("TABLENAME"));
		uc.insertIntegrityConstraints(table, uc);
		Attribute attr = table.getAttribute(1);
		assertEquals(ImmutableList.of(attr), table.getUniqueConstraints().get(0).getAttributes());
	}


	@Test
	public void testTables() throws MetadataExtractionException {
		MetadataProvider uc = CONSTRAINT_EXTRACTOR.extract(Optional.of(new File(DIR + "fkeys.lst")), md);
		List<RelationID> refs = uc.getRelationIDs();
		NamedRelationDefinition rel1 = uc.getRelation(refs.get(0));
		NamedRelationDefinition rel2 = uc.getRelation(refs.get(1));
		assertAll(
				() -> assertEquals(ImmutableList.of(), rel1.getUniqueConstraints()),
				() -> assertEquals(ImmutableList.of(), rel2.getUniqueConstraints()),
				() -> assertEquals(ImmutableList.of(), rel1.getForeignKeys()),
				() -> assertEquals(ImmutableList.of(), rel2.getForeignKeys()));
	}

	@Test
	public void testAddForeignKeys() throws MetadataExtractionException {
		MetadataProvider uc = CONSTRAINT_EXTRACTOR.extract(Optional.of(new File(DIR + "fkeys.lst")), md);
		NamedRelationDefinition table = uc.getRelation(uc.getQuotedIDFactory().createRelationID("TABLENAME"));
		uc.insertIntegrityConstraints(table, uc);
		ForeignKeyConstraint fk = table.getForeignKeys().get(0);
		Attribute ref = fk.getComponents().get(0).getReferencedAttribute();
		assertAll(
				() -> assertEquals(md.getQuotedIDFactory().createRelationID("TABLE2"), ((NamedRelationDefinition)ref.getRelation()).getID()),
				() -> assertEquals(md.getQuotedIDFactory().createAttributeID("KEY1"), ref.getID()));
	}

	@Test
	public void testAddKeys() throws MetadataExtractionException {
		MetadataProvider uc = CONSTRAINT_EXTRACTOR.extract(Optional.of(new File(DIR + "keys.lst")), md);
		NamedRelationDefinition table = uc.getRelation(uc.getQuotedIDFactory().createRelationID("TABLENAME"));
		uc.insertIntegrityConstraints(table, uc);
		ForeignKeyConstraint fk = table.getForeignKeys().get(0);
		Attribute ref = fk.getComponents().get(0).getReferencedAttribute();
		assertAll(
				() -> assertEquals(md.getQuotedIDFactory().createRelationID("TABLE2"), ((NamedRelationDefinition)ref.getRelation()).getID()),
				() -> assertEquals(md.getQuotedIDFactory().createAttributeID("KEY1"), ref.getID()),
				() -> assertEquals(ImmutableList.of(table.getAttribute(1)), table.getUniqueConstraints().get(0).getAttributes()));
	}
}
