import {
  normalizePath,
  splitPath,
  getBaseName,
  isDescendantPath,
  canMoveFolder,
  reparentFolderPath,
  joinPath,
  getParentPath,
  reparentEntityPath,
} from "shared/lib/helpers/virtualFilesystem";

describe("normalizePath", () => {
  test("should replace backslashes with forward slashes", () => {
    expect(normalizePath("folder\\subfolder\\file.txt")).toBe(
      "folder/subfolder/file.txt",
    );
  });

  test("should collapse duplicate slashes", () => {
    expect(normalizePath("folder//sub///file")).toBe("folder/sub/file");
  });

  test("should remove leading slashes", () => {
    expect(normalizePath("/folder/sub")).toBe("folder/sub");
    expect(normalizePath("///folder/sub")).toBe("folder/sub");
  });

  test("should remove trailing slash", () => {
    expect(normalizePath("folder/sub/")).toBe("folder/sub");
  });

  test("should handle mixed separators and duplicates", () => {
    expect(normalizePath("\\folder\\\\sub//file\\")).toBe("folder/sub/file");
  });

  test("should return empty string for root or empty input", () => {
    expect(normalizePath("")).toBe("");
    expect(normalizePath("/")).toBe("");
    expect(normalizePath("////")).toBe("");
  });
});

describe("splitPath", () => {
  test("should split normalized path into parts", () => {
    expect(splitPath("folder/sub/file")).toEqual(["folder", "sub", "file"]);
  });

  test("should normalize before splitting", () => {
    expect(splitPath("\\folder//sub\\file")).toEqual(["folder", "sub", "file"]);
  });

  test("should return empty array for empty path", () => {
    expect(splitPath("")).toEqual([]);
    expect(splitPath("/")).toEqual([]);
  });
});

describe("joinPath", () => {
  test("should join two simple segments", () => {
    expect(joinPath("a", "b")).toBe("a/b");
  });

  test("should join multiple segments", () => {
    expect(joinPath("a", "b", "c")).toBe("a/b/c");
  });

  test("should ignore empty segments", () => {
    expect(joinPath("a", "", "b")).toBe("a/b");
    expect(joinPath("", "a", "b")).toBe("a/b");
    expect(joinPath("a", "b", "")).toBe("a/b");
  });

  test("should return empty string if all segments are empty", () => {
    expect(joinPath("", "", "")).toBe("");
  });

  test("should normalize duplicate slashes", () => {
    expect(joinPath("a//", "///b")).toBe("a/b");
  });

  test("should normalize backslashes", () => {
    expect(joinPath("a\\", "b\\c")).toBe("a/b/c");
  });

  test("should remove leading slashes", () => {
    expect(joinPath("/a", "b")).toBe("a/b");
  });

  test("should remove trailing slash in result", () => {
    expect(joinPath("a", "b/")).toBe("a/b");
  });

  test("should handle root-like joins correctly", () => {
    expect(joinPath("", "file.txt")).toBe("file.txt");
    expect(joinPath("", "a", "file.txt")).toBe("a/file.txt");
  });

  test("should handle deeply nested segments", () => {
    expect(joinPath("a", "b/c", "d")).toBe("a/b/c/d");
  });

  test("should collapse complex mixed separators", () => {
    expect(joinPath("\\a//", "/b\\", "c//d\\")).toBe("a/b/c/d");
  });

  test("should work with single segment", () => {
    expect(joinPath("a")).toBe("a");
  });

  test("should work with no segments", () => {
    expect(joinPath()).toBe("");
  });
});

describe("getBaseName", () => {
  test("should return last segment of path", () => {
    expect(getBaseName("folder/sub/file")).toBe("file");
  });

  test("should normalize before getting base name", () => {
    expect(getBaseName("\\folder//sub\\file")).toBe("file");
  });

  test("should return single segment if no slashes", () => {
    expect(getBaseName("file")).toBe("file");
  });

  test("should return empty string for empty path", () => {
    expect(getBaseName("")).toBe("");
    expect(getBaseName("/")).toBe("");
  });
});

describe("getParentPath", () => {
  test("should return parent of nested path", () => {
    expect(getParentPath("a/b/c")).toBe("a/b");
  });

  test("should return parent of two-level path", () => {
    expect(getParentPath("a/b")).toBe("a");
  });

  test("should return empty string for single segment", () => {
    expect(getParentPath("a")).toBe("");
  });

  test("should return empty string for empty path", () => {
    expect(getParentPath("")).toBe("");
  });

  test("should return empty string for root-like path", () => {
    expect(getParentPath("/")).toBe("");
    expect(getParentPath("////")).toBe("");
  });

  test("should normalize before computing parent", () => {
    expect(getParentPath("\\a//b\\c")).toBe("a/b");
  });

  test("should remove trailing slash before computing parent", () => {
    expect(getParentPath("a/b/c/")).toBe("a/b");
  });

  test("should handle deeply nested paths", () => {
    expect(getParentPath("a/b/c/d/e")).toBe("a/b/c/d");
  });

  test("should not treat similar prefixes specially", () => {
    expect(getParentPath("folderA/file.txt")).toBe("folderA");
  });

  test("should handle double slashes correctly", () => {
    expect(getParentPath("a//b///c")).toBe("a/b");
  });
});

describe("isDescendantPath", () => {
  test("should return true when child is direct descendant", () => {
    expect(isDescendantPath("folder", "folder/sub")).toBe(true);
  });

  test("should return true when child is deep descendant", () => {
    expect(isDescendantPath("folder", "folder/sub/file")).toBe(true);
  });

  test("should return true when parent and child are identical", () => {
    expect(isDescendantPath("folder/sub", "folder/sub")).toBe(true);
  });

  test("should return false when parent is empty", () => {
    expect(isDescendantPath("", "folder/sub")).toBe(false);
  });

  test("should return false when child does not start with parent", () => {
    expect(isDescendantPath("folder", "other/sub")).toBe(false);
  });

  test("should not treat similar prefixes as descendants", () => {
    expect(isDescendantPath("folderA", "folderAB/sub")).toBe(false);
  });

  test("should handle normalization before comparison", () => {
    expect(isDescendantPath("\\folder//sub", "folder/sub/file")).toBe(true);
  });

  test("should return false when child is shorter and not equal", () => {
    expect(isDescendantPath("folder/sub", "folder")).toBe(false);
  });
});

describe("canMoveFolder", () => {
  test("should allow moving folder to a different top-level folder", () => {
    expect(canMoveFolder("a", "b")).toBe(true);
  });

  test("should allow moving folder into sibling folder", () => {
    expect(canMoveFolder("a", "b/c")).toBe(true);
  });

  test("should allow moving folder into deeper unrelated folder", () => {
    expect(canMoveFolder("a", "x/y/z")).toBe(true);
  });

  test("should NOT allow moving folder into itself", () => {
    expect(canMoveFolder("a/b", "a/b")).toBe(false);
  });

  test("should NOT allow moving folder into its direct descendant", () => {
    expect(canMoveFolder("a", "a/b")).toBe(false);
  });

  test("should NOT allow moving folder into deep descendant", () => {
    expect(canMoveFolder("a", "a/b/c/d")).toBe(false);
  });

  test("should NOT treat similar prefixes as descendants", () => {
    expect(canMoveFolder("folderA", "folderAB/sub")).toBe(true);
  });

  test("should normalize paths before comparison", () => {
    expect(canMoveFolder("\\a//b", "a/b/c")).toBe(false);
  });

  test("should allow moving nested folder to root (empty)", () => {
    expect(canMoveFolder("a/b", "")).toBe(true);
  });

  test("should NOT allow moving root folder into descendant", () => {
    expect(canMoveFolder("", "a/b")).toBe(false);
  });
});

describe("reparentFolderPath", () => {
  test("should not allow moving a single file to new folder", () => {
    expect(reparentFolderPath("a/file.txt", "a/file.txt", "b")).toBeNull();
  });

  test("should return null for non-matching single item", () => {
    expect(reparentFolderPath("a/other.txt", "a/file.txt", "b")).toBeNull();
  });

  test("should move direct child of folder", () => {
    expect(reparentFolderPath("a/file.txt", "a", "b")).toBe("b/a/file.txt");
  });

  test("should move deep descendant of folder", () => {
    expect(reparentFolderPath("a/sub/file.txt", "a", "b")).toBe(
      "b/a/sub/file.txt",
    );
  });

  test("should move deep nested structure correctly", () => {
    expect(reparentFolderPath("a/sub/inner/file.txt", "a", "b")).toBe(
      "b/a/sub/inner/file.txt",
    );
  });

  test("should move folder contents to root", () => {
    expect(reparentFolderPath("a/file.txt", "a", "")).toBe("a/file.txt");
  });

  test("should normalize paths before reparenting", () => {
    expect(reparentFolderPath("\\a//sub\\file.txt", "a", "b")).toBe(
      "b/a/sub/file.txt",
    );
  });

  test("should return null when original is not inside dragged folder", () => {
    expect(reparentFolderPath("x/file.txt", "a", "b")).toBeNull();
  });

  test("should not treat similar prefixes as descendants", () => {
    expect(reparentFolderPath("folderAB/file.txt", "folderA", "b")).toBeNull();
  });

  test("should handle moving folder into deep path", () => {
    expect(reparentFolderPath("a/file.txt", "a", "x/y/z")).toBe(
      "x/y/z/a/file.txt",
    );
  });

  test("should collapse duplicate slashes in result", () => {
    expect(reparentFolderPath("a/file.txt", "a", "b//c")).toBe(
      "b/c/a/file.txt",
    );
  });

  test("should return null when original path is empty and not matching", () => {
    expect(reparentFolderPath("", "a", "b")).toBeNull();
  });

  test("should handle moving root folder explicitly", () => {
    expect(reparentFolderPath("", "", "x")).toBeNull();
  });
});

describe("reparentEntityPath", () => {
  test("should move named entity to new folder", () => {
    expect(reparentEntityPath("a/file.txt", "b")).toBe("b/file.txt");
  });

  test("should move named entity to deep folder", () => {
    expect(reparentEntityPath("a/file.txt", "x/y/z")).toBe("x/y/z/file.txt");
  });

  test("should move named entity to root (empty path)", () => {
    expect(reparentEntityPath("a/file.txt", "")).toBe("file.txt");
  });

  test("should normalize original path before extracting basename", () => {
    expect(reparentEntityPath("\\a//sub\\file.txt", "b")).toBe("b/file.txt");
  });

  test("should normalize destination path before joining", () => {
    expect(reparentEntityPath("a/file.txt", "\\x//y\\")).toBe("x/y/file.txt");
  });

  test("should handle unnamed entity (trailing slash) into folder", () => {
    expect(reparentEntityPath("a/", "b")).toBe("b/");
  });

  test("should handle unnamed entity with backslash", () => {
    expect(reparentEntityPath("a\\", "b")).toBe("b/");
  });

  test("should handle unnamed entity moved to deep folder", () => {
    expect(reparentEntityPath("a/", "x/y")).toBe("x/y/");
  });

  test("should handle unnamed entity moved to root", () => {
    expect(reparentEntityPath("a/", "")).toBe("");
  });

  test("should handle already-root unnamed entity", () => {
    expect(reparentEntityPath("", "a/b")).toBe("a/b/");
  });

  test("should return empty string when both original and destination are root-like", () => {
    expect(reparentEntityPath("", "")).toBe("");
  });

  test("should collapse duplicate slashes in result", () => {
    expect(reparentEntityPath("a/file.txt", "b//c")).toBe("b/c/file.txt");
  });
});