import * as tslib_1 from "tslib";
import { AbstractSqliteDriver } from "../driver/sqlite-abstract/AbstractSqliteDriver";
import { createLiteralRepository } from "./LiteralRepository";
/**
 * Repository is supposed to work with your entity objects. Find entities, insert, update, delete, etc.
 */
export function createLiteralTreeRepository(_a) {
    var manager = _a.manager, metadata = _a.metadata, queryRunner = _a.queryRunner;
    function createRelationMaps(metadata, alias, rawResults) {
        return rawResults.map(function (rawResult) {
            var joinColumn = metadata.treeParentRelation.joinColumns[0];
            // fixes issue #2518, default to databaseName property when givenDatabaseName is not set
            var joinColumnName = joinColumn.givenDatabaseName || joinColumn.databaseName;
            var id = rawResult[alias + "_" + metadata.primaryColumns[0].databaseName];
            var parentId = rawResult[alias + "_" + joinColumnName];
            return {
                id: metadata.connection.driver.prepareHydratedValue(id, metadata.primaryColumns[0]),
                parentId: metadata.connection.driver.prepareHydratedValue(parentId, joinColumn),
            };
        });
    }
    function buildChildrenEntityTree(metadata, entity, entities, relationMaps) {
        var childProperty = metadata.treeChildrenRelation.propertyName;
        var parentEntityId = metadata.primaryColumns[0].getEntityValue(entity);
        var childRelationMaps = relationMaps.filter(function (relationMap) { return relationMap.parentId === parentEntityId; });
        var childIds = childRelationMaps.map(function (relationMap) { return relationMap.id; });
        entity[childProperty] = entities.filter(function (entity) { return childIds.indexOf(entity.id) !== -1; });
        entity[childProperty].forEach(function (childEntity) {
            buildChildrenEntityTree(metadata, childEntity, entities, relationMaps);
        });
    }
    function buildParentEntityTree(metadata, entity, entities, relationMaps) {
        var parentProperty = metadata.treeParentRelation.propertyName;
        var entityId = metadata.primaryColumns[0].getEntityValue(entity);
        var parentRelationMap = relationMaps.find(function (relationMap) { return relationMap.id === entityId; });
        var parentEntity = entities.find(function (entity) {
            if (!parentRelationMap)
                return false;
            return entity[metadata.primaryColumns[0].propertyName] === parentRelationMap.parentId;
        });
        if (parentEntity) {
            entity[parentProperty] = parentEntity;
            buildParentEntityTree(metadata, entity[parentProperty], entities, relationMaps);
        }
    }
    // -------------------------------------------------------------------------
    return tslib_1.__assign({}, createLiteralRepository({ manager: manager, metadata: metadata, queryRunner: queryRunner }), { findTrees: function () {
            return tslib_1.__awaiter(this, void 0, void 0, function () {
                var roots;
                var _this = this;
                return tslib_1.__generator(this, function (_a) {
                    switch (_a.label) {
                        case 0: return [4 /*yield*/, this.findRoots()];
                        case 1:
                            roots = _a.sent();
                            return [4 /*yield*/, Promise.all(roots.map(function (root) { return _this.findDescendantsTree(root); }))];
                        case 2:
                            _a.sent();
                            return [2 /*return*/, roots];
                    }
                });
            });
        },
        findRoots: function () {
            var _this = this;
            var escapeAlias = function (alias) { return _this.manager.connection.driver.escape(alias); };
            var escapeColumn = function (column) { return _this.manager.connection.driver.escape(column); };
            var parentPropertyName = this.manager.connection.namingStrategy.joinColumnName(this.metadata.treeParentRelation.propertyName, "id");
            return this.createQueryBuilder("treeEntity")
                .where(escapeAlias("treeEntity") + "." + escapeColumn(parentPropertyName) + " IS NULL")
                .getMany();
        },
        findDescendants: function (entity) {
            return this
                .createDescendantsQueryBuilder("treeEntity", "treeClosure", entity)
                .getMany();
        },
        findDescendantsTree: function (entity) {
            var _this = this;
            // todo: throw exception if there is no column of this relation?
            return this
                .createDescendantsQueryBuilder("treeEntity", "treeClosure", entity)
                .getRawAndEntities()
                .then(function (entitiesAndScalars) {
                var relationMaps = createRelationMaps(_this.metadata, "treeEntity", entitiesAndScalars.raw);
                buildChildrenEntityTree(_this.metadata, entity, entitiesAndScalars.entities, relationMaps);
                return entity;
            });
        },
        countDescendants: function (entity) {
            return this
                .createDescendantsQueryBuilder("treeEntity", "treeClosure", entity)
                .getCount();
        },
        createDescendantsQueryBuilder: function (alias, closureTableAlias, entity) {
            var _this = this;
            // create shortcuts for better readability
            var escape = function (alias) { return _this.manager.connection.driver.escape(alias); };
            if (this.metadata.treeType === "closure-table") {
                var joinCondition = this.metadata.closureJunctionTable.descendantColumns.map(function (column) {
                    return escape(closureTableAlias) + "." + escape(column.propertyPath) + " = " + escape(alias) + "." + escape(column.referencedColumn.propertyPath);
                }).join(" AND ");
                var parameters_1 = {};
                var whereCondition = this.metadata.closureJunctionTable.ancestorColumns.map(function (column) {
                    parameters_1[column.referencedColumn.propertyName] = column.referencedColumn.getEntityValue(entity);
                    return escape(closureTableAlias) + "." + escape(column.propertyPath) + " = :" + column.referencedColumn.propertyName;
                }).join(" AND ");
                return this
                    .createQueryBuilder(alias)
                    .innerJoin(this.metadata.closureJunctionTable.tableName, closureTableAlias, joinCondition)
                    .where(whereCondition)
                    .setParameters(parameters_1);
            }
            else if (this.metadata.treeType === "nested-set") {
                var whereCondition = alias + "." + this.metadata.nestedSetLeftColumn.propertyPath + " BETWEEN " +
                    "joined." + this.metadata.nestedSetLeftColumn.propertyPath + " AND joined." + this.metadata.nestedSetRightColumn.propertyPath;
                var parameters_2 = {};
                var joinCondition = this.metadata.treeParentRelation.joinColumns.map(function (joinColumn) {
                    var parameterName = joinColumn.referencedColumn.propertyPath.replace(".", "_");
                    parameters_2[parameterName] = joinColumn.referencedColumn.getEntityValue(entity);
                    return "joined." + joinColumn.referencedColumn.propertyPath + " = :" + parameterName;
                }).join(" AND ");
                return this
                    .createQueryBuilder(alias)
                    .innerJoin(this.metadata.targetName, "joined", whereCondition)
                    .where(joinCondition, parameters_2);
            }
            else if (this.metadata.treeType === "materialized-path") {
                return this
                    .createQueryBuilder(alias)
                    .where(function (qb) {
                    var subQuery = qb.subQuery()
                        .select(_this.metadata.targetName + "." + _this.metadata.materializedPathColumn.propertyPath, "path")
                        .from(_this.metadata.target, _this.metadata.targetName)
                        .whereInIds(_this.metadata.getEntityIdMap(entity));
                    qb.setNativeParameters(subQuery.expressionMap.nativeParameters);
                    if (_this.manager.connection.driver instanceof AbstractSqliteDriver) {
                        return alias + "." + _this.metadata.materializedPathColumn.propertyPath + " LIKE " + subQuery.getQuery() + " || '%'";
                    }
                    else {
                        return alias + "." + _this.metadata.materializedPathColumn.propertyPath + " LIKE CONCAT(" + subQuery.getQuery() + ", '%')";
                    }
                });
            }
            throw new Error("Supported only in tree entities");
        },
        findAncestors: function (entity) {
            return this
                .createAncestorsQueryBuilder("treeEntity", "treeClosure", entity)
                .getMany();
        },
        findAncestorsTree: function (entity) {
            var _this = this;
            // todo: throw exception if there is no column of this relation?
            return this
                .createAncestorsQueryBuilder("treeEntity", "treeClosure", entity)
                .getRawAndEntities()
                .then(function (entitiesAndScalars) {
                var relationMaps = createRelationMaps(_this.metadata, "treeEntity", entitiesAndScalars.raw);
                buildParentEntityTree(_this.metadata, entity, entitiesAndScalars.entities, relationMaps);
                return entity;
            });
        },
        countAncestors: function (entity) {
            return this
                .createAncestorsQueryBuilder("treeEntity", "treeClosure", entity)
                .getCount();
        },
        createAncestorsQueryBuilder: function (alias, closureTableAlias, entity) {
            // create shortcuts for better readability
            // const escape = (alias: string) => this.manager.connection.driver.escape(alias);
            var _this = this;
            if (this.metadata.treeType === "closure-table") {
                var joinCondition = this.metadata.closureJunctionTable.ancestorColumns.map(function (column) {
                    return closureTableAlias + "." + column.propertyPath + " = " + alias + "." + column.referencedColumn.propertyPath;
                }).join(" AND ");
                var parameters_3 = {};
                var whereCondition = this.metadata.closureJunctionTable.descendantColumns.map(function (column) {
                    parameters_3[column.referencedColumn.propertyName] = column.referencedColumn.getEntityValue(entity);
                    return closureTableAlias + "." + column.propertyPath + " = :" + column.referencedColumn.propertyName;
                }).join(" AND ");
                return this
                    .createQueryBuilder(alias)
                    .innerJoin(this.metadata.closureJunctionTable.tableName, closureTableAlias, joinCondition)
                    .where(whereCondition)
                    .setParameters(parameters_3);
            }
            else if (this.metadata.treeType === "nested-set") {
                var joinCondition = "joined." + this.metadata.nestedSetLeftColumn.propertyPath + " BETWEEN " +
                    alias + "." + this.metadata.nestedSetLeftColumn.propertyPath + " AND " + alias + "." + this.metadata.nestedSetRightColumn.propertyPath;
                var parameters_4 = {};
                var whereCondition = this.metadata.treeParentRelation.joinColumns.map(function (joinColumn) {
                    var parameterName = joinColumn.referencedColumn.propertyPath.replace(".", "_");
                    parameters_4[parameterName] = joinColumn.referencedColumn.getEntityValue(entity);
                    return "joined." + joinColumn.referencedColumn.propertyPath + " = :" + parameterName;
                }).join(" AND ");
                return this
                    .createQueryBuilder(alias)
                    .innerJoin(this.metadata.targetName, "joined", joinCondition)
                    .where(whereCondition, parameters_4);
            }
            else if (this.metadata.treeType === "materialized-path") {
                // example: SELECT * FROM category category WHERE (SELECT mpath FROM `category` WHERE id = 2) LIKE CONCAT(category.mpath, '%');
                return this
                    .createQueryBuilder(alias)
                    .where(function (qb) {
                    var subQuery = qb.subQuery()
                        .select(_this.metadata.targetName + "." + _this.metadata.materializedPathColumn.propertyPath, "path")
                        .from(_this.metadata.target, _this.metadata.targetName)
                        .whereInIds(_this.metadata.getEntityIdMap(entity));
                    qb.setNativeParameters(subQuery.expressionMap.nativeParameters);
                    if (_this.manager.connection.driver instanceof AbstractSqliteDriver) {
                        return subQuery.getQuery() + " LIKE " + alias + "." + _this.metadata.materializedPathColumn.propertyPath + " || '%'";
                    }
                    else {
                        return subQuery.getQuery() + " LIKE CONCAT(" + alias + "." + _this.metadata.materializedPathColumn.propertyPath + ", '%')";
                    }
                });
            }
            throw new Error("Supported only in tree entities");
        } });
}

//# sourceMappingURL=LiteralTreeRepository.js.map
