///<reference path='Scripts\typings\node\node.d.ts' />
var __extends = this.__extends || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    __.prototype = b.prototype;
    d.prototype = new __();
};
var http = require("http");
var url = require("url");
var ai = require("./ai");
// environment variables
var ENV_azurePrefix = "APPSETTING_";
var ENV_iKey = "APPINSIGHTS_INSTRUMENTATION_KEY";
var ENV_appId = "APPINSIGHTS_APPLICATION_ID";
var ENV_appVer = "APPINSIGHTS_APPLICATION_VERSION";
var AppInsights = (function (_super) {
    __extends(AppInsights, _super);
    function AppInsights(config) {
        // ensure we have an instrumentationKey
        if (!config || !config.instrumentationKey) {
            var iKey = process.env[ENV_iKey] || process.env[ENV_azurePrefix + ENV_iKey];
            if (!iKey || iKey == "") {
                throw new Error("Instrumentation key not found, pass the key in the config to this method or set the key in the environment variable APPINSIGHTS_INSTRUMENTATION_KEY before starting the server");
            }
            config = config || {};
            config.instrumentationKey = iKey;
        }
        // load contexts/dependencies
        this._url = require("url");
        this._os = require("os");
        // set default values
        this.config = {
            instrumentationKey: config.instrumentationKey,
            endpointUrl: config.endpointUrl || "//dc.services.visualstudio.com/v2/track",
            accountId: config.accountId,
            appUserId: config.appUserId,
            sessionRenewalMs: 30 * 60 * 1000,
            sessionExpirationMs: 24 * 60 * 60 * 1000,
            maxPayloadSizeInBytes: config.maxPayloadSizeInBytes > 0 ? config.maxPayloadSizeInBytes : 200000,
            maxBatchSizeInBytes: config.maxBatchSizeInBytes > 0 ? config.maxBatchSizeInBytes : 1000000,
            maxBatchInterval: !isNaN(config.maxBatchInterval) ? config.maxBatchInterval : 15000,
            enableDebug: !!config.enableDebug,
            disableTelemetry: !!config.disableTelemetry,
            verboseLogging: !!config.verboseLogging,
            diagnosticLogInterval: config.diagnosticLogInterval || 10000,
            autoCollectErrors: false
        };
        this._enableCacheOnError = !!config.enableCacheOnError;
        // initialize base class
        _super.call(this, this.config);
        // load contexts
        this.context.application = new ai.Context.Application(process.env[ENV_appId] || process.env[ENV_azurePrefix + ENV_appId]);
        this.context.application.ver = process.env[ENV_appVer] || process.env[ENV_azurePrefix + ENV_appVer];
        this.context.device = new ai.Context.Device();
        this.context.device.id = this._os.hostname();
        this.context.device.os = this._os.type() + " " + this._os.release();
        this.context.device.osVersion = this._os.release();
        this.context.device.type = "server";
        this.context.location = new ai.Context.Location();
        // list of request types to ignore
        this._ignoredRequests = [];
        // wrap Javascript SDK sender to send data via HTTP requests
        var Sender = require("./Sender");
        var browserSender = this.context._sender;
        var sender = new Sender(browserSender._config);
        if (this._enableCacheOnError) {
            sender.enableCacheOnError();
        }
        browserSender._sender = function (payload) { return sender.send(payload); };
    }
    AppInsights.prototype.trackAllHttpServerRequests = function (ignoredRequests) {
        if (!this._originalServer) {
            var self = this;
            if (ignoredRequests) {
                this._ignoredRequests = this._ignoredRequests.concat(ignoredRequests);
            }
            this._originalServer = http.createServer;
            http.createServer = function (onRequest) {
                var lambda = function (request, response) {
                    if (self._shouldTrack(request)) {
                        self.trackRequest(request, response);
                    }
                    if (typeof onRequest === "function") {
                        onRequest(request, response);
                    }
                };
                return self._originalServer(lambda);
            };
        }
        return this;
    };
    /**
     * Restore original http.createServer (disable auto-collection of requests)
     */
    AppInsights.prototype.restoreHttpServerRequests = function () {
        if (this._originalServer) {
            http.createServer = this._originalServer;
            this._originalServer = undefined;
            delete this._originalServer;
        }
    };
    /**
     * Wrap process.on('uncaughtException') to automatically track exceptions
     */
    AppInsights.prototype.trackAllUncaughtExceptions = function () {
        var self = this;
        if (!this._exceptionListenerHandle) {
            this._exceptionListenerHandle = function (error) {
                self.trackException(error, "uncaughtException", { autoCollected: true });
                // Ensure i/o to transmit queued telemetry is initiated before re-throwing
                self.context._sender.triggerSend();
                throw error;
            };
            process.on("uncaughtException", this._exceptionListenerHandle);
        }
        return this;
    };
    /**
     * Restore original http.createServer (disable auto-collection of requests)
     */
    AppInsights.prototype.restoreUncaughtExceptions = function () {
        if (this._exceptionListenerHandle) {
            process.removeListener("uncaughtException", this._exceptionListenerHandle);
            this._exceptionListenerHandle = undefined;
            delete this._exceptionListenerHandle;
        }
    };
    /**
     * Tracks a request
     */
    AppInsights.prototype.trackRequest = function (request, response) {
        var _this = this;
        if (!request) {
            return;
        }
        // gather information about the request
        var startTime = +new Date;
        var pathname = url.parse(request.url).pathname;
        var name = request.method + " " + pathname;
        var properties = {
            rawURL: request.url.toString()
        };
        // set user/session context (this must be done before the response finish event fires)
        this._configureCookieHandlers(request, response);
        var user = new ai.Context.User(this.config.accountId);
        var session = new ai.Context.Session();
        session.update();
        session.update = undefined; // don't let the track method auto-update
        // response listeners
        if (response && response.once) {
            response.once('finish', function () {
                // gather information about the response
                var endTime = +new Date;
                var duration = endTime - startTime;
                var responseCode = response.statusCode;
                var success = (response.statusCode < 400);
                // create telemetry object
                var requestTelemetry;
                requestTelemetry = new ai.Telemetry.Request(name, startTime, duration, responseCode, success, properties);
                requestTelemetry.location = new ai.Context.Location();
                requestTelemetry.location.IP = _this._getClientIp(request);
                // add user/session context
                requestTelemetry.user = user;
                requestTelemetry.session = session;
                // track
                _this.context.track(requestTelemetry);
            });
        }
        // track an exception if the request throws an error
        request.on('error', function (e) {
            var error = new Error(e);
            var properties = { rawURL: request.url.toString() };
            var measurements = { "FailedAfter[ms]": +new Date - startTime };
            var exception;
            exception = new ai.Telemetry.Exception(error, "request.on('error')", properties, measurements);
            exception.location = new ai.Context.Location();
            exception.location.IP = _this._getClientIp(request);
            exception.user = user;
            exception.session = session;
            _this.context.track(exception);
        });
    };
    /**
     * filters requests specified in the filteredRequests array
     */
    AppInsights.prototype._shouldTrack = function (request) {
        if (request && this._ignoredRequests.length > 0) {
            var path = "" + this._url.parse(request.url).pathname;
            for (var i = 0; i < this._ignoredRequests.length; i++) {
                var x = "" + this._ignoredRequests[i];
                if (path.indexOf(x) > -1) {
                    return false;
                }
            }
        }
        return true;
    };
    AppInsights.prototype._getClientIp = function (request) {
        if (request) {
            // attempt to get IP from headers in case there is a proxy
            if (request.headers && request.headers['x-forwarded-for']) {
                var forwardedFor = request.headers['x-forwarded-for'];
                if (typeof forwardedFor.split === "function") {
                    forwardedFor.split(",")[0];
                    return forwardedFor.split(",")[0];
                }
            }
            // attempt to get IP from request
            if (request.connection && request.connection.remoteAddress) {
                return request.connection.remoteAddress;
            }
            else if (request.socket && request.socket.remoteAddress) {
                return request.socket.remoteAddress;
            }
            else if (request.connection && request.connection.socket && request.connection.socket.remoteAddress) {
                return request.connection.socket.remoteAddress;
            }
        }
        return "";
    };
    AppInsights.prototype._configureCookieHandlers = function (request, response) {
        ai.Util["document"] = {
            cookie: request.headers.cookie || ""
        };
        var cookieIndex = {};
        ai.Util.setCookie = function (name, value) {
            var headers = response.getHeader("Set-Cookie") || [];
            if (typeof headers == "string") {
                headers = [headers];
            }
            // overwrite existing cookies
            var data = name + "=" + value;
            if (cookieIndex[name]) {
                var index = cookieIndex[name];
                headers[index] = data;
            }
            else {
                cookieIndex[name] = headers.length;
                headers.push(data);
            }
            if (response && response["set"] && http["OutgoingMessage"] && http["OutgoingMessage"].prototype) {
                // use prototype if express is in use
                http["OutgoingMessage"].prototype.call(response, 'Set-Cookie', headers);
            }
            else {
                // otherwise use http.server default
                response.setHeader("Set-Cookie", headers);
            }
        };
    };
    return AppInsights;
})(ai.AppInsights);
var AppInsights;
(function (AppInsights) {
    // For compatibility with existing code, continue to
    // publish the class as a member of the imported module
    AppInsights.NodeAppInsights = AppInsights;
})(AppInsights || (AppInsights = {}));
module.exports = AppInsights;
