'use strict';

var async   = require('async');
var mongodb = require('mongodb');
var _       = require('underscore');

var connect = function(config) {
  // set up database stuff
  var host = config.mongodb.server  || 'localhost';
  var port = config.mongodb.port    || mongodb.Connection.DEFAULT_PORT;
  var dbOptions = {
    auto_reconnect: config.mongodb.autoReconnect,
    poolSize: config.mongodb.poolSize,
  };

  var db;

  if (Array.isArray(host)) {
    host = host.map(function(host) {
      return new mongodb.Server(host, port, dbOptions);
    });

    db = new mongodb.Db('local', new mongodb.ReplSet(host), { safe: true, w: 0 });
  } else {
    db = new mongodb.Db('local', new mongodb.Server(host, port, dbOptions), {safe:true});
  }

  var connections = {};
  var databases   = [];
  var collections = {};

  //get admin instance
  var adminDb = db.admin();
  var mainConn; // main db connection

  // update the collections list
  var updateCollections = function(db, dbName, callback) {
    db.listCollections().toArray(function(err, result) {
      var names = [];

      for (var r in result) {
        names.push(result[r].name);
      }

      collections[dbName] = names.sort();

      if (callback) {
        callback(err);
      }
    });
  };

  // update database list
  var updateDatabases = function(admin, callback) {
    admin.listDatabases(function(err, dbs) {
      databases = [];
      if (err) {
        //TODO: handle error
        console.error(err);
        databases = _.pluck(config.mongodb.auth, 'database');
      } else {
        for (var i = 0; i < dbs.databases.length; i++) {
          var dbName = dbs.databases[i].name;

          //'local' is special database, ignore it
          if (dbName === 'local') {
            continue;
          }

          if (config.mongodb.whitelist.length !== 0) {
            if (!_.include(config.mongodb.whitelist, dbName)) {
              continue;
            }
          }

          if (config.mongodb.blacklist.length !== 0) {
            if (_.include(config.mongodb.blacklist, dbName)) {
              continue;
            }
          }

          connections[dbName] = mainConn.db(dbName);
          databases.push(dbName);
          updateCollections(connections[dbName], dbName);
        }
      }

      //Sort database names
      databases = databases.sort();

      if (callback) {
        callback(databases);
      }
    });
  };

  // connect to mongodb database
  db.open(function(err, db) {
    if (err) {
      throw err;
    }

    console.log('Database connected');

    mainConn = db;

    //Check if admin features are on
    if (config.mongodb.admin === true) {
      if (config.mongodb.adminUsername.length === 0) {
        console.log('Admin Database connected');
        updateDatabases(adminDb);
      } else {
        //auth details were supplied, authenticate admin account with them
        adminDb.authenticate(config.mongodb.adminUsername, config.mongodb.adminPassword, function(err) {
          if (err) {
            //TODO: handle error
            console.error(err);
          }

          console.log('Admin Database connected');
          updateDatabases(adminDb);
        });
      }
    } else {
      //Regular user authentication
      if (typeof config.mongodb.auth === 'undefined' || config.mongodb.auth.length === 0) {
        throw new Error('Add auth details to config or turn on admin!');
      }

      async.forEachSeries(config.mongodb.auth, function(auth, callback) {
        console.log('Connecting to ' + auth.database + '...');
        connections[auth.database] = mainConn.db(auth.database);
        databases.push(auth.database);

        if (typeof auth.username !== 'undefined' && auth.username.length !== 0) {
          connections[auth.database].authenticate(auth.username, auth.password, function(err, success) {
            if (err) {
              //TODO: handle error
              console.error(err);
            }

            if (!success) {
              console.error('Could not authenticate to database "' + auth.database + '"');
            }

            updateCollections(connections[auth.database], auth.database);
            console.log('Database ' + auth.database + ' connected');
            callback();
          });
        } else {
          updateCollections(connections[auth.database], auth.database);
          console.log('Database ' + auth.database + ' connected');
          callback();
        }
      });
    }
  });

  return {
    adminDb:            adminDb,
    collections:        collections,
    connections:        connections,
    databases:          databases,
    mainConn:           mainConn,
    updateCollections:  updateCollections,
    updateDatabases:    updateDatabases,
  };
};

module.exports = connect;
