import { mapColumnDataTypeToExpressionFunctionType } from '../../../Utilities/adaptableQlUtils';
import { isQlLogicalOperator } from '../../../parser/src/predicate';
import { booleanExpressionFunctions } from '../../../Utilities/ExpressionFunctions/booleanExpressionFunctions';
import { deepClone } from '../../../Utilities/Extensions/ObjectExtensions';
import { booleanExpressionsAvailableInQueryBuilder } from './booleanExpressions';
export const reorder = (predicate, from, to) => {
    const predicateCopy = deepClone(predicate);
    // we start from the root so no need to traverse first level
    const [_, ...fromPath] = from.split('/');
    const [__, ...toPath] = to.split('/');
    // remove from old position and get the item
    // path = '0/1/2'
    let itemToMove = predicateCopy;
    let prev = null;
    for (const index of fromPath) {
        prev = itemToMove;
        itemToMove = itemToMove.args[parseInt(index)];
    }
    prev.args.splice(parseInt(fromPath[fromPath.length - 1]), 1);
    // find the parent into wich we want to insert the item
    let cursor = predicateCopy;
    let parent = null;
    for (const index of toPath) {
        parent = cursor;
        cursor = cursor.args[parseInt(index)];
    }
    parent.args.splice(parseInt(toPath[toPath.length - 1]), 0, itemToMove);
    return predicateCopy;
};
export const getOperatorMatchingInputs = (columnType, inputs, includeAllInputs = false) => {
    if (Array.isArray(inputs[0])) {
        const matchingInput = inputs.find((input) => input[0] === columnType);
        const [_, ...restOfArgs] = matchingInput ?? [];
        return restOfArgs;
    }
    const [_, ...restOfArgs] = inputs;
    return includeAllInputs
        ? inputs
        : restOfArgs;
};
export const getFunctionsForColumnType = (dataType, availableBooleanFunctions) => {
    const columnType = mapColumnDataTypeToExpressionFunctionType(dataType);
    return booleanExpressionsAvailableInQueryBuilder.filter((boolFnName) => {
        if (!availableBooleanFunctions.includes(boolFnName)) {
            return false;
        }
        const functionDef = booleanExpressionFunctions[boolFnName];
        const inputs = functionDef.inputs;
        let matchingInputTypes = [];
        if (inputs && Array.isArray(inputs[0])) {
            matchingInputTypes = inputs.find((input) => input[0] === columnType);
        }
        else {
            matchingInputTypes = inputs;
        }
        return matchingInputTypes?.[0] === columnType;
    });
};
export const getUnsuportedExpressionFromQlPredicate = (predicate, context) => {
    if (!predicate) {
        return null;
    }
    if (typeof predicate === 'string' && isFieldValue(predicate)) {
        const field = mapExpressionToFieldValue(predicate);
        if (!context.supportedFields?.some((field) => field?.value === mapExpressionToFieldValue(predicate))) {
            return `Expression uses an unregistered field: "${field}"`;
        }
        else {
            return null;
        }
    }
    if (typeof predicate === 'object' && 'errorMessage' in predicate) {
        return null;
    }
    if (typeof predicate !== 'object' || !('operator' in predicate) || !predicate.operator) {
        return null;
    }
    if (!isQlLogicalOperator(predicate.operator) &&
        !booleanExpressionsAvailableInQueryBuilder.includes(predicate.operator)) {
        return `${predicate.operator} is not supported in the Query Builder`;
    }
    if (predicate.args.length === 0) {
        return null;
    }
    let firstInvalidArg = null;
    for (let arg of predicate.args) {
        firstInvalidArg = getUnsuportedExpressionFromQlPredicate(arg, context);
        if (firstInvalidArg) {
            break;
        }
    }
    if (firstInvalidArg) {
        return firstInvalidArg;
    }
    return null;
};
export const mapFieldValueToExpression = (field) => `FIELD('${field}')`;
export const mapExpressionToFieldValue = (expression) => {
    if (!expression) {
        return '';
    }
    try {
        return expression.match(/FIELD\(['"](.+)['"]\)/)?.[1];
    }
    catch (e) {
        return '';
    }
};
export const mapColumnExpressionToColumnId = (expression) => {
    if (!expression) {
        return null;
    }
    try {
        return expression.match(/\[(.+)\]/)?.[1];
    }
    catch (e) {
        return expression;
    }
};
export const isFieldValue = (expression) => {
    return typeof expression === 'string' && expression.includes('FIELD');
};
