'use strict';

var _               = require('underscore');
var basicAuth       = require('basic-auth-connect');
var bodyParser      = require('body-parser');
var cookieParser    = require('cookie-parser');
var db              = require('./db');
var errorHandler    = require('errorhandler');
var express         = require('express');
var favicon         = require('serve-favicon');
var logger          = require('morgan');
var methodOverride  = require('method-override');
var mongodb         = require('mongodb');
var routes          = require('./routes');
var session         = require('express-session');

var router = function(config) {
  // appRouter configuration
  var appRouter = express.Router();
  var mongo = db(config);

  if (config.useBasicAuth) {
    appRouter.use(basicAuth(config.basicAuth.username, config.basicAuth.password));
  }

  appRouter.use(favicon(__dirname + '/public/images/favicon.ico'));
  appRouter.use(logger('dev'));
  appRouter.use('/', express.static(__dirname + '/public'));
  appRouter.use(bodyParser.urlencoded({
    extended: true,
    limit:    config.site.requestSizeLimit,
  }));
  appRouter.use(cookieParser(config.site.cookieSecret));
  appRouter.use(session({
    key:                config.site.cookieKeyName,
    resave:             true,
    saveUninitialized:  true,
    secret:             config.site.sessionSecret,
  }));
  appRouter.use(methodOverride(function(req) {
    if (req.body && typeof req.body === 'object' && '_method' in req.body) {
      // look in urlencoded POST bodies and delete it
      var method = req.body._method;
      delete req.body._method;
      return method;
    }
  }));

  if (process.env.NODE_ENV === 'development') {
    appRouter.use(errorHandler());
  }

  // view helper, sets local variables used in templates
  appRouter.all('*', function(req, res, next) {
    // ensure a trailing slash on the baseHref (used as a prefix in routes and views)
    res.locals.baseHref = req.app.mountpath + (req.app.mountpath[req.app.mountpath.length - 1] === '/' ? '' : '/');
    res.locals.databases = mongo.databases;
    res.locals.collections = mongo.collections;

    //Flash messages
    if (req.session.success) {
      res.locals.messageSuccess = req.session.success;
      delete req.session.success;
    }

    if (req.session.error) {
      res.locals.messageError = req.session.error;
      delete req.session.error;
    }

    mongo.updateDatabases(mongo.adminDb, function(databases) {
      mongo.databases = databases;
      res.locals.databases = mongo.databases;
      return next();
    });
  });

  // route param pre-conditions
  appRouter.param('database', function(req, res, next, id) {
    //Make sure database exists
    if (!_.include(mongo.databases, id)) {
      req.session.error = 'Database not found!';
      return res.redirect(res.locals.baseHref);
    }

    req.dbName = id;
    res.locals.dbName = id;

    if (mongo.connections[id] !== undefined) {
      req.db = mongo.connections[id];
    } else {
      mongo.connections[id] = mongo.mainConn.db(id);
      req.db = mongo.connections[id];
    }

    next();
  });

  // :collection param MUST be preceded by a :database param
  appRouter.param('collection', function(req, res, next, id) {
    //Make sure collection exists
    if (!_.include(mongo.collections[req.dbName], id)) {
      req.session.error = 'Collection not found!';
      return res.redirect(res.locals.baseHref + 'db/' + req.dbName); // XXX
    }

    req.collectionName = id;
    res.locals.collectionName = id;

    mongo.connections[req.dbName].collection(id, function(err, coll) {
      if (err || coll === null) {
        req.session.error = 'Collection not found!';
        return res.redirect(res.locals.baseHref + 'db/' + req.dbName);
      }

      req.collection = coll;

      next();
    });
  });

  // :document param MUST be preceded by a :collection param
  appRouter.param('document', function(req, res, next, id) {
    id = JSON.parse(decodeURIComponent(id));
    var obj_id;

    // Attempt to create ObjectID from passed 'id'
    try {
      obj_id = new mongodb.ObjectID.createFromHexString(id);
    } catch (err) {
    }

    // If an ObjectID was correctly created from passed id param, try getting the ObjID first else falling back to try getting the string id
    // If not valid ObjectID created, try getting string id

    if (obj_id) {
      // passed id has successfully been turned into a valid ObjectID
      req.collection.findOne({_id: obj_id}, function(err, doc) {
        if (err) {
          req.session.error = 'Error: ' + err;
          return res.redirect(res.locals.baseHref + 'db/' + req.dbName + '/' + req.collectionName);
        }

        if (doc === null) {
          // No document found with obj_id, try again with straight id
          req.collection.findOne({_id: id }, function(err, doc) {
            if (err) {
              req.session.error = 'Error: ' + err;
              return res.redirect(res.locals.baseHref + 'db/' + req.dbName + '/' + req.collectionName);
            }

            if (doc === null) {
              req.session.error = 'Document not found!';
              return res.redirect(res.locals.baseHref + 'db/' + req.dbName + '/' + req.collectionName);
            }

            // Document found - send it back
            req.document = doc;
            res.locals.document = doc;

            next();
          });
        } else {
          // Document found - send it back
          req.document = doc;
          res.locals.document = doc;

          next();
        }

      });
    } else {
      // Passed id was NOT a valid ObjectID
      req.collection.findOne({_id: id}, function(err, doc) {
        if (err) {
          req.session.error = 'Error: ' + err;
          return res.redirect(res.locals.baseHref + 'db/' + req.dbName + '/' + req.collectionName);
        }

        if (doc === null) {
          req.session.error = 'Document not found!';
          return res.redirect(res.locals.baseHref + 'db/' + req.dbName + '/' + req.collectionName);
        }

        req.document = doc;
        res.locals.document = doc;

        next();
      });
    }
  });

  // mongodb mongoMiddleware
  var mongoMiddleware = function(req, res, next) {
    req.adminDb     = mongo.adminDb;
    req.databases   = mongo.databases; //List of database names
    req.collections = mongo.collections; //List of collection names in all databases

    //Allow page handlers to request an update for collection list
    req.updateCollections = mongo.updateCollections;

    next();
  };

  // routes
  appRouter.get('/', mongoMiddleware, routes(config).index);

  appRouter.post('/checkValid', mongoMiddleware, routes(config).checkValid);

  appRouter.get('/db/:database/expArr/:collection', mongoMiddleware, routes(config).exportColArray);
  appRouter.get('/db/:database/export/:collection', mongoMiddleware, routes(config).exportCollection);
  appRouter.get('/db/:database/updateCollections', mongoMiddleware, routes(config).updateCollections);

  appRouter.get('/db/:database/:collection/:document', mongoMiddleware, routes(config).viewDocument);
  appRouter.put('/db/:database/:collection/:document', mongoMiddleware, routes(config).updateDocument);
  appRouter.delete('/db/:database/:collection/:document', mongoMiddleware, routes(config).deleteDocument);
  appRouter.post('/db/:database/:collection', mongoMiddleware, routes(config).addDocument);

  appRouter.get('/db/:database/:collection', mongoMiddleware, routes(config).viewCollection);
  appRouter.put('/db/:database/:collection', mongoMiddleware, routes(config).renameCollection);
  appRouter.delete('/db/:database/:collection', mongoMiddleware, routes(config).deleteCollection);
  appRouter.post('/db/:database', mongoMiddleware, routes(config).addCollection);

  appRouter.get('/db/:database', mongoMiddleware, routes(config).viewDatabase);

  return appRouter;
};

module.exports = router;
