/**
 * Copyright 2017 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * This file has been modified by Keymetrics
 */

'use strict';
var shimmer = require('shimmer');
var methods = require('methods').concat('use', 'route', 'param', 'all');

var SUPPORTED_VERSIONS = '4.x';

function patchModuleRoot(express, api) {
  var labels = api.labels;
  function applicationActionWrap(method) {
    return function expressActionTrace() {
      if (!this._google_trace_patched && !this._router) {
        this._google_trace_patched = true;
        this.use(middleware);
      }
      return method.apply(this, arguments);
    };
  }

  function middleware(req, res, next) {
    var options = {
      name: req.path,
      traceContext: req.get(api.constants.TRACE_CONTEXT_HEADER_NAME),
      url: req.originalUrl,
      ip: req.connection.remoteAddress,
      method: req.method,
      skipFrames: 3
    };
    api.runInRootSpan(options, function(rootSpan) {
      if (!rootSpan) {
        next();
        return;
      }

      // Set outgoing trace context.
      res.set(api.constants.TRACE_CONTEXT_HEADER_NAME,
        rootSpan.getTraceContext());

      api.wrapEmitter(req);
      api.wrapEmitter(res);

      var url = req.protocol + '://' + req.hostname + req.originalUrl;
      rootSpan.addLabel(labels.HTTP_METHOD_LABEL_KEY, req.method);
      rootSpan.addLabel(labels.HTTP_PATH_LABEL_KEY, options.name);
      rootSpan.addLabel(labels.HTTP_URL_LABEL_KEY, url);
      rootSpan.addLabel(labels.HTTP_SOURCE_IP, req.connection.remoteAddress);

      // wrap end
      var originalEnd = res.end;
      res.end = function(chunk, encoding) {
        res.end = originalEnd;
        var returned = res.end(chunk, encoding);

        if (req.route && req.route.path) {
          rootSpan.addLabel('express/request.route.path', req.route.path);
        }
        rootSpan.addLabel(labels.HTTP_RESPONSE_CODE_LABEL_KEY, res.statusCode);
        rootSpan.endSpan();
        return returned;
      };

      next();
    });
  }

  methods.forEach(function(method) {
    shimmer.wrap(express.application, method, applicationActionWrap);
  });
}

function unpatchModuleRoot(express) {
  methods.forEach(function(method) {
    shimmer.unwrap(express.application, method);
  });
}

module.exports = [{
    versions: SUPPORTED_VERSIONS,
    patch: patchModuleRoot,
    unpatch: unpatchModuleRoot
}];
