var chokidar = require('chokidar');
var mongoose = require('mongoose');
var reload   = require('require-reload')(require)
var Q        = require('q')
var _map     = require('lodash/collection/map')

/**
 * Connect to the database
 */
var connect = function() {
  var uristring = 'mongodb://localhost/test';
  var mongoOptions = { };
   
  mongoose.connect(uristring, mongoOptions, function (err, res) {
    if (err) { 
        throw err
    } else {
        console.log('Successfully connected to: ' + uristring);
    }
  });  
}

/**
 * Wrap a response in a standard message format
 * @param  {Object} data   the result if successful
 * @param  {Number} status the HTTP status code
 * @param  {String} error  the error message
 * @param  {Array}  errors a list of detailed errors
 * @return {Object} the wrapped message
 */
var wrap = function(data, status, error, errors) {
  var status = status || 200

  return {
    meta: {
      status: status,
      error: error,
      errors: errors,
    },
    result: data,
  }
}

/**
 * Dynamic route storage
 * @type {Object}
 */
var routes = {}

/**
 * Reloads the widget backend definition file
 * @param  {String} backend the path to the backend file
 * @return {undefined}
 */
var loadBackend = function(app, backend) {  
  backend = reload(backend)
  mongoose.models = {};
  mongoose.modelSchemas = {};
  var Schema = mongoose.Schema
  var FactSchema = new Schema(backend.schema)   
  var Fact = mongoose.model('Fact', FactSchema)

  // update the CRUD routes
  routes['get'] = function(req, res) {
    Fact.findById(req.params.id, function (err, fact) {
      if(err)   return res.status(400).send(wrap(null, 400, err.message))
      if(!fact) return res.status(404).send(wrap(null, 404, 'The fact is missing.'))
      
      res.send(wrap(fact))
    })
  }

  routes['post'] = function(req, res) {
    var f = new Fact(req.body)

    f.save(function (err) {
      if (err) {
        var errors = _map(err.errors, function(error) {
          return error.message
        })
        res.status(400).send(wrap(null, 400, err.message, errors))
      } else {
        res.send(wrap(f))
      }
    })
  }

  routes['put'] = function(req, res) {
    Fact.findByIdAndUpdate(req.params.id, { $set: req.body }, function (err, fact) {
      if (err) {
        var errors = _map(err.errors, function(error) {
          return error.message
        })
        res.status(400).send(wrap(null, 400, err.message, errors))
      } else {
        res.send(wrap(fact))
      }
    });
  }

  routes['list'] = function(req, res) {
    // TODO: pagination, selecting fields
    Fact.find(null)
      // .limit(10)
      // .sort({ _id: 1 })
      // .select({ name: 1, occupation: 1 })
      .exec(function (err, fact) {
        if(err) return res.status(400).send(wrap(null, 400, err.message))        
        res.send(wrap(fact))
      })
  }

  // update custom routes
  _map(backend.routes, function(handler, route) {
    if(!routes[route]) {
      app.get('/api/v2/compositions/*/facts/' + route, function(req, res) {
        var deferred = Q.defer()
        routes[route](req, Fact, deferred)
        deferred.promise.then(function(result) {
          res.send(wrap(result))
        }, function(err) {
          res.status(400).send(wrap(null, 400, err.message))
        })
      })
    }
    routes[route] = handler
  })
}

// initialise the auto-reloading backend server
function init(app, backend) {
  connect()

  // defined the CRUD routes
  app.get( '/api/v2/compositions/*/facts/',                  function(req, res) { routes['list'](req, res) });
  app.post('/api/v2/compositions/*/facts/',                  function(req, res) { routes['post'](req, res) });
  app.get( '/api/v2/compositions/*/facts/:id([0-9a-f]{24})', function(req, res) { routes['get' ](req, res) });
  app.put( '/api/v2/compositions/*/facts/:id([0-9a-f]{24})', function(req, res) { routes['put' ](req, res) });

  // watch the backend definition file for changes
  chokidar.watch(backend).on('all', function(event, path) {
    loadBackend(app, backend)
    console.log('backend reloaded')
  });
}

module.exports = init