/** @module dbMigrate */

/**
 * Migration module. Used internally to perform the principle database migrations.
 *
 * @author Frank Villasenor
 * @param {external:Database} dbConnection The Database connection on which to perform the actions
 * @param {object} migrations (can probably create a link to more doc)
 * @returns {promise} Which resolves once all
 */
module.exports = function (dbConnection, migrations) {
  "use strict";

  const $npm = {
    debug: require('debug')('db-migration:dbMigrate'),
    Version: require('./dbVersion'),
    dbInfo: require('./dbInfo'),
    dbConnection: dbConnection,
    Promise: require('bluebird')
  };

  function processVersion(migrationDataObj) {
    var versionMigrations = migrationDataObj.migrations;
    var version = migrationDataObj.version;

    return $npm.dbConnection.tx(function(t){
      var batchList = [];

      //tables
      versionMigrations.tables.map(function(tableSql) {
        batchList.push( t.none(tableSql) );
      });

      //inserts
      versionMigrations.data.map(function(dataSql) {
        batchList.push( t.tx(function(t1) {return t1.oneOrNone(dataSql); } ));
      });

      //indexes
      versionMigrations.indexes.map(function(indexSql){
        batchList.push( t.none(indexSql) );
      });

      batchList.push( t.one("UPDATE pg_migration_dbinfo set value = $1 WHERE key = 'db_version' returning value;", [version]) );

      return t.batch(batchList).then(function(output){
        //$npm.debug('[then] ', output);
        return {result:true, output:output};
      }).catch(function(er){
        $npm.debug('[error-1] Error:', er);
        throw er;
      });
    });
  }

  //filter out already deployed ersions
  return $npm.dbInfo(dbConnection)
    .then(function(verObj) {
      $npm.debug('version: ', verObj.version); //this will give us the version info.
      const currentVersion = new $npm.Version(verObj.version);

      //Remove deployed versions
      var versions = Object.keys( migrations );
      versions = versions.filter(function(v) { return v != 'latest'; });
      versions = versions.filter(function(v) {
        const myVersion = new $npm.Version(v);
        return myVersion.amNewer( currentVersion );
      });

      //we take the keys and order them...
      versions = versions.sort(function(a, b){
        var versionA = new $npm.Version(a);
        var versionB = new $npm.Version(b);
        var compareResult = versionA.compare(versionB);
        $npm.debug('[sort] compareResult', compareResult);
        return compareResult;
      });

      //Now, we need to set up a serial promise...
      var orderedVersions = versions.map(function(ver) {
        return {migrations: migrations[ver], version: ver};
      });

      return $npm.Promise.mapSeries(orderedVersions, function(item, index, length){
        return processVersion(item);
      }).then(function(out) {
        $npm.debug('[then] mapSeries is done', out);
        var result = {overallResult:true};
        out.forEach(function(val, idx) {
          result.overallResult = result.overallResult && val.result;
          result[ versions[idx] ] = val;
        });
        return result;
      }).catch(function(er){
        $npm.debug('[catch] error from mapSeries', er);
        throw er;
      });
    });
};