"use strict";
const path = require("path");
const semver = require("semver");
const constants = require("./constants");
class NpmInstallationManager {
    constructor($npm, $childProcess, $logger, $errors, $options, $fs, $staticConfig) {
        this.$npm = $npm;
        this.$childProcess = $childProcess;
        this.$logger = $logger;
        this.$errors = $errors;
        this.$options = $options;
        this.$fs = $fs;
        this.$staticConfig = $staticConfig;
    }
    getLatestVersion(packageName) {
        return (() => {
            return this.getVersion(packageName, constants.PackageVersion.LATEST).wait();
        }).future()();
    }
    getNextVersion(packageName) {
        return (() => {
            return this.getVersion(packageName, constants.PackageVersion.NEXT).wait();
        }).future()();
    }
    getLatestCompatibleVersion(packageName) {
        return (() => {
            let cliVersionRange = `~${this.$staticConfig.version}`;
            let latestVersion = this.getLatestVersion(packageName).wait();
            if (semver.satisfies(latestVersion, cliVersionRange)) {
                return latestVersion;
            }
            let data = this.$npm.view(packageName, { json: true, "versions": true }).wait();
            return semver.maxSatisfying(data, cliVersionRange) || latestVersion;
        }).future()();
    }
    install(packageName, projectDir, opts) {
        return (() => {
            try {
                let packageToInstall = this.$options.frameworkPath || packageName;
                let pathToSave = projectDir;
                let version = (opts && opts.version) || null;
                let dependencyType = (opts && opts.dependencyType) || null;
                return this.installCore(packageToInstall, pathToSave, version, dependencyType).wait();
            }
            catch (error) {
                this.$logger.debug(error);
                this.$errors.fail("%s. Error: %s", NpmInstallationManager.NPM_LOAD_FAILED, error);
            }
        }).future()();
    }
    getInspectorFromCache(inspectorNpmPackageName, projectDir) {
        return (() => {
            let inspectorPath = path.join(projectDir, "node_modules", inspectorNpmPackageName);
            if (!this.inspectorAlreadyInstalled(inspectorPath)) {
                let cachepath = this.$childProcess.exec("npm get cache").wait().trim();
                let version = this.getLatestCompatibleVersion(inspectorNpmPackageName).wait();
                let pathToPackageInCache = path.join(cachepath, inspectorNpmPackageName, version);
                let pathToUnzippedInspector = path.join(pathToPackageInCache, "package");
                if (!this.$fs.exists(pathToPackageInCache)) {
                    this.$childProcess.exec(`npm cache add ${inspectorNpmPackageName}@${version}`).wait();
                    let inspectorTgzPathInCache = path.join(pathToPackageInCache, "package.tgz");
                    this.$childProcess.exec(`tar -xf ${inspectorTgzPathInCache} -C ${pathToPackageInCache}`).wait();
                    this.$childProcess.exec(`npm install --prefix ${pathToUnzippedInspector}`).wait();
                }
                this.$logger.out("Using inspector from cache.");
                return pathToUnzippedInspector;
            }
            return inspectorPath;
        }).future()();
    }
    inspectorAlreadyInstalled(pathToInspector) {
        if (this.$fs.exists(pathToInspector)) {
            return true;
        }
        return false;
    }
    installCore(packageName, pathToSave, version, dependencyType) {
        return (() => {
            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 || this.getLatestCompatibleVersion(packageName).wait();
            }
            let installedModuleNames = this.npmInstall(packageName, pathToSave, version, dependencyType).wait();
            let installedPackageName = installedModuleNames[0];
            let pathToInstalledPackage = path.join(pathToSave, "node_modules", installedPackageName);
            return pathToInstalledPackage;
        }).future()();
    }
    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 (() => {
            this.$logger.out("Installing ", packageName);
            packageName = packageName + (version ? `@${version}` : "");
            let npmOptions = { silent: true };
            if (dependencyType) {
                npmOptions[dependencyType] = true;
            }
            return this.$npm.install(packageName, pathToSave, npmOptions).wait();
        }).future()();
    }
    getVersion(packageName, version) {
        return (() => {
            let data = this.$npm.view(packageName, { json: true, "dist-tags": true }).wait();
            this.$logger.trace("Using version %s. ", data[version]);
            return data[version];
        }).future()();
    }
}
NpmInstallationManager.NPM_LOAD_FAILED = "Failed to retrieve data from npm. Please try again a little bit later.";
exports.NpmInstallationManager = NpmInstallationManager;
$injector.register("npmInstallationManager", NpmInstallationManager);
