import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
/**
 * Copyright (c) 2020-present, Goldman Sachs
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import { forwardRef, useEffect, useMemo, useRef, useState } from 'react';
import { observer } from 'mobx-react-lite';
import { Tooltip, clsx, Dialog, TreeView, BlankPanelContent, ControlledDropdownMenu, ContextMenu, MenuContent, MenuContentItem, MenuContentItemIcon, MenuContentItemLabel, StringTypeIcon, ToggleIcon, HashtagIcon, ClockIcon, ChevronDownIcon, ChevronRightIcon, MoreVerticalIcon, CompressIcon, EyeIcon, InfoCircleIcon, PURE_ClassIcon, CheckIcon, SearchIcon, PanelLoadingIndicator, DragPreviewLayer, useDragPreviewLayer, Modal, ModalBody, ModalFooter, ModalHeader, ModalFooterButton, PanelHeaderActionItem, PanelHeaderActions, PanelHeader, TimesIcon, ClickAwayListener, } from '@finos/legend-art';
import { QUERY_BUILDER_EXPLORER_TREE_DND_TYPE, QueryBuilderExplorerTreeRootNodeData, QueryBuilderExplorerTreePropertyNodeData, QueryBuilderExplorerTreeSubTypeNodeData, buildPropertyExpressionFromExplorerTreeNodeData, } from '../../stores/explorer/QueryBuilderExplorerState.js';
import { useDrag } from 'react-dnd';
import { QueryBuilderPropertyInfoTooltip, QueryBuilderTaggedValueInfoTooltip, } from '../shared/QueryBuilderPropertyInfoTooltip.js';
import { flowResult } from 'mobx';
import { getPropertyChainName } from '../../stores/QueryBuilderPropertyEditorState.js';
import { Class, DerivedProperty, PrimitiveType, PrecisePrimitiveType, PRIMITIVE_TYPE, PRECISE_PRIMITIVE_TYPE, Enumeration, TYPE_CAST_TOKEN, getMultiplicityDescription, isElementDeprecated, extractElementNameFromPath, } from '@finos/legend-graph';
import { useApplicationStore } from '@finos/legend-application';
import { QUERY_BUILDER_TEST_ID } from '../../__lib__/QueryBuilderTesting.js';
import { debounce, filterByType, isNonNullable, prettyCONSTName, } from '@finos/legend-shared';
import { QueryBuilderPropertySearchPanel } from './QueryBuilderPropertySearchPanel.js';
import { QueryBuilderTDSState } from '../../stores/fetch-structure/tds/QueryBuilderTDSState.js';
import { QueryBuilderSimpleProjectionColumnState } from '../../stores/fetch-structure/tds/projection/QueryBuilderProjectionColumnState.js';
import { getClassPropertyIcon } from '@finos/legend-lego/graph-editor';
import { getStandardPrimitiveTypeEquivalent } from '../../stores/QueryBuilderValueSpecificationHelper.js';
import { QueryBuilderRootClassInfoTooltip } from '../shared/QueryBuilderRootClassInfoTooltip.js';
import { QueryBuilderTelemetryHelper } from '../../__lib__/QueryBuilderTelemetryHelper.js';
import { QueryBuilderRelationExplorerPanel } from './QueryBuilderRelationExplorerPanel.js';
export const checkForDeprecatedNode = (node, graph, treeData) => {
    if (node.type instanceof Class && isElementDeprecated(node.type, graph)) {
        return true;
    }
    if (node instanceof QueryBuilderExplorerTreePropertyNodeData) {
        if (isElementDeprecated(node.property, graph)) {
            return true;
        }
    }
    if (node instanceof QueryBuilderExplorerTreeSubTypeNodeData) {
        const parentNode = treeData.nodes.get(node.parentId);
        if (parentNode?.type instanceof Class &&
            isElementDeprecated(parentNode.type, graph)) {
            return true;
        }
    }
    return false;
};
export const isExplorerTreeNodeAlreadyUsed = (node, queryBuilderState) => {
    // if the fetch-structure is non-empty, it means we can mark the root node as used
    if (node instanceof QueryBuilderExplorerTreeRootNodeData &&
        queryBuilderState.fetchStructureState.implementation
            .usedExplorerTreePropertyNodeIDs.length > 0) {
        return true;
    }
    // in the explorer tree, we represent subtype as a subtree, we want to flatten this to only
    // the subtype that actually being used to make comparison simpler
    const simplifiedNodeID = node.id.replaceAll(/(?:@[^.]+)+/g, (val) => {
        const chunks = val.split(TYPE_CAST_TOKEN);
        return `${TYPE_CAST_TOKEN}${chunks[chunks.length - 1]}`;
    });
    return queryBuilderState.fetchStructureState.implementation.usedExplorerTreePropertyNodeIDs.includes(simplifiedNodeID);
};
export const QueryBuilderSubclassInfoTooltip = (props) => {
    const { subclass, path, isMapped, children, placement, multiplicity } = props;
    const [open, setIsOpen] = useState(false);
    return (_jsx(ClickAwayListener, { onClickAway: () => setIsOpen(false), mouseEvent: "onMouseDown", children: _jsx("div", { children: _jsx(Tooltip, { arrow: true, ...(placement !== undefined ? { placement } : {}), classes: {
                    tooltip: 'query-builder__tooltip',
                    arrow: 'query-builder__tooltip__arrow',
                    tooltipPlacementRight: 'query-builder__tooltip--right',
                }, open: open, onClose: () => setIsOpen(false), slotProps: {
                    transition: {
                        // disable transition
                        // NOTE: somehow, this is the only workaround we have, if for example
                        // we set `appear = true`, the tooltip will jump out of position
                        timeout: 0,
                    },
                }, disableFocusListener: true, disableHoverListener: true, disableTouchListener: true, title: _jsxs("div", { className: "query-builder__tooltip__content", children: [_jsxs("div", { className: "query-builder__tooltip__item", children: [_jsx("div", { className: "query-builder__tooltip__item__label", children: "Type" }), _jsx("div", { className: "query-builder__tooltip__item__value", children: subclass.path })] }), _jsxs("div", { className: "query-builder__tooltip__item", children: [_jsx("div", { className: "query-builder__tooltip__item__label", children: "Path" }), _jsx("div", { className: "query-builder__tooltip__item__value", children: path })] }), _jsxs("div", { className: "query-builder__tooltip__item", children: [_jsx("div", { className: "query-builder__tooltip__item__label", children: "Multiplicity" }), _jsx("div", { className: "query-builder__tooltip__item__value", children: getMultiplicityDescription(multiplicity) })] }), _jsxs("div", { className: "query-builder__tooltip__item", children: [_jsx("div", { className: "query-builder__tooltip__item__label", children: "Mapped" }), _jsx("div", { className: "query-builder__tooltip__item__value", children: isMapped ? 'Yes' : 'No' })] }), _jsx(QueryBuilderTaggedValueInfoTooltip, { taggedValues: subclass.taggedValues })] }), children: _jsx("div", { onClick: (event) => {
                        setIsOpen(!open);
                        event.stopPropagation();
                    }, children: children }) }) }) }));
};
const QueryBuilderExplorerPreviewDataModal = observer((props) => {
    const { queryBuilderState } = props;
    const applicationStore = queryBuilderState.applicationStore;
    const previewDataState = queryBuilderState.explorerState.previewDataState;
    const close = () => {
        if (previewDataState.previewDataAbortController) {
            previewDataState.previewDataAbortController.abort();
            previewDataState.setPreviewDataAbortController(undefined);
        }
        previewDataState.setIsGeneratingPreviewData(false);
        previewDataState.setPreviewData(undefined);
    };
    return (_jsx(Dialog, { open: Boolean(previewDataState.previewData) ||
            previewDataState.isGeneratingPreviewData, onClose: close, classes: {
            root: 'editor-modal__root-container',
            container: 'editor-modal__container',
            paper: 'editor-modal__content',
        }, children: _jsx("div", { "data-testid": QUERY_BUILDER_TEST_ID.QUERY_BUILDER_PREVIEW_DATA_MODAL, children: _jsxs(Modal, { darkMode: !applicationStore.layoutService
                    .TEMPORARY__isLightColorThemeEnabled, className: "editor-modal query-builder__explorer__preview-data-modal", children: [_jsx(ModalHeader, { title: prettyCONSTName(previewDataState.propertyName) }), _jsx(PanelLoadingIndicator, { isLoading: previewDataState.isGeneratingPreviewData }), _jsxs(ModalBody, { className: "query-builder__explorer__preview-data-modal__body", children: [previewDataState.isGeneratingPreviewData && (_jsx("div", { className: "query-builder__explorer__preview-data-modal__placeholder", children: "Loading preview data..." })), previewDataState.previewData && (_jsxs("table", { className: "table", children: [_jsx("thead", { children: _jsx("tr", { children: previewDataState.previewData.columns.map((column, idx) => (
                                            // eslint-disable-next-line react/no-array-index-key
                                            _jsx("th", { className: "table__cell--left", children: column }, idx))) }) }), _jsx("tbody", { children: previewDataState.previewData.rows.map((row, rowIdx) => (
                                        // eslint-disable-next-line react/no-array-index-key
                                        _jsx("tr", { children: row.values.map((value, idx) => (
                                            // eslint-disable-next-line react/no-array-index-key
                                            _jsx("td", { className: "table__cell--left", children: value }, idx))) }, rowIdx))) })] }))] }), _jsx(ModalFooter, { children: _jsx(ModalFooterButton, { text: "Close", onClick: close, type: "secondary" }) })] }) }) }));
});
const QueryBuilderExplorerContextMenu = observer(forwardRef(function QueryBuilderExplorerContextMenu(props, ref) {
    const { queryBuilderState, openNode, node } = props;
    const addNodeToFetchStructure = () => queryBuilderState.fetchStructureState.fetchProperty(node);
    const addNodeChildrenToFetchStructure = () => {
        openNode();
        if (node.type instanceof Class) {
            // NOTE: here we require the node to already been expanded so the child nodes are generated
            // we don't allow adding unopened node. Maybe if it helps, we can show a warning.
            const nodesToAdd = node.childrenIds
                .map((childId) => queryBuilderState.explorerState.nonNullableTreeData.nodes.get(childId))
                .filter(filterByType(QueryBuilderExplorerTreePropertyNodeData))
                .filter((childNode) => !(childNode.type instanceof Class) &&
                childNode.mappingData.mapped)
                .filter((childNode) => {
                const propertyExpression = buildPropertyExpressionFromExplorerTreeNodeData(childNode, queryBuilderState.explorerState);
                if (queryBuilderState.fetchStructureState.implementation instanceof
                    QueryBuilderTDSState) {
                    const currentProjectColumns = queryBuilderState.fetchStructureState.implementation.projectionColumns.find((currCol) => {
                        if (currCol instanceof QueryBuilderSimpleProjectionColumnState) {
                            return (currCol.propertyExpressionState.path === childNode.id &&
                                currCol.columnName ===
                                    getPropertyChainName(propertyExpression, true));
                        }
                        return undefined;
                    });
                    return currentProjectColumns === undefined;
                }
                return true;
            });
            nodesToAdd.forEach((nodeToAdd) => queryBuilderState.fetchStructureState.fetchProperty(nodeToAdd));
        }
    };
    return (_jsxs(MenuContent, { ref: ref, children: [node instanceof QueryBuilderExplorerTreePropertyNodeData &&
                !(node.type instanceof Class) && (_jsx(MenuContentItem, { onClick: addNodeToFetchStructure, children: "Add Property to Fetch Structure" })), node.type instanceof Class && (_jsx(MenuContentItem, { onClick: addNodeChildrenToFetchStructure, children: "Add Properties to Fetch Structure" }))] }));
}));
const PRECISE_PRIMITIVE_STRING_TYPES = new Set([
    extractElementNameFromPath(PRECISE_PRIMITIVE_TYPE.VARCHAR),
]);
const PRECISE_PRIMITIVE_NUMBER_TYPES = new Set([
    extractElementNameFromPath(PRECISE_PRIMITIVE_TYPE.INT),
    extractElementNameFromPath(PRECISE_PRIMITIVE_TYPE.TINY_INT),
    extractElementNameFromPath(PRECISE_PRIMITIVE_TYPE.U_TINY_INT),
    extractElementNameFromPath(PRECISE_PRIMITIVE_TYPE.SMALL_INT),
    extractElementNameFromPath(PRECISE_PRIMITIVE_TYPE.U_SMALL_INT),
    extractElementNameFromPath(PRECISE_PRIMITIVE_TYPE.U_INT),
    extractElementNameFromPath(PRECISE_PRIMITIVE_TYPE.BIG_INT),
    extractElementNameFromPath(PRECISE_PRIMITIVE_TYPE.U_BIG_INT),
    extractElementNameFromPath(PRECISE_PRIMITIVE_TYPE.FLOAT),
    extractElementNameFromPath(PRECISE_PRIMITIVE_TYPE.DOUBLE),
    extractElementNameFromPath(PRECISE_PRIMITIVE_TYPE.NUMERIC),
    extractElementNameFromPath(PRECISE_PRIMITIVE_TYPE.DECIMAL),
]);
const PRECISE_PRIMITIVE_TIME_TYPES = new Set([
    extractElementNameFromPath(PRECISE_PRIMITIVE_TYPE.TIMESTAMP),
    extractElementNameFromPath(PRECISE_PRIMITIVE_TYPE.STRICTDATE),
    extractElementNameFromPath(PRECISE_PRIMITIVE_TYPE.DATETIME),
    extractElementNameFromPath(PRECISE_PRIMITIVE_TYPE.STRICTTIME),
]);
export const renderPropertyTypeIcon = (type) => {
    if (type instanceof PrimitiveType || type instanceof PrimitiveType) {
        const typeName = getStandardPrimitiveTypeEquivalent(type) ?? type.path;
        if (typeName === PRIMITIVE_TYPE.STRING) {
            return (_jsx(StringTypeIcon, { className: "query-builder-explorer-tree__icon query-builder-explorer-tree__icon__string" }));
        }
        else if (typeName === PRIMITIVE_TYPE.BOOLEAN) {
            return (_jsx(ToggleIcon, { className: "query-builder-explorer-tree__icon query-builder-explorer-tree__icon__boolean" }));
        }
        else if (typeName === PRIMITIVE_TYPE.NUMBER ||
            typeName === PRIMITIVE_TYPE.INTEGER ||
            typeName === PRIMITIVE_TYPE.FLOAT ||
            typeName === PRIMITIVE_TYPE.DECIMAL) {
            return (_jsx(HashtagIcon, { className: "query-builder-explorer-tree__icon query-builder-explorer-tree__icon__number" }));
        }
        else if (typeName === PRIMITIVE_TYPE.DATE ||
            typeName === PRIMITIVE_TYPE.DATETIME ||
            typeName === PRIMITIVE_TYPE.STRICTDATE) {
            return (_jsx(ClockIcon, { className: "query-builder-explorer-tree__icon query-builder-explorer-tree__icon__time" }));
        }
    }
    else if (type instanceof PrecisePrimitiveType) {
        if (PRECISE_PRIMITIVE_STRING_TYPES.has(type.name)) {
            return (_jsx(StringTypeIcon, { className: "query-builder-explorer-tree__icon query-builder-explorer-tree__icon__string" }));
        }
        else if (PRECISE_PRIMITIVE_NUMBER_TYPES.has(type.name)) {
            return (_jsx(HashtagIcon, { className: "query-builder-explorer-tree__icon query-builder-explorer-tree__icon__number" }));
        }
        else if (PRECISE_PRIMITIVE_TIME_TYPES.has(type.name)) {
            return (_jsx(ClockIcon, { className: "query-builder-explorer-tree__icon query-builder-explorer-tree__icon__time" }));
        }
    }
    return getClassPropertyIcon(type);
};
const QueryBuilderExplorerTreeNodeContainer = observer((props) => {
    const { node, level, stepPaddingInRem, onNodeSelect, innerProps } = props;
    const { queryBuilderState } = innerProps;
    const [isSelectedFromContextMenu, setIsSelectedFromContextMenu] = useState(false);
    const applicationStore = useApplicationStore();
    const explorerState = queryBuilderState.explorerState;
    const [, dragConnector, dragPreviewConnector] = useDrag(() => ({
        type: node instanceof QueryBuilderExplorerTreePropertyNodeData
            ? node.type instanceof Enumeration
                ? QUERY_BUILDER_EXPLORER_TREE_DND_TYPE.ENUM_PROPERTY
                : node.type instanceof Class
                    ? QUERY_BUILDER_EXPLORER_TREE_DND_TYPE.CLASS_PROPERTY
                    : QUERY_BUILDER_EXPLORER_TREE_DND_TYPE.PRIMITIVE_PROPERTY
            : QUERY_BUILDER_EXPLORER_TREE_DND_TYPE.ROOT,
        item: () => node instanceof QueryBuilderExplorerTreePropertyNodeData
            ? { node }
            : {},
    }), [node]);
    const ref = useRef(null);
    dragConnector(ref);
    useDragPreviewLayer(dragPreviewConnector);
    const isExpandable = Boolean(node.childrenIds.length);
    const isDerivedProperty = node instanceof QueryBuilderExplorerTreePropertyNodeData &&
        node.property instanceof DerivedProperty;
    const isMultiple = (node instanceof QueryBuilderExplorerTreePropertyNodeData &&
        (node.property.multiplicity.upperBound === undefined ||
            node.property.multiplicity.upperBound > 1)) ||
        (node instanceof QueryBuilderExplorerTreeSubTypeNodeData &&
            (node.multiplicity.upperBound === undefined ||
                node.multiplicity.upperBound > 1));
    const allowPreview = node.mappingData.mapped &&
        node instanceof QueryBuilderExplorerTreePropertyNodeData &&
        node.type instanceof PrimitiveType &&
        !node.isPartOfDerivedPropertyBranch;
    const nodeExpandIcon = isExpandable ? (node.isOpen ? (_jsx(ChevronDownIcon, {})) : (_jsx(ChevronRightIcon, {}))) : (_jsx("div", {}));
    const propertyName = explorerState.humanizePropertyName
        ? node instanceof QueryBuilderExplorerTreeSubTypeNodeData
            ? TYPE_CAST_TOKEN + prettyCONSTName(node.label)
            : prettyCONSTName(node.label)
        : node instanceof QueryBuilderExplorerTreeSubTypeNodeData
            ? TYPE_CAST_TOKEN + node.label
            : node.label;
    const selectNode = () => onNodeSelect?.(node);
    const openNode = () => {
        if (!node.isOpen) {
            onNodeSelect?.(node);
        }
    };
    // context menu
    const onContextMenuOpen = () => setIsSelectedFromContextMenu(true);
    const onContextMenuClose = () => setIsSelectedFromContextMenu(false);
    const previewData = () => {
        if (node instanceof QueryBuilderExplorerTreePropertyNodeData) {
            flowResult(explorerState.previewData(node)).catch(applicationStore.alertUnhandledError);
        }
    };
    if (!node.mappingData.mapped &&
        // NOTE: we always want to show at least the root node
        !(node instanceof QueryBuilderExplorerTreeRootNodeData) &&
        !explorerState.showUnmappedProperties) {
        return null;
    }
    return (_jsx(ContextMenu, { content: _jsx(QueryBuilderExplorerContextMenu, { queryBuilderState: queryBuilderState, openNode: openNode, node: node }), disabled: !(node instanceof QueryBuilderExplorerTreePropertyNodeData ||
            node instanceof QueryBuilderExplorerTreeRootNodeData) || applicationStore.layoutService.showBackdrop, menuProps: { elevation: 7 }, onOpen: onContextMenuOpen, onClose: onContextMenuClose, children: _jsxs("div", { className: clsx('tree-view__node__container query-builder-explorer-tree__node__container', {
                'query-builder-explorer-tree__node__container--selected-from-context-menu': isSelectedFromContextMenu,
                'query-builder-explorer-tree__node__container--unmapped': !node.mappingData.mapped,
                'query-builder-explorer-tree__node__container--selected': node.isSelected,
                'query-builder-explorer-tree__node__container--highlighted': explorerState.highlightUsedProperties &&
                    isExplorerTreeNodeAlreadyUsed(node, queryBuilderState),
            }), title: !node.mappingData.mapped
                ? node instanceof QueryBuilderExplorerTreeRootNodeData
                    ? 'Root class is not mapped'
                    : 'Property is not mapped'
                : undefined, onClick: selectNode, ref: node.mappingData.mapped && !isExpandable ? ref : undefined, style: {
                paddingLeft: `${(level - 1) * (stepPaddingInRem ?? 1) + 0.5}rem`,
                display: 'flex',
            }, children: [node instanceof QueryBuilderExplorerTreeRootNodeData && (_jsxs(_Fragment, { children: [_jsxs("div", { className: "tree-view__node__icon query-builder-explorer-tree__node__icon", children: [_jsx("div", { className: "query-builder-explorer-tree__expand-icon", children: nodeExpandIcon }), _jsx("div", { className: "query-builder-explorer-tree__type-icon", children: _jsx(PURE_ClassIcon, {}) })] }), _jsx("div", { className: "tree-view__node__label query-builder-explorer-tree__node__label query-builder-explorer-tree__node__label--with-action", children: node.label }), _jsx("div", { className: "query-builder-explorer-tree__node__actions", children: queryBuilderState.sourceClass && (_jsx(QueryBuilderRootClassInfoTooltip, { _class: queryBuilderState.sourceClass, children: _jsx("div", { className: "query-builder-explorer-tree__node__action query-builder-explorer-tree__node__info", "data-testid": QUERY_BUILDER_TEST_ID.QUERY_BUILDER_TOOLTIP_ICON, children: _jsx(InfoCircleIcon, {}) }) })) })] })), (node instanceof QueryBuilderExplorerTreePropertyNodeData ||
                    node instanceof QueryBuilderExplorerTreeSubTypeNodeData) && (_jsxs(_Fragment, { children: [_jsxs("div", { className: "tree-view__node__icon query-builder-explorer-tree__node__icon", ref: node.elementRef, children: [_jsx("div", { className: "query-builder-explorer-tree__expand-icon", children: nodeExpandIcon }), _jsx("div", { className: "query-builder-explorer-tree__type-icon", children: renderPropertyTypeIcon(node.type) })] }), _jsxs("div", { className: clsx('tree-view__node__label query-builder-explorer-tree__node__label query-builder-explorer-tree__node__label--with-action', {
                                'query-builder-explorer-tree__node__label--with-preview': allowPreview,
                            }, {
                                'query-builder-explorer-tree__node__label--highlight': node.isHighlighting,
                            }), onAnimationEnd: () => node.setIsHighlighting(false), children: [_jsx("div", { className: clsx('query-builder-explorer-tree__node__label--property__name', {
                                        'query-builder-explorer-tree__node__label--deprecated': checkForDeprecatedNode(node, explorerState.queryBuilderState.graphManagerState
                                            .graph, explorerState.nonNullableTreeData),
                                    }), children: propertyName }), isDerivedProperty && (_jsx("div", { className: "query-builder-explorer-tree__node__label__derived-property", title: "Property is derived and may require user to specify parameter values", children: "(...)" })), isMultiple && (_jsx("div", { className: "query-builder-explorer-tree__node__label__multiple", title: "Multiple values of this property can cause row explosion", children: "*" }))] }), _jsxs("div", { className: "query-builder-explorer-tree__node__actions", children: [allowPreview && (_jsx("button", { className: "query-builder-explorer-tree__node__action", disabled: explorerState.previewDataState.isGeneratingPreviewData, tabIndex: -1, title: "Preview Data", onClick: previewData, children: _jsx(EyeIcon, {}) })), node instanceof QueryBuilderExplorerTreePropertyNodeData && (_jsx(QueryBuilderPropertyInfoTooltip, { title: propertyName, property: node.property, path: node.id, isMapped: node.mappingData.mapped, type: node.type, children: _jsx("div", { className: "query-builder-explorer-tree__node__action query-builder-explorer-tree__node__info", "data-testid": QUERY_BUILDER_TEST_ID.QUERY_BUILDER_TOOLTIP_ICON, children: _jsx(InfoCircleIcon, {}) }) })), node instanceof QueryBuilderExplorerTreeSubTypeNodeData && (_jsx(QueryBuilderSubclassInfoTooltip, { subclass: node.subclass, path: node.id, isMapped: node.mappingData.mapped, multiplicity: node.multiplicity, children: _jsx("div", { className: "query-builder-explorer-tree__node__action query-builder-explorer-tree__node__info", "data-testid": QUERY_BUILDER_TEST_ID.QUERY_BUILDER_TOOLTIP_ICON, children: _jsx(InfoCircleIcon, {}) }) }))] })] }))] }) }));
});
const QueryBuilderExplorerTreeNodeView = observer((props) => {
    const { node, level, onNodeSelect, getChildNodes, stepPaddingInRem, innerProps, } = props;
    const { queryBuilderState } = innerProps;
    if (!node.mappingData.mapped &&
        // NOTE: we always want to show at least the root node
        !(node instanceof QueryBuilderExplorerTreeRootNodeData) &&
        !queryBuilderState.explorerState.showUnmappedProperties) {
        return null;
    }
    return (_jsxs("div", { className: "tree-view__node__block", children: [_jsx(QueryBuilderExplorerTreeNodeContainer, { node: node, level: level + 1, stepPaddingInRem: stepPaddingInRem, onNodeSelect: onNodeSelect, innerProps: innerProps }), node.isOpen &&
                getChildNodes(node).map((childNode) => (_jsx(QueryBuilderExplorerTreeNodeView, { node: childNode, level: level + 1, onNodeSelect: onNodeSelect, getChildNodes: getChildNodes, innerProps: innerProps }, childNode.id)))] }));
});
/**
 * Sort order for Query Builder tree nodes will be ranked by return type as followed:
 * 1. Primitive
 * 2. Enumeration
 * 3. Class
 * 4. Class Subtypes
 * Note: Derived property nodes will be ranked lower for each relevant return type
 */
export const getQueryBuilderExplorerTreeNodeSortRank = (node) => {
    if (node instanceof QueryBuilderExplorerTreeSubTypeNodeData) {
        return 0;
    }
    else if (node.type instanceof Class) {
        return node.isPartOfDerivedPropertyBranch ? 1 : 2;
    }
    else if (node.type instanceof Enumeration) {
        return node.isPartOfDerivedPropertyBranch ? 3 : 4;
    }
    else {
        return node.isPartOfDerivedPropertyBranch ? 5 : 6;
    }
};
const QueryBuilderExplorerTree = observer((props) => {
    const { queryBuilderState } = props;
    const explorerState = queryBuilderState.explorerState;
    const treeData = explorerState.nonNullableTreeData;
    const onNodeSelect = (node) => {
        if (node.childrenIds.length) {
            node.setIsOpen(!node.isOpen);
            explorerState.generateOpenNodeChildren(node);
        }
    };
    const getChildNodes = (node) => node.childrenIds
        .map((id) => treeData.nodes.get(id))
        .filter((childNode) => childNode instanceof QueryBuilderExplorerTreeSubTypeNodeData ||
        childNode instanceof QueryBuilderExplorerTreePropertyNodeData)
        // simple properties come first
        .sort((a, b) => a.label.localeCompare(b.label))
        .sort((a, b) => getQueryBuilderExplorerTreeNodeSortRank(b) -
        getQueryBuilderExplorerTreeNodeSortRank(a));
    return (_jsx(TreeView, { components: {
            TreeNodeContainer: QueryBuilderExplorerTreeNodeContainer,
            TreeNodeView: QueryBuilderExplorerTreeNodeView,
        }, className: "query-builder-explorer-tree__root", treeData: treeData, onNodeSelect: onNodeSelect, getChildNodes: getChildNodes, innerProps: {
            queryBuilderState,
        } }));
});
export const QUERY_BUILDER_EXPLORER_SEARCH_INPUT_NAME = 'query-builder-explorer-search-input';
const QUERY_BUILDER_PROPERTY_SEARCH_MIN_SEARCH_LENGTH = 2;
const QueryBuilderExplorerSearchInput = observer(forwardRef(function QueryBuilderExplorerSearchInput(props, ref) {
    const { propertySearchState } = props;
    // initialize search state on mount
    useEffect(() => {
        if (!propertySearchState.initializationState.hasSucceeded &&
            !propertySearchState.initializationState.isInProgress &&
            isNonNullable(propertySearchState.queryBuilderState.explorerState.treeData)) {
            propertySearchState
                .initialize()
                .catch(propertySearchState.queryBuilderState.applicationStore
                .alertUnhandledError);
        }
    }, [
        propertySearchState,
        propertySearchState.initializationState,
        propertySearchState.queryBuilderState.explorerState.treeData,
    ]);
    // search text
    const debouncedSearchProperty = useMemo(() => debounce(() => propertySearchState.search(), 100), [propertySearchState]);
    const onSearchPropertyTextChange = (event) => {
        (async () => {
            propertySearchState.setSearchText(event.target.value);
            if (event.target.value.length >=
                QUERY_BUILDER_PROPERTY_SEARCH_MIN_SEARCH_LENGTH) {
                if (propertySearchState.queryBuilderState.explorerState.treeData &&
                    !propertySearchState.isSearchPanelOpen) {
                    propertySearchState.setIsSearchPanelOpen(true);
                    if (!propertySearchState.initializationState.hasSucceeded &&
                        !propertySearchState.initializationState.isInProgress) {
                        await propertySearchState.initialize();
                    }
                }
                await debouncedSearchProperty();
            }
            else {
                propertySearchState.setIsSearchPanelOpen(false);
            }
        })().catch(propertySearchState.queryBuilderState.applicationStore
            .alertUnhandledError);
    };
    // search actions
    const clearSearch = () => {
        propertySearchState.resetSearch();
    };
    return (_jsxs("div", { className: "query-builder__explorer__property-search__input__container", children: [_jsx("input", { ref: ref, name: QUERY_BUILDER_EXPLORER_SEARCH_INPUT_NAME, className: clsx('query-builder__explorer__property-search__input input--dark', {
                    'query-builder__explorer__property-search__input--searching': propertySearchState.searchText,
                }), spellCheck: false, onChange: onSearchPropertyTextChange, onKeyDown: (event) => {
                    if (event.key === 'Escape') {
                        clearSearch();
                        propertySearchState.setIsSearchPanelOpen(false);
                    }
                }, value: propertySearchState.searchText, placeholder: "One or more terms, ESC to clear" }), propertySearchState.searchText.length >=
                QUERY_BUILDER_PROPERTY_SEARCH_MIN_SEARCH_LENGTH && (_jsx("div", { className: "query-builder__explorer__property-search__input__search__count", children: propertySearchState.filteredSearchResults.length +
                    (propertySearchState.isOverSearchLimit &&
                        propertySearchState.filteredSearchResults.length !== 0
                        ? '+'
                        : '') })), !propertySearchState.searchText ? (_jsx(_Fragment, { children: _jsx("div", { className: "query-builder__explorer__property-search__input__search__icon", children: _jsx(SearchIcon, {}) }) })) : (_jsx("button", { className: "query-builder__explorer__property-search__input__clear-btn", tabIndex: -1, onClick: clearSearch, title: "Clear", children: _jsx(TimesIcon, {}) }))] }));
}));
const QueryBuilderClassExplorerPanel = observer((props) => {
    const { queryBuilderState } = props;
    const searchInputRef = useRef(null);
    const explorerState = queryBuilderState.explorerState;
    const propertySearchPanelState = explorerState.propertySearchState;
    const applicationStore = useApplicationStore();
    const collapseTree = () => {
        if (explorerState.treeData) {
            Array.from(explorerState.treeData.nodes.values()).forEach((node) => {
                if (!(node instanceof QueryBuilderExplorerTreeRootNodeData)) {
                    node.setIsOpen(false);
                }
            });
        }
    };
    const toggleShowUnmappedProperties = () => {
        QueryBuilderTelemetryHelper.logEvent_ShowUnmappedPropertyInExplorerTreeLaunched(applicationStore.telemetryService);
        explorerState.setShowUnmappedProperties(!explorerState.showUnmappedProperties);
    };
    const toggleHumanizePropertyName = () => explorerState.setHumanizePropertyName(!explorerState.humanizePropertyName);
    const toggleHighlightUsedProperties = () => explorerState.setHighlightUsedProperties(!explorerState.highlightUsedProperties);
    useEffect(() => {
        flowResult(explorerState.analyzeMappingModelCoverage()).catch(applicationStore.alertUnhandledError);
    }, [
        applicationStore,
        explorerState,
        queryBuilderState.executionContextState.mapping,
    ]);
    return (_jsxs("div", { "data-testid": QUERY_BUILDER_TEST_ID.QUERY_BUILDER_EXPLORER, className: clsx('panel query-builder__explorer', {
            backdrop__element: applicationStore.layoutService.showBackdrop,
        }), children: [_jsxs(PanelHeader, { title: "explorer", children: [_jsx(QueryBuilderExplorerSearchInput, { propertySearchState: propertySearchPanelState, ref: searchInputRef }), _jsxs(PanelHeaderActions, { children: [_jsx(PanelHeaderActionItem, { onClick: collapseTree, title: "Collapse Tree", children: _jsx(CompressIcon, {}) }), _jsx(ControlledDropdownMenu, { className: "panel__header__action", title: "Show Options Menu...", onOpen: () => {
                                    QueryBuilderTelemetryHelper.logEvent_ShowPropertyExplorerOptions(applicationStore.telemetryService);
                                }, content: _jsxs(MenuContent, { children: [_jsxs(MenuContentItem, { onClick: toggleShowUnmappedProperties, children: [_jsx(MenuContentItemIcon, { children: explorerState.showUnmappedProperties ? (_jsx(CheckIcon, {})) : null }), _jsx(MenuContentItemLabel, { children: "Show Unmapped Properties" })] }), _jsxs(MenuContentItem, { onClick: toggleHumanizePropertyName, children: [_jsx(MenuContentItemIcon, { children: explorerState.humanizePropertyName ? (_jsx(CheckIcon, {})) : null }), _jsx(MenuContentItemLabel, { children: "Humanize Property Name" })] }), _jsxs(MenuContentItem, { onClick: toggleHighlightUsedProperties, children: [_jsx(MenuContentItemIcon, { children: explorerState.highlightUsedProperties ? (_jsx(CheckIcon, {})) : null }), _jsx(MenuContentItemLabel, { children: "Highlight already used properties" })] })] }), menuProps: {
                                    anchorOrigin: { vertical: 'bottom', horizontal: 'left' },
                                    transformOrigin: { vertical: 'top', horizontal: 'left' },
                                    elevation: 7,
                                }, children: _jsx(MoreVerticalIcon, { className: "query-builder__icon__more-options" }) })] }), propertySearchPanelState.isSearchPanelOpen && (_jsx(QueryBuilderPropertySearchPanel, { queryBuilderState: queryBuilderState, triggerElement: searchInputRef.current, clearSearch: () => propertySearchPanelState.resetSearch() }))] }), _jsxs("div", { className: "panel__content query-builder-explorer-tree__content", children: [_jsx(PanelLoadingIndicator, { isLoading: explorerState.mappingModelCoverageAnalysisState.isInProgress }), _jsx(DragPreviewLayer, { labelGetter: (item) => explorerState.humanizePropertyName
                            ? prettyCONSTName(item.node.label)
                            : item.node.label, types: Object.values(QUERY_BUILDER_EXPLORER_TREE_DND_TYPE) }), explorerState.mappingModelCoverageAnalysisState.isInProgress ? (_jsx(BlankPanelContent, { children: explorerState.mappingModelCoverageAnalysisState.message })) : (_jsxs(_Fragment, { children: [!explorerState.treeData && (_jsx(BlankPanelContent, { children: "Specify the class, mapping, and runtime to start building query" })), explorerState.treeData && (_jsx(QueryBuilderExplorerTree, { queryBuilderState: queryBuilderState }))] })), _jsx(QueryBuilderExplorerPreviewDataModal, { queryBuilderState: queryBuilderState })] })] }));
});
export const QueryBuilderExplorerPanel = observer((props) => {
    const { queryBuilderState } = props;
    const accessor = queryBuilderState.sourceAccessor;
    if (accessor) {
        return (_jsx(QueryBuilderRelationExplorerPanel, { queryBuilderState: queryBuilderState, accessor: accessor }));
    }
    return (_jsx(QueryBuilderClassExplorerPanel, { queryBuilderState: queryBuilderState }));
});
//# sourceMappingURL=QueryBuilderExplorerPanel.js.map