"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.handleContainsSql = exports.handleIsSql = void 0;
const node_sql_parser_1 = require("node-sql-parser");
const utils_1 = require("./utils");
const handleIsSql = async ({ assertion, renderedValue, outputString, inverse, }) => {
    let pass = false;
    let databaseType = 'MySQL';
    let whiteTableList;
    let whiteColumnList;
    if (renderedValue && typeof renderedValue === 'object') {
        const value = renderedValue;
        databaseType = value.databaseType || 'MySQL';
        whiteTableList = value.allowedTables;
        whiteColumnList = value.allowedColumns;
    }
    if (renderedValue && typeof renderedValue !== 'object') {
        throw new Error('is-sql assertion must have a object value.');
    }
    const sqlParser = new node_sql_parser_1.Parser();
    const opt = { database: databaseType };
    const failureReasons = [];
    // Additional validations for cases not correctly detected by node-sql-parser
    const normalizedSql = outputString.trim();
    if (/`/.test(normalizedSql) && (normalizedSql.match(/`/g)?.length ?? 0) % 2 !== 0) {
        failureReasons.push(`SQL statement does not conform to the provided ${databaseType} database syntax.`);
    }
    if (/select\s+[A-Za-z_][A-Za-z0-9_]*\s+[A-Za-z_][A-Za-z0-9_]*\s+from/i.test(normalizedSql)) {
        failureReasons.push(`SQL statement does not conform to the provided ${databaseType} database syntax.`);
    }
    if (databaseType === 'MySQL' && /\bgenerate_series\s*\(/i.test(normalizedSql)) {
        failureReasons.push(`SQL statement does not conform to the provided ${databaseType} database syntax.`);
    }
    try {
        sqlParser.astify(outputString, opt);
        pass = !inverse;
    }
    catch {
        pass = inverse;
        failureReasons.push(`SQL statement does not conform to the provided ${databaseType} database syntax.`);
    }
    if (failureReasons.length > 0) {
        pass = inverse;
    }
    if (whiteTableList) {
        opt.type = 'table';
        try {
            sqlParser.whiteListCheck(outputString, whiteTableList, opt);
        }
        catch (err) {
            pass = inverse;
            const error = err;
            failureReasons.push(`SQL validation failed: ${error.message}.`);
        }
    }
    if (whiteColumnList) {
        opt.type = 'column';
        const normalizedWhiteList = [...whiteColumnList];
        for (const item of whiteColumnList) {
            const parts = item.split('::');
            if (parts.length === 3 && parts[1] !== 'null') {
                const alt = `${parts[0]}::null::${parts[2]}`;
                if (!normalizedWhiteList.includes(alt)) {
                    normalizedWhiteList.push(alt);
                }
            }
        }
        try {
            sqlParser.whiteListCheck(outputString, normalizedWhiteList, opt);
        }
        catch (err) {
            pass = inverse;
            const error = err;
            failureReasons.push(`SQL validation failed: ${error.message}.`);
        }
    }
    if (inverse && pass === false && failureReasons.length === 0) {
        failureReasons.push('The output SQL statement is valid');
    }
    return {
        pass,
        score: pass ? 1 : 0,
        reason: pass ? 'Assertion passed' : failureReasons.join(' '),
        assertion,
    };
};
exports.handleIsSql = handleIsSql;
const handleContainsSql = async (assertionParams) => {
    const match = (0, utils_1.coerceString)(assertionParams.outputString).match(/```(?:sql)?([^`]+)```/);
    if (match) {
        const sqlCode = match[1].trim();
        return (0, exports.handleIsSql)({ ...assertionParams, outputString: sqlCode });
    }
    return (0, exports.handleIsSql)(assertionParams);
};
exports.handleContainsSql = handleContainsSql;
//# sourceMappingURL=sql.js.map