var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
import ObjectID from "../../../Types/ObjectID";
import Text from "../../../Types/Text";
import Typeof from "../../../Types/Typeof";
import { FindOperator, Raw } from "typeorm";
import CaptureSpan from "../../Utils/Telemetry/CaptureSpan";
export default class QueryHelper {
    static modulo(moduloBy, reminder) {
        const rid = Text.generateRandomText(10);
        const rid2 = Text.generateRandomText(10);
        return Raw((alias) => {
            return `(${alias} % :${rid} = :${rid2})`;
        }, {
            [rid]: moduloBy,
            [rid2]: reminder,
        });
    }
    static findWithSameText(text) {
        let isString = false;
        if (typeof text === Typeof.String) {
            text = text.toLowerCase().trim();
            isString = true;
        }
        const rid = Text.generateRandomText(10);
        return Raw((alias) => {
            return isString
                ? `(LOWER(${alias}) = :${rid})`
                : `(${alias} = :${rid})`;
        }, {
            [rid]: `${text}`,
        });
    }
    static isNull() {
        return Raw((alias) => {
            return `(${alias} IS NULL)`;
        });
    }
    static notNull() {
        return Raw((alias) => {
            return `(${alias} IS NOT NULL)`;
        });
    }
    static equalToOrNull(value) {
        const rid = [];
        const valuesObj = {};
        if (Array.isArray(value)) {
            for (const item of value) {
                const temp = Text.generateRandomText(10);
                rid.push(temp);
                valuesObj[temp] = item.toString();
            }
        }
        else {
            const temp = Text.generateRandomText(10);
            rid.push(temp);
            valuesObj[temp] = value.toString();
        }
        const constructQuery = (alias) => {
            let query = "(";
            query += rid
                .map((item) => {
                return `${alias} = :${item}`;
            })
                .join(" or ");
            query += ` or ${alias} IS NULL)`;
            return query;
        };
        return Raw((alias) => {
            return constructQuery(alias);
        }, Object.assign({}, valuesObj));
    }
    static notEquals(value) {
        const rid = Text.generateRandomText(10);
        return Raw((alias) => {
            return `(${alias} != :${rid})`;
        }, {
            [rid]: value.toString(),
        });
    }
    static search(name) {
        name = name.toLowerCase().trim();
        const rid = Text.generateRandomText(10);
        return Raw((alias) => {
            return `((CAST(${alias} AS TEXT) ILIKE :${rid}))`;
        }, {
            [rid]: `%${name}%`,
        });
    }
    /**
     * Searches the provided entity property names with a single OR-joined ILIKE.
     *
     * IMPORTANT: emit unquoted `alias.propertyName` references and let
     * TypeORM's `replacePropertyNamesForTheWholeQuery` post-processor escape
     * the table alias and translate property names → DB column names. Pre-
     * quoting (e.g. `Incident."title"`) bypasses that pass, which leaves an
     * unquoted `Incident` in the final SQL — Postgres then lowercases it and
     * fails with `missing FROM-clause entry for table "incident"`.
     */
    static multiSearch(entityPropertyNames, value) {
        const trimmed = value.toLowerCase().trim();
        const rid = Text.generateRandomText(10);
        return Raw((alias) => {
            const tableAlias = alias.includes(".")
                ? alias.split(".")[0]
                : alias;
            const orConditions = entityPropertyNames
                .map((field) => {
                return `(CAST(${tableAlias}.${field} AS TEXT) ILIKE :${rid})`;
            })
                .join(" OR ");
            return `(${orConditions})`;
        }, {
            [rid]: `%${trimmed}%`,
        });
    }
    static notContains(name) {
        name = name.toLowerCase().trim();
        const rid = Text.generateRandomText(10);
        return Raw((alias) => {
            return `(CAST(${alias} AS TEXT) NOT ILIKE :${rid} OR ${alias} IS NULL)`;
        }, {
            [rid]: `%${name}%`,
        });
    }
    static startsWith(name) {
        name = name.toLowerCase().trim();
        const rid = Text.generateRandomText(10);
        return Raw((alias) => {
            return `(CAST(${alias} AS TEXT) ILIKE :${rid})`;
        }, {
            [rid]: `${name}%`,
        });
    }
    static endsWith(name) {
        name = name.toLowerCase().trim();
        const rid = Text.generateRandomText(10);
        return Raw((alias) => {
            return `(CAST(${alias} AS TEXT) ILIKE :${rid})`;
        }, {
            [rid]: `%${name}`,
        });
    }
    static all(values) {
        values = values.map((value) => {
            return value.toString();
        });
        const rid = Text.generateRandomText(10);
        if (!values || values.length === 0) {
            return Raw(() => {
                return `TRUE = FALSE`; // this will always return false
            }, {});
        }
        return Raw((alias) => {
            return `(${alias} = ALL(:${rid}))`;
        }, {
            [rid]: values,
        });
    }
    static any(values) {
        return this.in(values); // any and in are the same
    }
    /**
     * Returns a filter that matches owner rows that are linked to *none* of the
     * provided related entity ids through a many-to-many join table. The
     * returned FindOperator is intended to be applied to the primary id column
     * of the owner entity.
     */
    static noneEntitiesInManyToMany(data) {
        const values = data.values.map((value) => {
            return value.toString();
        });
        if (!values || values.length === 0) {
            return Raw(() => {
                return `TRUE = TRUE`;
            }, {});
        }
        const valuesRid = Text.generateRandomText(10);
        const joinTable = data.joinTableName.replace(/"/g, '""');
        const ownerCol = data.ownerColumnName.replace(/"/g, '""');
        const relationCol = data.relationColumnName.replace(/"/g, '""');
        return Raw((alias) => {
            return `(${alias} NOT IN (SELECT "${joinTable}"."${ownerCol}" FROM "${joinTable}" WHERE "${joinTable}"."${relationCol}" IN (:...${valuesRid})))`;
        }, {
            [valuesRid]: values,
        });
    }
    /**
     * Returns a filter that matches owner rows that are linked to *any* of the
     * provided related entity ids through a many-to-many join table. The
     * returned FindOperator is intended to be applied to the primary id column
     * of the owner entity. An empty values array matches nothing (fail closed).
     */
    static anyOfEntitiesInManyToMany(data) {
        const values = data.values.map((value) => {
            return value.toString();
        });
        if (!values || values.length === 0) {
            return Raw(() => {
                return `TRUE = FALSE`;
            }, {});
        }
        const valuesRid = Text.generateRandomText(10);
        const joinTable = data.joinTableName.replace(/"/g, '""');
        const ownerCol = data.ownerColumnName.replace(/"/g, '""');
        const relationCol = data.relationColumnName.replace(/"/g, '""');
        return Raw((alias) => {
            return `(${alias} IN (SELECT "${joinTable}"."${ownerCol}" FROM "${joinTable}" WHERE "${joinTable}"."${relationCol}" IN (:...${valuesRid})))`;
        }, {
            [valuesRid]: values,
        });
    }
    /**
     * Matches owner rows that have no rows in the join table — i.e. the
     * many-to-many collection is empty. Apply to the owner's primary id column.
     */
    static noEntitiesInManyToMany(data) {
        const joinTable = data.joinTableName.replace(/"/g, '""');
        const ownerCol = data.ownerColumnName.replace(/"/g, '""');
        return Raw((alias) => {
            return `(${alias} NOT IN (SELECT "${joinTable}"."${ownerCol}" FROM "${joinTable}" WHERE "${joinTable}"."${ownerCol}" IS NOT NULL))`;
        }, {});
    }
    /**
     * Matches owner rows that have at least one row in the join table — i.e.
     * the many-to-many collection has at least one entry. Apply to the
     * owner's primary id column.
     */
    static anyEntitiesInManyToMany(data) {
        const joinTable = data.joinTableName.replace(/"/g, '""');
        const ownerCol = data.ownerColumnName.replace(/"/g, '""');
        return Raw((alias) => {
            return `(${alias} IN (SELECT "${joinTable}"."${ownerCol}" FROM "${joinTable}" WHERE "${joinTable}"."${ownerCol}" IS NOT NULL))`;
        }, {});
    }
    /**
     * Returns a filter that matches owner rows that are linked to *all* of the
     * provided related entity ids through a many-to-many join table. The
     * returned FindOperator is intended to be applied to the primary id column
     * of the owner entity.
     */
    static allEntitiesInManyToMany(data) {
        const values = data.values.map((value) => {
            return value.toString();
        });
        if (!values || values.length === 0) {
            return Raw(() => {
                return `TRUE = FALSE`;
            }, {});
        }
        const valuesRid = Text.generateRandomText(10);
        const countRid = Text.generateRandomText(10);
        // Escape identifiers so they can safely be embedded in the SQL string.
        const joinTable = data.joinTableName.replace(/"/g, '""');
        const ownerCol = data.ownerColumnName.replace(/"/g, '""');
        const relationCol = data.relationColumnName.replace(/"/g, '""');
        return Raw((alias) => {
            return `(${alias} IN (SELECT "${joinTable}"."${ownerCol}" FROM "${joinTable}" WHERE "${joinTable}"."${relationCol}" IN (:...${valuesRid}) GROUP BY "${joinTable}"."${ownerCol}" HAVING COUNT(DISTINCT "${joinTable}"."${relationCol}") >= :${countRid}))`;
        }, {
            [valuesRid]: values,
            [countRid]: values.length,
        });
    }
    static in(values) {
        values = values.map((value) => {
            return value.toString();
        });
        const rid = Text.generateRandomText(10);
        if (!values || values.length === 0) {
            return Raw(() => {
                return `TRUE = FALSE`; // this will always return false
            }, {});
        }
        return Raw((alias) => {
            return `(${alias} IN (:...${rid}))`;
        }, {
            [rid]: values,
        });
    }
    static notIn(values) {
        values = values.map((value) => {
            return value.toString();
        });
        const rid = Text.generateRandomText(10);
        if (!values || values.length === 0) {
            return Raw(() => {
                return `TRUE = TRUE`; // this will always return true
            }, {});
        }
        return Raw((alias) => {
            return `(${alias} NOT IN (:...${rid}))`;
        }, {
            [rid]: values,
        });
    }
    static notInOrNull(values) {
        values = values.map((value) => {
            return value.toString();
        });
        const rid = Text.generateRandomText(10);
        if (!values || values.length === 0) {
            return Raw(() => {
                return `TRUE = TRUE`; // this will always return true
            }, {});
        }
        return Raw((alias) => {
            return `(${alias} NOT IN (:...${rid}) or ${alias} IS NULL)`;
        }, {
            [rid]: values,
        });
    }
    static inRelationArray(values) {
        return values.map((item) => {
            if (item instanceof ObjectID) {
                return item;
            }
            return item.id;
        });
    }
    static equalTo(value) {
        const rid = Text.generateRandomText(10);
        return Raw((alias) => {
            return `(${alias} = :${rid})`;
        }, {
            [rid]: value.toString(),
        });
    }
    static greaterThanEqualTo(value) {
        const rid = Text.generateRandomText(10);
        return Raw((alias) => {
            return `(${alias} >= :${rid})`;
        }, {
            [rid]: value,
        });
    }
    static greaterThanEqualToOrNull(value) {
        const rid = Text.generateRandomText(10);
        return Raw((alias) => {
            return `(${alias} >= :${rid} or ${alias} IS NULL)`;
        }, {
            [rid]: value,
        });
    }
    static lessThanEqualTo(value) {
        const rid = Text.generateRandomText(10);
        return Raw((alias) => {
            return `(${alias} <= :${rid})`;
        }, {
            [rid]: value,
        });
    }
    static lessThanOrNull(value) {
        const rid = Text.generateRandomText(10);
        return Raw((alias) => {
            return `(${alias} < :${rid} or ${alias} IS NULL)`;
        }, {
            [rid]: value,
        });
    }
    static lessThanEqualToOrNull(value) {
        const rid = Text.generateRandomText(10);
        return Raw((alias) => {
            return `(${alias} <= :${rid} or ${alias} IS NULL)`;
        }, {
            [rid]: value,
        });
    }
    static greaterThan(value) {
        const rid = Text.generateRandomText(10);
        return Raw((alias) => {
            return `(${alias} > :${rid})`;
        }, {
            [rid]: value,
        });
    }
    static greaterThanOrNull(value) {
        const rid = Text.generateRandomText(10);
        return Raw((alias) => {
            return `(${alias} >= :${rid} or ${alias} IS NULL)`;
        }, {
            [rid]: value,
        });
    }
    static inBetween(startValue, endValue) {
        const rid1 = Text.generateRandomText(10);
        const rid2 = Text.generateRandomText(10);
        return Raw((alias) => {
            return `(${alias} >= :${rid1} and ${alias} <= :${rid2})`;
        }, {
            [rid1]: startValue,
            [rid2]: endValue,
        });
    }
    static inBetweenOrNull(startValue, endValue) {
        const rid1 = Text.generateRandomText(10);
        const rid2 = Text.generateRandomText(10);
        return Raw((alias) => {
            return `(((${alias} >= :${rid1} and ${alias} <= :${rid2})) or (${alias} IS NULL))`;
        }, {
            [rid1]: startValue,
            [rid2]: endValue,
        });
    }
    static notInBetween(startValue, endValue) {
        const rid1 = Text.generateRandomText(10);
        const rid2 = Text.generateRandomText(10);
        return Raw((alias) => {
            return `(${alias} < :${rid1} or ${alias} > :${rid2})`;
        }, {
            [rid1]: startValue,
            [rid2]: endValue,
        });
    }
    static queryJson(value) {
        // seed random text
        const values = {};
        let queryText = "";
        if (typeof value === Typeof.String) {
            value = JSON.parse(value.toString());
        }
        if (value instanceof FindOperator) {
            return value;
        }
        const hasValue = value && Object.keys(value).length > 0;
        for (const key in value) {
            const temp = Text.generateRandomText(10);
            values[temp] = value[key];
            if (!queryText) {
                queryText = `(COLUMN_NAME_ALIAS->>'${key}' = :${temp}`;
            }
            else {
                queryText += ` AND COLUMN_NAME_ALIAS->>'${key}' = :${temp}`;
            }
        }
        if (hasValue) {
            queryText += ")";
        }
        else {
            queryText = "(COLUMN_NAME_ALIAS IS NULL OR COLUMN_NAME_ALIAS = '{}')";
        }
        return Raw((alias) => {
            /*
             * alias is table name + column name like tableName.columnName
             * we need to convert this to "tableName"."columnName"
             */
            alias = alias
                .split(".")
                .map((item) => {
                return `"${item}"`;
            })
                .join(".");
            queryText = Text.replaceAll(queryText, "COLUMN_NAME_ALIAS", `${alias}`);
            return queryText;
        }, values);
    }
    static lessThan(value) {
        const rid = Text.generateRandomText(10);
        return Raw((alias) => {
            return `(${alias} < :${rid})`;
        }, {
            [rid]: value,
        });
    }
}
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Number, Number]),
    __metadata("design:returntype", Object)
], QueryHelper, "modulo", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Object)
], QueryHelper, "findWithSameText", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", []),
    __metadata("design:returntype", Object)
], QueryHelper, "isNull", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", []),
    __metadata("design:returntype", Object)
], QueryHelper, "notNull", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Object)
], QueryHelper, "equalToOrNull", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Object)
], QueryHelper, "notEquals", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String]),
    __metadata("design:returntype", Object)
], QueryHelper, "search", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Array, String]),
    __metadata("design:returntype", Object)
], QueryHelper, "multiSearch", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String]),
    __metadata("design:returntype", Object)
], QueryHelper, "notContains", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String]),
    __metadata("design:returntype", Object)
], QueryHelper, "startsWith", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String]),
    __metadata("design:returntype", Object)
], QueryHelper, "endsWith", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Array]),
    __metadata("design:returntype", Object)
], QueryHelper, "all", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Array]),
    __metadata("design:returntype", Object)
], QueryHelper, "any", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Object)
], QueryHelper, "noneEntitiesInManyToMany", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Object)
], QueryHelper, "anyOfEntitiesInManyToMany", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Object)
], QueryHelper, "noEntitiesInManyToMany", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Object)
], QueryHelper, "anyEntitiesInManyToMany", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Object)
], QueryHelper, "allEntitiesInManyToMany", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Array]),
    __metadata("design:returntype", Object)
], QueryHelper, "notIn", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Array]),
    __metadata("design:returntype", Object)
], QueryHelper, "notInOrNull", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Array]),
    __metadata("design:returntype", Array)
], QueryHelper, "inRelationArray", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String]),
    __metadata("design:returntype", Object)
], QueryHelper, "equalTo", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [typeof (_a = typeof T !== "undefined" && T) === "function" ? _a : Object]),
    __metadata("design:returntype", Object)
], QueryHelper, "greaterThanEqualTo", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [typeof (_b = typeof T !== "undefined" && T) === "function" ? _b : Object]),
    __metadata("design:returntype", Object)
], QueryHelper, "greaterThanEqualToOrNull", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [typeof (_c = typeof T !== "undefined" && T) === "function" ? _c : Object]),
    __metadata("design:returntype", Object)
], QueryHelper, "lessThanEqualTo", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [typeof (_d = typeof T !== "undefined" && T) === "function" ? _d : Object]),
    __metadata("design:returntype", Object)
], QueryHelper, "lessThanOrNull", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [typeof (_e = typeof T !== "undefined" && T) === "function" ? _e : Object]),
    __metadata("design:returntype", Object)
], QueryHelper, "lessThanEqualToOrNull", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [typeof (_f = typeof T !== "undefined" && T) === "function" ? _f : Object]),
    __metadata("design:returntype", Object)
], QueryHelper, "greaterThan", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [typeof (_g = typeof T !== "undefined" && T) === "function" ? _g : Object]),
    __metadata("design:returntype", Object)
], QueryHelper, "greaterThanOrNull", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [typeof (_h = typeof T !== "undefined" && T) === "function" ? _h : Object, typeof (_j = typeof T !== "undefined" && T) === "function" ? _j : Object]),
    __metadata("design:returntype", Object)
], QueryHelper, "inBetween", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [typeof (_k = typeof T !== "undefined" && T) === "function" ? _k : Object, typeof (_l = typeof T !== "undefined" && T) === "function" ? _l : Object]),
    __metadata("design:returntype", Object)
], QueryHelper, "inBetweenOrNull", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [typeof (_m = typeof T !== "undefined" && T) === "function" ? _m : Object, typeof (_o = typeof T !== "undefined" && T) === "function" ? _o : Object]),
    __metadata("design:returntype", Object)
], QueryHelper, "notInBetween", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Object)
], QueryHelper, "queryJson", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [typeof (_p = typeof T !== "undefined" && T) === "function" ? _p : Object]),
    __metadata("design:returntype", Object)
], QueryHelper, "lessThan", null);
//# sourceMappingURL=QueryHelper.js.map