"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = sqlGenerator;
/**
 * # SQL Query Generator
 * @description Generates an SQL Query for node module `mysql` or `serverless-mysql`
 */
function sqlGenerator({ tableName, genObject, dbFullName, }) {
    if (!genObject)
        return undefined;
    const finalQuery = genObject.query ? genObject.query : undefined;
    const queryKeys = finalQuery ? Object.keys(finalQuery) : undefined;
    const sqlSearhValues = [];
    const finalDbName = dbFullName ? `${dbFullName}.` : "";
    /**
     * # Generate Query
     */
    function genSqlSrchStr({ queryObj, join, field, }) {
        const finalFieldName = (() => {
            if (queryObj === null || queryObj === void 0 ? void 0 : queryObj.tableName) {
                return `${finalDbName}${queryObj.tableName}.${field}`;
            }
            if (join) {
                return `${finalDbName}${tableName}.${field}`;
            }
            return field;
        })();
        let str = `${finalFieldName}=?`;
        if (typeof queryObj.value == "string" ||
            typeof queryObj.value == "number") {
            const valueParsed = String(queryObj.value);
            if (queryObj.equality == "LIKE") {
                str = `LOWER(${finalFieldName}) LIKE LOWER('%${valueParsed}%')`;
            }
            else if (queryObj.equality == "NOT EQUAL") {
                str = `${finalFieldName} != ?`;
                sqlSearhValues.push(valueParsed);
            }
            else {
                sqlSearhValues.push(valueParsed);
            }
        }
        else if (Array.isArray(queryObj.value)) {
            /** @type {string[]} */
            const strArray = [];
            queryObj.value.forEach((val) => {
                const valueParsed = val;
                if (queryObj.equality == "LIKE") {
                    strArray.push(`LOWER(${finalFieldName}) LIKE LOWER('%${valueParsed}%')`);
                }
                else if (queryObj.equality == "NOT EQUAL") {
                    strArray.push(`${finalFieldName} != ?`);
                    sqlSearhValues.push(valueParsed);
                }
                else {
                    strArray.push(`${finalFieldName} = ?`);
                    sqlSearhValues.push(valueParsed);
                }
            });
            str = "(" + strArray.join(` ${queryObj.operator || "AND"} `) + ")";
        }
        return str;
    }
    const sqlSearhString = queryKeys === null || queryKeys === void 0 ? void 0 : queryKeys.map((field) => {
        const queryObj = 
        /** @type {import("../../../types").ServerQueryQueryObject} */ finalQuery === null || finalQuery === void 0 ? void 0 : finalQuery[field];
        if (!queryObj)
            return;
        if (queryObj.__query) {
            const subQueryGroup = 
            /** @type {import("../../../types").ServerQueryQueryObject}} */ queryObj.__query;
            const subSearchKeys = Object.keys(subQueryGroup);
            const subSearchString = subSearchKeys.map((_field) => {
                const newSubQueryObj = subQueryGroup === null || subQueryGroup === void 0 ? void 0 : subQueryGroup[_field];
                return genSqlSrchStr({
                    queryObj: newSubQueryObj,
                    field: _field,
                    join: genObject.join,
                });
            });
            console.log("queryObj.operator", queryObj.operator);
            return ("(" +
                subSearchString.join(` ${queryObj.operator || "AND"} `) +
                ")");
        }
        return genSqlSrchStr({ queryObj, field, join: genObject.join });
    });
    function generateJoinStr(
    /** @type {import("../../../types").ServerQueryParamsJoinMatchObject} */ mtch, 
    /** @type {import("../../../types").ServerQueryParamsJoin} */ join) {
        return `${finalDbName}${typeof mtch.source == "object" ? mtch.source.tableName : tableName}.${typeof mtch.source == "object" ? mtch.source.fieldName : mtch.source}=${(() => {
            if (mtch.targetLiteral) {
                return `'${mtch.targetLiteral}'`;
            }
            if (join.alias) {
                return `${finalDbName}${typeof mtch.target == "object"
                    ? mtch.target.tableName
                    : join.alias}.${typeof mtch.target == "object"
                    ? mtch.target.fieldName
                    : mtch.target}`;
            }
            return `${finalDbName}${typeof mtch.target == "object"
                ? mtch.target.tableName
                : join.tableName}.${typeof mtch.target == "object"
                ? mtch.target.fieldName
                : mtch.target}`;
        })()}`;
    }
    let queryString = (() => {
        var _a, _b, _c;
        let str = "SELECT";
        if ((_a = genObject.selectFields) === null || _a === void 0 ? void 0 : _a[0]) {
            if (genObject.join) {
                str += ` ${(_b = genObject.selectFields) === null || _b === void 0 ? void 0 : _b.map((fld) => `${finalDbName}${tableName}.${fld}`).join(",")}`;
            }
            else {
                str += ` ${(_c = genObject.selectFields) === null || _c === void 0 ? void 0 : _c.join(",")}`;
            }
        }
        else {
            if (genObject.join) {
                str += ` ${finalDbName}${tableName}.*`;
            }
            else {
                str += " *";
            }
        }
        if (genObject.join) {
            /** @type {string[]} */
            const existingJoinTableNames = [tableName];
            str +=
                "," +
                    genObject.join
                        .map((joinObj) => {
                        const joinTableName = joinObj.alias
                            ? joinObj.alias
                            : joinObj.tableName;
                        if (existingJoinTableNames.includes(joinTableName))
                            return null;
                        existingJoinTableNames.push(joinTableName);
                        if (joinObj.selectFields) {
                            return joinObj.selectFields
                                .map((selectField) => {
                                if (typeof selectField == "string") {
                                    return `${finalDbName}${joinTableName}.${selectField}`;
                                }
                                else if (typeof selectField == "object") {
                                    let aliasSelectField = selectField.count
                                        ? `COUNT(${finalDbName}${joinTableName}.${selectField.field})`
                                        : `${finalDbName}${joinTableName}.${selectField.field}`;
                                    if (selectField.alias)
                                        aliasSelectField += ` AS ${selectField.alias}`;
                                    return aliasSelectField;
                                }
                            })
                                .join(",");
                        }
                        else {
                            return `${finalDbName}${joinTableName}.*`;
                        }
                    })
                        .filter((_) => Boolean(_))
                        .join(",");
        }
        str += ` FROM ${finalDbName}${tableName}`;
        if (genObject.join) {
            str +=
                " " +
                    genObject.join
                        .map((join) => {
                        return (join.joinType +
                            " " +
                            (join.alias
                                ? `${finalDbName}${join.tableName}` +
                                    " " +
                                    join.alias
                                : `${finalDbName}${join.tableName}`) +
                            " ON " +
                            (() => {
                                if (Array.isArray(join.match)) {
                                    return ("(" +
                                        join.match
                                            .map((mtch) => generateJoinStr(mtch, join))
                                            .join(join.operator
                                            ? ` ${join.operator} `
                                            : " AND ") +
                                        ")");
                                }
                                else if (typeof join.match == "object") {
                                    return generateJoinStr(join.match, join);
                                }
                            })());
                    })
                        .join(" ");
        }
        return str;
    })();
    if ((sqlSearhString === null || sqlSearhString === void 0 ? void 0 : sqlSearhString[0]) && sqlSearhString.find((str) => str)) {
        const stringOperator = (genObject === null || genObject === void 0 ? void 0 : genObject.searchOperator) || "AND";
        queryString += ` WHERE ${sqlSearhString.join(` ${stringOperator} `)} `;
    }
    if (genObject.order)
        queryString += ` ORDER BY ${genObject.join
            ? `${finalDbName}${tableName}.${genObject.order.field}`
            : genObject.order.field} ${genObject.order.strategy}`;
    if (genObject.limit)
        queryString += ` LIMIT ${genObject.limit}`;
    if (genObject.offset)
        queryString += ` OFFSET ${genObject.offset}`;
    return {
        string: queryString,
        values: sqlSearhValues,
    };
}