import { readFileSync } from "node:fs";
import { resolve } from "node:path";
import mockFs from "mock-fs";
import { createCombinedFetch, createFakeFetch } from "../../../utils/fetch-utils";
import { PlatformType } from "@/platforms/platform-type";
import { SecureString } from "@/utils/security/secure-string";
import { FileInfo } from "@/utils/io/file-info";
import { FormData } from "@/utils/net/form-data";
import { VersionType } from "@/utils/versioning/version-type";
import { JavaVersion } from "@/utils/java/java-version";
import { parseDependency } from "@/dependencies/dependency";
import { CURSEFORGE_ETERNAL_API_URL } from "@/platforms/curseforge/curseforge-eternal-api-client";
import { CURSEFORGE_UPLOAD_API_URL } from "@/platforms/curseforge/curseforge-upload-api-client";
import { CurseForgeVersionInitMetadata } from "@/platforms/curseforge/curseforge-version";
import { CurseForgeDependencyType } from "@/platforms/curseforge/curseforge-dependency-type";
import { CurseForgeUploader } from "@/platforms/curseforge/curseforge-uploader";

const DB = Object.freeze({
    versionTypes: Object.freeze(JSON.parse(
        readFileSync(resolve(__dirname, "../../../content/curseforge/version-types.json"), "utf8")
    )),

    versions: Object.freeze(JSON.parse(
        readFileSync(resolve(__dirname, "../../../content/curseforge/versions.json"), "utf8")
    )),
});

const CURSEFORGE_ETERNAL_FETCH = createFakeFetch({
    baseUrl: CURSEFORGE_ETERNAL_API_URL,
    requiredHeaders: ["X-Api-Key"],

    GET: {
        "^\\/mods\\/search\\?gameId=432&slug=foo$": () => ({
            data: [{
                id: 1,
                slug: "foo",
                links: { websiteUrl: "https://www.curseforge.com/minecraft/mc-mods/foo" },
            }],
        }),

        "^\\/mods\\/306612$": () => ({
            data: {
                id: 306612,
                slug: "fabric-api",
                links: { websiteUrl: "https://www.curseforge.com/minecraft/mc-mods/fabric-api" },
            },
        }),
    },
});

const CURSEFORGE_UPLOAD_FETCH = createFakeFetch({
    baseUrl: CURSEFORGE_UPLOAD_API_URL,
    requiredHeaders: ["X-Api-Token"],

    GET: {
        "^\\/game\\/version-types": () => DB.versionTypes,

        "^\\/game\\/versions": () => DB.versions,
    },

    POST: {
        "^\\/projects\\/1\\/upload-file$": (_, { body }) => {
            const formData = body as FormData;
            const metadata = JSON.parse(formData.get("metadata") as string) as CurseForgeVersionInitMetadata;

            expect(metadata.changelog).toBe("Changelog");
            expect(metadata.changelogType).toBe("markdown");
            expect(metadata.displayName).toBe("Version v1.0.0");
            expect(metadata.releaseType).toBe(VersionType.ALPHA);
            expect(metadata.parentFileID).toBeUndefined();
            expect(metadata.gameVersions).toEqual([9008, 7499, 8326]);
            expect(metadata.relations).toEqual({
                projects: [{
                    slug: "fabric-api",
                    type: CurseForgeDependencyType.REQUIRED_DEPENDENCY,
                }],
            });

            expect(formData.get("file")).toHaveProperty("name", "file.txt");

            return {
                id: 42,
                files: [{
                    id: 42,
                    name: "file.txt",
                    url: "https://www.curseforge.com/api/v1/mods/1/files/42/download",
                }],
            };
        },
    },
});

const CURSEFORGE_FETCH = createCombinedFetch(
    CURSEFORGE_ETERNAL_FETCH,
    CURSEFORGE_UPLOAD_FETCH,
);

beforeEach(() => {
    mockFs({
        "file.txt": "",
    });
});

afterEach(() => {
    mockFs.restore();
});

describe("CurseForgeUploader", () => {
    describe("platform", () => {
        test("returns `PlatformType.CURSEFORGE`", () => {
            const uploader = new CurseForgeUploader({ });

            expect(uploader.platform).toBe(PlatformType.CURSEFORGE);
        });
    });

    describe("upload", () => {
        test("fulfills the specified upload request", async () => {
            const uploader = new CurseForgeUploader({ fetch: CURSEFORGE_FETCH });

            const report = await uploader.upload({
                token: SecureString.from("token"),
                id: "foo",
                name: "Version v1.0.0",
                version: "1.0.0",
                versionType: VersionType.ALPHA,
                changelog: "Changelog",
                files: [FileInfo.of("file.txt")],
                dependencies: [parseDependency("fabric@0.75.0(required){curseforge:306612}")],
                gameVersions: ["1.18.2"],
                loaders: ["fabric", "unknown"],
                java: [JavaVersion.of(17)],
            });

            expect(report).toEqual({
                id: 1,
                version: 42,
                url: "https://www.curseforge.com/minecraft/mc-mods/foo/files/42",
                files: [{
                    id: 42,
                    name: "Version v1.0.0",
                    url: "https://www.curseforge.com/api/v1/mods/1/files/42/download",
                }],
            });
        });
    });
});