import React from 'react';
import { Draggable, Droppable } from '../../dnd';
import { isArgumentColumnOrField, isQlLogicalOperator, } from '../../../parser/src/predicate';
import { mapColumnDataTypeToExpressionFunctionType } from '../../../Utilities/adaptableQlUtils';
import { booleanExpressionFunctions } from '../../../Utilities/ExpressionFunctions/booleanExpressionFunctions';
import { useAdaptable } from '../../../View/AdaptableContext';
import DropdownButton from '../../DropdownButton';
import ErrorBox from '../../ErrorBox';
import { Icon } from '../../icons';
import SimpleButton from '../../SimpleButton';
import { CombinatorSelector, ExpressionSelector, PrimitiveValueInput, PrimitiveColumnOrFieldSelector, PrimitiveMultiValueInput, } from './QueryBuilderInputs';
import { getOperatorMatchingInputs as getFunctionMatchingInputTypes, mapExpressionToFieldValue, } from './utils';
import { Box, Flex } from '../../Flex';
const ITEM_HEIGHT = 40;
const BASE_CLASS_NAME = 'ab-QueryBuilder-predicate-editor';
const Handle = (props) => (React.createElement(Flex, { className: `${BASE_CLASS_NAME}__handle twa:mr-1`, style: { height: ITEM_HEIGHT }, alignItems: "center", ...props },
    React.createElement(Icon, { name: "drag" })));
const QueryPredicateButtons = (props) => {
    return (React.createElement(React.Fragment, null,
        !props.hideAdd && (React.createElement(DropdownButton, { listMinWidth: 150, columns: ['label'], items: [
                { label: 'Condition', onClick: () => props.onNewPredicate('filter') },
                { label: 'AND / OR Group', onClick: () => props.onNewPredicate('group') },
            ], variant: "text" },
            React.createElement(Icon, { name: "plus" }))),
        !props.hideDelete && (React.createElement(SimpleButton, { icon: "delete", variant: "text", onClick: () => {
                props.onChange(null);
            } }))));
};
const LogicalFunctionEditor = (props) => {
    const level = props.id.split('/').length - 1;
    const className = `
    ${BASE_CLASS_NAME}
    ${BASE_CLASS_NAME}-level-${level}
    ${BASE_CLASS_NAME}-combinator
    ${props.lastChild ? `${BASE_CLASS_NAME}--last-child` : ''}
    ${props.isRoot ? `${BASE_CLASS_NAME}--root` : `${BASE_CLASS_NAME}--child`}
  `;
    const getCombinatorEl = (handleProps, className) => (React.createElement(Droppable, { droppableId: props.id, type: props.id }, (provided, snapshot) => {
        return (React.createElement("div", { ...provided.droppableProps, ref: provided.innerRef, className: className },
            React.createElement(Flex, null,
                props.isRoot ? null : React.createElement(Handle, { ...handleProps }),
                React.createElement(Flex, { className: "twa:flex-1", alignItems: "center", height: ITEM_HEIGHT },
                    React.createElement(CombinatorSelector, { value: props.predicate.operator, onChange: (combinator) => {
                            props.onChange({
                                ...props.predicate,
                                operator: combinator,
                            });
                        } }),
                    React.createElement(Box, { className: "twa:flex-1" }),
                    React.createElement(QueryPredicateButtons, { hideDelete: props.isRoot, hideAdd: true, ...props }))),
            React.createElement(Box, { className: `${BASE_CLASS_NAME}__children-wrapper` },
                props.predicate.args.map((arg, index) => {
                    const id = `${props.id}/${index}`;
                    return (React.createElement(QueryPredicateBuilder, { key: id, lastChild: index === props.predicate.args.length - 1, index: index, id: id, predicate: arg, onNewPredicate: (type) => {
                            const newPredicate = {
                                operator: type === 'filter' ? undefined : 'AND',
                                args: [],
                            };
                            if (typeof arg === 'object' &&
                                'operator' in arg &&
                                isQlLogicalOperator(arg.operator)) {
                                // add as a child
                                const newArg = {
                                    ...arg,
                                    args: [...arg.args, newPredicate],
                                };
                                const args = [...props.predicate.args];
                                args[index] = newArg;
                                props.onChange({
                                    ...props.predicate,
                                    args,
                                });
                            }
                            else {
                                // add as a sibling
                                const prevArgs = [...props.predicate.args];
                                prevArgs.splice(index + 1, 0, newPredicate);
                                props.onChange({
                                    ...props.predicate,
                                    args: prevArgs,
                                });
                            }
                        }, onChange: (predicate) => {
                            const args = [...props.predicate.args];
                            if (predicate) {
                                args[index] = predicate;
                            }
                            else {
                                args.splice(index, 1);
                            }
                            props.onChange({
                                ...props.predicate,
                                args,
                            });
                        } }));
                }),
                provided.placeholder,
                React.createElement("div", { className: `${BASE_CLASS_NAME}__root-actions` },
                    React.createElement(QueryPredicateButtons, { ...props, hideDelete: true })))));
    }));
    if (props.isRoot) {
        return getCombinatorEl({ className: className });
    }
    else {
        return (React.createElement(Draggable, { key: props.id, draggableId: props.id, index: props.index }, (provided, snapshot) => {
            return (React.createElement("div", { ...provided.draggableProps, ref: provided.innerRef, className: className }, getCombinatorEl(provided.dragHandleProps)));
        }));
    }
};
const PrimitiveFunctionEditor = (props) => {
    // [handle] [column] [operator-dropdown] [...args] [delete-button] [plus-button]
    const adaptable = useAdaptable();
    const [columnOrFieldExpression, ...restOfArgs] = props.predicate.args;
    const columnOrField = columnOrFieldExpression;
    let columnOrFieldId = null;
    let columnOrFieldDataType = null;
    let columnInputDataType = null;
    let functionInputInputDataTypes = null;
    // Thsese are the type of inputs ommiting the column
    // [[column-data-type], number, number]
    let restOfFunctionInputDataTypes = [];
    if (columnOrField) {
        if (!isArgumentColumnOrField(columnOrField)) {
            return React.createElement(ErrorBox, null, "Expression must start with a column or a filed!");
        }
        if (columnOrField.includes('FIELD')) {
            // we let the full expression so we can difirienciate between column and field
            columnOrFieldId = columnOrField;
            const fieldValue = mapExpressionToFieldValue(columnOrField);
            columnOrFieldDataType = adaptable.api.expressionApi.internalApi.getFieldType(fieldValue);
        }
        else if (columnOrField.includes('[')) {
            columnOrFieldId = columnOrField;
            const columnId = columnOrField.replace(/[\[\]]/g, '');
            columnOrFieldDataType = adaptable.api.columnApi.getColumnDataTypeForColumnId(columnId);
        }
        columnInputDataType = mapColumnDataTypeToExpressionFunctionType(columnOrFieldDataType);
        functionInputInputDataTypes = booleanExpressionFunctions[props.predicate.operator]?.inputs;
        restOfFunctionInputDataTypes = functionInputInputDataTypes
            ? getFunctionMatchingInputTypes(columnInputDataType, functionInputInputDataTypes)
            : [];
    }
    const level = props.id.split('/').length - 1;
    return (React.createElement(Draggable, { key: props.id, draggableId: props.id, index: props.index }, (provided) => {
        return (React.createElement(Flex, { className: `twa:pb-2 ${BASE_CLASS_NAME} ${BASE_CLASS_NAME}-level-${level} ${BASE_CLASS_NAME}-primitive ${props.lastChild ? `${BASE_CLASS_NAME}--last-child` : ''} `, ref: provided.innerRef, ...provided.draggableProps, style: { ...provided.draggableProps.style, minHeight: ITEM_HEIGHT } },
            React.createElement(Handle, { ...provided.dragHandleProps }),
            React.createElement(Flex, { alignItems: "center", style: { height: ITEM_HEIGHT } },
                React.createElement(Box, { className: "twa:mr-2" },
                    React.createElement(PrimitiveColumnOrFieldSelector, { onChange: (colOrField) => {
                            props.onChange({
                                ...props.predicate,
                                args: [colOrField],
                                operator: null,
                            });
                        }, fieldOrColumn: columnOrFieldId })),
                columnOrFieldId && columnOrFieldDataType && (React.createElement(ExpressionSelector, { dataType: columnOrFieldDataType, onExpressionChange: (operator) => {
                        let args = [props.predicate.args[0]];
                        if (columnOrFieldDataType === 'boolean' && operator !== 'NOT') {
                            args = [props.predicate.args[0], 'TRUE'];
                        }
                        props.onChange({
                            ...props.predicate,
                            operator,
                            // discard arguments
                            args,
                        });
                    }, value: props.predicate.operator })),
                React.createElement(Flex, { className: "twa:flex-1 twa:ml-2" }, restOfFunctionInputDataTypes.map((type, index) => {
                    const key = type + index;
                    const commonProps = {
                        lefthandColumnIdParam: columnOrFieldId,
                        inputType: type,
                    };
                    if (type.includes('[]')) {
                        return (React.createElement(PrimitiveMultiValueInput, { ...commonProps, key: key, value: restOfArgs, onChange: (values) => {
                                const args = [...props.predicate.args.slice(0, 1), ...values];
                                props.onChange({
                                    ...props.predicate,
                                    args,
                                });
                            } }));
                    }
                    return (React.createElement(PrimitiveValueInput, { ...commonProps, key: key, value: restOfArgs[index] ?? null, onChange: (value) => {
                            const args = [...props.predicate.args];
                            // +1 because col is the first argument
                            args[index + 1] = value;
                            props.onChange({
                                ...props.predicate,
                                args,
                            });
                        } }));
                }))),
            React.createElement(Box, { className: "twa:flex-1" }),
            React.createElement(QueryPredicateButtons, { ...props })));
    }));
};
/**
 * Two types:
 * - combinatory operator
 * - can contain both expressons and combinators
 * [handle] [combinator-dropdown]
 *   [children]
 *
 * - boolean function: ars do not contain other combinators
 * [handle] [column] [operator-dropdown] [...args] [delete-button] [plus-button]
 */
export const QueryPredicateBuilder = (props) => {
    if (isQlLogicalOperator(props.predicate.operator)) {
        return React.createElement(LogicalFunctionEditor, { ...props });
    }
    else {
        return React.createElement(PrimitiveFunctionEditor, { ...props });
    }
};
