"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.mapQlPredicateToExpression = mapQlPredicateToExpression;
exports.getQlPredicateSymbol = getQlPredicateSymbol;
const types_1 = require("./types");
function mapQlPredicateToExpression(predicate, symbolMapper = getQlPredicateSymbol) {
    if (!predicate) {
        return '';
    }
    const predicateType = getPredicateType(predicate.operator);
    const { operator, args } = predicate;
    if (!args || args.length === 0) {
        return operator;
    }
    if (operator === null || !operator) {
        return '';
    }
    const operatorSymbol = symbolMapper(operator);
    // If a combinator with only one branch, remove the combinator.
    if (args.length === 1 && typeof args[0] === 'object' && (0, types_1.isQlLogicalOperator)(operator)) {
        return mapQlPredicateToExpression(args[0]);
    }
    let [firstArg, ...restOfArgs] = args;
    const mappedArgs = restOfArgs
        .filter((arg) => arg !== undefined)
        .map((arg) => {
        if (typeof arg === 'string') {
            if (arg.includes('[')) {
                return arg;
            }
            if (arg.includes('FIELD')) {
                return arg;
            }
            if (arg.includes('DATE')) {
                return arg;
            }
            if (arg === 'TRUE' || arg === 'FALSE') {
                return arg;
            }
            return `"${arg}"`;
        }
        if (arg && typeof arg === 'object' && 'operator' in arg) {
            if ((0, types_1.isQlLogicalOperator)(arg.operator)) {
                return `(${mapQlPredicateToExpression(arg)})`;
            }
            return mapQlPredicateToExpression(arg);
        }
        if (typeof arg === 'boolean') {
            return arg ? 'TRUE' : 'FALSE';
        }
        return arg;
    });
    if (typeof firstArg === 'object' && 'operator' in firstArg) {
        firstArg = mapQlPredicateToExpression(firstArg);
    }
    if ((0, types_1.isQlLogicalOperator)(operator)) {
        // need to compose with AND [] AND ...
        return mappedArgs.length
            ? `${firstArg} ${operatorSymbol} ${mappedArgs.join(` ${operatorSymbol} `)}`
            : `${firstArg}`;
    }
    if (predicateType === 'INFIX') {
        if (operator === 'BETWEEN') {
            return `${firstArg} ${operatorSymbol} (${mappedArgs})`;
        }
        if (operator === 'IN') {
            return `${firstArg} ${operatorSymbol} (${mappedArgs.join(', ')})`;
        }
        return `${firstArg} ${operatorSymbol} ${mappedArgs.join(', ')}`;
    }
    if (predicateType === 'FUNCTION') {
        return `${operatorSymbol}(${[firstArg, ...mappedArgs].join(', ')})`;
    }
    return `${operatorSymbol}(${mappedArgs.join(', ')})`;
}
function getQlPredicateSymbol(predicateName) {
    switch (predicateName) {
        case 'NOT':
            return '!';
        case 'EQ':
            return '=';
        case 'NEQ':
            return '!=';
        case 'LT':
            return '<';
        case 'LTE':
            return '<=';
        case 'GT':
            return '>';
        case 'GTE':
            return '>=';
        default:
            return predicateName;
    }
}
function getPredicateType(predicateName) {
    switch (predicateName) {
        case 'COL':
        case 'FIELD':
            return 'VALUE_REF';
        case 'IF':
        case 'CASE':
            return 'CONDITIONAL';
        case 'NOT':
            return 'UNARY';
        case 'ADD':
        case 'SUB':
        case 'MUL':
        case 'DIV':
        case 'MOD':
        case 'POW':
        case 'OR':
        case 'AND':
        case 'EQ':
        case 'NEQ':
        case 'LT':
        case 'LTE':
        case 'GT':
        case 'GTE':
        case 'BETWEEN':
        case 'STARTS_WITH':
        case 'ENDS_WITH':
        case 'CONTAINS':
        case 'IN':
        case 'WHERE':
            return 'INFIX';
        default:
            return 'FUNCTION';
    }
}
