"use strict";

Object.defineProperty(exports, "__esModule", {
    value: true
});
exports.BaseRepository = undefined;

var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

var _stackerjsDb = require("stackerjs-db");

var _Util = require("./Util");

function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }

function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var BaseRepository = exports.BaseRepository = function () {
    function BaseRepository() {
        _classCallCheck(this, BaseRepository);

        this.errors = {};
        this.withs = [];
    }

    _createClass(BaseRepository, [{
        key: "addError",
        value: function addError(field, message) {
            if (!message) {
                message = field;
                field = "Database";
            }

            if (!Array.isArray(this.errors[field])) this.errors[field] = [];

            if (message instanceof Error) return this.errors[field].push(message.message);

            this.errors[field].push(message);
        }
    }, {
        key: "getErrors",
        value: function getErrors() {
            return this.errors;
        }
    }, {
        key: "hasErrors",
        value: function hasErrors() {
            return Object.keys(this.errors).length > 0;
        }
    }, {
        key: "beforeValidate",
        value: function beforeValidate() {
            return Promise.resolve(true);
        }
    }, {
        key: "afterValidate",
        value: function afterValidate() {
            return Promise.resolve(true);
        }
    }, {
        key: "with",
        value: function _with(withs) {
            this.withs.push(withs);

            return this;
        }
    }, {
        key: "validate",
        value: function () {
            var _ref = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee(entity) {
                var _this = this;

                return regeneratorRuntime.wrap(function _callee$(_context) {
                    while (1) {
                        switch (_context.prev = _context.next) {
                            case 0:
                                _context.next = 2;
                                return this.beforeValidate(entity);

                            case 2:
                                if (_context.sent) {
                                    _context.next = 5;
                                    break;
                                }

                                if (!this.hasErrors()) this.addError("validation", "Presented problems before validating");
                                return _context.abrupt("return", false);

                            case 5:

                                this.entity.metadata().fields.forEach(function (field) {
                                    var fieldName = field.alias ? field.alias : field.name;
                                    if (field.required && (typeof entity[fieldName] === "undefined" || entity[fieldName] === null || typeof entity[fieldName] === "string" && entity[fieldName].length < 1)) _this.addError(field.name, "Field is required");

                                    if (field.max && entity[fieldName] && (entity[fieldName] > field.max || entity[fieldName].length > field.max)) _this.addError(field.name, "Field length must be under " + field.max);

                                    if (field.min && entity[fieldName] && (entity[fieldName] < field.min || entity[fieldName].length < field.min)) _this.addError(field.name, "Field length must be over " + field.min);
                                });

                                _context.next = 8;
                                return this.afterValidate(entity);

                            case 8:
                                if (_context.sent) {
                                    _context.next = 10;
                                    break;
                                }

                                return _context.abrupt("return", false);

                            case 10:
                                return _context.abrupt("return", !this.hasErrors());

                            case 11:
                            case "end":
                                return _context.stop();
                        }
                    }
                }, _callee, this);
            }));

            function validate(_x) {
                return _ref.apply(this, arguments);
            }

            return validate;
        }()
    }, {
        key: "beforeSave",
        value: function beforeSave() {
            return Promise.resolve(true);
        }
    }, {
        key: "afterSave",
        value: function afterSave() {
            return Promise.resolve(true);
        }
    }, {
        key: "save",
        value: function () {
            var _ref2 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee2(entity) {
                var _this2 = this;

                var validate = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
                return regeneratorRuntime.wrap(function _callee2$(_context2) {
                    while (1) {
                        switch (_context2.prev = _context2.next) {
                            case 0:
                                this.prepare(entity);

                                _context2.t0 = validate;

                                if (!_context2.t0) {
                                    _context2.next = 6;
                                    break;
                                }

                                _context2.next = 5;
                                return this.validate(entity);

                            case 5:
                                _context2.t0 = !_context2.sent;

                            case 6:
                                if (!_context2.t0) {
                                    _context2.next = 8;
                                    break;
                                }

                                return _context2.abrupt("return", false);

                            case 8:
                                _context2.next = 10;
                                return this.beforeSave(entity);

                            case 10:
                                if (_context2.sent) {
                                    _context2.next = 13;
                                    break;
                                }

                                if (!this.hasErrors()) this.addError("validation", "Presented problems before saving");
                                return _context2.abrupt("return", false);

                            case 13:
                                if (!this.isNewRecord(entity)) {
                                    _context2.next = 15;
                                    break;
                                }

                                return _context2.abrupt("return", this.insert(entity).then(function (response) {
                                    if (!response) return response;

                                    return _this2.afterSave(entity);
                                }));

                            case 15:
                                return _context2.abrupt("return", this.update(entity).then(function (response) {
                                    if (!response) return response;

                                    return _this2.afterSave(entity);
                                }));

                            case 16:
                            case "end":
                                return _context2.stop();
                        }
                    }
                }, _callee2, this);
            }));

            function save(_x3) {
                return _ref2.apply(this, arguments);
            }

            return save;
        }()
    }, {
        key: "findById",
        value: function findById(id) {
            var _this3 = this;

            var expr = _stackerjsDb.DB.Factory.getQueryCriteria();
            var queryBuilder = _stackerjsDb.DB.Factory.getQueryBuilder().select().set("*").from(this.entity.metadata().table).where(expr.eq(this.getFieldByType("pk"), id)).limit(1);

            return queryBuilder.execute().then(function () {
                var _ref3 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee3(results) {
                    return regeneratorRuntime.wrap(function _callee3$(_context3) {
                        while (1) {
                            switch (_context3.prev = _context3.next) {
                                case 0:
                                    if (!(results.length <= 0)) {
                                        _context3.next = 2;
                                        break;
                                    }

                                    return _context3.abrupt("return", null);

                                case 2:
                                    _context3.next = 4;
                                    return _Util.Util.makeEntity(_this3.entity, results[0], _this3.withs);

                                case 4:
                                    return _context3.abrupt("return", _context3.sent);

                                case 5:
                                case "end":
                                    return _context3.stop();
                            }
                        }
                    }, _callee3, _this3);
                }));

                return function (_x4) {
                    return _ref3.apply(this, arguments);
                };
            }());
        }
    }, {
        key: "find",
        value: function find() {
            var filter = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
            var limit = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 100;

            var _this4 = this;

            var offset = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
            var orders = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;

            var queryBuilder = _stackerjsDb.DB.Factory.getQueryBuilder().select().from(this.entity.metadata().table).set("*").limit(limit).offset(offset);

            if (filter) queryBuilder.where(filter);

            if (orders) queryBuilder.order.apply(queryBuilder, _toConsumableArray(orders.map(function (o) {
                var _o$split = o.split(" "),
                    _o$split2 = _slicedToArray(_o$split, 2),
                    order = _o$split2[0],
                    direction = _o$split2[1];

                return [order, direction || "ASC"];
            })));

            return queryBuilder.execute().then(function (results) {
                return Promise.all(results.map(function (result) {
                    return _Util.Util.makeEntity(_this4.entity, result, _this4.withs);
                }));
            });
        }
    }, {
        key: "findOne",
        value: function findOne(filter) {
            var _this5 = this;

            var queryBuilder = _stackerjsDb.DB.Factory.getQueryBuilder().select().from(this.entity.metadata().table).set("*").limit(1);

            if (filter) queryBuilder.where(filter);

            return queryBuilder.execute().then(function (_ref4) {
                var _ref5 = _slicedToArray(_ref4, 1),
                    result = _ref5[0];

                if (!result) return null;

                return _Util.Util.makeEntity(_this5.entity, result, _this5.withs);
            });
        }
    }, {
        key: "count",
        value: function count() {
            var filters = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;

            var queryBuilder = _stackerjsDb.DB.Factory.getQueryBuilder().select().set(["COUNT(*)", "total"]).from(this.entity.metadata().table);

            if (filters) queryBuilder.where(filters);

            return queryBuilder.execute().then(function (results) {
                return results[0].total;
            });
        }
    }, {
        key: "delete",
        value: function _delete(entity) {
            var _this6 = this;

            var expr = _stackerjsDb.DB.Factory.getQueryCriteria();
            var queryBuilder = _stackerjsDb.DB.Factory.getQueryBuilder().delete().from(this.entity.metadata().table).where(expr.eq(this.getFieldByType("pk"), entity[this.getFieldByType("pk")]));

            return queryBuilder.execute().then(function () {
                return true;
            }).catch(function (err) {
                _this6.addError(err.message);
                return false;
            });
        }
    }, {
        key: "insert",
        value: function insert(entity) {
            var _this7 = this;

            var queryBuilder = _stackerjsDb.DB.Factory.getQueryBuilder().insert().into(this.entity.metadata().table);

            var createdAt = this.getFieldByType("created_at");
            if (createdAt) entity[createdAt] = this.getCurrentTimeStamp();

            this.entity.metadata().fields.forEach(function (field) {
                if (field.type !== "pk" && field.type !== "updated_at" && entity[field.alias ? field.alias : field.name] !== null && typeof entity[field.alias ? field.alias : field.name] !== "undefined") queryBuilder.set(field.name, entity[field.alias ? field.alias : field.name]);
            });

            return queryBuilder.execute().then(function (response) {
                entity[_this7.getFieldByType("pk", true)] = response.lastInsertedId;
                return true;
            }).catch(function (err) {
                _this7.addError(err.message);
                return false;
            });
        }
    }, {
        key: "update",
        value: function update(entity) {
            var _this8 = this;

            var parameters = 0;
            var expr = _stackerjsDb.DB.Factory.getQueryCriteria();
            var queryBuilder = _stackerjsDb.DB.Factory.getQueryBuilder().update().into(this.entity.metadata().table);

            var updatedAt = this.getFieldByType("updated_at");
            if (updatedAt) entity[updatedAt] = this.getCurrentTimeStamp();

            this.entity.metadata().fields.forEach(function (field) {
                var fieldName = field.alias ? field.alias : field.name;
                if (field.type !== "pk" && field.type !== "created_at" && queryBuilder.treatValue(entity[fieldName]) !== queryBuilder.treatValue(entity._attributes[field.name])) {
                    parameters++;
                    queryBuilder.set(field.name, entity[fieldName]);
                }
            });
            if (parameters <= 1) return Promise.resolve(true);

            queryBuilder.where(expr.eq(this.getFieldByType("pk"), entity._attributes[this.getFieldByType("pk")]));

            return queryBuilder.execute().then(function () {
                return true;
            }).catch(function (err) {
                _this8.addError(err.message);
                return false;
            });
        }
    }, {
        key: "isNewRecord",
        value: function isNewRecord(entity) {
            return !entity._attributes || typeof entity._attributes[this.getFieldByType("pk")] === "undefined" || !entity._attributes[this.getFieldByType("pk")];
        }
    }, {
        key: "getFieldByType",
        value: function getFieldByType(type) {
            var alias = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
            var _iteratorNormalCompletion = true;
            var _didIteratorError = false;
            var _iteratorError = undefined;

            try {
                for (var _iterator = this.entity.metadata().fields[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
                    var field = _step.value;

                    if (field.type === type) return field.alias && alias ? field.alias : field.name;
                }
            } catch (err) {
                _didIteratorError = true;
                _iteratorError = err;
            } finally {
                try {
                    if (!_iteratorNormalCompletion && _iterator.return) {
                        _iterator.return();
                    }
                } finally {
                    if (_didIteratorError) {
                        throw _iteratorError;
                    }
                }
            }

            return null;
        }
    }, {
        key: "prepare",
        value: function prepare(entity) {
            this.entity.metadata().fields.forEach(function (field) {
                var fieldName = field.alias ? field.alias : field.name;
                if (typeof entity[fieldName] === "undefined" && (field.default || field.default === 0)) entity[fieldName] = field.default;
            });
        }
    }, {
        key: "getCurrentTimeStamp",
        value: function getCurrentTimeStamp() {
            return parseInt(new Date().getTime().toString().slice(0, -3));
        }
    }]);

    return BaseRepository;
}();