/*******************************************************************************
 * Copyright 2015 IBM Corp.
 *
 * 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.
 *******************************************************************************/
'use strict';

var Probe = require('../lib/probe.js');
var aspect = require('../lib/aspect.js');
var request = require('../lib/request.js');
var am = require('../');
var util = require('util');

function LeveldownProbe() {
  Probe.call(this, 'leveldown');
}
util.inherits(LeveldownProbe, Probe);

function aspectLvldownMethod(dbTarget, methods, probe) {
  aspect.before(dbTarget, methods, function(dbTarget, methodName, methodArgs, probeData) {
    probe.metricsProbeStart(probeData, dbTarget, methodName, methodArgs);
    probe.requestProbeStart(probeData, dbTarget, methodName, methodArgs);
    if (aspect.findCallbackArg(methodArgs) != undefined) {
      aspect.aroundCallback(methodArgs, probeData, function(dbTarget, args, probeData) {

        probe.metricsProbeEnd(probeData, methodName, methodArgs);
        probe.requestProbeEnd(probeData, methodName, methodArgs);
      });
    }
  });
}

// Attaches probe to module
LeveldownProbe.prototype.attach = function(name, target) {
  var that = this; // Referencing probe
  var methods = ['put', 'get', 'del', 'batch']; // Monitored leveldown methods
  if (name != 'leveldown') return target;
  if (target.__ddProbeAttached__) return target;

  // Wrapping the target in new function as leveldown returns constructor
  var newTarget = function() {
    var lvldownObj = target.apply(null, arguments);
    lvldownObj._ddProbeAttached_ = true;
    aspectLvldownMethod(lvldownObj, methods, that);
    return lvldownObj;
  };
  return newTarget;
};

/*
 * Lightweight metrics probe for leveldown queries
 *
 * These provide:
 * 		time:		time event started
 * 		method: 	leveldown method being executed
 *		key:		The key being used for a call to `get`, `put` or `del`
 *		value: 		The value being added to the LevelDB database using `put`
 *		opCount: 	The number of operations being performed by `batch`
 *		duration: 	The time taken for the LevelDB query to respond in ms
 *
 *		Note: key, value and opCount are undefined for methods other than those *		stated
 */

LeveldownProbe.prototype.metricsEnd = function(probeData, method, methodArgs) {
  if (probeData && probeData.timer) {
    probeData.timer.stop();
    if (method == 'put') {
      am.emit('leveldown', {
        time: probeData.timer.startTimeMillis,
        method: method,
        key: methodArgs[0],
        value: methodArgs[1],
        duration: probeData.timer.timeDelta,
      });
    } else if (method == 'del' || method == 'get') {
      am.emit('leveldown', {
        time: probeData.timer.startTimeMillis,
        method: method,
        key: methodArgs[0],
        duration: probeData.timer.timeDelta,
      });
    } else if (method == 'batch') {
      am.emit('leveldown', {
        time: probeData.timer.startTimeMillis,
        method: method,
        opCount: methodArgs[0].length,
        duration: probeData.timer.timeDelta,
      });
    }
  }
};

/*
 * Heavyweight request probes for leveldown queries
 */
LeveldownProbe.prototype.requestStart = function(probeData, dbTarget, method, methodArgs) {
  // FIXME(sam) req is used as a global to communicate with requestEnd, almost
  // certainly a bug, what happens if two requests are started before the first
  // ends? This is a bug, but it is being marked to be skipped by eslint
  // temporarily until it is fixed. See:
  //   https://github.com/RuntimeTools/appmetrics/pull/274#discussion_r122651577
  /* eslint-disable */
  req = request.startRequest('leveldown', 'query');
  req.setContext({ leveldown: methodArgs[0] });
  /* eslint-enable */
};

LeveldownProbe.prototype.requestEnd = function(probeData, method, methodArgs) {
  /* eslint-disable */
  if (probeData && probeData.req) req.stop({ leveldown: methodArgs[0] });
  /* eslint-enable */
};

module.exports = LeveldownProbe;
