"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments)).next());
    });
};
const path = require("path");
const semver = require("semver");
const constants = require("./constants");
class NpmInstallationManager {
    constructor($npm, $childProcess, $logger, $options, $fs, $staticConfig) {
        this.$npm = $npm;
        this.$childProcess = $childProcess;
        this.$logger = $logger;
        this.$options = $options;
        this.$fs = $fs;
        this.$staticConfig = $staticConfig;
    }
    getLatestVersion(packageName) {
        return __awaiter(this, void 0, void 0, function* () {
            return yield this.getVersion(packageName, constants.PackageVersion.LATEST);
        });
    }
    getNextVersion(packageName) {
        return __awaiter(this, void 0, void 0, function* () {
            return yield this.getVersion(packageName, constants.PackageVersion.NEXT);
        });
    }
    getLatestCompatibleVersion(packageName) {
        return __awaiter(this, void 0, void 0, function* () {
            let cliVersionRange = `~${this.$staticConfig.version}`;
            let latestVersion = yield this.getLatestVersion(packageName);
            if (semver.satisfies(latestVersion, cliVersionRange)) {
                return latestVersion;
            }
            let data = yield this.$npm.view(packageName, { "versions": true });
            return semver.maxSatisfying(data, cliVersionRange) || latestVersion;
        });
    }
    install(packageName, projectDir, opts) {
        return __awaiter(this, void 0, void 0, function* () {
            try {
                let packageToInstall = this.$options.frameworkPath || packageName;
                let pathToSave = projectDir;
                let version = (opts && opts.version) || null;
                let dependencyType = (opts && opts.dependencyType) || null;
                return yield this.installCore(packageToInstall, pathToSave, version, dependencyType);
            }
            catch (error) {
                this.$logger.debug(error);
                throw new Error(error);
            }
        });
    }
    getInspectorFromCache(inspectorNpmPackageName, projectDir) {
        return __awaiter(this, void 0, void 0, function* () {
            let inspectorPath = path.join(projectDir, "node_modules", inspectorNpmPackageName);
            if (!this.inspectorAlreadyInstalled(inspectorPath)) {
                let cachepath = (yield this.$childProcess.exec("npm get cache")).trim();
                let version = yield this.getLatestCompatibleVersion(inspectorNpmPackageName);
                let pathToPackageInCache = path.join(cachepath, inspectorNpmPackageName, version);
                let pathToUnzippedInspector = path.join(pathToPackageInCache, "package");
                if (!this.$fs.exists(pathToPackageInCache)) {
                    yield this.$childProcess.exec(`npm cache add ${inspectorNpmPackageName}@${version}`);
                    let inspectorTgzPathInCache = path.join(pathToPackageInCache, "package.tgz");
                    yield this.$childProcess.exec(`tar -xf ${inspectorTgzPathInCache} -C ${pathToPackageInCache}`);
                    yield this.$childProcess.exec(`npm install --prefix ${pathToUnzippedInspector}`);
                }
                this.$logger.out("Using inspector from cache.");
                return pathToUnzippedInspector;
            }
            return inspectorPath;
        });
    }
    inspectorAlreadyInstalled(pathToInspector) {
        if (this.$fs.exists(pathToInspector)) {
            return true;
        }
        return false;
    }
    installCore(packageName, pathToSave, version, dependencyType) {
        return __awaiter(this, void 0, void 0, function* () {
            const possiblePackageName = path.resolve(packageName);
            if (this.$fs.exists(possiblePackageName)) {
                packageName = possiblePackageName;
            }
            if (packageName.indexOf(".tgz") >= 0) {
                version = null;
            }
            if (this.isURL(packageName) || this.$fs.exists(packageName)) {
                version = null;
            }
            else {
                version = version || (yield this.getLatestCompatibleVersion(packageName));
            }
            let installResultInfo = yield this.npmInstall(packageName, pathToSave, version, dependencyType);
            let installedPackageName = installResultInfo.name;
            let pathToInstalledPackage = path.join(pathToSave, "node_modules", installedPackageName);
            return pathToInstalledPackage;
        });
    }
    isURL(str) {
        let urlRegex = '^(?!mailto:)(?:(?:http|https|ftp)://)(?:\\S+(?::\\S*)?@)?(?:(?:(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[0-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})))|localhost)(?::\\d{2,5})?(?:(/|\\?|#)[^\\s]*)?$';
        let url = new RegExp(urlRegex, 'i');
        return str.length < 2083 && url.test(str);
    }
    npmInstall(packageName, pathToSave, version, dependencyType) {
        return __awaiter(this, void 0, void 0, function* () {
            this.$logger.out("Installing ", packageName);
            packageName = packageName + (version ? `@${version}` : "");
            let npmOptions = { silent: true, "save-exact": true };
            if (dependencyType) {
                npmOptions[dependencyType] = true;
            }
            return yield this.$npm.install(packageName, pathToSave, npmOptions);
        });
    }
    getVersion(packageName, version) {
        return __awaiter(this, void 0, void 0, function* () {
            let data = yield this.$npm.view(packageName, { "dist-tags": true });
            this.$logger.trace("Using version %s. ", data[version]);
            return data[version];
        });
    }
}
exports.NpmInstallationManager = NpmInstallationManager;
$injector.register("npmInstallationManager", NpmInstallationManager);
