// SPDX-License-Identifier: Apache-2.0
import fs from 'node:fs';
import * as helpers from '../helpers.js';
import * as constants from '../constants.js';
import { Templates } from '../templates.js';
import { ShellRunner } from '../shell-runner.js';
import * as semver from 'semver';
import { OS_WIN32, OS_WINDOWS } from '../constants.js';
import { MissingArgumentError } from '../errors/missing-argument-error.js';
import { SoloError } from '../errors/solo-error.js';
/**
 * Base class for dependency managers that download and manage CLI tools
 * Common functionality for downloading, checking versions, and managing executables
 */
export class BaseDependencyManager extends ShellRunner {
    downloader;
    installationDirectory;
    requiredVersion;
    executableName;
    downloadBaseUrl;
    osPlatform;
    osArch;
    localExecutablePath;
    globalExecutablePath = '';
    artifactName;
    downloadURL;
    checksumURL;
    constructor(downloader, installationDirectory, osPlatform, osArch, requiredVersion, executableName, downloadBaseUrl) {
        super();
        this.downloader = downloader;
        this.installationDirectory = installationDirectory;
        this.requiredVersion = requiredVersion;
        this.executableName = executableName;
        this.downloadBaseUrl = downloadBaseUrl;
        if (!installationDirectory) {
            throw new MissingArgumentError('installation directory is required');
        }
        // Node.js uses 'win32' for windows but many tools use 'windows'
        this.osPlatform = osPlatform === OS_WIN32 ? OS_WINDOWS : osPlatform;
        // Normalize architecture naming - many tools use 'amd64' instead of 'x64'
        this.osArch = ['x64', 'x86-64'].includes(osArch) ? 'amd64' : osArch;
        // Set the path to the local installation
        this.localExecutablePath = Templates.installationPath(executableName, this.osPlatform, installationDirectory);
        // Set artifact name and URLs - these will be overridden by child classes
        this.artifactName = this.getArtifactName();
        this.downloadURL = this.getDownloadURL();
        this.checksumURL = this.getChecksumURL();
    }
    /**
     * Get the path to the executable (global or local)
     */
    getExecutablePath() {
        return this.localExecutablePath;
    }
    /**
     * Find the global executable using 'which' or 'where' command
     */
    async getGlobalExecutablePath() {
        try {
            if (this.globalExecutablePath) {
                return this.globalExecutablePath;
            }
            const cmd = this.osPlatform === constants.OS_WINDOWS ? 'where' : 'which';
            const path = await this.run(`${cmd} ${this.executableName}`);
            if (path.length === 0) {
                return false;
            }
            this.globalExecutablePath = path[0];
            return path[0];
        }
        catch {
            return false;
        }
    }
    /**
     * Check if the given installation meets version requirements
     */
    async installationMeetsRequirements(path) {
        const version = await this.getVersion(path);
        return semver.gte(version, this.getRequiredVersion());
    }
    /**
     * Check if the tool is installed globally and meets requirements
     */
    async isInstalledGloballyAndMeetsRequirements() {
        const path = await this.getGlobalExecutablePath();
        return path && (await this.installationMeetsRequirements(path));
    }
    /**
     * Check if the tool is installed locally and meets requirements
     */
    async isInstalledLocallyAndMeetsRequirements() {
        return this.isInstalledLocally() && (await this.installationMeetsRequirements(this.localExecutablePath));
    }
    /**
     * Check if the tool is installed locally
     */
    isInstalledLocally() {
        return fs.existsSync(this.localExecutablePath);
    }
    /**
     * Uninstall the local version
     */
    uninstallLocal() {
        if (this.isInstalledLocally()) {
            fs.rmSync(this.localExecutablePath);
        }
    }
    /**
     * Install the tool
     */
    async install(temporaryDirectory = helpers.getTemporaryDirectory()) {
        // Check if it is already installed locally
        if (await this.isInstalledLocallyAndMeetsRequirements()) {
            this.logger.debug(`${this.executableName} is installed at ${this.installationDirectory} and meets version requirements.`);
            return true;
        }
        // If it is installed globally and meets requirements, copy it to the local path
        if (await this.isInstalledGloballyAndMeetsRequirements()) {
            this.logger.debug(`${this.executableName} is installed at globally and meets version requirements.`);
            fs.cpSync(this.globalExecutablePath, this.localExecutablePath);
            this.logger.debug(`Copied ${this.executableName} executable to ${this.installationDirectory}`);
            return true;
        }
        // If not installed, download and install
        this.logger.debug(`Downloading and installing ${this.executableName} executable...`);
        const packageFile = await this.downloader.fetchPackage(this.downloadURL, this.checksumURL, temporaryDirectory);
        const processedFile = await this.processDownloadedPackage(packageFile, temporaryDirectory);
        if (!fs.existsSync(this.installationDirectory)) {
            fs.mkdirSync(this.installationDirectory, { recursive: true });
        }
        // In case there is an existing local installation, which did not meet the requirements - remove it
        this.uninstallLocal();
        try {
            fs.cpSync(processedFile, this.localExecutablePath);
            fs.chmodSync(this.localExecutablePath, 0o755);
        }
        catch (error) {
            throw new SoloError(`Failed to install ${this.executableName}: ${error.message}`);
        }
        return this.isInstalledLocally();
    }
    /**
     * Get the tool's required version
     */
    getRequiredVersion() {
        return this.requiredVersion;
    }
}
//# sourceMappingURL=base-dependency-manager.js.map