#!/usr/bin/env node

'use strict';

var _ = require('lodash');
var $ = require('preconditions').singleton();
var async = require('async');
var log = require('npmlog');
log.debug = log.verbose;
log.disableColor();
var mongodb = require('mongodb');
var moment = require('moment');

var config = require('../config');
var storage = require('./storage');

function Stats(opts) {
  opts = opts || {};

  this.network = opts.network || 'livenet';
  this.from = moment(opts.from || '2015-01-01');
  this.to = moment(opts.to);
  this.fromTs = Math.floor(this.from.startOf('day').valueOf() / 1000);
  this.toTs = Math.floor(this.to.endOf('day').valueOf() / 1000);
};

Stats.prototype.run = function(cb) {
  var self = this;

  var uri = config.storageOpts.mongoDb.uri;
  mongodb.MongoClient.connect(uri, function(err, db) {
    if (err) {
      log.error('Unable to connect to the mongoDB', err);
      return cb(err, null);
    }
    log.info('Connection established to ' + uri);
    self.db = db;
    self._getStats(function(err, stats) {
      if (err) return cb(err);
      return cb(null, stats);
    });
  });
};

Stats.prototype._getStats = function(cb) {
  var self = this;
  var result = {};
  async.parallel([

    function(next) {
      self._getNewWallets(next);
    },
    function(next) {
      self._getTxProposals(next);
    },
  ], function(err, results) {
    if (err) return cb(err);

    result.newWallets = results[0];
    result.txProposals = results[1];
    return cb(null, result);
  });
};

Stats.prototype._countBy = function(data, key) {
  return _.map(_.groupBy(data, key), function(v, k) {
    var item = {};
    item[key] = k;
    item['count'] = v.length;
    return item;
  });
};

Stats.prototype._sumBy = function(data, key, attr) {
  return _.map(_.groupBy(data, key), function(v, k) {
    var item = {};
    item[key] = k;
    item[attr] = _.reduce(v, function(memo, x) {
      return memo + x[attr];
    }, 0);
    return item;
  });
};

Stats.prototype._getNewWallets = function(cb) {
  var self = this;

  self.db.collection(storage.collections.WALLETS)
    .find({
      network: self.network,
      createdOn: {
        $gte: self.fromTs,
        $lte: self.toTs,
      },
    })
    .toArray(function(err, wallets) {
      if (err) return cb(err);

      var data = _.map(wallets, function(wallet) {
        return {
          day: moment(wallet.createdOn * 1000).format('YYYYMMDD'),
          type: (wallet.m == 1 && wallet.n == 1) ? 'personal' : 'shared',
          config: wallet.m + '-of-' + wallet.n,
        };
      });

      var stats = {
        byDay: self._countBy(data, 'day'),
        byConfig: self._countBy(data, 'config'),
      };

      stats.byTypeThenDay = _.groupBy(data, 'type');
      _.each(stats.byTypeThenDay, function(v, k) {
        stats.byTypeThenDay[k] = self._countBy(v, 'day');
      });

      return cb(null, stats);
    });
};

Stats.prototype._getTxProposals = function(cb) {
  var self = this;

  self.db.collection(storage.collections.TXS)
    .find({
      network: self.network,
      status: 'broadcasted',
      createdOn: {
        $gte: self.fromTs,
        $lte: self.toTs,
      },
    })
    .toArray(function(err, txps) {
      if (err) return cb(err);

      var data = _.map(txps, function(txp) {
        return {
          day: moment(txp.createdOn * 1000).format('YYYYMMDD'),
          amount: txp.amount,
        };
      });

      var stats = {
        nbByDay: self._countBy(data, 'day'),
        amountByDay: self._sumBy(data, 'day', 'amount'),
      };

      return cb(null, stats);
    });
};

module.exports = Stats;