'use strict';

var
  _             = require('underscore'),
  async         = require('async'),
  errors        = require('src/lib/errors'),
  Request       = require('../util/Request'),
  Response      = require('../util/Response'),
  ExpandsURLMap = require('../util/ExpandsURLMap');


/**
 * BaseController
 */
class BaseController
{
  constructor() {

    /**
     * @type {Model}
     */
    this.Model = null;

    /**
     * Nested references output config
     *
     * @type {ExpandsURLMap}
     */
    this.expandsURLMap = new ExpandsURLMap();
  }

  /**
   * Retrieve one Model element
   */
  getOne(req, res, next) {

    var
      request  = new Request(req),
      response = new Response(request, this.expandsURLMap),
      criteria = this._buildCriteria(request);

    this.Model
      .findOne(criteria)
      .exec(function(err, model) {
        /* istanbul ignore next */
        if (err)    { return next(err); }
        if (!model) { return next(new errors.NotFound()); }

        response.formatOutput(model, function(err, output) {
          /* istanbul ignore next */
          if (err) { return next(err); }

          res.json(output);
        });
      });
  }


  /**
   * Retrieve all the model instances, paginated
   */
  getAll(req, res, next) {

    var
      request    = new Request(req),
      response   = new Response(request, this.expandsURLMap),
      criteria   = this._buildCriteria(request);


    this.Model.paginate(criteria, request.options, function(err, paginatedResults, pageCount, itemCount) {
      /* istanbul ignore next */
      if (err) { return next(err); }

      response
        .setPaginationParams(pageCount, itemCount)
        .formatOutput(paginatedResults, function(err, output) {
          /* istanbul ignore next */
          if (err) { return next(err); }

          res.json(output);
        });

    });
  }


  /**
   * Create one Model instance and save it
   * @abstract
   */
  /* istanbul ignore next */
  create(req, res, next) { next(); }


  /**
   * Edit one Model instance
   * @abstract
   */
  /* istanbul ignore next */
  update(req, res, next) { next(); }


  /**
   * Delete one Model instance
   */
  delete(req, res, next) {

    var
      request  = new Request(req),
      response = new Response(request, this.expandsURLMap),
      criteria = this._buildCriteria(request);

    this.Model
      .findOne(criteria)
      .exec(function(err, model) {
        /* istanbul ignore next */
        if (err)    { return next(err); }
        if (!model) { return next( new errors.NotFound() ); }

        model.remove(function() {
          response.formatOutput(model, function(err, output) {
            /* istanbul ignore next */
            if (err) { return next(err); }

            res.json(output);
          });
        });
    });
  }


  // Aux. "private" methods
  // (actually they're not private so can be easily tested)
  // =============================================================================

  _buildCriteria(request) {
    var criteria = {
      owner: request.getOwnerFromAuth()
    };

    if(request.req.params.id) {
      criteria._id = request.req.params.id;
    }

    return criteria;
  }


  _getAssignableAttributes(request, customAttrs) {
    customAttrs = customAttrs || {};
    return _.extend(
      { owner: request.req.user.userId },
      customAttrs,
      _.pick(request.req.body, this.Model.safeAttrs)
    );
  }


  _validate(model, options, callback) {
    model.validate(function (err) {
      /* istanbul ignore next */
      if (err) { return callback(err); }
      callback(null, model, options);
    });
  }


  _save(model, options, callback) {
    model.save(function(err) {
      /* istanbul ignore next */
      if (err) { return callback(err); }
      callback(null, model, options);
    });
  }

}


module.exports = BaseController;

@@ -2127,32 +2127,167 @@
 ) %7B next(); %7D%0A%0A%0A
+  /**%0A   * Edit one Model instance%0A   * @abstract%0A   */%0A  updatePartial(req, res, next) %7B%0A    this.update(req, res, next, true);%0A  %7D%0A%0A%0A
   /**%0A   * Delet
@@ -3376,60 +3376,198 @@
 st, 
-customAttrs) %7B%0A    customAttrs = customAttrs %7C%7C
+patch) %7B%0A    var defaults = %7B%7D;%0A%0A    if(!patch) %7B%0A      defaults = this.Model.safeAttrs.reduce(function(memo, key) %7B%0A        memo%5Bkey%5D = undefined;%0A        return memo;%0A      %7D,
  %7B%7D
+)
 ;%0A
+    %7D%0A%0A
     
@@ -3635,18 +3635,15 @@
     
-customAttr
+default
 s,%0A 

add support for full/partial updates