text: const debug = require('debug')('sequelize-resource:base-rest-controller');
  const querystring = require('querystring');
  const ResourceController = require('../resource-controller/ResourceController');
  const ResourceError = require('../errors/ResourceError');
  
  /**
   *
   * @class BaseRestController
   * @author Marco Villarreal
   */
  class BaseRestController {
    /**
     *
     * @constructor
     * @param {Sequelize} sequelize
     * @param {Sequelize.Model} model
     */
    constructor(sequelize, model) {
      this.resource = new ResourceController(sequelize, model);
      this.model = model;
      this.baseURL = null;
    }
  
    checkJsonHeader(req) {
      debug(`Unimplemented checkJsonHeader method on request ${req}`);
      throw new ResourceError(ResourceError.ERRORS.INTERNAL, 'Unimplemented checkJsonHeader method');
    }
  
    async create(req, res) {
      if (this.checkJsonHeader(req)) {
        const { error, response } = await this.resource.create(req.body);
        if (error) {
          this.sendResponse(res, error.code, error);
        } else {
          this.sendResponse(res, 201, this.attachResourceLink(response.get({ plain: true }), req));
        }
      } else {
        const error = new ResourceError(
          ResourceError.ERRORS.FAILED_PRECONDITION,
          'Request body must be application/json'
        );
        this.sendResponse(res, error.code, error);
      }
    }
  
    /**
     *  Note: implemented a little polifyll to satisfy query params on express(a collission issue caused by restify)
     * @param {Request} req
     * @param {Response} res
     */
    async list(req, res) {
      let queryParams = req.query;
      if (typeof req.query === 'function') {
        queryParams = querystring.parse(req.query());
      }
      const { sortField, sortOrder } = queryParams;
      const requestArgs = { sortField, sortOrder };
      let { limit, offset } = queryParams;
      if (limit != null) {
        limit = parseInt(limit, 10);
        requestArgs.limit = limit;
      }
  
      if (offset != null) {
        offset = parseInt(offset, 0);
        requestArgs.offset = offset;
      }
  
      const { error, response } = await this.resource.findAll(requestArgs);
  
      if (error) {
        this.sendResponse(res, error.code, error);
      } else {
        this.sendResponse(res, 200, this.attachResourcesLinks(response, req));
      }
    }
  
    async getOne(req, res) {
      const { error, response } = await this.resource.findOne(req.params);
      if (error) {
        this.sendResponse(res, error.code, error);
      } else {
        const resource = this.attachResourceLink(response.get({ plain: true }), req);
        this.setHeaders(res, { Location: resource.links.self.href });
        this.sendResponse(res, 200, resource);
      }
    }
  
    async getOneMiddleware(req, res, next) {
      const { response, error } = await this.resource.findOne(req.params);
      if (error) {
        this.sendResponse(res, error.code, error);
      } else {
        req.resource = response;
        next();
      }
    }
  
    async destroy(req, res) {
      if (!req.resource) {
        this.setStatus(res, 412);
        this.response(res, null);
      }
      const { response, error } = await this.resource.destroy(req.resource);
      if (error) {
        this.sendResponse(res, error.code, error);
      } else {
        this.sendResponse(res, 200, response);
      }
    }
  
    async update(req, res) {
      if (this.checkJsonHeader(req)) {
        if (!req.resource) {
          this.setStatus(res, 412);
          this.response(res, null);
        } else {
          const { response, error } = await this.resource.update(req.resource, req.body);
          if (error) {
            this.sendResponse(res, error.code, error);
          } else {
            this.sendResponse(res, 200, this.attachResourceLink(response));
          }
        }
      } else {
        const error = new ResourceError(
          ResourceError.ERRORS.FAILED_PRECONDITION,
          'Request body must be application/json'
        );
        this.sendResponse(res, error.code, error);
      }
    }
  
    /**
     *
     * @method attachResourceLink
     * @param {Object} resource
     * @param {Request} requestObj
     * @return {Object}
     */
    attachResourceLink(resource, requestObj) {
      const linkedResource = resource;
      try {
        const baseURL = this.createHATEOABaseURL(requestObj);
        const primaryKeys = this.resource.primaryKey.map(attribute => linkedResource[attribute]);
        const href = `${baseURL}/${primaryKeys.join('/')}`;
        linkedResource.links = {
          self: { href }
        };
      } catch (error) {
        debug('Error found attaching resource link %o', error);
      }
  
      return linkedResource;
    }
  
    /**
     * @method attachResourcesLinks
     * @param {Object} paginatedResources
     * @param {Request} requestObj
     * @return {Object}
     */
    attachResourcesLinks(paginatedResources, requestObj) {
      const pagingInfo = paginatedResources.paging;
      const baseURL = this.createHATEOABaseURL(requestObj);
      const { first, next, previous } = pagingInfo;
  
      const firstLink =
        first != null ? `${baseURL}?limit=${first.limit}&offset=${pagingInfo.first.offset}` : null;
      const nextLink =
        next != null ? `${baseURL}?limit=${next.limit}&offset=${pagingInfo.next.offset}` : null;
      const previousLink =
        previous != null ? `${baseURL}?limit=${previous.limit}&offset=${previous.offset}` : null;
  
      const linkedResources = paginatedResources.rows.map(dataRow =>
        this.attachResourceLink(dataRow, requestObj)
      );
  
      return {
        rows: linkedResources,
        links: {
          first: firstLink,
          next: nextLink,
          previous: previousLink
        }
      };
    }
  }
  
  module.exports = BaseRestController;
  