package sqlancer;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

public interface TestOracle {

	public void check() throws SQLException;

	public default boolean onlyWorksForNonEmptyTables() {
		return false;
	}

	public static void assumeResultSetsAreEqual(List<String> resultSet, List<String> secondResultSet,
			String originalQueryString, List<String> combinedString, GlobalState<?> state) {
		if (resultSet.size() != secondResultSet.size()) {
			String queryFormatString = "%s; -- cardinality: %d";
			String firstQueryString = String.format(queryFormatString, originalQueryString, resultSet.size());
			String secondQueryString = String.format(queryFormatString, combinedString.stream().collect(Collectors.joining(";")), secondResultSet.size());
			state.getState().statements.add(new QueryAdapter(firstQueryString));
			state.getState().statements.add(new QueryAdapter(secondQueryString));
			String assertionMessage = String.format("the size of the result sets mismatch (%d and %d)!\n%s\n%s",
					resultSet.size(), secondResultSet.size(), firstQueryString, secondQueryString);
			throw new AssertionError(assertionMessage);
		}

		Set<String> firstHashSet = new HashSet<>(resultSet);
		Set<String> secondHashSet = new HashSet<>(secondResultSet);

		if (!firstHashSet.equals(secondHashSet)) {
			Set<String> firstResultSetMisses = new HashSet<>(firstHashSet);
			firstResultSetMisses.removeAll(secondHashSet);
			Set<String> secondResultSetMisses = new HashSet<>(secondHashSet);
			secondResultSetMisses.removeAll(firstHashSet);
			String queryFormatString = "%s; -- misses: %s";
			String firstQueryString = String.format(queryFormatString, originalQueryString, firstResultSetMisses);
			String secondQueryString = String.format(queryFormatString, combinedString.stream().collect(Collectors.joining(";")), secondResultSetMisses);
			state.getState().statements.add(new QueryAdapter(firstQueryString));
			state.getState().statements.add(new QueryAdapter(secondQueryString));
			String assertionMessage = String.format("the content of the result sets mismatch!\n%s\n%s",
					firstQueryString, secondQueryString);
			throw new AssertionError(assertionMessage);
		}
	}
	
	public static List<String> getCombinedResultSet(String firstQueryString, String secondQueryString,
			String thirdQueryString, List<String> combinedString, boolean asUnion, GlobalState<?> state, Set<String> errors) throws SQLException {
		List<String> secondResultSet;
		if (asUnion) {
			String unionString = firstQueryString + " UNION ALL " + secondQueryString + " UNION ALL "
					+ thirdQueryString;
			combinedString.add(unionString);
			secondResultSet = DatabaseProvider.getResultSetFirstColumnAsString(unionString, errors,
					state.getConnection(), state);
		} else {
			secondResultSet = new ArrayList<>();
			secondResultSet.addAll(DatabaseProvider.getResultSetFirstColumnAsString(firstQueryString, errors,
					state.getConnection(), state));
			secondResultSet.addAll(DatabaseProvider.getResultSetFirstColumnAsString(secondQueryString, errors,
					state.getConnection(), state));
			secondResultSet.addAll(DatabaseProvider.getResultSetFirstColumnAsString(thirdQueryString, errors,
					state.getConnection(), state));
			combinedString.add(firstQueryString);
			combinedString.add(secondQueryString);
			combinedString.add(thirdQueryString);
		}
		return secondResultSet;
	}

	public static List<String> getCombinedResultSetNoDuplicates(String firstQueryString, String secondQueryString,
			String thirdQueryString, List<String> combinedString, boolean asUnion, GlobalState<?> state, Set<String> errors) throws SQLException {
		List<String> secondResultSet;
		if (asUnion) {
			String unionString = firstQueryString + " UNION " + secondQueryString + " UNION "
					+ thirdQueryString;
			combinedString.add(unionString);
			secondResultSet = DatabaseProvider.getResultSetFirstColumnAsString(unionString, errors,
					state.getConnection(), state);
		} else {
			secondResultSet = new ArrayList<>();
			secondResultSet.addAll(DatabaseProvider.getResultSetFirstColumnAsString(firstQueryString, errors,
					state.getConnection(), state));
			secondResultSet.addAll(DatabaseProvider.getResultSetFirstColumnAsString(secondQueryString, errors,
					state.getConnection(), state));
			secondResultSet.addAll(DatabaseProvider.getResultSetFirstColumnAsString(thirdQueryString, errors,
					state.getConnection(), state));
			combinedString.add(firstQueryString);
			combinedString.add(secondQueryString);
			combinedString.add(thirdQueryString);
		}
		return secondResultSet;
	}
	
}

@@ -774,33 +774,38 @@
 eryFormatString,
- 
+%0A%09%09%09%09%09
 combinedString.s
@@ -1875,17 +1875,22 @@
 tString,
- 
+%0A%09%09%09%09%09
 combined
@@ -2300,17 +2300,16 @@
 %0A%09%09%7D%0A%09%7D%0A
-%09
 %0A%09public
@@ -2488,33 +2488,36 @@
 lState%3C?%3E state,
- 
+%0A%09%09%09
 Set%3CString%3E erro
@@ -3703,17 +3703,20 @@
 %3E state,
- 
+%0A%09%09%09
 Set%3CStri
@@ -3741,32 +3741,85 @@
  SQLException %7B%0A
+%09%09if (!asUnion) %7B%0A%09%09%09throw new AssertionError();%0A%09%09%7D%0A
 %09%09List%3CString%3E s
@@ -3834,34 +3834,16 @@
 ultSet;%0A
-%09%09if (asUnion) %7B%0A%09
 %09%09String
@@ -3917,22 +3917,17 @@
  UNION %22
-%0A%09%09%09%09%09
+ 
 + thirdQ
@@ -3930,33 +3930,32 @@
 irdQueryString;%0A
-%09
 %09%09combinedString
@@ -3965,33 +3965,32 @@
 d(unionString);%0A
-%09
 %09%09secondResultSe
@@ -4066,638 +4066,43 @@
 ors,
-%0A%09%09%09%09%09state.getConnection(), state);%0A%09%09%7D else %7B%0A%09%09%09secondResultSet = new ArrayList%3C%3E();%0A%09%09%09secondResultSet.addAll(DatabaseProvider.getResultSetFirstColumnAsString(firstQueryString, errors,%0A%09%09%09%09%09state.getConnection(), state));%0A%09%09%09secondResultSet.addAll(DatabaseProvider.getResultSetFirstColumnAsString(secondQueryString, errors,%0A%09%09%09%09%09state.getConnection(), state));%0A%09%09%09secondResultSet.addAll(DatabaseProvider.getResultSetFirstColumnAsString(thirdQueryString, errors,%0A%09%09%09%09%09state.getConnection(), state));%0A%09%09%09combinedString.add(firstQueryString);%0A%09%09%09combinedString.add(secondQueryString);%0A%09%09%09combinedString.add(thirdQueryString);%0A%09%09%7D
+ state.getConnection(),%0A%09%09%09%09state);
 %0A%09%09r
@@ -4127,12 +4127,11 @@
 Set;%0A%09%7D%0A
-%09
 %0A%7D%0A

Add some test oracle simplifications