import { print, ASTNode } from "graphql";
import { diff } from "jest-diff";
import { matcherHint, printExpected, printReceived } from "jest-matcher-utils";

// replace whitespace
// also ignore commas (not supe precise but fixes some edge cases with comparing)
const replaceWhitespace = (str: string) =>
  str ? str.replace(/[\s,]+/g, " ").trim() : str;

const removeWhitespaceBeforeBrackets = (str: string) =>
  str ? str.replace(/\(\s/g, "(").replace(/\s\)/g, ")") : null;
const name = `toEqualGraphql`;

const cleanString = (str: string) =>
  removeWhitespaceBeforeBrackets(replaceWhitespace(str));

const toEqualGraphql = (
  received: ASTNode | ASTNode[],
  expected: string | string[],
) => {
  const receivedArray = Array.isArray(received) ? received : [received];
  const expectedArray = Array.isArray(expected) ? expected : [expected];
  const expectedArrayCompressed = expectedArray.map(cleanString);
  const receivedPrinted = receivedArray.map((node) => cleanString(print(node)));
  const pass = receivedPrinted.every(
    (s, index) => s === expectedArrayCompressed[index],
  );

  const message = pass
    ? () =>
        `${matcherHint(`.not.${name}`)}\n\n` +
        `  ${printExpected(expectedArrayCompressed)}\n` +
        `Received:\n` +
        `  ${printReceived(receivedPrinted)}`
    : () => {
        const diffString = diff(expectedArrayCompressed, receivedPrinted);
        return (
          `${matcherHint(`.${name}`)}\n\n` +
          `Expected value to equal:\n` +
          `  ${printExpected(expectedArrayCompressed)}\n` +
          `Received:\n` +
          `  ${printReceived(receivedPrinted)}${
            diffString ? `\n\nDifference:\n\n${diffString}` : ``
          }`
        );
      };
  return {
    actual: received,
    expected,
    message,
    name,
    pass,
  };
};

expect.extend({
  toEqualGraphql,
});