"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    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) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.VersionInformationType = void 0;
const constants = require("../constants");
const helpers = require("../common/helpers");
const semver = require("semver");
const path = require("path");
const _ = require("lodash");
const yok_1 = require("../common/yok");
var VersionInformationType;
(function (VersionInformationType) {
    VersionInformationType["UpToDate"] = "UpToDate";
    VersionInformationType["UpdateAvailable"] = "UpdateAvailable";
    VersionInformationType["NotInstalled"] = "NotInstalled";
})(VersionInformationType = exports.VersionInformationType || (exports.VersionInformationType = {}));
class VersionsService {
    constructor($fs, $packageInstallationManager, $injector, $logger, $staticConfig, $pluginsService, $projectDataService, $terminalSpinnerService) {
        this.$fs = $fs;
        this.$packageInstallationManager = $packageInstallationManager;
        this.$injector = $injector;
        this.$logger = $logger;
        this.$staticConfig = $staticConfig;
        this.$pluginsService = $pluginsService;
        this.$projectDataService = $projectDataService;
        this.$terminalSpinnerService = $terminalSpinnerService;
        this.projectData = this.getProjectData();
    }
    getNativescriptCliVersion() {
        return __awaiter(this, void 0, void 0, function* () {
            const currentCliVersion = this.$staticConfig.version;
            const latestCliVersion = yield this.$packageInstallationManager.getLatestVersion(constants.NATIVESCRIPT_KEY_NAME);
            return {
                componentName: constants.NATIVESCRIPT_KEY_NAME,
                currentVersion: currentCliVersion,
                latestVersion: latestCliVersion,
            };
        });
    }
    getTnsCoreModulesVersion() {
        return __awaiter(this, void 0, void 0, function* () {
            const latestTnsCoreModulesVersion = yield this.$packageInstallationManager.getLatestVersion(constants.TNS_CORE_MODULES_NAME);
            const nativescriptCoreModulesInfo = {
                componentName: constants.TNS_CORE_MODULES_NAME,
                latestVersion: latestTnsCoreModulesVersion,
            };
            const versionInformations = [];
            if (this.projectData) {
                const nodeModulesPath = path.join(this.projectData.projectDir, constants.NODE_MODULES_FOLDER_NAME);
                const scopedPackagePath = path.join(nodeModulesPath, constants.SCOPED_TNS_CORE_MODULES);
                const tnsCoreModulesPath = path.join(nodeModulesPath, constants.TNS_CORE_MODULES_NAME);
                const dependsOnNonScopedPackage = !!this.projectData.dependencies[constants.TNS_CORE_MODULES_NAME];
                const dependsOnScopedPackage = !!this.projectData.dependencies[constants.SCOPED_TNS_CORE_MODULES];
                if (!this.$fs.exists(nodeModulesPath) ||
                    (dependsOnNonScopedPackage && !this.$fs.exists(tnsCoreModulesPath)) ||
                    (dependsOnScopedPackage && !this.$fs.exists(scopedPackagePath))) {
                    yield this.$pluginsService.ensureAllDependenciesAreInstalled(this.projectData);
                }
                if (dependsOnNonScopedPackage && this.$fs.exists(tnsCoreModulesPath)) {
                    const currentTnsCoreModulesVersion = this.$fs.readJson(path.join(tnsCoreModulesPath, constants.PACKAGE_JSON_FILE_NAME)).version;
                    nativescriptCoreModulesInfo.currentVersion = currentTnsCoreModulesVersion;
                    versionInformations.push(nativescriptCoreModulesInfo);
                }
                if (dependsOnScopedPackage && this.$fs.exists(scopedPackagePath)) {
                    const scopedModulesInformation = {
                        componentName: constants.SCOPED_TNS_CORE_MODULES,
                        latestVersion: yield this.$packageInstallationManager.getLatestVersion(constants.SCOPED_TNS_CORE_MODULES),
                    };
                    const currentScopedPackageVersion = this.$fs.readJson(path.join(scopedPackagePath, constants.PACKAGE_JSON_FILE_NAME)).version;
                    scopedModulesInformation.currentVersion = currentScopedPackageVersion;
                    versionInformations.push(scopedModulesInformation);
                }
            }
            else {
                versionInformations.push(nativescriptCoreModulesInfo);
            }
            return versionInformations;
        });
    }
    getRuntimesVersions() {
        return __awaiter(this, void 0, void 0, function* () {
            const iosRuntime = this.$projectDataService.getRuntimePackage(this.projectData.projectDir, "ios");
            const androidRuntime = this.$projectDataService.getRuntimePackage(this.projectData.projectDir, "android");
            const runtimes = [iosRuntime, androidRuntime];
            const runtimesVersions = yield Promise.all(runtimes.map((runtime) => __awaiter(this, void 0, void 0, function* () {
                const latestVersion = yield this.$packageInstallationManager.getLatestVersion(runtime.name);
                const runtimeInformation = {
                    componentName: runtime.name,
                    currentVersion: runtime.version,
                    latestVersion,
                };
                return runtimeInformation;
            })));
            return runtimesVersions;
        });
    }
    getAllComponentsVersions() {
        return __awaiter(this, void 0, void 0, function* () {
            try {
                let allComponents = [];
                const nativescriptCliInformation = yield this.getNativescriptCliVersion();
                if (nativescriptCliInformation) {
                    allComponents.push(nativescriptCliInformation);
                }
                if (this.projectData) {
                    const nativescriptCoreModulesInformation = yield this.getTnsCoreModulesVersion();
                    if (nativescriptCoreModulesInformation) {
                        allComponents.push(...nativescriptCoreModulesInformation);
                    }
                    const runtimesVersions = yield this.getRuntimesVersions();
                    allComponents = allComponents.concat(runtimesVersions);
                }
                return allComponents.map((componentInformation) => {
                    if (componentInformation.currentVersion) {
                        if (this.hasUpdate(componentInformation)) {
                            componentInformation.type = VersionInformationType.UpdateAvailable;
                            componentInformation.message = `${VersionsService.UPDATE_AVAILABLE_MESSAGE} for component ${componentInformation.componentName}. Your current version is ${componentInformation.currentVersion} and the latest available version is ${componentInformation.latestVersion}.`;
                        }
                        else {
                            componentInformation.type = VersionInformationType.UpToDate;
                            componentInformation.message = `Component ${componentInformation.componentName} has ${componentInformation.currentVersion} version and is ${VersionsService.UP_TO_DATE_MESSAGE}.`;
                        }
                    }
                    else {
                        componentInformation.type = VersionInformationType.NotInstalled;
                        componentInformation.message = `Component ${componentInformation.componentName} is ${VersionsService.NOT_INSTALLED_MESSAGE}.`;
                    }
                    return componentInformation;
                });
            }
            catch (error) {
                this.$logger.trace("Error while trying to get component information. Error is: ", error);
                return [];
            }
        });
    }
    printVersionsInformation() {
        return __awaiter(this, void 0, void 0, function* () {
            const versionsInformation = yield this.$terminalSpinnerService.execute({
                text: `Getting NativeScript components versions information...`,
            }, () => this.getAllComponentsVersions());
            if (!helpers.isInteractive()) {
                versionsInformation.map((componentInformation) => this.$logger.info(componentInformation.message));
            }
            _.forEach(versionsInformation, (componentInformation) => {
                const spinner = this.$terminalSpinnerService.createSpinner();
                spinner.text = componentInformation.message;
                switch (componentInformation.type) {
                    case VersionInformationType.UpToDate:
                        spinner.succeed();
                        break;
                    case VersionInformationType.UpdateAvailable:
                        spinner.warn();
                        break;
                    case VersionInformationType.NotInstalled:
                        spinner.fail();
                        break;
                }
            });
        });
    }
    getProjectData() {
        try {
            const projectData = this.$injector.resolve("projectData");
            projectData.initializeProjectData();
            return projectData;
        }
        catch (error) {
            return null;
        }
    }
    hasUpdate(component) {
        return !semver.satisfies(component.latestVersion, semver.validRange(component.currentVersion));
    }
}
VersionsService.UP_TO_DATE_MESSAGE = "up to date";
VersionsService.UPDATE_AVAILABLE_MESSAGE = "Update available";
VersionsService.NOT_INSTALLED_MESSAGE = "not installed";
yok_1.injector.register("versionsService", VersionsService);
