"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.TyError = void 0;

var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));

var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));

var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));

var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));

var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));

var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));

var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));

var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));

var _wrapNativeSuper2 = _interopRequireDefault(require("@babel/runtime/helpers/wrapNativeSuper"));

var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));

var _tsFns = require("ts-fns");

function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }

function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { (0, _defineProperty2.default)(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }

function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = (0, _getPrototypeOf2.default)(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = (0, _getPrototypeOf2.default)(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return (0, _possibleConstructorReturn2.default)(this, result); }; }

function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }

var TyError = /*#__PURE__*/function (_TypeError) {
  (0, _inherits2.default)(TyError, _TypeError);

  var _super = _createSuper(TyError);

  function TyError(resource) {
    var _this;

    (0, _classCallCheck2.default)(this, TyError);
    _this = _super.call(this);
    _this.resources = [];
    _this.traces = [];

    _this.init(resource);

    return _this;
  }

  (0, _createClass2.default)(TyError, [{
    key: "init",
    value: function init(resource) {
      if (resource) {
        this.add(resource);
        this.commit();
        this.format();
      }
    }
  }, {
    key: "message",
    get: function get() {
      if (this._message) {
        return this._message;
      }

      var message = this.format();
      this._message = message;
      return message;
    }
  }, {
    key: "count",
    get: function get() {
      return this.resources.length;
    }
    /**
     * @param {string|error|object} resource
     */

  }, {
    key: "add",
    value: function add(resource) {
      if ((0, _tsFns.isString)(resource)) {
        resource = new Error(resource);
      }

      if ((0, _tsFns.isInstanceOf)(resource, Error) || (0, _tsFns.isObject)(resource)) {
        this.resources.push(resource);
      }

      return this;
    }
  }, {
    key: "replace",
    value: function replace(resource) {
      this.resources = [];
      this.add(resource);
      return this;
    }
  }, {
    key: "keep",
    value: function keep() {
      // do nothing
      return this;
    }
  }, {
    key: "commit",
    value: function commit() {
      var traces = this.traces = [];
      var items = makeErrorTraces(this);
      traces.push.apply(traces, (0, _toConsumableArray2.default)(items));
      this.format();
      return this;
    }
  }, {
    key: "error",
    value: function error() {
      return this.count ? this : null;
    }
  }, {
    key: "format",
    value: function format() {
      var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      var traces = this.traces;
      var _options$keyPathPrefi = options.keyPathPrefix,
          keyPathPrefix = _options$keyPathPrefi === void 0 ? TyError.keyPathPrefix : _options$keyPathPrefi,
          _options$breaktag = options.breaktag,
          breaktag = _options$breaktag === void 0 ? traces.length < 2 ? '' : '\n' : _options$breaktag,
          _options$breakline = options.breakline,
          breakline = _options$breakline === void 0 ? TyError.shouldBreakLongMessage : _options$breakline,
          _options$sensitive = options.sensitive,
          sensitive = _options$sensitive === void 0 ? TyError.shouldHideSensitiveData : _options$sensitive,
          _options$templates = options.templates,
          templates = _options$templates === void 0 ? {} : _options$templates,
          _options$messagePrefi = options.messagePrefix,
          messagePrefix = _options$messagePrefi === void 0 ? '' : _options$messagePrefi,
          _options$messageSuffi = options.messageSuffix,
          messageSuffix = _options$messageSuffi === void 0 ? '' : _options$messageSuffi;

      var bands = _objectSpread(_objectSpread({}, TyError.defaultMessages), templates);

      var messages = traces.map(function (trace, i) {
        var type = trace.type,
            keyPath = trace.keyPath,
            value = trace.value,
            name = trace.name,
            pattern = trace.pattern;
        var info = name && !(0, _tsFns.isUndefined)(pattern) ? [name, pattern] : name ? [name] : pattern ? [pattern] : [];
        var keys = (0, _tsFns.isArray)(keyPath) ? (0, _toConsumableArray2.default)(keyPath) : (0, _tsFns.makeKeyChain)(keyPath);
        var key = keys.pop();
        var params = {
          i: i + 1,
          key: key,
          keyPath: keyPathPrefix + (0, _tsFns.makeKeyPath)(keyPath),
          should: info.length ? makeErrorShould(info, breakline) : '',
          receive: (0, _tsFns.inObject)('value', trace) ? makeErrorReceive(value, breakline, 0, sensitive) : ''
        };
        var text = makeErrorMessage(breaktag, params, bands) + makeErrorMessage(type, params, bands);
        return text;
      });
      var message = messages.join('');
      var text = messagePrefix + message + messageSuffix;
      this._message = text;
      return text;
    }
  }, {
    key: "translate",
    value: function translate(message, prefix, suffix) {
      var formatter = {
        keyPathPrefix: ''
      };

      if (message) {
        formatter.templates = {
          exception: message,
          unexcepted: message,
          dirty: message,
          overflow: message,
          missing: message,
          illegal: message,
          notin: message
        };
      }

      if (prefix) {
        formatter.messagePrefix = prefix;
      }

      if (suffix) {
        formatter.messageSuffix = suffix;
      }

      this.format(formatter);
    }
  }]);
  return TyError;
}( /*#__PURE__*/(0, _wrapNativeSuper2.default)(TypeError)); // ====================


exports.TyError = TyError;
(0, _defineProperty2.default)(TyError, "shouldHideSensitiveData", false);
(0, _defineProperty2.default)(TyError, "shouldBreakLongMessage", false);
(0, _defineProperty2.default)(TyError, "defaultMessages", {
  exception: '{keyPath} should match `{should}`, but receive `{receive}`.',
  unexcepted: '{keyPath} should not match `{should}`, but receive `{receive}`.',
  dirty: '{keyPath} receive `{receive}` whose length does not match `{should}`.',
  overflow: '{keyPath} should not exists.',
  missing: '{keyPath} is missing.',
  illegal: 'key `{key}` at {keyPath} should match `{should}`',
  notin: '{keyPath} recieve `{receive}` did not match `{should}` in enum.'
});
(0, _defineProperty2.default)(TyError, "keyPathPrefix", '$.');
(0, _defineProperty2.default)(TyError, "messagePrefix", '');

function createSpace() {
  var count = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;

  if (!count) {
    return '';
  }

  var str = '';

  for (var i = 0; i < count; i++) {
    str += ' ';
  }

  return str;
}

function makeErrorMessage(type, params, templates) {
  var message = templates[type] || type;
  var text = message.replace(/\{(.*?)\}/g, function (match, key) {
    return (0, _tsFns.inObject)(key, params) ? params[key] : match;
  });
  return text;
}

function makeValueString(value) {
  var sensitive = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
  var breakline = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
  var space = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 2;

  var britems = function britems(items, start, end) {
    var space = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 2;

    if (!breakline) {
      return start + items.join(',') + end;
    }

    if (items.join(',').length < 25 && items.length < 6) {
      return start + items.join(',') + end;
    }

    var str = start;
    var spacestr = createSpace(space);
    items.forEach(function (item) {
      str += '\n    ' + spacestr + item + ',';
    });
    str += '\n    ' + createSpace(space - 2) + end;
    return str;
  };

  var stringify = function stringify(value) {
    var space = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 2;

    if ((0, _tsFns.isObject)(value)) {
      if ((0, _tsFns.isEmpty)(value)) {
        return '{}';
      }

      var str = '{';

      if (!breakline) {
        (0, _tsFns.each)(value, function (value, key) {
          str += key + ':' + stringify(value) + ',';
        });
        str = str.substr(0, str.length - 1);
        str += '}';
        return str;
      }

      var spacestr = createSpace(space);
      (0, _tsFns.each)(value, function (value, key) {
        str += '\n    ' + spacestr + key + ': ' + stringify(value, space + 2) + ',';
      });
      str += '\n    ' + createSpace(space - 2) + '}';
      return str;
    } else if ((0, _tsFns.isArray)(value)) {
      if (!value.length) {
        return '[]';
      }

      var _str = '[';

      if (!breakline) {
        value.forEach(function (item) {
          _str += stringify(item);
        });
        _str = _str.substr(0, _str.length - 1);
        _str += ']';
        return _str;
      }

      var _spacestr = createSpace(space);

      value.forEach(function (item) {
        _str += '\n    ' + _spacestr + stringify(item, space + 2) + ',';
      });
      _str += '\n    ' + createSpace(space - 2) + ']';
      return _str;
    } else {
      return make(value, sensitive, breakline, space);
    }
  };

  var records = [];

  function make(value) {
    var sensitive = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
    var breakline = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
    var space = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 2;
    var totype = (0, _typeof2.default)(value);

    if ((0, _tsFns.inArray)(totype, ['boolean', 'undefined']) || (0, _tsFns.isNull)(value) || (0, _tsFns.isNaN)(value)) {
      return value + '';
    } else if (totype === 'number') {
      return sensitive ? '***' : value + '';
    } else if (totype === 'string') {
      return JSON.stringify(sensitive ? '***' : value);
    } else if ((0, _tsFns.isFunction)(value)) {
      return value.name + '()';
    } else if ((0, _tsFns.isArray)(value)) {
      var items = value.map(function (item) {
        return make(item, sensitive, breakline, space + 2);
      });
      var output = britems(items, '[', ']', space);
      return output;
    } else if ((0, _tsFns.isObject)(value)) {
      var keys = Object.keys(value);

      var _output = sensitive ? britems(keys, '{', '}', space) : stringify(value, space);

      return _output;
    } else if ((0, _typeof2.default)(value) === 'object') {
      // for class instances
      // type or rule
      if ((0, _tsFns.inObject)('pattern', value)) {
        var name = value.name; // deep self-ref

        if ((0, _tsFns.inArray)(value, records)) {
          return (0, _tsFns.isString)(name) ? name : "ref:".concat(name);
        }

        records.push(value);

        var _output2 = make(value.pattern, sensitive, breakline, space);

        return (0, _tsFns.isString)(name) ? name + '(' + _output2 + ')' : _output2;
      } else {
        return value.name ? value.name : value.constructor ? value.constructor.name : 'Object';
      }
    } else if (typeof value === 'function') {
      // for native functions or classes
      return value.name ? value.name : value.constructor ? value.constructor.name : 'Function';
    } else {
      var _output3 = value.toString();

      return _output3;
    }
  }

  return make(value, sensitive, breakline, space);
}

function makeErrorReceive(value) {
  var breakline = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
  var space = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
  var sensitive = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
  var output = makeValueString(value, sensitive, breakline, space);
  return output;
}

function makeErrorShould(info) {
  var breakline = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
  var space = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;

  if (info.length === 0) {
    return '%unknown';
  }

  if (info.length === 1) {
    return makeValueString(info[0], false, breakline, space);
  }

  var _info = (0, _slicedToArray2.default)(info, 2),
      name = _info[0],
      pattern = _info[1];

  var output = name + '(' + makeValueString(pattern, false, breakline, space) + ')';
  return output;
}

function makeErrorTraces(tyerr) {
  var keyPath = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
  var resources = tyerr.resources;
  var traces = [];
  resources.forEach(function (resource) {
    var innerTraces = _makeErrorInnerTraces(resource, (0, _toConsumableArray2.default)(keyPath));

    traces.push.apply(traces, (0, _toConsumableArray2.default)(innerTraces));
  });
  return traces;
}

function _makeErrorInnerTraces(resource) {
  var keyPath = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
  var traces = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [];

  if ((0, _tsFns.isInstanceOf)(resource, TyError)) {
    var items = makeErrorTraces(resource, (0, _toConsumableArray2.default)(keyPath));
    traces.push.apply(traces, (0, _toConsumableArray2.default)(items));
    return traces;
  } else if ((0, _tsFns.isInstanceOf)(resource, Error)) {
    traces.push({
      type: resource.message,
      keyPath: keyPath
    });
    return traces;
  }

  var key = resource.key,
      index = resource.index,
      type = resource.type,
      value = resource.value,
      name = resource.name,
      pattern = resource.pattern,
      error = resource.error,
      errors = resource.errors;

  if (!(0, _tsFns.isUndefined)(key)) {
    keyPath.push(key);
  } else if (!(0, _tsFns.isUndefined)(index)) {
    keyPath.push(index);
  }

  if ((0, _tsFns.inArray)(type, ['dirty', 'overflow', 'missing', 'illegal'])) {
    traces.push({
      type: type,
      keyPath: keyPath,
      name: name,
      value: value,
      pattern: pattern
    });
    return traces;
  }

  if (type === 'notin' && (0, _tsFns.isArray)(errors)) {
    errors.forEach(function (error) {
      var items = makeErrorTraces(error, (0, _toConsumableArray2.default)(keyPath));
      traces.push.apply(traces, (0, _toConsumableArray2.default)(items.map(function (item) {
        return _objectSpread(_objectSpread({}, item), {}, {
          type: 'notin'
        });
      })));
    });
  } else if (!error) {
    traces.push({
      type: type,
      keyPath: keyPath,
      value: value,
      name: name,
      pattern: pattern
    });
  } else if ((0, _tsFns.isInstanceOf)(error, TyError)) {
    var _items = makeErrorTraces(error, (0, _toConsumableArray2.default)(keyPath));

    traces.push.apply(traces, (0, _toConsumableArray2.default)(_items));
  } else if ((0, _tsFns.isInstanceOf)(error, Error)) {
    traces.push({
      type: error.message,
      keyPath: keyPath
    });
  }

  return traces;
}