'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
exports.HttpMetricsConfig = void 0;
const shimmer = require("shimmer");
const debug_1 = require("debug");
const configuration_1 = require("../configuration");
const serviceManager_1 = require("../serviceManager");
const histogram_1 = require("../utils/metrics/histogram");
const requireMiddle = require("require-in-the-middle");
const metrics_1 = require("../services/metrics");
class HttpMetricsConfig {
}
exports.HttpMetricsConfig = HttpMetricsConfig;
class HttpMetrics {
    constructor() {
        this.defaultConf = {
            http: true
        };
        this.metrics = new Map();
        this.logger = (0, debug_1.default)('axm:features:metrics:http');
        this.modules = {};
    }
    init(config) {
        if (config === false)
            return;
        if (config === undefined) {
            config = this.defaultConf;
        }
        if (typeof config !== 'object') {
            config = this.defaultConf;
        }
        this.logger('init');
        configuration_1.default.configureModule({
            latency: true
        });
        this.metricService = serviceManager_1.ServiceManager.get('metrics');
        if (this.metricService === undefined)
            return this.logger(`Failed to load metric service`);
        this.logger('hooking to require');
        this.hookRequire();
    }
    registerHttpMetric() {
        if (this.metricService === undefined)
            return this.logger(`Failed to load metric service`);
        const histogram = new histogram_1.default();
        const p50 = {
            name: `HTTP Mean Latency`,
            id: 'internal/http/builtin/latency/p50',
            type: metrics_1.MetricType.histogram,
            historic: true,
            implementation: histogram,
            unit: 'ms',
            handler: () => {
                const percentiles = histogram.percentiles([0.5]);
                return percentiles[0.5];
            }
        };
        const p95 = {
            name: `HTTP P95 Latency`,
            id: 'internal/http/builtin/latency/p95',
            type: metrics_1.MetricType.histogram,
            historic: true,
            implementation: histogram,
            handler: () => {
                const percentiles = histogram.percentiles([0.95]);
                return percentiles[0.95];
            },
            unit: 'ms'
        };
        const meter = {
            name: 'HTTP',
            historic: true,
            id: 'internal/http/builtin/reqs',
            unit: 'req/min'
        };
        this.metricService.registerMetric(p50);
        this.metricService.registerMetric(p95);
        this.metrics.set('http.latency', histogram);
        this.metrics.set('http.meter', this.metricService.meter(meter));
    }
    registerHttpsMetric() {
        if (this.metricService === undefined)
            return this.logger(`Failed to load metric service`);
        const histogram = new histogram_1.default();
        const p50 = {
            name: `HTTPS Mean Latency`,
            id: 'internal/https/builtin/latency/p50',
            type: metrics_1.MetricType.histogram,
            historic: true,
            implementation: histogram,
            unit: 'ms',
            handler: () => {
                const percentiles = histogram.percentiles([0.5]);
                return percentiles[0.5];
            }
        };
        const p95 = {
            name: `HTTPS P95 Latency`,
            id: 'internal/https/builtin/latency/p95',
            type: metrics_1.MetricType.histogram,
            historic: true,
            implementation: histogram,
            handler: () => {
                const percentiles = histogram.percentiles([0.95]);
                return percentiles[0.95];
            },
            unit: 'ms'
        };
        const meter = {
            name: 'HTTPS',
            historic: true,
            id: 'internal/https/builtin/reqs',
            unit: 'req/min'
        };
        this.metricService.registerMetric(p50);
        this.metricService.registerMetric(p95);
        this.metrics.set('https.latency', histogram);
        this.metrics.set('https.meter', this.metricService.meter(meter));
    }
    destroy() {
        if (this.modules.http !== undefined) {
            this.logger('unwraping http module');
            shimmer.unwrap(this.modules.http, 'emit');
            this.modules.http = undefined;
        }
        if (this.modules.https !== undefined) {
            this.logger('unwraping https module');
            shimmer.unwrap(this.modules.https, 'emit');
            this.modules.https = undefined;
        }
        if (this.hooks) {
            this.hooks.unhook();
        }
        this.logger('destroy');
    }
    hookHttp(nodule, name) {
        if (nodule.Server === undefined || nodule.Server.prototype === undefined)
            return;
        if (this.modules[name] !== undefined)
            return this.logger(`Module ${name} already hooked`);
        this.logger(`Hooking to ${name} module`);
        this.modules[name] = nodule.Server.prototype;
        if (name === 'http') {
            this.registerHttpMetric();
        }
        else if (name === 'https') {
            this.registerHttpsMetric();
        }
        const self = this;
        shimmer.wrap(nodule.Server.prototype, 'emit', (original) => {
            return function (event, req, res) {
                if (event !== 'request')
                    return original.apply(this, arguments);
                const meter = self.metrics.get(`${name}.meter`);
                if (meter !== undefined) {
                    meter.mark();
                }
                const latency = self.metrics.get(`${name}.latency`);
                if (latency === undefined)
                    return original.apply(this, arguments);
                if (res === undefined || res === null)
                    return original.apply(this, arguments);
                const startTime = Date.now();
                res.once('finish', _ => {
                    latency.update(Date.now() - startTime);
                });
                return original.apply(this, arguments);
            };
        });
    }
    hookRequire() {
        this.hooks = requireMiddle(['http', 'https'], (exports, name) => {
            this.hookHttp(exports, name);
            return exports;
        });
    }
}
exports.default = HttpMetrics;
