import * as tempy from 'tempy';
import * as child_process from 'child_process';
import * as path from 'path';

type UrlResolver = string | ((platform: Pick<PlatformInfo, 'name'>) => string);
type BinaryPathResolver = string | ((platform: PlatformInfo<string>) => string)

interface PlatformInfo<
    Url extends UrlResolver = UrlResolver,
    BinaryPath extends BinaryPathResolver = BinaryPathResolver,
    > {
    name: string;
    url: Url;
    binaryPath?: BinaryPath;
}

function getJohnVanSicklePlatform(name: string) {
    switch(name) {
        case 'linux-arm':
            return 'armhf';
        case 'linux-arm64':
            return 'arm64';
        case 'linux-ia32':
            return 'i686';
        case 'linux-x64':
            return 'amd64';
        default:
            throw new Error(`Unknown: ${name}`);
    }
}

const getJohnVanSickleUrl: UrlResolver = ({ name }) => {
    const platform = getJohnVanSicklePlatform(name);

    return `https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-${platform}-static.tar.xz`;
}

const getJohnVanSickleBinaryPath: BinaryPathResolver = ({ url }) => {
    const segments = url.split('/');
    const tarFileName = segments[segments.length - 1];
    const tarFileNameSegments = tarFileName.split('.');
    const directory = tarFileNameSegments[0];

    return path.join(directory, 'ffmpeg');
}

const platforms: PlatformInfo[] = [
    {
        name: 'linux-arm',
        url: getJohnVanSickleUrl,
        binaryPath: getJohnVanSickleBinaryPath,
    },
    {
        name: 'linux-arm64',
        url: getJohnVanSickleUrl,
        binaryPath: getJohnVanSickleBinaryPath,
    },
    {
        name: 'linux-ia32',
        url: getJohnVanSickleUrl,
        binaryPath: getJohnVanSickleBinaryPath,
    },
    {
        name: 'linux-x64',
        url: getJohnVanSickleUrl,
        binaryPath: getJohnVanSickleBinaryPath,
    },

    {
        name: 'darwin-x64',
        url: 'https://evermeet.cx/ffmpeg/ffmpeg-4.3.2.7z',
    },

    {
        name: 'win32-ia32',
        url: 'https://github.com/sudo-nautilus/FFmpeg-Builds-Win32/releases/download/autobuild-2021-03-23-06-25/ffmpeg-n4.3.2-160-gfbb9368226-win32-gpl-4.3.zip',
        binaryPath: 'ffmpeg.exe',
    },
    {
        name: 'win32-x64',
        url: 'https://github.com/BtbN/FFmpeg-Builds/releases/download/autobuild-2021-03-21-12-59/ffmpeg-n4.3.2-160-gfbb9368226-win64-gpl-4.3.zip',
        binaryPath: 'ffmpeg.exe',
    },
];

function getDecompressCommand(url: string, source: string, destination: string): [string, string[]] {
    if (url.endsWith('.tar.xz')) {
        return ['tar', ['xvf', source, '-C', destination]];
    }

    return ['7z', ['e', source, `-o${destination}`]];
}

async function downloadBinaries() {
    for (const platform of platforms) {
        const tempFile = tempy.file();
        const tempDir = tempy.directory();

        const url = typeof platform.url === 'string' ? platform.url : platform.url(platform);

        await new Promise((resolve) => {
            const download = child_process.spawn('wget', [url, '-O', tempFile]);

            console.log(`Downloading ${url} to ${tempFile}\n`);

            download.stdout.on('data', (message) => process.stdout.write(message.toString()));
            download.stderr.on('data', (message) => process.stderr.write(message.toString()));

            download.on('exit', resolve);
        });

        await new Promise((resolve) => {
            const decompress = child_process.spawn(...getDecompressCommand(url, tempFile, tempDir));

            console.log(`Decompressing ${url} to ${tempDir}\n`);

            decompress.stdout.on('data', (message) => process.stdout.write(message.toString()));
            decompress.stderr.on('data', (message) => process.stderr.write(message.toString()));

            decompress.on('exit', resolve);
        });

        const binaryPath = typeof platform.binaryPath === 'string' ? platform.binaryPath : platform.binaryPath?.({ ...platform, url }) ?? 'ffmpeg';
        const binaryPathAbsolute = path.join(tempDir, binaryPath);
        const destinationPath = path.resolve('platforms', platform.name);

        console.log(`Copying ${binaryPathAbsolute} to ${destinationPath}\n`);

        child_process.spawnSync('cp', [binaryPathAbsolute, destinationPath]);

        process.exit(0);
    }
}

downloadBinaries();
