'use strict';

// NB: THIS SCRIPT CURRENTLY CALCULATES PAYOUTS FROM THE BEGINNING OF TIME
// NB: TAKE CARE TO UPDATE IT BEFORE THE NEXT PAYOUT!

const log = require('../lib/logger');
const fs = require('fs');
const csvWriter = require('csv-write-stream');
const Config = require('../lib/config');
const Storage = require('../lib/storage');
const config = new Config(process.env.NODE_ENV);
const storage = new Storage(config.storage);
const cursor = storage.models.Shard.find({}).cursor();
const csvOutPath = process.argv[2];

if (!csvOutPath) {
  log.info('NO OUT PATH SUPPLIED FOR CSV, WILL PRINT TO CONSOLE');
}

var reports = {};

function FarmerReport(nodeId) {
  this.nodeID = nodeId;
  this.contracts = 0;
  this.storedBytes = 0;
  this.storedTime = 0;
  this.downloadedBytes = 0;
  this.downloadCount = 0;
  this.paymentDestination = '';
  this.gigabyteHours = 0;
}

function TotalReport(reports) {
  FarmerReport.call(this, null);

  delete this.nodeID;
  delete this.paymentDestinations;

  this.farmers = 0;

  for (let nodeID in reports) {
    this.farmers++;
    this.contracts += reports[nodeID].contracts;
    this.storedBytes += reports[nodeID].storedBytes;
    this.storedTime += reports[nodeID].storedTime;
    this.downloadedBytes += reports[nodeID].downloadedBytes;
    this.downloadCount += reports[nodeID].downloadCount;
    this.gigabyteHours += reports[nodeID].gigabyteHours;
  }
}

cursor.on('data', function(doc) {

  function getDownloadCountForContract(nodeId) {
    var count = 0;

    doc.meta.forEach(function(subdoc) {
      if (subdoc.nodeID !== nodeId) {
        return false;
      }

      count = subdoc.meta ? (subdoc.meta.downloadCount || 0) : 0;
    });

    return count;
  }

  doc.contracts.forEach(function(subdoc) {
    if (subdoc.nodeID.length !== 40) {
      return false;
    }

    if (!reports[subdoc.nodeID]) {
      reports[subdoc.nodeID] = new FarmerReport(subdoc.nodeID);
    }

    if (!subdoc.contract) {
      return false;
    }

    var c = getDownloadCountForContract(subdoc.nodeID);
    var contractIsActive = Date.now() < subdoc.contract.store_end;
    var dest = subdoc.contract.payment_destination;

    reports[subdoc.nodeID].downloadedBytes += c * subdoc.contract.data_size;
    reports[subdoc.nodeID].contracts++;
    reports[subdoc.nodeID].downloadCount += c;
    var bytes = reports[subdoc.nodeID].storedBytes += subdoc.contract.data_size;

    var time = reports[subdoc.nodeID].storedTime += contractIsActive ?
      Date.now() - subdoc.contract.store_begin :
      subdoc.contract.store_end - subdoc.contract.store_begin;
    var hours = parseInt((time / (1000 * 60 * 60)) % 24);
    var gigabytes = bytes / 1073741824;

    if (dest) {
      reports[subdoc.nodeID].paymentDestination = dest;
    }

    reports[subdoc.nodeID].gigabyteHours += gigabytes * hours;
  });
});

cursor.on('close', function() {
  if (!csvOutPath) {
    console.log('Farmer Reports:');
    console.log('---------------');
    console.log('');

    for (let report in reports) {
      console.log(reports[report]);
      console.log('');
    }

    console.log('Totals Reports:');
    console.log('---------------');
    console.log('');
    console.log(new TotalReport(reports));
    process.exit();
  }

  var writeOptions = {
    headers: [
      'Node ID',
      'Total Contracts',
      'Stored Bytes',
      'Stored Time',
      'Downloaded Bytes',
      'Download Count',
      'Payment Destination',
      'Gigabyte Hours'
    ]
  };

  function runTotalReportOutToCSV(callback) {
    var writeStream = fs.createWriteStream('totals.' + csvOutPath);
    var writer = csvWriter(writeOptions);
    var totals = new TotalReport(reports);

    writer.pipe(writeStream).on('finish', function() {
      console.log('Total report CSV written to %s', csvOutPath);
      callback();
    });

    writer.write([
      totals.nodeID || 'none',
      totals.contracts,
      totals.storedBytes,
      totals.storedTime,
      totals.downloadedBytes,
      totals.downloadCount,
      totals.paymentDestination || 'none',
      totals.gigabyteHours
    ]);

    writer.end();
  }

  function runFarmerReportOutToCSV(callback) {
    var writeStream = fs.createWriteStream(csvOutPath);
    var writer = csvWriter(writeOptions);

    writer.pipe(writeStream).on('finish', function() {
      console.log('Farmer report CSV written to %s', csvOutPath);
      callback();
    });

    for (let report in reports) {
      writer.write([
        reports[report].nodeID || 'none',
        reports[report].contracts,
        reports[report].storedBytes,
        reports[report].storedTime,
        reports[report].downloadedBytes,
        reports[report].downloadCount,
        reports[report].paymentDestination || 'none',
        reports[report].gigabyteHours
      ]);
    }

    writer.end();
  }

  runFarmerReportOutToCSV(function() {
    log.info('Farmer report written to %s', csvOutPath);

    runTotalReportOutToCSV(function() {
      log.info('Totals report written to totals.%s', csvOutPath);
      process.exit();
    });
  });
});

@@ -864,16 +864,42 @@
 rs = 0;%0A
+  this.gibibyteHours = 0;%0A
 %7D%0A%0Afunct
@@ -1418,16 +1418,73 @@
 eHours;%0A
+    this.gibibyteHours += reports%5BnodeID%5D.gibibyteHours;%0A
   %7D%0A%7D%0A%0Ac
@@ -2760,14 +2760,70 @@
 / 10
-737418
+00 * 1000 * 1000;%0A    var gibibytes = bytes / 1024 * 1024 * 10
 24;%0A
@@ -2965,16 +2965,80 @@
  hours;%0A
+    reports%5Bsubdoc.nodeID%5D.gibibyteHours += gibibytes * hours;%0A%0A
   %7D);%0A%7D)
@@ -4281,24 +4281,51 @@
 gabyteHours%0A
+      totals.gibibyteHours%0A
     %5D);%0A%0A   
@@ -4969,32 +4969,32 @@
 tion %7C%7C 'none',%0A
-
         reports%5B
@@ -5011,24 +5011,62 @@
 gabyteHours%0A
+        reports%5Breport%5D.gibibyteHours%0A
       %5D);%0A  

update payouts.js with gibibytes