"use strict";

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

var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };

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 _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };

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 _fs = require("fs");

var _path = require("path");

var path = _interopRequireWildcard(_path);

var _lodash = require("lodash");

var _lodash2 = _interopRequireDefault(_lodash);

var _v = require("uuid/v1");

var _v2 = _interopRequireDefault(_v);

var _QueryCriteria = require("./QueryCriteria");

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }

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 FileAdapter = exports.FileAdapter = function () {
    function FileAdapter(host, name) {
        _classCallCheck(this, FileAdapter);

        this.path = path.resolve(host, name);
    }

    _createClass(FileAdapter, [{
        key: "validateQuery",
        value: function validateQuery(query) {
            if (!query.type) throw new Error("Invalid Query. Missing \"type\" parameter.");

            if (!["ADD", "LIST", "CHANGE", "REMOVE", "DROP", "CREATE"].includes(query.type.toUpperCase())) throw new Error("Invalid Query. There's no \"" + query.type + "\".");

            if (!query.at) throw new Error("Invalid Query. Missing \"at\" parameter");
        }
    }, {
        key: "execute",
        value: function () {
            var _ref = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee(query) {
                var type;
                return regeneratorRuntime.wrap(function _callee$(_context) {
                    while (1) {
                        switch (_context.prev = _context.next) {
                            case 0:
                                this.validateQuery(query);

                                type = query.type;
                                return _context.abrupt("return", this[type.toLowerCase()](query));

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

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

            return execute;
        }()
    }, {
        key: "add",
        value: function add(_ref2) {
            var _this = this;

            var at = _ref2.at,
                data = _ref2.data;

            if (!Array.isArray(data)) data = [data];

            return this.acquire(at).then(function (collection) {
                var lastInsertedId = void 0;
                data.forEach(function (item) {
                    item.id = lastInsertedId = (0, _v2.default)();
                    collection.data.push(item);
                });

                return _this.persist(at, collection).then(function () {
                    return {
                        lastInsertedId: lastInsertedId,
                        affectedRows: 0,
                        changedRows: 0
                    };
                });
            });
        }
    }, {
        key: "list",
        value: function list(_ref3) {
            var _this2 = this;

            var at = _ref3.at,
                filters = _ref3.filters,
                fields = _ref3.fields,
                limit = _ref3.limit,
                offset = _ref3.offset;

            if (!fields) fields = "*";

            return this.acquire(at).then(function (collection) {
                return collection.data.filter(_this2.parseFilters(filters)).filter(function (item, index) {
                    return !offset || index >= offset;
                }).filter(function (item, index) {
                    return !limit || index < limit;
                }).map(function (item) {
                    if (fields === "*") return item;

                    var itemResolved = {};
                    fields.forEach(function (field) {
                        return itemResolved[field] = typeof item[field] !== "undefined" ? item[field] : null;
                    });

                    return itemResolved;
                });
            });
        }
    }, {
        key: "change",
        value: function change(_ref4) {
            var _this3 = this;

            var at = _ref4.at,
                data = _ref4.data,
                filters = _ref4.filters;

            var queryResponse = {
                lastInsertedId: null,
                affectedRows: 0,
                changedRows: 0
            };

            var filter = this.parseFilters(filters);
            return this.acquire(at).then(function (collection) {
                collection.data = collection.data.map(function (item) {
                    if (!filter(item)) return item;

                    queryResponse.affectedRows++;
                    var changedItem = _extends({}, item, data);

                    if (!_lodash2.default.isEqual(item, changedItem)) queryResponse.changedRows++;

                    return changedItem;
                });

                return _this3.persist(at, collection).then(function () {
                    return queryResponse;
                });
            });
        }
    }, {
        key: "remove",
        value: function remove(_ref5) {
            var _this4 = this;

            var at = _ref5.at,
                filters = _ref5.filters;

            var queryResponse = { changedRows: 0, affectedRows: 0 },
                filter = this.parseFilters(filters);

            return this.acquire(at).then(function (collection) {
                collection.data = collection.data.filter(function (item) {
                    if (!filter(item)) return true;

                    queryResponse.affectedRows++;
                    return false;
                });

                return _this4.persist(at, collection).then(function () {
                    return queryResponse;
                });
            });
        }
    }, {
        key: "drop",
        value: function drop(_ref6) {
            var _this5 = this;

            var at = _ref6.at;

            return new Promise(function (resolve, reject) {
                (0, _fs.unlink)(_this5.getCollectionPath(at), function (err) {
                    return err ? reject(err) : resolve(true);
                });
            });
        }
    }, {
        key: "parseFilters",
        value: function parseFilters(filters) {
            if (!filters) return function () {
                return true;
            };

            if (typeof filters === "function") return filters;

            var criteria = new _QueryCriteria.QueryCriteria();
            return function (item) {
                var response = true;
                Object.keys(filters).forEach(function (key) {
                    if (!response) return null;

                    if (Array.isArray(filters[key])) {
                        var _filters$key = _slicedToArray(filters[key], 2),
                            comp = _filters$key[0],
                            value = _filters$key[1];

                        return response = response && criteria[comp.toLowerCase()](key, value)(item);
                    }

                    if (filters[key] && _typeof(filters[key]) === "object") return Object.keys(filters[key]).forEach(function (comp) {
                        return response = response && criteria[comp.toLowerCase()](key, filters[key][comp])(item);
                    });

                    response = criteria.eq(key, filters[key])(item);
                });

                return response;
            };
        }
    }, {
        key: "persist",
        value: function persist(at, collection) {
            var _this6 = this;

            return new Promise(function (resolve) {
                (0, _fs.writeFile)(_this6.getCollectionPath(at), JSON.stringify(collection, null, 4), function () {
                    return resolve(true);
                });
            });
        }
    }, {
        key: "acquire",
        value: function acquire(at) {
            var _this7 = this;

            return new Promise(function (resolve) {
                (0, _fs.exists)(_this7.getCollectionPath(at), function (existsCollection) {
                    if (!existsCollection) return resolve(FileAdapter.buildCollection(at));

                    (0, _fs.readFile)(_this7.getCollectionPath(at), { encoding: "utf8" }, function (err, data) {
                        return resolve(JSON.parse(data));
                    });
                });
            });
        }
    }, {
        key: "getCollectionPath",
        value: function getCollectionPath(at) {
            return this.path + "/" + at + ".json";
        }
    }]);

    return FileAdapter;
}();

FileAdapter.buildCollection = function (name) {
    var rules = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};

    return {
        name: name,
        rules: rules,
        data: []
    };
};