import { createWriteStream, mkdirSync, rmSync } from 'fs';
import { Readable } from 'stream';

interface Hashes {
    sha1: string
    sha512: string
}
interface File {
    hashes: Hashes
    url: string
    filename: string
    primary: boolean
    size: number
    file_type: string
}
interface Dependency {
    version_id: string
    project_id: string
    file_name: string
    dependency_type: string
}
interface ModVersion {
    game_versions: string[]
    loaders: string[]
    id: string
    project_id: string
    author_id: string
    featured: boolean
    name: string
    version_number: string
    changelog: string
    changelog_url: string
    date_published: string
    downloads: number
    version_type: string
    status: string
    files: File[]
    dependencies: Dependency[]
}

const LOADER = 'fabric';

const game_version = process.argv[2] ?? '';
const action = process.argv[3] ?? 'check';

const getVersions = async(slug: string): Promise<ModVersion[]> => {
    const res = await fetch(`https://api.modrinth.com/v2/project/${slug}/version`);

    return res.ok ?
        await res.json() as ModVersion[] :
        [];
};

const checkModCompat = async(slug: string): Promise<ModVersion | null> => {
    const versions = await getVersions(slug);

    const matching = versions.filter((ver) =>
        ver.game_versions.includes(game_version) &&
        ver.loaders.includes(LOADER));

    if (matching.length === 0) {
        return null;
    }

    const timeSorted = matching.sort((a, b) => {
        const dateA = new Date(a.date_published);
        const dateB = new Date(b.date_published);

        return dateB.getTime() - dateA.getTime();
    });

    return timeSorted.some((v) => v.version_type === 'release') ?
        timeSorted.filter((v) => v.version_type === 'release')[0] :
        timeSorted[0];
};

const getDownloadUrls = (version: ModVersion): string[] => version.files.map((file) => file.url);

const showModDownloadUrls = async(modName: string): Promise<boolean> => {
    const ver = await checkModCompat(modName);

    if (ver) {
        const urls = getDownloadUrls(ver);

        console.log(`\n${modName}:`, urls);

        return true;
    }
    else {
        return false;
    }
};

const download = async(url: string, path: string) => Readable
    .fromWeb((await fetch(url)).body!).pipe(createWriteStream(path));

const main = () => {
    const mods = [
        'badpackets',
        'c2me-fabric',
        'cloth-config',
        'clumps',
        'fabric-api',
        'ferrite-core',
        'leaves-be-gone',
        'lithium',
        'modernfix',
        'no-chat-reports',
        'noisium',
        'vmp-fabric',
        'wthit',
    ];

    if (action === 'check') {
        mods.forEach(async(modName) => {
            if (!(await showModDownloadUrls(modName))) {
                console.error(`No matching releases of ${modName} were found for ${game_version}`);
            }
        });
    }
    else if (action === 'download') {
        rmSync('./out', { force: true, recursive: true });
        mkdirSync('./out');

        mods.forEach(async(modName) => {
            const ver = await checkModCompat(modName);

            if (ver === null) {
                console.error(`No matching releases of ${modName} were found for ${game_version}`);

                return;
            }

            const fileName =
                `${modName}-${ver.version_number}.jar`;

            console.log(`Downloading ${fileName}`);

            await download(
                ver.files[0].url,
                `./out/${fileName}`,
            );
        });
    }
};

main();