text: var _ = require('lodash'),
      uuid = require('node-uuid'),
      util = require('util');
  
  function GhostError(options) {
      options = options || {};
      var self = this;
  
      if (_.isString(options)) {
          throw new Error('Please instantiate Errors with the option pattern. e.g. new errors.GhostError({message: ...})');
      }
  
      Error.call(this);
      Error.captureStackTrace(this, GhostError);
  
      /**
       * defaults
       */
      this.statusCode = 500;
      this.errorType = 'InternalServerError';
      this.level = 'normal';
      this.id = uuid.v1();
  
      /**
       * custom overrides
       */
      this.statusCode = options.statusCode || this.statusCode;
      this.level = options.level || this.level;
      this.context = options.context || this.context;
      this.help = options.help || this.help;
      this.errorType = this.name = options.errorType || this.errorType;
      this.errorDetails = options.errorDetails;
      this.code = options.code || null;
  
      // @TODO: ?
      this.property = options.property;
      this.value = options.value;
  
      this.message = options.message;
      this.hideStack = options.hideStack;
  
      // error to inherit from, override!
      // nested objects are getting copied over in one piece (can be changed, but not needed right now)
      if (options.err) {
          // it can happen that third party libs return errors as strings, yes really
          // we are creating an error stack from this line, but we need to ensure not loosing the original error message
          if (_.isString(options.err)) {
              options.err = new Error(options.err);
          }
  
          Object.getOwnPropertyNames(options.err).forEach(function (property) {
              // original message is part of the stack, no need to pick it
              if (['errorType', 'name', 'statusCode'].indexOf(property) !== -1) {
                  return;
              }
  
              if (property === 'message' && !self[property]) {
                  self[property] = options.err[property];
                  return;
              }
  
              if (property === 'stack') {
                  self[property] += '\n\n' + options.err[property];
                  return;
              }
  
              self[property] = options.err[property] || self[property];
          });
      }
  }
  
  // jscs:disable
  var errors = {
      DataExportError: function DataExportError(options) {
          GhostError.call(this, _.merge({
              statusCode: 500,
              errorType: 'DataExportError'
          }, options));
      },
      DataImportError: function DataImportError(options) {
          GhostError.call(this, _.merge({
              statusCode: 500,
              errorType: 'DataImportError'
          }, options));
      },
      IncorrectUsageError: function IncorrectUsageError(options) {
          GhostError.call(this, _.merge({
              statusCode: 400,
              level: 'critical',
              errorType: 'IncorrectUsageError'
          }, options));
      },
      NotFoundError: function NotFoundError(options) {
          GhostError.call(this, _.merge({
              statusCode: 404,
              errorType: 'NotFoundError'
          }, options));
      },
      BadRequestError: function BadRequestError(options) {
          GhostError.call(this, _.merge({
              statusCode: 400,
              errorType: 'BadRequestError'
          }, options));
      },
      DatabaseVersionError: function DatabaseVersionError(options) {
          GhostError.call(this, _.merge({
              hideStack: true,
              statusCode: 500,
              errorType: 'DatabaseVersionError'
          }, options));
      },
      DatabaseNotPopulatedError: function DatabaseNotPopulatedError(options) {
          GhostError.call(this, _.merge({
              statusCode: 500,
              errorType: 'DatabaseNotPopulatedError'
          }, options));
      },
      DatabaseNotSeededError: function DatabaseNotSeededError(options) {
          GhostError.call(this, _.merge({
              statusCode: 500,
              errorType: 'DatabaseNotSeededError'
          }, options));
      },
      UnauthorizedError: function UnauthorizedError(options) {
          GhostError.call(this, _.merge({
              statusCode: 401,
              errorType: 'UnauthorizedError'
          }, options));
      },
      NoPermissionError: function NoPermissionError(options) {
          GhostError.call(this, _.merge({
              statusCode: 403,
              errorType: 'NoPermissionError'
          }, options));
      },
      ValidationError: function ValidationError(options) {
          GhostError.call(this, _.merge({
              statusCode: 422,
              errorType: 'ValidationError'
          }, options));
      },
      UnsupportedMediaTypeError: function UnsupportedMediaTypeError(options) {
          GhostError.call(this, _.merge({
              statusCode: 415,
              errorType: 'UnsupportedMediaTypeError'
          }, options));
      },
      VersionMismatchError: function VersionMismatchError(options) {
          GhostError.call(this, _.merge({
              statusCode: 400,
              errorType: 'VersionMismatchError'
          }, options));
      },
      TokenRevocationError: function TokenRevocationError(options) {
          GhostError.call(this, _.merge({
              statusCode: 503,
              errorType: 'TokenRevocationError'
          }, options));
      },
      EmailError: function EmailError(options) {
          GhostError.call(this, _.merge({
              statusCode: 500,
              errorType: 'EmailError'
          }, options));
      },
      TooManyRequestsError: function TooManyRequestsError(options) {
          GhostError.call(this, _.merge({
              statusCode: 429,
              errorType: 'TooManyRequestsError'
          }, options));
      },
      MaintenanceError: function MaintenanceError(options) {
          GhostError.call(this, _.merge({
              statusCode: 503,
              errorType: 'MaintenanceError'
          }, options));
      },
      ThemeValidationError: function ThemeValidationError(options) {
          GhostError.call(this, _.merge({
              statusCode: 422,
              errorType: 'ThemeValidationError',
              errorDetails: {}
          }, options));
      }
  };
  
  util.inherits(GhostError, Error);
  _.each(errors, function (error) {
      util.inherits(error, GhostError);
  });
  
  module.exports = errors;
  module.exports.GhostError = GhostError;
  
  
  