import type { RawQueryCompareResult } from "../common/interface-types";
import type { RawResultSet } from "../common/raw-result-types";

/**
 * Compare the rows of two queries. Use deep equality to determine if
 * rows have been added or removed across two invocations of a query.
 *
 * Assumptions:
 *
 * 1. Queries have the same sort order
 * 2. Queries have same number and order of columns
 * 3. Rows are not changed or re-ordered, they are only added or removed
 *
 * @param fromResults the source query
 * @param toResults the target query
 *
 * @throws Error when:
 *  1. number of columns do not match
 *  2. If either query is empty
 *  3. If the queries are 100% disjoint
 *
 * @deprecated This function is only used when the `bqrs diff` command does not
 * support `--result-sets`. It should be removed when all supported CLI versions
 * support this option.
 */
export default function resultsDiff(
  fromResults: RawResultSet,
  toResults: RawResultSet,
): RawQueryCompareResult {
  const results: RawQueryCompareResult = {
    kind: "raw",
    columns: fromResults.columns,
    from: arrayDiff(fromResults.rows, toResults.rows),
    to: arrayDiff(toResults.rows, fromResults.rows),
  };

  if (
    fromResults.rows.length === results.from.length &&
    toResults.rows.length === results.to.length
  ) {
    throw new Error("CodeQL Compare: No overlap between the selected queries.");
  }

  return results;
}

function arrayDiff<T>(source: readonly T[], toRemove: readonly T[]): T[] {
  // Stringify the object so that we can compare hashes in the set
  const rest = new Set(toRemove.map((item) => JSON.stringify(item)));
  return source.filter((element) => !rest.has(JSON.stringify(element)));
}
