"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PackageInstallationManager = void 0;
const path = require("path");
const constants = require("./constants");
const yok_1 = require("./common/yok");
const semver = require("semver");
class PackageInstallationManager {
    constructor($packageManager, $childProcess, $logger, $settingsService, $fs, $staticConfig, $projectDataService) {
        this.$packageManager = $packageManager;
        this.$childProcess = $childProcess;
        this.$logger = $logger;
        this.$settingsService = $settingsService;
        this.$fs = $fs;
        this.$staticConfig = $staticConfig;
        this.$projectDataService = $projectDataService;
    }
    async getLatestVersion(packageName) {
        return await this.getVersion(packageName, constants.PackageVersion.LATEST);
    }
    async getNextVersion(packageName) {
        return await this.getVersion(packageName, constants.PackageVersion.NEXT);
    }
    async getLatestCompatibleVersion(packageName, referenceVersion) {
        referenceVersion = referenceVersion || this.$staticConfig.version;
        const isPreReleaseVersion = semver.prerelease(referenceVersion) !== null;
        const compatibleVersionRange = isPreReleaseVersion
            ? `~${referenceVersion}`
            : `~${semver.major(referenceVersion)}.${semver.minor(referenceVersion)}.0`;
        const latestVersion = await this.getLatestVersion(packageName);
        if (semver.satisfies(latestVersion, compatibleVersionRange)) {
            return latestVersion;
        }
        return ((await this.getMaxSatisfyingVersion(packageName, compatibleVersionRange)) || latestVersion);
    }
    async getMaxSatisfyingVersion(packageName, versionRange) {
        var _a;
        const data = await this.$packageManager.view(packageName, {
            versions: true,
        });
        let versions;
        if (typeof data === "string") {
            versions = [data];
        }
        else if (data === null || data === void 0 ? void 0 : data.versions) {
            versions = data.versions;
        }
        else {
            versions = data;
        }
        if (!versions || !Array.isArray(versions)) {
            return null;
        }
        return (_a = semver.maxSatisfying(versions, versionRange)) === null || _a === void 0 ? void 0 : _a.toString();
    }
    async getMaxSatisfyingVersionSafe(packageName, versionIdentifier) {
        let maxDependencyVersion;
        if (semver.valid(versionIdentifier)) {
            maxDependencyVersion = versionIdentifier;
        }
        else if (semver.validRange(versionIdentifier)) {
            maxDependencyVersion = await this.getMaxSatisfyingVersion(packageName, versionIdentifier);
        }
        else {
            maxDependencyVersion = await this.$packageManager.getTagVersion(packageName, versionIdentifier);
        }
        return maxDependencyVersion;
    }
    async getInstalledDependencyVersion(packageName, projectDir) {
        const projectData = this.$projectDataService.getProjectData(projectDir);
        const devDependencies = projectData.devDependencies || {};
        const dependencies = projectData.dependencies || {};
        const referencedVersion = dependencies[packageName] || devDependencies[packageName];
        const installedVersion = await this.getMaxSatisfyingVersionSafe(packageName, referencedVersion);
        return installedVersion;
    }
    async getLatestCompatibleVersionSafe(packageName, referenceVersion) {
        let version = "";
        const canGetVersionFromNpm = await this.$packageManager.isRegistered(packageName);
        if (canGetVersionFromNpm) {
            version = await this.getLatestCompatibleVersion(packageName, referenceVersion);
        }
        return version;
    }
    async install(packageToInstall, projectDir, opts) {
        try {
            const pathToSave = projectDir;
            const version = (opts && opts.version) || null;
            const dependencyType = (opts && opts.dependencyType) || null;
            return await this.installCore(packageToInstall, pathToSave, version, dependencyType);
        }
        catch (error) {
            this.$logger.trace(error);
            throw error;
        }
    }
    async uninstall(packageToUninstall, projectDir, opts) {
        try {
            return await this.$packageManager.uninstall(packageToUninstall, opts, projectDir);
        }
        catch (error) {
            this.$logger.trace(error);
            throw error;
        }
    }
    async getInspectorFromCache(inspectorNpmPackageName, projectDir) {
        const inspectorPath = path.join(projectDir, constants.NODE_MODULES_FOLDER_NAME, inspectorNpmPackageName);
        if (this.inspectorAlreadyInstalled(inspectorPath)) {
            return inspectorPath;
        }
        const cachePath = this.getInspectorCachePath();
        this.prepareCacheDir(cachePath);
        const pathToPackageInCache = path.join(cachePath, constants.NODE_MODULES_FOLDER_NAME, inspectorNpmPackageName);
        const iOSFrameworkNSValue = this.$projectDataService.getRuntimePackage(projectDir, "ios");
        const version = await this.getLatestCompatibleVersion(inspectorNpmPackageName, iOSFrameworkNSValue.version);
        let shouldInstall = !this.$fs.exists(pathToPackageInCache);
        if (!shouldInstall) {
            try {
                const installedVersion = this.$fs.readJson(path.join(pathToPackageInCache, constants.PACKAGE_JSON_FILE_NAME)).version;
                shouldInstall = version !== installedVersion;
            }
            catch (err) {
                shouldInstall = true;
            }
        }
        if (shouldInstall) {
            await this.$childProcess.exec(`npm install ${inspectorNpmPackageName}@${version} --prefix ${cachePath}`, { maxBuffer: 250 * 1024 });
        }
        this.$logger.info("Using inspector from cache.");
        return pathToPackageInCache;
    }
    clearInspectorCache() {
        this.$fs.deleteDirectorySafe(this.getInspectorCachePath());
    }
    getInspectorCachePath() {
        return path.join(this.$settingsService.getProfileDir(), constants.INSPECTOR_CACHE_DIRNAME);
    }
    prepareCacheDir(cacheDirName) {
        this.$fs.ensureDirectoryExists(cacheDirName);
        const cacheDirPackageJsonLocation = path.join(cacheDirName, constants.PACKAGE_JSON_FILE_NAME);
        if (!this.$fs.exists(cacheDirPackageJsonLocation)) {
            this.$fs.writeJson(cacheDirPackageJsonLocation, {
                name: constants.INSPECTOR_CACHE_DIRNAME,
                version: "0.1.0",
            });
        }
    }
    inspectorAlreadyInstalled(pathToInspector) {
        if (this.$fs.exists(pathToInspector)) {
            return true;
        }
        return false;
    }
    async installCore(packageName, pathToSave, version, dependencyType) {
        const possiblePackageName = path.resolve(packageName);
        if (this.$fs.exists(possiblePackageName)) {
            packageName = possiblePackageName;
        }
        version =
            version || (await this.getLatestCompatibleVersionSafe(packageName));
        const installResultInfo = await this.npmInstall(packageName, pathToSave, version, dependencyType);
        const installedPackageName = installResultInfo.name;
        const pathToInstalledPackage = path.join(pathToSave, "node_modules", installedPackageName);
        return pathToInstalledPackage;
    }
    async npmInstall(packageName, pathToSave, version, dependencyType) {
        this.$logger.info(`Installing ${packageName}`);
        packageName = packageName + (version ? `@${version}` : "");
        const npmOptions = { silent: true, "save-exact": true };
        if (dependencyType) {
            npmOptions[dependencyType] = true;
        }
        return await this.$packageManager.install(packageName, pathToSave, npmOptions);
    }
    async getVersion(packageName, version) {
        var _a;
        let data = await this.$packageManager.view(packageName, {
            "dist-tags": true,
        });
        data = (_a = data === null || data === void 0 ? void 0 : data["dist-tags"]) !== null && _a !== void 0 ? _a : data;
        this.$logger.trace("Using version %s. ", data[version]);
        return data[version];
    }
}
exports.PackageInstallationManager = PackageInstallationManager;
yok_1.injector.register("packageInstallationManager", PackageInstallationManager);
