import { getListDiff } from "../src/list-diff";

describe("getListDiff", () => {
  it("returns an empty diff if no lists are provided", () => {
    expect(getListDiff(null, null)).toStrictEqual({
      type: "list",
      status: "equal",
      diff: [],
    });
  });
  it("consider previous list as completely deleted if no next list is provided", () => {
    expect(
      getListDiff(["mbappe", "mendes", "verratti", "ruiz"], null)
    ).toStrictEqual({
      type: "list",
      status: "deleted",
      diff: [
        {
          value: "mbappe",
          prevIndex: 0,
          newIndex: null,
          indexDiff: null,
          status: "deleted",
        },
        {
          value: "mendes",
          prevIndex: 1,
          newIndex: null,
          indexDiff: null,
          status: "deleted",
        },
        {
          value: "verratti",
          prevIndex: 2,
          newIndex: null,
          indexDiff: null,
          status: "deleted",
        },
        {
          value: "ruiz",
          prevIndex: 3,
          newIndex: null,
          indexDiff: null,
          status: "deleted",
        },
      ],
    });
  });
  it("consider next list as completely added if no previous list is provided", () => {
    expect(
      getListDiff(null, ["mbappe", "mendes", "verratti", "ruiz"])
    ).toStrictEqual({
      type: "list",
      status: "added",
      diff: [
        {
          value: "mbappe",
          prevIndex: null,
          newIndex: 0,
          indexDiff: null,
          status: "added",
        },
        {
          value: "mendes",
          prevIndex: null,
          newIndex: 1,
          indexDiff: null,
          status: "added",
        },
        {
          value: "verratti",
          prevIndex: null,
          newIndex: 2,
          indexDiff: null,
          status: "added",
        },
        {
          value: "ruiz",
          prevIndex: null,
          newIndex: 3,
          indexDiff: null,
          status: "added",
        },
      ],
    });
  });
  it("detects changes between string lists", () => {
    expect(
      getListDiff(
        ["mbappe", "mendes", "verratti", "ruiz"],
        ["mbappe", "messi", "ruiz"]
      )
    ).toStrictEqual({
      type: "list",
      status: "updated",
      diff: [
        {
          value: "mbappe",
          prevIndex: 0,
          newIndex: 0,
          indexDiff: 0,
          status: "equal",
        },
        {
          value: "mendes",
          prevIndex: 1,
          newIndex: null,
          indexDiff: null,
          status: "deleted",
        },
        {
          value: "verratti",
          prevIndex: 2,
          newIndex: null,
          indexDiff: null,
          status: "deleted",
        },
        {
          value: "messi",
          prevIndex: null,
          newIndex: 1,
          indexDiff: null,
          status: "added",
        },
        {
          value: "ruiz",
          prevIndex: 3,
          newIndex: 2,
          indexDiff: -1,
          status: "moved",
        },
      ],
    });
  });
  it("detects changes between number lists", () => {
    expect(getListDiff([54, 234, 76, 0], [54, 200, 0])).toStrictEqual({
      type: "list",
      status: "updated",
      diff: [
        {
          value: 54,
          prevIndex: 0,
          newIndex: 0,
          indexDiff: 0,
          status: "equal",
        },
        {
          value: 234,
          prevIndex: 1,
          newIndex: null,
          indexDiff: null,
          status: "deleted",
        },
        {
          value: 76,
          prevIndex: 2,
          newIndex: null,
          indexDiff: null,
          status: "deleted",
        },
        {
          value: 200,
          prevIndex: null,
          newIndex: 1,
          indexDiff: null,
          status: "added",
        },
        {
          value: 0,
          prevIndex: 3,
          newIndex: 2,
          indexDiff: -1,
          status: "moved",
        },
      ],
    });
  });
  it("detects changes between object lists", () => {
    expect(
      getListDiff(
        [
          { name: "joe", age: 87 },
          { name: "nina", age: 23 },
          { name: "paul", age: 32 },
        ],
        [
          { name: "paul", age: 32 },
          { name: "joe", age: 88 },
          { name: "nina", age: 23 },
        ]
      )
    ).toStrictEqual({
      type: "list",
      status: "updated",
      diff: [
        {
          value: { name: "joe", age: 87 },
          prevIndex: 0,
          newIndex: null,
          indexDiff: null,
          status: "deleted",
        },
        {
          value: { name: "paul", age: 32 },
          prevIndex: 2,
          newIndex: 0,
          indexDiff: -2,
          status: "moved",
        },
        {
          value: { name: "joe", age: 88 },
          prevIndex: null,
          newIndex: 1,
          indexDiff: null,
          status: "added",
        },
        {
          value: { name: "nina", age: 23 },
          prevIndex: 1,
          newIndex: 2,
          indexDiff: 1,
          status: "moved",
        },
      ],
    });
  });
  it("detects changes between lists containing duplicated values", () => {
    expect(
      getListDiff(["mbappe", "messi"], ["mbappe", "mbappe", "messi"])
    ).toStrictEqual({
      type: "list",
      status: "updated",
      diff: [
        {
          value: "mbappe",
          prevIndex: 0,
          newIndex: 0,
          indexDiff: 0,
          status: "equal",
        },
        {
          value: "mbappe",
          prevIndex: null,
          newIndex: 1,
          indexDiff: null,
          status: "added",
        },
        {
          value: "messi",
          prevIndex: 1,
          newIndex: 2,
          indexDiff: 1,
          status: "moved",
        },
      ],
    });
    expect(
      getListDiff(
        ["mbappe", "messi", "messi", "mbappe"],
        ["mbappe", "messi", "messi"]
      )
    ).toStrictEqual({
      type: "list",
      status: "updated",
      diff: [
        {
          value: "mbappe",
          prevIndex: 0,
          newIndex: 0,
          indexDiff: 0,
          status: "equal",
        },
        {
          value: "messi",
          prevIndex: 1,
          newIndex: 1,
          indexDiff: 0,
          status: "equal",
        },
        {
          value: "messi",
          prevIndex: 2,
          newIndex: 2,
          indexDiff: 0,
          status: "equal",
        },
        {
          value: "mbappe",
          prevIndex: 3,
          newIndex: null,
          indexDiff: null,
          status: "deleted",
        },
      ],
    });
    expect(
      getListDiff(
        [
          false,
          true,
          true,
          undefined,
          "hello",
          { name: "joe", age: 88 },
          false,
          13,
        ],
        [
          false,
          false,
          true,
          undefined,
          "hello",
          { name: "joe", age: 88 },
          false,
          { name: "joe", age: 88 },
        ]
      )
    ).toStrictEqual({
      type: "list",
      status: "updated",
      diff: [
        {
          value: false,
          prevIndex: 0,
          newIndex: 0,
          indexDiff: 0,
          status: "equal",
        },
        {
          value: false,
          prevIndex: 6,
          newIndex: 1,
          indexDiff: -5,
          status: "moved",
        },
        {
          value: true,
          prevIndex: 2,
          newIndex: null,
          indexDiff: null,
          status: "deleted",
        },
        {
          value: true,
          prevIndex: 1,
          newIndex: 2,
          indexDiff: 1,
          status: "moved",
        },
        {
          value: undefined,
          prevIndex: 3,
          newIndex: 3,
          indexDiff: 0,
          status: "equal",
        },
        {
          value: "hello",
          prevIndex: 4,
          newIndex: 4,
          indexDiff: 0,
          status: "equal",
        },
        {
          value: { name: "joe", age: 88 },
          prevIndex: 5,
          newIndex: 5,
          indexDiff: 0,
          status: "equal",
        },
        {
          value: 13,
          prevIndex: 7,
          newIndex: null,
          indexDiff: null,
          status: "deleted",
        },
        {
          value: false,
          prevIndex: null,
          newIndex: 6,
          indexDiff: null,
          status: "added",
        },
        {
          value: { name: "joe", age: 88 },
          prevIndex: null,
          newIndex: 7,
          indexDiff: null,
          status: "added",
        },
      ],
    });
  });
  it("showOnly added and deleted values", () => {
    expect(
      getListDiff(
        [
          false,
          true,
          true,
          undefined,
          "hello",
          { name: "joe", age: 88 },
          false,
          13,
        ],
        [
          false,
          false,
          true,
          undefined,
          "hello",
          { name: "joe", age: 88 },
          false,
          { name: "joe", age: 88 },
        ],
        { showOnly: ["added", "deleted"] }
      )
    ).toStrictEqual({
      type: "list",
      status: "updated",
      diff: [
        {
          value: true,
          prevIndex: 2,
          newIndex: null,
          indexDiff: null,
          status: "deleted",
        },
        {
          value: 13,
          prevIndex: 7,
          newIndex: null,
          indexDiff: null,
          status: "deleted",
        },
        {
          value: false,
          prevIndex: null,
          newIndex: 6,
          indexDiff: null,
          status: "added",
        },
        {
          value: { name: "joe", age: 88 },
          prevIndex: null,
          newIndex: 7,
          indexDiff: null,
          status: "added",
        },
      ],
    });
  });
  it("returns an empty diff if no property match the required statuses output", () => {
    expect(getListDiff(null, null)).toStrictEqual({
      type: "list",
      status: "equal",
      diff: [],
    });
    expect(
      getListDiff(["mbappe", "mendes", "verratti", "ruiz"], null, {
        showOnly: ["moved", "updated"],
      })
    ).toStrictEqual({
      type: "list",
      status: "deleted",
      diff: [],
    });
  });
  it("returns all values if their status match the required statuses", () => {
    expect(
      getListDiff(null, ["mbappe", "mendes", "verratti", "ruiz"], {
        showOnly: ["added"],
      })
    ).toStrictEqual({
      type: "list",
      status: "added",
      diff: [
        {
          value: "mbappe",
          prevIndex: null,
          newIndex: 0,
          indexDiff: null,
          status: "added",
        },
        {
          value: "mendes",
          prevIndex: null,
          newIndex: 1,
          indexDiff: null,
          status: "added",
        },
        {
          value: "verratti",
          prevIndex: null,
          newIndex: 2,
          indexDiff: null,
          status: "added",
        },
        {
          value: "ruiz",
          prevIndex: null,
          newIndex: 3,
          indexDiff: null,
          status: "added",
        },
      ],
    });
  });
  it("consider object updated if a reference property is given and this property hasn't changed", () => {
    expect(
      getListDiff(
        [
          "hello",
          { id: 37, isCool: true, hobbies: ["golf", "ski"] },
          { id: 38, isCool: false, hobbies: ["football"] },
          undefined,
          { id: 8, age: 77 },
          { id: 55, character: { strength: 66 } },
        ],
        [
          { id: 8, age: 77 },
          { id: 37, isCool: false, hobbies: ["golf", "ski"] },
          { id: 38, isCool: false, hobbies: ["football"] },
          undefined,
          { id: 99, character: { strength: 69 } },
        ],
        {
          referenceProperty: "id",
        }
      )
    ).toStrictEqual({
      type: "list",
      status: "updated",
      diff: [
        {
          value: "hello",
          prevIndex: 0,
          newIndex: null,
          indexDiff: null,
          status: "deleted",
        },
        {
          value: { id: 8, age: 77 },
          prevIndex: 4,
          newIndex: 0,
          indexDiff: -4,
          status: "moved",
        },
        {
          value: { id: 37, isCool: false, hobbies: ["golf", "ski"] },
          prevIndex: 1,
          newIndex: 1,
          indexDiff: 0,
          status: "updated",
        },
        {
          value: { id: 38, isCool: false, hobbies: ["football"] },
          prevIndex: 2,
          newIndex: 2,
          indexDiff: 0,
          status: "equal",
        },
        {
          value: undefined,
          prevIndex: 3,
          newIndex: 3,
          indexDiff: 0,
          status: "equal",
        },
        {
          value: { id: 55, character: { strength: 66 } },
          prevIndex: 5,
          newIndex: null,
          indexDiff: null,
          status: "deleted",
        },
        {
          value: { id: 99, character: { strength: 69 } },
          prevIndex: null,
          newIndex: 4,
          indexDiff: null,
          status: "added",
        },
      ],
    });
  });
  it("consider moved values as updated if the considerMoveAsUpdate option is true", () => {
    expect(
      getListDiff(["mbappe", "messi"], ["mbappe", "mbappe", "messi"], {
        considerMoveAsUpdate: true,
      })
    ).toStrictEqual({
      type: "list",
      status: "updated",
      diff: [
        {
          value: "mbappe",
          prevIndex: 0,
          newIndex: 0,
          indexDiff: 0,
          status: "equal",
        },
        {
          value: "mbappe",
          prevIndex: null,
          newIndex: 1,
          indexDiff: null,
          status: "added",
        },
        {
          value: "messi",
          prevIndex: 1,
          newIndex: 2,
          indexDiff: 1,
          status: "updated",
        },
      ],
    });
    expect(
      getListDiff(
        [
          "hello",
          { id: 37, isCool: true, hobbies: ["golf", "ski"] },
          { id: 38, isCool: false, hobbies: ["football"] },
          undefined,
          { id: 8, age: 77 },
          { id: 55, character: { strength: 66 } },
        ],
        [
          { id: 8, age: 77 },
          { id: 37, isCool: false, hobbies: ["golf", "ski"] },
          { id: 38, isCool: false, hobbies: ["football"] },
          undefined,
          { id: 99, character: { strength: 69 } },
        ],
        {
          referenceProperty: "id",
          considerMoveAsUpdate: true,
        }
      )
    ).toStrictEqual({
      type: "list",
      status: "updated",
      diff: [
        {
          value: "hello",
          prevIndex: 0,
          newIndex: null,
          indexDiff: null,
          status: "deleted",
        },
        {
          value: { id: 8, age: 77 },
          prevIndex: 4,
          newIndex: 0,
          indexDiff: -4,
          status: "updated",
        },
        {
          value: { id: 37, isCool: false, hobbies: ["golf", "ski"] },
          prevIndex: 1,
          newIndex: 1,
          indexDiff: 0,
          status: "updated",
        },
        {
          value: { id: 38, isCool: false, hobbies: ["football"] },
          prevIndex: 2,
          newIndex: 2,
          indexDiff: 0,
          status: "equal",
        },
        {
          value: undefined,
          prevIndex: 3,
          newIndex: 3,
          indexDiff: 0,
          status: "equal",
        },
        {
          value: { id: 55, character: { strength: 66 } },
          prevIndex: 5,
          newIndex: null,
          indexDiff: null,
          status: "deleted",
        },
        {
          value: { id: 99, character: { strength: 69 } },
          prevIndex: null,
          newIndex: 4,
          indexDiff: null,
          status: "added",
        },
      ],
    });
  });
});