import {
  AddColumnsRowsCommand,
  CarouselData,
  DeleteSheetCommand,
  DuplicateSheetCommand,
  MoveRangeCommand,
  RemoveColumnsRowsCommand,
  ResizeColumnsRowsCommand,
} from "../../../src";
import { transform } from "../../../src/collaborative/ot/ot";
import { toZone } from "../../../src/helpers/zones";
import {
  OT_TESTS_SINGLE_CELL_COMMANDS,
  TEST_COMMANDS,
  TEST_COMMANDS_RANGE_DEPENDENT,
  TEST_COMMANDS_TARGET_DEPENDENT,
  TEST_COMMANDS_ZONE_DEPENDENT,
} from "../../test_helpers/constants";
import { toRangeData, toRangesData } from "../../test_helpers/helpers";
import { getFormulaStringCommands } from "./ot_helper";

describe("OT with DELETE_SHEET", () => {
  const deletedSheetId = "deletedSheet";
  const deletedSheetName = "DeletedSheetName";
  const sheetId = "stillPresent";
  const deleteSheet: DeleteSheetCommand = {
    type: "DELETE_SHEET",
    sheetId: deletedSheetId,
    sheetName: deletedSheetName,
  };

  const addColumns: Omit<AddColumnsRowsCommand, "sheetId"> = {
    ...TEST_COMMANDS.ADD_COLUMNS_ROWS,
    dimension: "COL",
  };
  const addRows: Omit<AddColumnsRowsCommand, "sheetId"> = {
    ...TEST_COMMANDS.ADD_COLUMNS_ROWS,
    dimension: "ROW",
  };
  const removeColumn: RemoveColumnsRowsCommand = {
    ...TEST_COMMANDS.REMOVE_COLUMNS_ROWS,
    dimension: "COL",
  };
  const removeRows: RemoveColumnsRowsCommand = {
    ...TEST_COMMANDS.REMOVE_COLUMNS_ROWS,
    dimension: "ROW",
  };

  const resizeColumns: ResizeColumnsRowsCommand = {
    ...TEST_COMMANDS.RESIZE_COLUMNS_ROWS,
    dimension: "COL",
  };
  const resizeRows: ResizeColumnsRowsCommand = {
    ...TEST_COMMANDS.RESIZE_COLUMNS_ROWS,
    dimension: "ROW",
  };

  describe.each([
    ...OT_TESTS_SINGLE_CELL_COMMANDS,
    ...TEST_COMMANDS_TARGET_DEPENDENT,
    ...TEST_COMMANDS_ZONE_DEPENDENT,
    addColumns,
    addRows,
    removeColumn,
    removeRows,
    TEST_COMMANDS.ADD_MERGE,
    TEST_COMMANDS.REMOVE_MERGE,
    TEST_COMMANDS.MOVE_SHEET,
    TEST_COMMANDS.HIDE_SHEET,
    TEST_COMMANDS.SHOW_SHEET,
    TEST_COMMANDS.RENAME_SHEET,
    TEST_COMMANDS.COLOR_SHEET,
    TEST_COMMANDS.CREATE_FIGURE,
    TEST_COMMANDS.CREATE_CHART,
    resizeColumns,
    resizeRows,
    TEST_COMMANDS.REMOVE_CONDITIONAL_FORMAT,
    TEST_COMMANDS.DELETE_SHEET,
    TEST_COMMANDS.MOVE_RANGES,
    TEST_COMMANDS.GROUP_HEADERS,
    TEST_COMMANDS.UNGROUP_HEADERS,
    TEST_COMMANDS.FOLD_HEADER_GROUP,
    TEST_COMMANDS.UNFOLD_HEADER_GROUP,
    TEST_COMMANDS.REMOVE_DATA_VALIDATION_RULE,
  ])("Delete sheet", (cmd) => {
    test("Delete the sheet on which the command is triggered", () => {
      const result = transform({ ...cmd, sheetId: deletedSheetId }, deleteSheet);
      expect(result).toBeUndefined();
    });

    test("Delete a sheet other than the one on which the command is triggered", () => {
      const command = { ...cmd, sheetId };
      const result = transform(command, deleteSheet);
      expect(result).toEqual(command);
    });
  });

  describe("Delete sheet with duplicate sheet", () => {
    const cmd: Omit<DuplicateSheetCommand, "sheetId"> = {
      type: "DUPLICATE_SHEET",
      sheetIdTo: "sheetIdTo",
      sheetNameTo: "Copy of Sheet1",
    };

    test("Delete the sheet on which the command is triggered", () => {
      const result = transform({ ...cmd, sheetId: deletedSheetId }, deleteSheet);
      expect(result).toBeUndefined();
    });

    test("Delete the sheet on which the command is triggered", () => {
      const command = { ...cmd, sheetId };
      const result = transform(command, deleteSheet);
      expect(result).toEqual(command);
    });
  });

  describe("Delete sheet with move ranges", () => {
    const cmd: Omit<MoveRangeCommand, "targetSheetId"> = {
      type: "MOVE_RANGES",
      sheetId,
      col: 0,
      row: 0,
      target: [toZone("A1")],
      sheetName: "",
    };

    test("Delete the sheet on which the command is triggered", () => {
      const result = transform({ ...cmd, targetSheetId: deletedSheetId }, deleteSheet);
      expect(result).toBeUndefined();
    });

    test("Delete another sheet", () => {
      const command = { ...cmd, sheetId, targetSheetId: sheetId };
      const result = transform(command, deleteSheet);
      expect(result).toEqual(command);
    });

    test("Delete the sheet source and target sheet", () => {
      const result = transform(
        { ...cmd, sheetId: deletedSheetId, targetSheetId: deletedSheetId },
        deleteSheet
      );
      expect(result).toBeUndefined();
    });
  });

  describe("Delete sheet with range dependant command", () => {
    test.each(TEST_COMMANDS_RANGE_DEPENDENT.filter((cmd) => "sheetId" in cmd))(
      "Delete the sheet of the command",
      (cmd) => {
        const cmdToTransform = {
          ...cmd,
          sheetId: deletedSheetId,
          ranges: toRangesData(sheetId, "A1:B1"),
        };
        const result = transform(cmdToTransform, deleteSheet);
        expect(result).toBeUndefined();
      }
    );

    test.each(TEST_COMMANDS_RANGE_DEPENDENT)("Delete the sheet of the ranges", (cmd) => {
      const cmdToTransform = {
        ...cmd,
        sheetId: sheetId,
        ranges: toRangesData(deletedSheetId, "A1:B1"),
      };
      const result = transform(cmdToTransform, deleteSheet);
      expect(result).toBeUndefined();
    });

    test.each(TEST_COMMANDS_RANGE_DEPENDENT)("Delete the sheet of some of the ranges", (cmd) => {
      const cmdToTransform = {
        ...cmd,
        sheetId: sheetId,
        ranges: [...toRangesData(deletedSheetId, "A1:B1"), ...toRangesData(sheetId, "A1:B1")],
      };
      const result = transform(cmdToTransform, deleteSheet);
      expect(result).toEqual({ ...cmdToTransform, ranges: toRangesData(sheetId, "A1:B1") });
    });
  });

  describe("Delete sheed with string formula dependant command", () => {
    const cmdsOnOthersheet = getFormulaStringCommands(
      sheetId,
      "=" + deletedSheetName + "!A1",
      "=" + deletedSheetName + "!A1"
    );

    test.each(cmdsOnOthersheet)("%s on other sheet", (cmd, expected) => {
      const result = transform(cmd, deleteSheet);
      expect(result).toEqual(expected);
    });

    const cmdsOnDeletedSheet = getFormulaStringCommands(
      deletedSheetId,
      "=" + deletedSheetName + "!A1",
      "=" + deletedSheetName + "!A1"
    ).filter(([cmd]) => cmd.type !== "ADD_PIVOT"); // pivot are not removed when a measure references a deleted sheet

    test.each(cmdsOnDeletedSheet)("%s on deleted sheet", (cmd, expected) => {
      const result = transform(cmd, deleteSheet);
      expect(result).toEqual(undefined);
    });
  });
});

describe("OT with delete sheet and carousel commands", () => {
  const columnWeights = [500, 250, 250];
  const carousel: CarouselData = {
    items: [
      { type: "carouselDataView", rangeData: toRangeData("sh1", "C3:E5"), columnWeights },
      { type: "carouselDataView", rangeData: toRangeData("sh2", "C3:E5"), columnWeights },
    ],
  };

  const deleteSheet: DeleteSheetCommand = {
    type: "DELETE_SHEET",
    sheetId: "sh2",
    sheetName: "Sheet2",
  };

  test.each([TEST_COMMANDS.CREATE_CAROUSEL, TEST_COMMANDS.UPDATE_CAROUSEL])(
    "command is removed when the carousel is on the deleted sheet",
    (cmd) => {
      const toTransform = { ...cmd, sheetId: "sh2", definition: carousel };
      const result = transform(toTransform, deleteSheet) as any;
      expect(result).toBeUndefined();
    }
  );

  test.each([TEST_COMMANDS.CREATE_CAROUSEL, TEST_COMMANDS.UPDATE_CAROUSEL])(
    "ranges are deleted on the same sheet as deleteSheet",
    (cmd) => {
      const toTransform = { ...cmd, sheetId: "sh1", definition: carousel };
      const result = transform(toTransform, deleteSheet) as any;
      expect(result?.definition).toMatchObject({
        items: [
          { type: "carouselDataView", rangeData: toRangeData("sh1", "C3:E5"), columnWeights },
          { type: "carouselDataView", rangeData: undefined, columnWeights: undefined },
        ],
      });
    }
  );
});
