"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __generator = (this && this.__generator) || function (thisArg, body) {
    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
    function verb(n) { return function (v) { return step([n, v]); }; }
    function step(op) {
        if (f) throw new TypeError("Generator is already executing.");
        while (_) try {
            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
            if (y = 0, t) op = [op[0] & 2, t.value];
            switch (op[0]) {
                case 0: case 1: t = op; break;
                case 4: _.label++; return { value: op[1], done: false };
                case 5: _.label++; y = op[1]; op = [0]; continue;
                case 7: op = _.ops.pop(); _.trys.pop(); continue;
                default:
                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                    if (t[2]) _.ops.pop();
                    _.trys.pop(); continue;
            }
            op = body.call(thisArg, _);
        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
    }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DatabaseFactory = void 0;
var mongodb_1 = require("mongodb");
var bfast_database_tree_1 = require("bfast-database-tree");
var buffer_1 = require("buffer");
var uuid_1 = require("uuid");
var const_util_1 = require("../utils/const.util");
var app_events_factory_1 = require("./app-events.factory");
var debug_util_1 = require("../utils/debug.util");
var ipfs_factory_1 = require("./ipfs.factory");
var DatabaseFactory = (function () {
    function DatabaseFactory() {
        this.ipfsFactory = new ipfs_factory_1.IpfsFactory();
    }
    DatabaseFactory.prototype.whenWantToSaveADataNodeToATree = function (cid, options) {
        var _this = this;
        return {
            nodeHandler: function (_a) {
                var path = _a.path, node = _a.node;
                return __awaiter(_this, void 0, void 0, function () {
                    var _loop_1, _i, _b, key;
                    return __generator(this, function (_c) {
                        switch (_c.label) {
                            case 0:
                                _loop_1 = function (key) {
                                    var $setMap, client, conn;
                                    return __generator(this, function (_d) {
                                        switch (_d.label) {
                                            case 0:
                                                $setMap = {};
                                                if (typeof node[key] === "object") {
                                                    $setMap = Object.keys(node[key]).reduce(function (a, b) {
                                                        a["value." + b] = node[key][b];
                                                        return a;
                                                    }, {});
                                                }
                                                else {
                                                    $setMap = {
                                                        value: node[key]
                                                    };
                                                }
                                                debug_util_1.devLog('start save a node', key);
                                                client = new mongodb_1.MongoClient(options.mongoDbUri);
                                                return [4, client.connect()];
                                            case 1:
                                                conn = _d.sent();
                                                return [4, conn.db().collection(DatabaseFactory.nodeTable(path.toString())).updateOne({
                                                        _id: isNaN(Number(key)) ? key.trim() : Number(key),
                                                    }, {
                                                        $set: $setMap
                                                    }, {
                                                        upsert: true,
                                                    })];
                                            case 2:
                                                _d.sent();
                                                return [4, conn.close()];
                                            case 3:
                                                _d.sent();
                                                debug_util_1.devLog('done save a node', key);
                                                return [2];
                                        }
                                    });
                                };
                                _i = 0, _b = Object.keys(node);
                                _c.label = 1;
                            case 1:
                                if (!(_i < _b.length)) return [3, 4];
                                key = _b[_i];
                                return [5, _loop_1(key)];
                            case 2:
                                _c.sent();
                                _c.label = 3;
                            case 3:
                                _i++;
                                return [3, 1];
                            case 4: return [2];
                        }
                    });
                });
            },
            nodeIdHandler: function () {
                return __awaiter(this, void 0, void 0, function () {
                    return __generator(this, function (_a) {
                        return [2, cid.toString()];
                    });
                });
            }
        };
    };
    DatabaseFactory.prototype.nodeSearchResult = function (nodeTable, targetNodeId, options) {
        return __awaiter(this, void 0, void 0, function () {
            var conn, cursor, docs, next, fn, conn, r;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        if (!(typeof targetNodeId === "object" && (targetNodeId === null || targetNodeId === void 0 ? void 0 : targetNodeId.hasOwnProperty('$fn')))) return [3, 7];
                        debug_util_1.devLog('handle query by expression', targetNodeId.$fn);
                        return [4, mongodb_1.MongoClient.connect(options.mongoDbUri)];
                    case 1:
                        conn = _a.sent();
                        cursor = conn.db().collection(nodeTable).find({});
                        if (targetNodeId.hasOwnProperty('$orderBy')) {
                            cursor = cursor.sort('_id', targetNodeId.$orderBy);
                        }
                        if (targetNodeId.hasOwnProperty('$limit')) {
                            cursor = cursor.limit(targetNodeId.$limit);
                        }
                        if (targetNodeId.hasOwnProperty('$skip')) {
                            cursor = cursor.skip(targetNodeId.$skip);
                        }
                        docs = [];
                        _a.label = 2;
                    case 2: return [4, cursor.hasNext()];
                    case 3:
                        if (!_a.sent()) return [3, 5];
                        return [4, cursor.next()];
                    case 4:
                        next = _a.sent();
                        fn = new Function('it', targetNodeId.$fn);
                        fn(next._id) === true ? docs.push(next) : null;
                        return [3, 2];
                    case 5: return [4, conn.close()];
                    case 6:
                        _a.sent();
                        debug_util_1.devLog('query has found items', docs.length);
                        return [2, docs.reduce(function (a, b) {
                                var _a;
                                if (typeof (b === null || b === void 0 ? void 0 : b.value) === 'string') {
                                    a.value = Object.assign(a.value, (_a = {}, _a[b._id ? b._id : b.id] = b.value, _a));
                                }
                                else {
                                    a.value = Object.assign(a.value, b.value);
                                }
                                return a;
                            }, { value: {} })];
                    case 7: return [4, mongodb_1.MongoClient.connect(options.mongoDbUri)];
                    case 8:
                        conn = _a.sent();
                        debug_util_1.devLog('handle query by node_id', targetNodeId);
                        return [4, conn.db().collection(nodeTable).findOne({ _id: targetNodeId })];
                    case 9:
                        r = _a.sent();
                        return [4, conn.close()];
                    case 10:
                        _a.sent();
                        return [2, r];
                }
            });
        });
    };
    DatabaseFactory.prototype.handleQueryObjectTree = function (mapOfNodesToQuery, domain, queryModel, options) {
        return __awaiter(this, void 0, void 0, function () {
            var nodesPathList, cids, skip, _all_p, _all;
            var _this = this;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        nodesPathList = Object.keys(mapOfNodesToQuery);
                        if (nodesPathList.length === 0) {
                            debug_util_1.devLog('try to get all data on this node', domain + "__id");
                            return [2, this.getAllContentsFromTreeTable(domain, queryModel, options)];
                        }
                        return [4, this.processTreeSearchResultToCidList(mapOfNodesToQuery, nodesPathList, domain, options)];
                    case 1:
                        cids = _a.sent();
                        if ((queryModel === null || queryModel === void 0 ? void 0 : queryModel.size) && (queryModel === null || queryModel === void 0 ? void 0 : queryModel.size) > 0) {
                            skip = (queryModel.skip && queryModel.skip >= 0) ? queryModel.skip : 0;
                            cids = cids.slice(skip, (queryModel.size + skip));
                        }
                        if ((queryModel === null || queryModel === void 0 ? void 0 : queryModel.count) === true) {
                            return [2, cids.length];
                        }
                        debug_util_1.devLog('total cids to fetch data from', cids.length);
                        if (queryModel.cids === true) {
                            return [2, cids.filter(function (c) { return c !== null; })];
                        }
                        _all_p = cids.map(function (x) {
                            return _this.ipfsFactory.generateDataFromCid(x, {
                                json: true
                            }, options);
                        });
                        return [4, Promise.all(_all_p)];
                    case 2:
                        _all = _a.sent();
                        return [2, _all.filter(function (t) { return t !== null; })];
                }
            });
        });
    };
    DatabaseFactory.nodeTable = function (nodePath) {
        return nodePath === null || nodePath === void 0 ? void 0 : nodePath.replace(new RegExp('/', 'ig'), '_').trim();
    };
    DatabaseFactory.pruneNode = function (nodeValue, nodeTable, domain, options) {
        return __awaiter(this, void 0, void 0, function () {
            var conn, _i, _a, k, _r1;
            var _b, _c;
            return __generator(this, function (_d) {
                switch (_d.label) {
                    case 0: return [4, mongodb_1.MongoClient.connect(options.mongoDbUri)];
                    case 1:
                        conn = _d.sent();
                        _i = 0, _a = Object.keys(nodeValue.value);
                        _d.label = 2;
                    case 2:
                        if (!(_i < _a.length)) return [3, 5];
                        k = _a[_i];
                        return [4, conn.db().collection(domain + "__id").findOne({
                                _id: k
                            })];
                    case 3:
                        _r1 = _d.sent();
                        if (!_r1) {
                            delete nodeValue.value[k];
                            conn.db().collection(nodeTable).updateOne((_b = {},
                                _b["value." + k] = {
                                    $exists: true
                                },
                                _b), {
                                $unset: (_c = {},
                                    _c["value." + k] = 1,
                                    _c)
                            }).catch(console.log);
                        }
                        _d.label = 4;
                    case 4:
                        _i++;
                        return [3, 2];
                    case 5: return [4, conn.close()];
                    case 6:
                        _d.sent();
                        return [2, nodeValue];
                }
            });
        });
    };
    DatabaseFactory.pruneNodeWithStringValue = function (targetNodeId, nodeValue, nodeTable, domain, options) {
        return __awaiter(this, void 0, void 0, function () {
            var conn, _r1;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4, mongodb_1.MongoClient.connect(options.mongoDbUri)];
                    case 1:
                        conn = _a.sent();
                        return [4, conn.db().collection(domain + "__id").findOne({
                                _id: nodeValue._id
                            })];
                    case 2:
                        _r1 = _a.sent();
                        if (!_r1) {
                            nodeValue.value = null;
                            conn.db().collection(nodeTable).updateOne({
                                _id: targetNodeId
                            }, {
                                $unset: {
                                    value: 1
                                }
                            }).catch(console.log);
                        }
                        return [4, conn.close()];
                    case 3:
                        _a.sent();
                        return [2, nodeValue];
                }
            });
        });
    };
    DatabaseFactory.prototype.handleDeleteObjectTree = function (deleteTree, domain, options) {
        return __awaiter(this, void 0, void 0, function () {
            var keys, cids, cidMap, _i, keys_1, key, nodeTable, id, result, _a, _b, v, conn, _c, cids_1, x;
            return __generator(this, function (_d) {
                switch (_d.label) {
                    case 0:
                        keys = Object.keys(deleteTree);
                        cids = [];
                        cidMap = {};
                        if (keys.length === 0) {
                            return [2, []];
                        }
                        _i = 0, keys_1 = keys;
                        _d.label = 1;
                    case 1:
                        if (!(_i < keys_1.length)) return [3, 7];
                        key = keys_1[_i];
                        nodeTable = DatabaseFactory.nodeTable(key);
                        id = deleteTree[key];
                        return [4, this.nodeSearchResult(nodeTable, id, options)];
                    case 2:
                        result = _d.sent();
                        if (!(result && result.value)) return [3, 6];
                        if (!(typeof result.value === "object")) return [3, 4];
                        return [4, DatabaseFactory.pruneNode(result, nodeTable, domain, options)];
                    case 3:
                        result = _d.sent();
                        for (_a = 0, _b = Object.keys(result.value); _a < _b.length; _a++) {
                            v = _b[_a];
                            cids.push(v);
                            if (cidMap[v]) {
                                cidMap[v] += 1;
                            }
                            else {
                                cidMap[v] = 1;
                            }
                        }
                        return [3, 6];
                    case 4:
                        if (!(typeof result.value === 'string')) return [3, 6];
                        return [4, DatabaseFactory.pruneNodeWithStringValue(id, result, nodeTable, domain, options)];
                    case 5:
                        result = _d.sent();
                        if (result && result._id) {
                            cids.push(result._id);
                            if (cidMap[result._id]) {
                                cidMap[result._id] += 1;
                            }
                            else {
                                cidMap[result._id] = 1;
                            }
                        }
                        _d.label = 6;
                    case 6:
                        _i++;
                        return [3, 1];
                    case 7:
                        cids = cids
                            .filter(function (x) { return cidMap[x] === keys.length; })
                            .reduce(function (a, b) { return a.add(b); }, new Set());
                        cids = Array.from(cids);
                        return [4, mongodb_1.MongoClient.connect(options.mongoDbUri)];
                    case 8:
                        conn = _d.sent();
                        _c = 0, cids_1 = cids;
                        _d.label = 9;
                    case 9:
                        if (!(_c < cids_1.length)) return [3, 12];
                        x = cids_1[_c];
                        return [4, conn.db().collection(domain + "__id").deleteOne({
                                _id: x
                            }, {})];
                    case 10:
                        _d.sent();
                        _d.label = 11;
                    case 11:
                        _c++;
                        return [3, 9];
                    case 12: return [4, conn.close()];
                    case 13:
                        _d.sent();
                        return [2, cids.map(function (y) {
                                return {
                                    _id: y
                                };
                            })];
                }
            });
        });
    };
    DatabaseFactory.prototype.writeMany = function (domain, data, context, options) {
        return __awaiter(this, void 0, void 0, function () {
            var _i, data_1, _data;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        _i = 0, data_1 = data;
                        _a.label = 1;
                    case 1:
                        if (!(_i < data_1.length)) return [3, 4];
                        _data = data_1[_i];
                        return [4, this.writeOne(domain, _data, context, options)];
                    case 2:
                        _a.sent();
                        _a.label = 3;
                    case 3:
                        _i++;
                        return [3, 1];
                    case 4: return [2, data];
                }
            });
        });
    };
    DatabaseFactory.prototype.writeOne = function (domain, data, context, options) {
        return __awaiter(this, void 0, void 0, function () {
            var dataCopy, buffer, cid, treeController;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        dataCopy = Object.assign({}, data);
                        buffer = buffer_1.Buffer.from(JSON.stringify(dataCopy));
                        return [4, this.ipfsFactory.generateCidFromData(dataCopy, buffer, domain, options)];
                    case 1:
                        cid = (_a.sent()).cid;
                        treeController = new bfast_database_tree_1.TreeController();
                        return [4, treeController.objectToTree(dataCopy, domain, this.whenWantToSaveADataNodeToATree(cid.toString(), options))];
                    case 2:
                        _a.sent();
                        return [2, dataCopy];
                }
            });
        });
    };
    DatabaseFactory.prototype.init = function (options) {
        return __awaiter(this, void 0, void 0, function () {
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4, this.ipfsFactory.ensureIpfs(options)];
                    case 1:
                        _a.sent();
                        return [2];
                }
            });
        });
    };
    DatabaseFactory.prototype.findOne = function (domain, queryModel, context, options) {
        return __awaiter(this, void 0, void 0, function () {
            var treeController, queryTree, table, id, conn, result, cid;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        treeController = new bfast_database_tree_1.TreeController();
                        return [4, treeController.query(domain, {
                                _id: queryModel._id
                            })];
                    case 1:
                        queryTree = _a.sent();
                        table = DatabaseFactory.nodeTable(Object.keys(queryTree)[0]);
                        id = Object.values(queryTree)[0];
                        return [4, mongodb_1.MongoClient.connect(options.mongoDbUri)];
                    case 2:
                        conn = _a.sent();
                        return [4, conn.db().collection(table).findOne({ _id: id }, {})];
                    case 3:
                        result = _a.sent();
                        return [4, conn.close()];
                    case 4:
                        _a.sent();
                        if (!result) {
                            return [2, null];
                        }
                        cid = result.value;
                        if (queryModel.cids === true) {
                            return [2, cid];
                        }
                        return [2, this.ipfsFactory.generateDataFromCid(cid, {
                                json: true
                            }, options)];
                }
            });
        });
    };
    DatabaseFactory.prototype.findMany = function (domain, queryModel, context, options) {
        return __awaiter(this, void 0, void 0, function () {
            var nodesToQueryData, treeController, e_1, resultMap_1, _i, nodesToQueryData_1, _tree, r, _result1;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        _a.trys.push([0, 2, , 3]);
                        treeController = new bfast_database_tree_1.TreeController();
                        return [4, treeController.query(domain, queryModel.filter)];
                    case 1:
                        nodesToQueryData = _a.sent();
                        return [3, 3];
                    case 2:
                        e_1 = _a.sent();
                        console.log(e_1);
                        return [2, []];
                    case 3:
                        if (!Array.isArray(nodesToQueryData)) return [3, 8];
                        resultMap_1 = {};
                        _i = 0, nodesToQueryData_1 = nodesToQueryData;
                        _a.label = 4;
                    case 4:
                        if (!(_i < nodesToQueryData_1.length)) return [3, 7];
                        _tree = nodesToQueryData_1[_i];
                        return [4, this.handleQueryObjectTree(_tree, domain, queryModel, options)];
                    case 5:
                        r = _a.sent();
                        Array.isArray(r) ?
                            (r.forEach(function (_r1) {
                                resultMap_1[_r1._id] = _r1;
                            }))
                            : resultMap_1[uuid_1.v4()] = r;
                        _a.label = 6;
                    case 6:
                        _i++;
                        return [3, 4];
                    case 7:
                        _result1 = Object.values(resultMap_1);
                        return [2, (queryModel === null || queryModel === void 0 ? void 0 : queryModel.count) ? _result1.reduce(function (a, b) { return a + b; }, 0) : _result1];
                    case 8: return [4, this.handleQueryObjectTree(nodesToQueryData, domain, queryModel, options)];
                    case 9: return [2, _a.sent()];
                }
            });
        });
    };
    DatabaseFactory.prototype.updateOne = function (domain, updateModel, context, options) {
        return __awaiter(this, void 0, void 0, function () {
            var oldDoc, updateParts, incrementParts, newDoc;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4, this.findOne(domain, {
                            _id: updateModel.id,
                            return: []
                        }, context, options)];
                    case 1:
                        oldDoc = _a.sent();
                        if (!oldDoc && updateModel.upsert === true) {
                            oldDoc = { _id: updateModel.id };
                        }
                        if (!oldDoc) {
                            return [2, null];
                        }
                        updateParts = updateModel.update.$set;
                        incrementParts = updateModel.update.$inc;
                        newDoc = Object.assign(oldDoc, updateParts);
                        newDoc = this.incrementFields(newDoc, incrementParts);
                        return [2, this.writeOne(domain, newDoc, context, options)];
                }
            });
        });
    };
    DatabaseFactory.prototype.updateMany = function (domain, updateModel, context, options) {
        return __awaiter(this, void 0, void 0, function () {
            var oldDocs, nDoc, incrementParts, _i, oldDocs_1, x, _a, _b;
            return __generator(this, function (_c) {
                switch (_c.label) {
                    case 0: return [4, this.findMany(domain, updateModel, context, options)];
                    case 1:
                        oldDocs = _c.sent();
                        if (Array.isArray(oldDocs) && oldDocs.length === 0 && updateModel.upsert === true) {
                            nDoc = Object.assign(updateModel.update.$set, updateModel.filter);
                            incrementParts = updateModel.update.$inc;
                            nDoc = this.incrementFields(nDoc, incrementParts);
                            oldDocs.push(nDoc);
                            return [2, this.writeMany(domain, oldDocs, context, options)];
                        }
                        _i = 0, oldDocs_1 = oldDocs;
                        _c.label = 2;
                    case 2:
                        if (!(_i < oldDocs_1.length)) return [3, 5];
                        x = oldDocs_1[_i];
                        _a = oldDocs;
                        _b = oldDocs.indexOf(x);
                        return [4, this.updateOne(domain, {
                                update: updateModel.update,
                                upsert: updateModel.upsert,
                                id: x._id,
                                return: []
                            }, context, options)];
                    case 3:
                        _a[_b] = _c.sent();
                        _c.label = 4;
                    case 4:
                        _i++;
                        return [3, 2];
                    case 5: return [2, oldDocs];
                }
            });
        });
    };
    DatabaseFactory.prototype.delete = function (domain, deleteModel, context, options) {
        return __awaiter(this, void 0, void 0, function () {
            var deleteTree, treeController, e_2, result, _i, deleteTree_1, _tree, r;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        _a.trys.push([0, 2, , 3]);
                        treeController = new bfast_database_tree_1.TreeController();
                        return [4, treeController.query(domain, deleteModel.id ? { _id: deleteModel.id } : deleteModel.filter)];
                    case 1:
                        deleteTree = _a.sent();
                        return [3, 3];
                    case 2:
                        e_2 = _a.sent();
                        console.log(e_2);
                        return [2, null];
                    case 3:
                        if (!Array.isArray(deleteTree)) return [3, 8];
                        result = [];
                        _i = 0, deleteTree_1 = deleteTree;
                        _a.label = 4;
                    case 4:
                        if (!(_i < deleteTree_1.length)) return [3, 7];
                        _tree = deleteTree_1[_i];
                        return [4, this.handleDeleteObjectTree(_tree, domain, options)];
                    case 5:
                        r = _a.sent();
                        Array.isArray(r) ? result.push.apply(result, r) : result.push(r);
                        _a.label = 6;
                    case 6:
                        _i++;
                        return [3, 4];
                    case 7: return [2, result];
                    case 8: return [2, this.handleDeleteObjectTree(deleteTree, domain, options)];
                }
            });
        });
    };
    DatabaseFactory.prototype.bulk = function (operations) {
        return __awaiter(this, void 0, void 0, function () {
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4, operations(null)];
                    case 1: return [2, _a.sent()];
                }
            });
        });
    };
    DatabaseFactory.prototype.changes = function (domain, pipeline, listener, resumeToken) {
        if (resumeToken === void 0) { resumeToken = undefined; }
        return __awaiter(this, void 0, void 0, function () {
            var appEventInst;
            return __generator(this, function (_a) {
                appEventInst = app_events_factory_1.AppEventsFactory.getInstance();
                appEventInst.sub(const_util_1.ConstUtil.DB_CHANGES_EVENT.concat(domain), listener);
                return [2, {
                        close: function () {
                            appEventInst.unSub(const_util_1.ConstUtil.DB_CHANGES_EVENT.concat(domain), listener);
                        }
                    }];
            });
        });
    };
    DatabaseFactory.prototype.incrementFields = function (newDoc, ip) {
        if (!newDoc) {
            newDoc = {};
        }
        if (!ip) {
            return newDoc;
        }
        else {
            for (var _i = 0, _a = Object.keys(ip); _i < _a.length; _i++) {
                var key = _a[_i];
                if (typeof ip[key] === "number") {
                    if (newDoc.hasOwnProperty(key) && !isNaN(newDoc[key])) {
                        newDoc[key] += ip[key];
                    }
                    else if (!newDoc.hasOwnProperty(key)) {
                        newDoc[key] = ip[key];
                    }
                }
                else if (typeof ip[key] === "object" && JSON.stringify(ip[key]).startsWith('{')) {
                    newDoc[key] = this.incrementFields(newDoc[key], ip[key]);
                }
            }
            return newDoc;
        }
    };
    DatabaseFactory.prototype.getAllContentsFromTreeTable = function (domain, queryModel, options) {
        return __awaiter(this, void 0, void 0, function () {
            var conn, result, skip, _all_p, _all;
            var _this = this;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4, mongodb_1.MongoClient.connect(options.mongoDbUri)];
                    case 1:
                        conn = _a.sent();
                        return [4, conn.db().collection(domain + "__id").find({}).toArray()];
                    case 2:
                        result = _a.sent();
                        return [4, conn.close()];
                    case 3:
                        _a.sent();
                        if (!(result && Array.isArray(result))) return [3, 5];
                        if ((queryModel === null || queryModel === void 0 ? void 0 : queryModel.size) && (queryModel === null || queryModel === void 0 ? void 0 : queryModel.size) > 0) {
                            skip = (queryModel.skip && queryModel.skip >= 1) ? queryModel.skip : 0;
                            result = result.slice(skip, (queryModel.size + skip));
                        }
                        if ((queryModel === null || queryModel === void 0 ? void 0 : queryModel.count) === true) {
                            return [2, result.length];
                        }
                        debug_util_1.devLog('total cids to fetch data from', result.length);
                        if (queryModel.cids === true) {
                            return [2, result.map(function (x) { return x === null || x === void 0 ? void 0 : x.value; }).filter(function (y) { return y !== null; })];
                        }
                        _all_p = result.map(function (x) {
                            return _this.ipfsFactory.generateDataFromCid(x === null || x === void 0 ? void 0 : x.value, {
                                json: true
                            }, options);
                        });
                        return [4, Promise.all(_all_p)];
                    case 4:
                        _all = _a.sent();
                        return [2, _all.filter(function (b) { return b !== null; })];
                    case 5: return [2, []];
                }
            });
        });
    };
    DatabaseFactory.prototype.processTreeSearchResultToCidList = function (mapOfNodesToQuery, nodesPathList, domain, options) {
        return __awaiter(this, void 0, void 0, function () {
            var cids, cidMap, _i, nodesPathList_1, nodePath, nodeTable, targetNodeId, result, _a, _b, v;
            return __generator(this, function (_c) {
                switch (_c.label) {
                    case 0:
                        cids = [];
                        cidMap = {};
                        _i = 0, nodesPathList_1 = nodesPathList;
                        _c.label = 1;
                    case 1:
                        if (!(_i < nodesPathList_1.length)) return [3, 7];
                        nodePath = nodesPathList_1[_i];
                        nodeTable = DatabaseFactory.nodeTable(nodePath);
                        targetNodeId = mapOfNodesToQuery[nodePath];
                        return [4, this.nodeSearchResult(nodeTable, targetNodeId, options)];
                    case 2:
                        result = _c.sent();
                        if (!(result && result.value)) return [3, 6];
                        if (!(typeof result.value === "object")) return [3, 4];
                        return [4, DatabaseFactory.pruneNode(result, nodeTable, domain, options)];
                    case 3:
                        result = _c.sent();
                        for (_a = 0, _b = Object.values(result.value); _a < _b.length; _a++) {
                            v = _b[_a];
                            cids.push(v);
                            if (cidMap[v]) {
                                cidMap[v] += 1;
                            }
                            else {
                                cidMap[v] = 1;
                            }
                        }
                        return [3, 6];
                    case 4:
                        if (!(typeof result.value === 'string')) return [3, 6];
                        return [4, DatabaseFactory.pruneNodeWithStringValue(targetNodeId, result, nodeTable, domain, options)];
                    case 5:
                        result = _c.sent();
                        if (result && result.value) {
                            cids.push(result.value);
                            if (cidMap[result.value]) {
                                cidMap[result.value] += 1;
                            }
                            else {
                                cidMap[result.value] = 1;
                            }
                        }
                        _c.label = 6;
                    case 6:
                        _i++;
                        return [3, 1];
                    case 7:
                        cids = cids.filter(function (x) { return cidMap[x] === nodesPathList.length; }).reduce(function (a, b) { return a.add(b); }, new Set());
                        cids = Array.from(cids);
                        return [2, cids];
                }
            });
        });
    };
    return DatabaseFactory;
}());
exports.DatabaseFactory = DatabaseFactory;
