import { DependencyType } from "@/dependencies/dependency-type";
import { PlatformType } from "@/platforms/platform-type";
import { parseVersionRange } from "@/utils/versioning/version-range";
import { createDependency, formatDependency, isDependency, parseDependency } from "@/dependencies/dependency";

describe("isDependency", () => {
    test("returns true for Dependency-like objects", () => {
        expect(isDependency(parseDependency("id"))).toBe(true);
    });

    test("returns false for non-Dependency-like objects", () => {
        expect(isDependency({})).toBe(false);
    });

    test("returns false for null and undefined", () => {
        expect(isDependency(null)).toBe(false);
        expect(isDependency(undefined)).toBe(false);
    });
});

describe("parseDependency", () => {
    test("parses a fully-formed dependency string", () => {
        const dependency = parseDependency("id@1.0.0-2.0.0-alpha.1(optional){modrinth:modrinth-slug}{curseforge:curseforge-slug}#(ignore:curseforge,github)");

        expect(dependency).toBeDefined();
        expect(dependency.id).toBe("id");
        expect(dependency.versions).toEqual(["1.0.0-2.0.0-alpha.1"]);
        expect(dependency.type).toBe(DependencyType.OPTIONAL);
        expect(dependency.getProjectId(PlatformType.MODRINTH)).toBe("modrinth-slug");
        expect(dependency.getProjectId(PlatformType.CURSEFORGE)).toBe("curseforge-slug");
        expect(dependency.getProjectId(PlatformType.GITHUB)).toBe("id");
        expect(dependency.isIgnored(PlatformType.MODRINTH)).toBe(false);
        expect(dependency.isIgnored(PlatformType.CURSEFORGE)).toBe(true);
        expect(dependency.isIgnored(PlatformType.GITHUB)).toBe(true);
    });

    test("parses a dependency string with omitted 'ignore' part", () => {
        const dependency = parseDependency("id@1.0.0-2.0.0-alpha.1(optional){modrinth:modrinth-slug}{curseforge:curseforge-slug}");

        expect(dependency).toBeDefined();
        expect(dependency.id).toBe("id");
        expect(dependency.versions).toEqual(["1.0.0-2.0.0-alpha.1"]);
        expect(dependency.type).toBe(DependencyType.OPTIONAL);
        expect(dependency.getProjectId(PlatformType.MODRINTH)).toBe("modrinth-slug");
        expect(dependency.getProjectId(PlatformType.CURSEFORGE)).toBe("curseforge-slug");
        expect(dependency.getProjectId(PlatformType.GITHUB)).toBe("id");
        expect(dependency.isIgnored(PlatformType.MODRINTH)).toBe(false);
        expect(dependency.isIgnored(PlatformType.CURSEFORGE)).toBe(false);
        expect(dependency.isIgnored(PlatformType.GITHUB)).toBe(false);
    });

    test("parses a dependency string with omitted 'aliases' part", () => {
        const dependency = parseDependency("id@1.0.0-2.0.0-alpha.1(optional)#(ignore:curseforge,github)");

        expect(dependency).toBeDefined();
        expect(dependency.id).toBe("id");
        expect(dependency.versions).toEqual(["1.0.0-2.0.0-alpha.1"]);
        expect(dependency.type).toBe(DependencyType.OPTIONAL);
        expect(dependency.getProjectId(PlatformType.MODRINTH)).toBe("id");
        expect(dependency.getProjectId(PlatformType.CURSEFORGE)).toBe("id");
        expect(dependency.getProjectId(PlatformType.GITHUB)).toBe("id");
        expect(dependency.isIgnored(PlatformType.MODRINTH)).toBe(false);
        expect(dependency.isIgnored(PlatformType.CURSEFORGE)).toBe(true);
        expect(dependency.isIgnored(PlatformType.GITHUB)).toBe(true);
    });

    test("parses a dependency string with omitted 'type' part", () => {
        const dependency = parseDependency("id@1.0.0-2.0.0-alpha.1{modrinth:modrinth-slug}{curseforge:curseforge-slug}#(ignore:curseforge,github)");

        expect(dependency).toBeDefined();
        expect(dependency.id).toBe("id");
        expect(dependency.versions).toEqual(["1.0.0-2.0.0-alpha.1"]);
        expect(dependency.type).toBe(DependencyType.REQUIRED);
        expect(dependency.getProjectId(PlatformType.MODRINTH)).toBe("modrinth-slug");
        expect(dependency.getProjectId(PlatformType.CURSEFORGE)).toBe("curseforge-slug");
        expect(dependency.getProjectId(PlatformType.GITHUB)).toBe("id");
        expect(dependency.isIgnored(PlatformType.MODRINTH)).toBe(false);
        expect(dependency.isIgnored(PlatformType.CURSEFORGE)).toBe(true);
        expect(dependency.isIgnored(PlatformType.GITHUB)).toBe(true);
    });

    test("parses a dependency string with omitted 'version' part", () => {
        const dependency = parseDependency("id(optional){modrinth:modrinth-slug}{curseforge:curseforge-slug}#(ignore:curseforge,github)");

        expect(dependency).toBeDefined();
        expect(dependency.id).toBe("id");
        expect(dependency.versions).toEqual(["*"]);
        expect(dependency.type).toBe(DependencyType.OPTIONAL);
        expect(dependency.getProjectId(PlatformType.MODRINTH)).toBe("modrinth-slug");
        expect(dependency.getProjectId(PlatformType.CURSEFORGE)).toBe("curseforge-slug");
        expect(dependency.getProjectId(PlatformType.GITHUB)).toBe("id");
        expect(dependency.isIgnored(PlatformType.MODRINTH)).toBe(false);
        expect(dependency.isIgnored(PlatformType.CURSEFORGE)).toBe(true);
        expect(dependency.isIgnored(PlatformType.GITHUB)).toBe(true);
    });

    test("parses a dependency string that only consists of id", () => {
        const dependency = parseDependency("id");

        expect(dependency).toBeDefined();
        expect(dependency.id).toBe("id");
        expect(dependency.versions).toEqual(["*"]);
        expect(dependency.type).toBe(DependencyType.REQUIRED);
        expect(dependency.getProjectId(PlatformType.MODRINTH)).toBe("id");
        expect(dependency.getProjectId(PlatformType.CURSEFORGE)).toBe("id");
        expect(dependency.getProjectId(PlatformType.GITHUB)).toBe("id");
        expect(dependency.isIgnored(PlatformType.MODRINTH)).toBe(false);
        expect(dependency.isIgnored(PlatformType.CURSEFORGE)).toBe(false);
        expect(dependency.isIgnored(PlatformType.GITHUB)).toBe(false);
    });

    test("parses a dependency string that consists of id and version", () => {
        const dependency = parseDependency("id@1.0.0");

        expect(dependency).toBeDefined();
        expect(dependency.id).toBe("id");
        expect(dependency.versions).toEqual(["1.0.0"]);
        expect(dependency.type).toBe(DependencyType.REQUIRED);
        expect(dependency.getProjectId(PlatformType.MODRINTH)).toBe("id");
        expect(dependency.getProjectId(PlatformType.CURSEFORGE)).toBe("id");
        expect(dependency.getProjectId(PlatformType.GITHUB)).toBe("id");
        expect(dependency.isIgnored(PlatformType.MODRINTH)).toBe(false);
        expect(dependency.isIgnored(PlatformType.CURSEFORGE)).toBe(false);
        expect(dependency.isIgnored(PlatformType.GITHUB)).toBe(false);
    });

    test("parses a dependency string that consists of id, version, and type", () => {
        const dependency = parseDependency("id@1.0.0(optional)");

        expect(dependency).toBeDefined();
        expect(dependency.id).toBe("id");
        expect(dependency.versions).toEqual(["1.0.0"]);
        expect(dependency.type).toBe(DependencyType.OPTIONAL);
        expect(dependency.getProjectId(PlatformType.MODRINTH)).toBe("id");
        expect(dependency.getProjectId(PlatformType.CURSEFORGE)).toBe("id");
        expect(dependency.getProjectId(PlatformType.GITHUB)).toBe("id");
        expect(dependency.isIgnored(PlatformType.MODRINTH)).toBe(false);
        expect(dependency.isIgnored(PlatformType.CURSEFORGE)).toBe(false);
        expect(dependency.isIgnored(PlatformType.GITHUB)).toBe(false);
    });

    test("parses a dependency string that consists of id and type", () => {
        const dependency = parseDependency("id(optional)");

        expect(dependency).toBeDefined();
        expect(dependency.id).toBe("id");
        expect(dependency.versions).toEqual(["*"]);
        expect(dependency.type).toBe(DependencyType.OPTIONAL);
        expect(dependency.getProjectId(PlatformType.MODRINTH)).toBe("id");
        expect(dependency.getProjectId(PlatformType.CURSEFORGE)).toBe("id");
        expect(dependency.getProjectId(PlatformType.GITHUB)).toBe("id");
        expect(dependency.isIgnored(PlatformType.MODRINTH)).toBe(false);
        expect(dependency.isIgnored(PlatformType.CURSEFORGE)).toBe(false);
        expect(dependency.isIgnored(PlatformType.GITHUB)).toBe(false);
    });

    test("dependency type parsing is case-insensitive", () => {
        for (const type of DependencyType.values()) {
            expect(parseDependency(`id(${type.toLowerCase()})`)?.type).toBe(type);
            expect(parseDependency(`id(${type.toUpperCase()})`)?.type).toBe(type);
            expect(parseDependency(`id(${DependencyType.friendlyNameOf(type)})`)?.type).toBe(type);
        }
    });

    test("platform type parsing is case-insensitive", () => {
        for (const platform of PlatformType.values()) {
            expect(parseDependency(`id{${platform.toLowerCase()}:another-id}`)?.getProjectId(platform)).toBe("another-id");
            expect(parseDependency(`id{${platform.toUpperCase()}:another-id}`)?.getProjectId(platform)).toBe("another-id");
            expect(parseDependency(`id{${PlatformType.friendlyNameOf(platform)}:another-id}`)?.getProjectId(platform)).toBe("another-id");

            expect(parseDependency(`id#(ignore:${platform.toLowerCase()})`)?.isIgnored(platform)).toBe(true);
            expect(parseDependency(`id#(ignore:${platform.toUpperCase()})`)?.isIgnored(platform)).toBe(true);
            expect(parseDependency(`id#(ignore:${PlatformType.friendlyNameOf(platform)})`)?.isIgnored(platform)).toBe(true);
        }
    });

    test("returns undefined if the input string is null, undefined, or empty", () => {
        expect(parseDependency(undefined)).toBeUndefined();
        expect(parseDependency(null)).toBeUndefined();
        expect(parseDependency("")).toBeUndefined();
    });
});

describe("cerateDependency", () => {
    test("parses dependency strings", () => {
        expect(createDependency("id")).toBeDefined();
        expect(createDependency("id@1.0.0")).toBeDefined();
        expect(createDependency("id@1.0.0(optional)")).toBeDefined();
        expect(createDependency("id(optional)")).toBeDefined();
        expect(createDependency("id@1.0.0-2.0.0-alpha.1(optional){modrinth:modrinth-slug}{curseforge:curseforge-slug}#(ignore:curseforge,github)")).toBeDefined();
        expect(createDependency("id@1.0.0-2.0.0-alpha.1(optional){modrinth:modrinth-slug}{curseforge:curseforge-slug}")).toBeDefined();
        expect(createDependency("id@1.0.0-2.0.0-alpha.1(optional)#(ignore:curseforge,github)")).toBeDefined();
        expect(createDependency("id@1.0.0-2.0.0-alpha.1{modrinth:modrinth-slug}{curseforge:curseforge-slug}#(ignore:curseforge,github)")).toBeDefined();
        expect(createDependency("id(optional){modrinth:modrinth-slug}{curseforge:curseforge-slug}#(ignore:curseforge,github)")).toBeDefined();
    });

    test("converts DependencyInfo-like objects to Dependency objects", () => {
        expect(createDependency({ id: "id" })?.id).toBe("id");
        expect(createDependency({ id: "id", type: DependencyType.EMBEDDED })?.type).toBe(DependencyType.EMBEDDED);
        expect(createDependency({ id: "id", versions: "1.0.0" })?.versions).toEqual(["1.0.0"]);
        expect(createDependency({ id: "id", versions: ["1.0.0", "2.0.0"] })?.versions).toEqual(["1.0.0", "2.0.0"]);
        expect(createDependency({ id: "id", versions: parseVersionRange("1.0.0") })?.versions).toEqual(["1.0.0"]);
        expect(createDependency({ id: "id", ignore: true })?.isIgnored()).toBe(true);
        expect(createDependency({ id: "id", ignoredPlatforms: [PlatformType.CURSEFORGE] })?.isIgnored(PlatformType.CURSEFORGE)).toBe(true);
        expect(createDependency({ id: "id", aliases: [[PlatformType.CURSEFORGE, "curseforge-slug"]] })?.getProjectId(PlatformType.CURSEFORGE)).toBe("curseforge-slug");
    });

    test("returns Dependency-like objects as is", () => {
        const dependency = parseDependency("id");

        expect(createDependency(dependency)).toBe(dependency);
    });

    test("returns undefined if the input is null, undefined, or an empty string", () => {
        expect(createDependency(null)).toBeUndefined();
        expect(createDependency(undefined)).toBeUndefined();
        expect(createDependency("")).toBeUndefined();
    });
});

describe("formatDependency", () => {
    test("formats fine-tuned dependencies as fully qualified dependency strings", () => {
        const dependencies = [
            "id@1.0.0-2.0.0-alpha.1(optional){curseforge:curseforge-slug}{modrinth:modrinth-slug}#(ignore:curseforge,github)",
            "id@1.0.0(embedded){modrinth:modrinth-slug}#(ignore:curseforge)",
            "id@1.0.0 || 2.0.0-alpha.1(conflicting){curseforge:curseforge-slug}#(ignore:github)",
        ];

        for (const dependency of dependencies) {
            expect(formatDependency(parseDependency(dependency))).toBe(dependency);
        }
    });

    test("formats dependencies and omits unused 'ignore' section", () => {
        const dependencies = [
            "id@1.0.0-2.0.0-alpha.1(optional){curseforge:curseforge-slug}{modrinth:modrinth-slug}",
            "id@1.0.0(embedded){modrinth:modrinth-slug}",
            "id@1.0.0 || 2.0.0-alpha.1(conflicting){curseforge:curseforge-slug}",
        ];

        for (const dependency of dependencies) {
            expect(formatDependency(parseDependency(dependency))).toBe(dependency);
        }
    });

    test("formats dependencies and omits unused 'aliases' section", () => {
        const dependencies = [
            "id@1.0.0-2.0.0-alpha.1(optional)#(ignore:curseforge,github)",
            "id@1.0.0(embedded)#(ignore:curseforge)",
            "id@1.0.0 || 2.0.0-alpha.1(conflicting)#(ignore:github)",
        ];

        for (const dependency of dependencies) {
            expect(formatDependency(parseDependency(dependency))).toBe(dependency);
        }
    });

    test("formats dependencies and omits unused 'version' section", () => {
        const dependencies = [
            "id(optional){curseforge:curseforge-slug}{modrinth:modrinth-slug}#(ignore:curseforge,github)",
            "id(embedded){modrinth:modrinth-slug}#(ignore:curseforge)",
            "id(conflicting){curseforge:curseforge-slug}#(ignore:github)",
        ];

        for (const dependency of dependencies) {
            expect(formatDependency(parseDependency(dependency))).toBe(dependency);
        }
    });

    test("returns an empty string for invalid dependencies", () => {
        expect(formatDependency(null)).toBe("");
        expect(formatDependency(undefined)).toBe("");
    });
});