"use strict";

const path = require('path');
const fs = require('fs').promises;
const os = require('os');

const ARCHITECTURE_MAPPING = {
    "x64": "x86_64",
    "arm64": "aarch_64"
};

const PLATFORM_MAPPING = {
    "darwin": "osx",
    "linux": "linux"
};

const EXTENSION_MAP = {
    "win32": ".exe",
    "default": ""
};

const SUCCESS_MESSAGES = {
    "install": "Installed CLI successfully",
    "uninstall": "Uninstalled CLI successfully"
};

function getGlobalBinPath() {
    const npmGlobalPrefix = process.env.PREFIX || process.env.npm_config_prefix || process.env.HOME;
    return path.join(npmGlobalPrefix, 'bin');
}

function validatePackageConfig(packageJson) {
    if (!packageJson.version || !packageJson.packageName || !packageJson.alias || !packageJson.binaries || typeof packageJson.binaries !== "object") {
        throw new Error("Invalid package.json. 'version', 'packageName', 'alias' and 'binaries' must be specified.");
    }
}

async function parsePackageJson() {
    console.log("Installing CLI");
    const platform = os.platform();
    const architecture = os.arch();

    console.log("Platform: " + platform);
    console.log("Architecture: " + architecture);

    if (!(os.arch() in ARCHITECTURE_MAPPING) || !(os.platform() in PLATFORM_MAPPING)) {
        throw new Error(`Installation is not supported for this ${platform}/${architecture} combination.`);
    }

    const packageJsonPath = path.join(".", "package.json");

    try {
        const packageJsonContent = await fs.readFile(packageJsonPath, "utf-8");
        const packageJson = JSON.parse(packageJsonContent.toString());
        validatePackageConfig(packageJson);

        const packageName = packageJson.packageName;
        const alias = packageJson.alias;
        const binaries = packageJson.binaries;
        const extension = EXTENSION_MAP[platform] || EXTENSION_MAP.default;
        const binaryKey = `${packageName}-${platform}-${architecture}`;
        const binaryPath = binaries[binaryKey];

        if (binaryPath) {
            console.log(`Binary found for your platform ${platform}-${architecture}: ${binaryPath}`);
        } else {
            throw new Error(`No binary found for your platform ${platform}-${architecture}.`);
        }

        return {
            alias,
            binaryKey,
            binaryPath,
            extension
        };
    } catch (error) {
        throw new Error("Unable to read or parse package.json. Please run this script at the root of the package you want to be installed.");
    }
}

async function removeSymlinkIfExists(symlinkPath) {
    try {
        await fs.unlink(symlinkPath);
        console.info(`Removed existing symlink ${symlinkPath}`);
    } catch (error) {
        // El enlace simbólico no existe, continuar sin hacer nada
    }
}

async function createSymlink(binarySource, binaryDestination) {
    const globalBinPath = getGlobalBinPath();

    console.log("Global bin path: " + globalBinPath);

    try {
        // Eliminar el enlace simbólico existente, si existe
        await removeSymlinkIfExists(path.join(globalBinPath, binaryDestination));

        const fullSymlinkPath = path.join(globalBinPath, binaryDestination);

        if (os.platform() === "win32") {
            // Crear un enlace de directorio para Windows
            // await fs.symlink(binarySource, fullSymlinkPath, "junction");
        } else {
            // Crear un enlace simbólico para macOS y Linux
            await fs.symlink(binarySource, fullSymlinkPath);
        }
        console.info(`Created symlink ${fullSymlinkPath} pointing to ${binarySource}`);
    } catch (error) {
        console.error("Error while creating symlink:", error);
        throw new Error("Failed to create symlink.");
    }
}

async function installCli() {
    const config = await parsePackageJson();

    console.info(`Creating symlink for the relevant binary for your platform ${os.platform()}-${os.arch()}`);

    const currentDir = __dirname;
    const targetDir = path.join(currentDir, '..');
    const binarySource = path.join(targetDir, config.binaryPath);
    const binaryDestination = config.alias + config.extension;

    console.info("Installing cli:", binarySource, binaryDestination);

    await createSymlink(binarySource, binaryDestination);
}

async function uninstallCli() {
    const config = await parsePackageJson();

    try {
        const globalBinPath = getGlobalBinPath();
        const symlinkFileName = config.alias + config.extension;

        // Eliminar el enlace simbólico si existe
        await removeSymlinkIfExists(path.join(globalBinPath, symlinkFileName));

        console.info(`Removed symlink: ${symlinkFileName}`);
    } catch (ex) {
        console.error("Error while uninstalling:", ex);
        throw new Error("Failed to uninstall the CLI.");
    }

    console.info(SUCCESS_MESSAGES.uninstall);
}

const actions = {
    "install": installCli,
    "uninstall": uninstallCli
};

const [cmd] = process.argv.slice(2);
if (cmd && actions[cmd]) {
    actions[cmd]().then(
        () => process.exit(0),
        (err) => {
            console.error(err);
            process.exit(1);
        }
    );
} else {
    console.log("Invalid command. `install` and `uninstall` are the only supported commands");
    process.exit(1);
}
