"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 helpers = require("../helpers");
const cliGlobal = global;
cliGlobal.XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;
cliGlobal.XMLHttpRequest.prototype.withCredentials = false;
class AnalyticsServiceBase {
    constructor($logger, $options, $staticConfig, $prompter, $userSettingsService, $analyticsSettingsService, $progressIndicator, $osInfo) {
        this.$logger = $logger;
        this.$options = $options;
        this.$staticConfig = $staticConfig;
        this.$prompter = $prompter;
        this.$userSettingsService = $userSettingsService;
        this.$analyticsSettingsService = $analyticsSettingsService;
        this.$progressIndicator = $progressIndicator;
        this.$osInfo = $osInfo;
        this.analyticsStatuses = {};
        this.isAnalyticsStatusesInitialized = false;
    }
    get acceptTrackFeatureSetting() {
        return `Accept${this.$staticConfig.TRACK_FEATURE_USAGE_SETTING_NAME}`;
    }
    checkConsent() {
        return __awaiter(this, void 0, void 0, function* () {
            if (yield this.$analyticsSettingsService.canDoRequest()) {
                if ((yield this.isNotConfirmed(this.$staticConfig.TRACK_FEATURE_USAGE_SETTING_NAME)) && helpers.isInteractive()) {
                    this.$logger.out("Do you want to help us improve "
                        + this.$analyticsSettingsService.getClientName()
                        + " by automatically sending anonymous usage statistics? We will not use this information to identify or contact you."
                        + " You can read our official Privacy Policy at");
                    let message = this.$analyticsSettingsService.getPrivacyPolicyLink();
                    let trackFeatureUsage = yield this.$prompter.confirm(message, () => true);
                    yield this.setStatus(this.$staticConfig.TRACK_FEATURE_USAGE_SETTING_NAME, trackFeatureUsage, true);
                    if (!trackFeatureUsage) {
                        yield this.setStatus(this.$staticConfig.ERROR_REPORT_SETTING_NAME, trackFeatureUsage, true);
                    }
                    yield this.checkConsentCore(trackFeatureUsage);
                }
                if (yield this.isNotConfirmed(this.$staticConfig.ERROR_REPORT_SETTING_NAME)) {
                    this.$logger.out(`Error reporting will be enabled. You can disable it by running '$ ${this.$staticConfig.CLIENT_NAME.toLowerCase()} error-reporting disable'.`);
                    yield this.setStatus(this.$staticConfig.ERROR_REPORT_SETTING_NAME, true);
                }
            }
        });
    }
    trackFeature(featureName) {
        let category = this.$options.analyticsClient ||
            (helpers.isInteractive() ? "CLI" : "Non-interactive");
        return this.track(category, featureName);
    }
    track(featureName, featureValue) {
        return __awaiter(this, void 0, void 0, function* () {
            yield this.initAnalyticsStatuses();
            this.$logger.trace(`Trying to track feature '${featureName}' with value '${featureValue}'.`);
            if (this.analyticsStatuses[this.$staticConfig.TRACK_FEATURE_USAGE_SETTING_NAME] === AnalyticsStatus.enabled) {
                yield this.trackFeatureCore(`${featureName}.${featureValue}`);
            }
        });
    }
    trackException(exception, message) {
        return __awaiter(this, void 0, void 0, function* () {
            yield this.initAnalyticsStatuses();
            this.$logger.trace(`Trying to track exception with message '${message}'.`);
            if (this.analyticsStatuses[this.$staticConfig.ERROR_REPORT_SETTING_NAME] === AnalyticsStatus.enabled
                && (yield this.$analyticsSettingsService.canDoRequest())) {
                try {
                    yield this.start();
                    if (this._eqatecMonitor) {
                        this.$logger.printInfoMessageOnSameLine("Sending exception report (press Ctrl+C to stop)...");
                        this._eqatecMonitor.trackException(exception, message);
                        yield this.$progressIndicator.showProgressIndicator(this.waitForSending(), 500);
                    }
                }
                catch (e) {
                    this.$logger.trace("Analytics exception: '%s'", e.toString());
                }
            }
        });
    }
    setStatus(settingName, enabled, doNotTrackSetting) {
        return __awaiter(this, void 0, void 0, function* () {
            this.analyticsStatuses[settingName] = enabled ? AnalyticsStatus.enabled : AnalyticsStatus.disabled;
            yield this.$userSettingsService.saveSetting(settingName, enabled.toString());
            if (!doNotTrackSetting) {
                yield this.trackFeatureCore(`${settingName}.${enabled ? "enabled" : "disabled"}`);
            }
            if (this.analyticsStatuses[settingName] === AnalyticsStatus.disabled
                && this.analyticsStatuses[settingName] === AnalyticsStatus.disabled) {
                this.tryStopEqatecMonitor();
            }
        });
    }
    isEnabled(settingName) {
        return __awaiter(this, void 0, void 0, function* () {
            let analyticsStatus = yield this.getStatus(settingName);
            return analyticsStatus === AnalyticsStatus.enabled;
        });
    }
    tryStopEqatecMonitor(code) {
        if (this._eqatecMonitor) {
            process.removeListener("exit", this.tryStopEqatecMonitor);
            this._eqatecMonitor.stop();
            this._eqatecMonitor = null;
        }
    }
    getStatusMessage(settingName, jsonFormat, readableSettingName) {
        if (jsonFormat) {
            return this.getJsonStatusMessage(settingName);
        }
        return this.getHumanReadableStatusMessage(settingName, readableSettingName);
    }
    restartEqatecMonitor(projectApiKey) {
        return __awaiter(this, void 0, void 0, function* () {
            this.tryStopEqatecMonitor();
            yield this.start(projectApiKey);
        });
    }
    checkConsentCore(trackFeatureUsage) {
        return this.trackFeatureCore(`${this.acceptTrackFeatureSetting}.${!!trackFeatureUsage}`);
    }
    trackFeatureCore(featureTrackString) {
        return __awaiter(this, void 0, void 0, function* () {
            try {
                if (yield this.$analyticsSettingsService.canDoRequest()) {
                    yield this.start();
                    if (this._eqatecMonitor) {
                        this._eqatecMonitor.trackFeature(featureTrackString);
                        yield this.waitForSending();
                    }
                }
            }
            catch (e) {
                this.$logger.trace("Analytics exception: '%s'", e.toString());
            }
        });
    }
    getStatus(settingName) {
        return __awaiter(this, void 0, void 0, function* () {
            if (!this.analyticsStatuses[settingName]) {
                let settingValue = yield this.$userSettingsService.getSettingValue(settingName);
                if (settingValue) {
                    let isEnabled = helpers.toBoolean(settingValue);
                    if (isEnabled) {
                        this.analyticsStatuses[settingName] = AnalyticsStatus.enabled;
                    }
                    else {
                        this.analyticsStatuses[settingName] = AnalyticsStatus.disabled;
                    }
                }
                else {
                    this.analyticsStatuses[settingName] = AnalyticsStatus.notConfirmed;
                }
            }
            return this.analyticsStatuses[settingName];
        });
    }
    start(analyticsProjectKey) {
        return __awaiter(this, void 0, void 0, function* () {
            if (this._eqatecMonitor) {
                return;
            }
            require("../vendor/EqatecMonitor.min");
            analyticsProjectKey = analyticsProjectKey || this.$staticConfig.ANALYTICS_API_KEY;
            let settings = cliGlobal._eqatec.createSettings(analyticsProjectKey);
            settings.useHttps = false;
            settings.userAgent = this.getUserAgentString();
            settings.version = this.$staticConfig.version;
            settings.useCookies = false;
            settings.loggingInterface = {
                logMessage: this.$logger.trace.bind(this.$logger),
                logError: this.$logger.debug.bind(this.$logger)
            };
            this._eqatecMonitor = cliGlobal._eqatec.createMonitor(settings);
            let guid = yield this.$userSettingsService.getSettingValue(this.$staticConfig.ANALYTICS_INSTALLATION_ID_SETTING_NAME);
            if (!guid) {
                guid = helpers.createGUID(false);
                yield this.$userSettingsService.saveSetting(this.$staticConfig.ANALYTICS_INSTALLATION_ID_SETTING_NAME, guid);
            }
            this.$logger.trace("%s: %s", this.$staticConfig.ANALYTICS_INSTALLATION_ID_SETTING_NAME, guid.toString());
            this._eqatecMonitor.setInstallationID(guid);
            try {
                yield this._eqatecMonitor.setUserID(this.$analyticsSettingsService.getUserId());
                let currentCount = yield this.$analyticsSettingsService.getUserSessionsCount(analyticsProjectKey);
                yield this.$analyticsSettingsService.setUserSessionsCount(++currentCount, analyticsProjectKey);
                this._eqatecMonitor.setStartCount(currentCount);
            }
            catch (e) {
                this.$logger.trace("Error while initializing eqatecMonitor", e);
            }
            this._eqatecMonitor.start();
            process.on("exit", this.tryStopEqatecMonitor);
            yield this.reportNodeVersion();
        });
    }
    reportNodeVersion() {
        return __awaiter(this, void 0, void 0, function* () {
            let reportedVersion = process.version.slice(1).replace(/[.]/g, "_");
            yield this.track("NodeJSVersion", reportedVersion);
        });
    }
    getUserAgentString() {
        let userAgentString;
        let osType = this.$osInfo.type();
        if (osType === "Windows_NT") {
            userAgentString = "(Windows NT " + this.$osInfo.release() + ")";
        }
        else if (osType === "Darwin") {
            userAgentString = "(Mac OS X " + this.$osInfo.release() + ")";
        }
        else {
            userAgentString = "(" + osType + ")";
        }
        return userAgentString;
    }
    isNotConfirmed(settingName) {
        return __awaiter(this, void 0, void 0, function* () {
            let analyticsStatus = yield this.getStatus(settingName);
            return analyticsStatus === AnalyticsStatus.notConfirmed;
        });
    }
    getHumanReadableStatusMessage(settingName, readableSettingName) {
        return __awaiter(this, void 0, void 0, function* () {
            let status = null;
            if (yield this.isNotConfirmed(settingName)) {
                status = "disabled until confirmed";
            }
            else {
                status = AnalyticsStatus[yield this.getStatus(settingName)];
            }
            return `${readableSettingName} is ${status}.`;
        });
    }
    getJsonStatusMessage(settingName) {
        return __awaiter(this, void 0, void 0, function* () {
            let status = yield this.getStatus(settingName);
            let enabled = status === AnalyticsStatus.notConfirmed ? null : status === AnalyticsStatus.disabled ? false : true;
            return JSON.stringify({ enabled: enabled });
        });
    }
    initAnalyticsStatuses() {
        return __awaiter(this, void 0, void 0, function* () {
            if (yield this.$analyticsSettingsService.canDoRequest()) {
                if (!this.isAnalyticsStatusesInitialized) {
                    this.$logger.trace("Initializing analytics statuses.");
                    let settingsNames = [this.$staticConfig.TRACK_FEATURE_USAGE_SETTING_NAME, this.$staticConfig.ERROR_REPORT_SETTING_NAME];
                    for (let settingsIndex = 0; settingsIndex < settingsNames.length; ++settingsIndex) {
                        const settingName = settingsNames[settingsIndex];
                        yield this.getStatus(settingName);
                    }
                    this.isAnalyticsStatusesInitialized = true;
                }
                this.$logger.trace("Analytics statuses: ");
                this.$logger.trace(this.analyticsStatuses);
            }
        });
    }
    getIsSending() {
        return this._eqatecMonitor.status().isSending;
    }
    waitForSending() {
        return new Promise((resolve, reject) => {
            let intervalTime = 1000;
            let remainingTime = AnalyticsServiceBase.MAX_WAIT_SENDING_INTERVAL;
            if (this.getIsSending()) {
                this.$logger.trace(`Waiting for analytics to send information. Will check in a ${intervalTime}ms.`);
                let interval = setInterval(() => {
                    if (!this.getIsSending() || (remainingTime <= 0)) {
                        clearInterval(interval);
                        resolve();
                    }
                    remainingTime -= intervalTime;
                    this.$logger.trace(`Waiting for analytics to send information. Will check in a ${intervalTime}ms. Remaining time is: ${remainingTime}`);
                }, intervalTime);
            }
            else {
                resolve();
            }
        });
    }
}
AnalyticsServiceBase.MAX_WAIT_SENDING_INTERVAL = 30000;
exports.AnalyticsServiceBase = AnalyticsServiceBase;
var AnalyticsStatus;
(function (AnalyticsStatus) {
    AnalyticsStatus[AnalyticsStatus["enabled"] = 0] = "enabled";
    AnalyticsStatus[AnalyticsStatus["disabled"] = 1] = "disabled";
    AnalyticsStatus[AnalyticsStatus["notConfirmed"] = 2] = "notConfirmed";
})(AnalyticsStatus = exports.AnalyticsStatus || (exports.AnalyticsStatus = {}));
