const fs = require('fs');
const path = require('path');
const https = require('https');
const { spawnSync } = require('child_process');
const { getArchAndPlatform } = require('./utils');

const DOTNET_VERSION = '9.0.9';
const DOTNET_RUNTIME_DIR = path.join(
    __dirname,
    '..',
    'build',
    'Electron',
    'dotnet-runtime'
);

async function downloadFile(url, targetPath) {
    return new Promise((resolve, reject) => {
        const file = fs.createWriteStream(targetPath);
        https
            .get(url, (response) => {
                if (response.statusCode !== 200) {
                    reject(
                        new Error(
                            `Failed to download, url: ${url} status code: ${response.statusCode}`
                        )
                    );
                    return;
                }
                response.pipe(file);
                file.on('finish', () => {
                    file.close(resolve);
                });
            })
            .on('error', (err) => {
                fs.unlink(targetPath, () => reject(err));
            });
    });
}

async function extractTarGz(tarGzPath, extractDir) {
    return new Promise((resolve, reject) => {
        const tar = spawnSync(
            'tar',
            ['-xzf', tarGzPath, '-C', extractDir, '--strip-components=1'],
            {
                stdio: 'inherit'
            }
        );

        if (tar.status === 0) {
            resolve();
        } else {
            reject(
                new Error(`tar extraction failed with status ${tar.status}`)
            );
        }
    });
}

async function downloadDotnetRuntime(arch, platform) {
    if (!arch || !platform) {
        throw new Error('Architecture and platform must be specified');
    }

    let dotnetPlatform = '';
    if (platform === 'linux') {
        dotnetPlatform = 'linux';
    } else if (platform === 'darwin') {
        dotnetPlatform = 'osx';
    } else if (platform === 'win32') {
        // Windows is a zip file instead of tar.gz, which we do not handle here, skip
        console.log('Skipping .NET runtime download on Windows');
        return;
        // dotnetPlatform = 'win';
    } else {
        throw new Error(`Unsupported platform: ${platform}`);
    }

    if (!fs.existsSync(DOTNET_RUNTIME_DIR)) {
        fs.mkdirSync(DOTNET_RUNTIME_DIR, { recursive: true });
    }

    console.log(
        `Downloading .NET ${DOTNET_VERSION}-${dotnetPlatform}-${arch} runtime...`
    );
    const tarGzPath = path.join(DOTNET_RUNTIME_DIR, 'dotnet-runtime.tar.gz');
    const dotnetRuntimeUrl = `https://builds.dotnet.microsoft.com/dotnet/Runtime/${DOTNET_VERSION}/dotnet-runtime-${DOTNET_VERSION}-${dotnetPlatform}-${arch}.tar.gz`;

    // Download .NET runtime
    await downloadFile(dotnetRuntimeUrl, tarGzPath);
    console.log('Download completed');

    // Extract .NET runtime to a temporary directory first
    const tempExtractDir = path.join(DOTNET_RUNTIME_DIR, 'temp');
    if (!fs.existsSync(tempExtractDir)) {
        fs.mkdirSync(tempExtractDir, { recursive: true });
    }

    console.log('Extracting .NET runtime...');
    await extractTarGz(tarGzPath, tempExtractDir);
    console.log('Extraction completed');

    // Clean up tar.gz file
    fs.unlinkSync(tarGzPath);
    console.log('Cleanup completed');

    // Ensure the dotnet executable is executable
    const extractedDotnet = path.join(tempExtractDir, 'dotnet');
    fs.chmodSync(extractedDotnet, 0o755);

    // Move all other files to the root of dotnet-runtime
    const files = fs.readdirSync(tempExtractDir);
    for (const file of files) {
        const sourcePath = path.join(tempExtractDir, file);
        const targetPath = path.join(DOTNET_RUNTIME_DIR, file);

        if (fs.existsSync(targetPath)) {
            if (fs.lstatSync(sourcePath).isDirectory()) {
                // Remove existing directory and move new one
                fs.rmSync(targetPath, { recursive: true, force: true });
            } else {
                // Remove existing file
                fs.unlinkSync(targetPath);
            }
        }

        fs.renameSync(sourcePath, targetPath);
    }

    // Clean up temp directory
    fs.rmSync(tempExtractDir, { recursive: true, force: true });

    console.log(
        `.NET runtime downloaded and extracted to: ${DOTNET_RUNTIME_DIR}`
    );
}

const { arch, platform } = getArchAndPlatform();
downloadDotnetRuntime(arch, platform);
