import * as React from 'react';
import { DragDropContext } from '../../dnd';
import { mapExpressionToQlPredicate, } from '../../../parser/src/predicate';
import { mapQlPredicateToExpression } from '../../../parser/src/predicate/mapQlPredicateToExpression';
import { useAdaptable } from '../../../View/AdaptableContext';
import ErrorBox from '../../ErrorBox';
import HelpBlock from '../../HelpBlock';
import Panel from '../../Panel';
import SimpleButton from '../../SimpleButton';
import { WarningBox } from '../../WarningBox';
import { QueryPredicateBuilder } from './QueryPredicateBuilder';
import { getFunctionsForColumnType, getUnsuportedExpressionFromQlPredicate, reorder, } from './utils';
import { Box, Flex } from '../../Flex';
const QUERY_BUILDER_CLASSNAME = 'ab-QueryBuilder';
const QueryBuilderContext = React.createContext(null);
export function useQueryBuilderContext() {
    const context = React.useContext(QueryBuilderContext);
    if (!context) {
        throw new Error('useQueryBuilderContext must be used within a QueryBuilderContext');
    }
    return context;
}
export const QueryBuilder = (props) => {
    const adaptable = useAdaptable();
    const [qlPredicate, setQlPredicate] = React.useState(() => {
        const qlPredicate = mapExpressionToQlPredicate(props.query);
        return qlPredicate;
    });
    const [expressionStr, setExpressionStr] = React.useState(props.query);
    const handleQlPredicateChange = (qlPredicate) => {
        setQlPredicate(qlPredicate);
        if (qlPredicate && !('errorMessage' in qlPredicate)) {
            const newQuery = mapQlPredicateToExpression(qlPredicate);
            props.onChange(newQuery);
            setExpressionStr(newQuery);
        }
    };
    const clearExpression = () => {
        const predicate = mapExpressionToQlPredicate('');
        setQlPredicate(predicate);
        setExpressionStr('');
        props.onChange('');
    };
    const booleanExpressions = React.useMemo(() => {
        const expressionMap = adaptable.api.internalApi
            .getQueryLanguageService()
            .getModuleExpressionFunctionsMap(props.module);
        const booleanExpressions = { ...expressionMap.booleanFunctions };
        const booleanExpressionsWithoutCombinators = booleanExpressions;
        delete booleanExpressionsWithoutCombinators.AND;
        delete booleanExpressionsWithoutCombinators.OR;
        return booleanExpressionsWithoutCombinators;
    }, []);
    const context = React.useMemo(() => {
        return {
            getColumns: props.getColumns,
            getFields: props.getFields,
            getExpressions: (columnType) => {
                return booleanExpressions
                    ? getFunctionsForColumnType(columnType, Object.keys(booleanExpressions))
                    : [];
            },
            onQlPredicateChange: handleQlPredicateChange,
        };
    }, []);
    const clearExpressionButton = (React.createElement(SimpleButton, { onClick: () => {
            clearExpression();
        } }, "Clear Expression"));
    const unsupportedExpressionFunction = getUnsuportedExpressionFromQlPredicate(qlPredicate, {
        supportedFields: props.getFields(),
    });
    let errorOrEditor = null;
    if (qlPredicate && 'errorMessage' in qlPredicate) {
        errorOrEditor = props.query?.includes?.('QUERY') ? (React.createElement(WarningBox, { "data-name": "unsupported-query-warning" },
            React.createElement(Flex, { alignItems: "center" },
                "Named Queries are not supported in the Query Builder",
                React.createElement(Box, { className: "twa:flex-1" }),
                clearExpressionButton))) : (React.createElement(ErrorBox, null,
            React.createElement(Flex, null,
                qlPredicate.errorMessage,
                React.createElement(Box, { className: "twa:flex-1" }),
                clearExpressionButton)));
    }
    else if (unsupportedExpressionFunction) {
        errorOrEditor = (React.createElement(WarningBox, { "data-name": "unsupported-expression-warning" }, unsupportedExpressionFunction));
    }
    else if (qlPredicate && !('errorMessage' in qlPredicate)) {
        errorOrEditor = (React.createElement(QueryPredicateBuilder, { isRoot: true, index: 0, id: "0", predicate: qlPredicate, onNewPredicate: (type) => {
                // add to its children
                const newPredicate = {
                    operator: type === 'filter' ? undefined : 'AND',
                    args: [],
                };
                handleQlPredicateChange({
                    ...qlPredicate,
                    args: [...qlPredicate.args, newPredicate],
                });
            }, onChange: (predicate) => {
                handleQlPredicateChange(predicate);
            } }));
    }
    return (React.createElement(DragDropContext, { onDragEnd: (result) => {
            if (!result.destination) {
                return;
            }
            const toPath = `${result.destination.droppableId}/${result.destination.index}`;
            const fromPath = result.draggableId;
            const predicate = reorder(qlPredicate, fromPath, toPath);
            handleQlPredicateChange(predicate);
        } },
        React.createElement(QueryBuilderContext.Provider, { value: context },
            React.createElement(Box, { className: QUERY_BUILDER_CLASSNAME },
                React.createElement(HelpBlock, { "data-name": "query-builder-help", className: "twa:my-2 twa:p-2 twa:text-3" }, "Build the Grid Filter by adding Column Conditions and AND / OR Groups as required"),
                errorOrEditor,
                React.createElement(Panel, { "data-name": "query-builder-expression-preview", variant: "default", header: "AdapTableQL Expression", className: "twa:mt-3" },
                    React.createElement(Box, { className: `${QUERY_BUILDER_CLASSNAME}__expression twa:min-h-[48px] twa:my-2 twa:p-3` }, expressionStr || 'Output Expression will display here'))))));
};
