import { jsx as _jsx, jsxs as _jsxs } 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 { useState, useCallback, forwardRef, useRef } from 'react';
import { observer } from 'mobx-react-lite';
import { CORE_DND_TYPE, MappingElementDragSource, } from '../../../../stores/editor/utils/DnDUtils.js';
import { clsx, TreeView, ContextMenu, PlusIcon, LockIcon, FireIcon, StickArrowCircleRightIcon, ChevronRightIcon, ChevronDownIcon, FilterIcon, PanelDropZone, BlankPanelPlaceholder, MenuContent, MenuContentItem, } from '@finos/legend-art';
import { MappingElementState } from '../../../../stores/editor/editor-state/element-editor-state/mapping/MappingElementState.js';
import { useDrop, useDrag } from 'react-dnd';
import { toSentenceCase } from '@finos/legend-shared';
import { getAllMappingElements, getMappingElementTarget, getMappingElementType, MappingEditorState, getMappingElementLabel, } from '../../../../stores/editor/editor-state/element-editor-state/mapping/MappingEditorState.js';
import { LEGEND_STUDIO_TEST_ID } from '../../../../__lib__/LegendStudioTesting.js';
import { getElementIcon } from '../../../ElementIconUtils.js';
import { NewMappingElementModal } from './NewMappingElementModal.js';
import { MappingElementDecorator } from '../../../../stores/editor/editor-state/element-editor-state/mapping/MappingElementDecorator.js';
import { flowResult } from 'mobx';
import { useEditorStore } from '../../EditorStoreProvider.js';
import { SetImplementation, EnumerationMapping, PropertyMapping, PureInstanceSetImplementation, stub_RawLambda, } from '@finos/legend-graph';
import { useApplicationStore } from '@finos/legend-application';
import { PureInstanceSetImplementationFilterState, PureInstanceSetImplementationState, } from '../../../../stores/editor/editor-state/element-editor-state/mapping/PureInstanceSetImplementationState.js';
import { pureInstanceSetImpl_setMappingFilter } from '../../../../stores/graph-modifier/DSL_Mapping_GraphModifierHelper.js';
export const MappingExplorerContextMenu = observer(forwardRef(function MappingExplorerContextMenu(props, ref) {
    const { mappingElement, openNewMapingModal } = props;
    const editorStore = useEditorStore();
    const applicationStore = useApplicationStore();
    const mappingEditorState = editorStore.tabManagerState.getCurrentEditorState(MappingEditorState);
    const currentMappingElement = mappingEditorState.currentTabState instanceof MappingElementState
        ? mappingEditorState.currentTabState.mappingElement
        : undefined;
    const removeMappingElement = () => {
        if (mappingElement) {
            flowResult(mappingEditorState.deleteMappingElement(mappingElement)).catch(applicationStore.alertUnhandledError);
        }
        if (currentMappingElement instanceof EnumerationMapping) {
            new MappingElementDecorator(editorStore).visitEnumerationMapping(currentMappingElement);
        }
        else if (currentMappingElement instanceof SetImplementation) {
            const mappingElementDecorator = new MappingElementDecorator(editorStore);
            mappingElementDecorator.editorStore = editorStore;
            currentMappingElement.accept_SetImplementationVisitor(new MappingElementDecorator(editorStore));
        }
        mappingEditorState.reprocessMappingExplorerTree();
    };
    const queryMappingElement = () => {
        if (mappingElement instanceof SetImplementation) {
            flowResult(mappingEditorState.buildExecution(mappingElement)).catch(applicationStore.alertUnhandledError);
        }
    };
    const createTestForMappingElement = () => {
        if (mappingElement instanceof SetImplementation) {
            flowResult(mappingEditorState.createNewTest(mappingElement)).catch(applicationStore.alertUnhandledError);
        }
    };
    const addMappingFilter = () => {
        if (mappingElement instanceof PureInstanceSetImplementation) {
            if (!mappingElement.filter) {
                const stubLambda = stub_RawLambda();
                pureInstanceSetImpl_setMappingFilter(mappingElement, stubLambda);
            }
            if (mappingEditorState.currentTabState instanceof
                PureInstanceSetImplementationState) {
                mappingEditorState.currentTabState.mappingFilterState =
                    new PureInstanceSetImplementationFilterState(mappingElement, editorStore);
            }
        }
    };
    const removeMappingFilter = applicationStore.guardUnhandledError(async () => {
        if (mappingEditorState.currentTabState instanceof
            PureInstanceSetImplementationState) {
            await flowResult(mappingEditorState.currentTabState.mappingFilterState?.convertLambdaObjectToGrammarString({ pretty: false }));
            mappingEditorState.currentTabState.mappingFilterState = undefined;
            if (mappingElement instanceof PureInstanceSetImplementation) {
                pureInstanceSetImpl_setMappingFilter(mappingElement, undefined);
            }
        }
    });
    const allowAddFilter = mappingElement instanceof PureInstanceSetImplementation &&
        !mappingElement.filter;
    const allowRemoveFilter = mappingElement instanceof PureInstanceSetImplementation &&
        Boolean(mappingElement.filter);
    return (_jsxs(MenuContent, { ref: ref, children: [mappingElement instanceof SetImplementation && (_jsx(MenuContentItem, { onClick: queryMappingElement, children: "Query" })), mappingElement instanceof SetImplementation && (_jsx(MenuContentItem, { onClick: createTestForMappingElement, children: "Test" })), allowAddFilter && (_jsx(MenuContentItem, { onClick: addMappingFilter, children: "Add Filter" })), allowRemoveFilter && (_jsx(MenuContentItem, { onClick: removeMappingFilter, children: "Remove Filter" })), mappingElement && (_jsx(MenuContentItem, { onClick: removeMappingElement, children: "Delete" })), !mappingElement && openNewMapingModal && (_jsx(MenuContentItem, { onClick: openNewMapingModal, children: "Create new mapping element" }))] }));
}));
export const MappingElementExplorer = observer((props) => {
    const { mappingElement, openNewMapingModal, isReadOnly } = props;
    const editorStore = useEditorStore();
    const mappingEditorState = editorStore.tabManagerState.getCurrentEditorState(MappingEditorState);
    const currentMappingElement = mappingEditorState.currentTabState instanceof MappingElementState
        ? mappingEditorState.currentTabState.mappingElement
        : undefined;
    const openMappingElement = () => mappingEditorState.openMappingElement(mappingElement, false);
    const mappingElementTarget = getMappingElementTarget(mappingElement);
    // Drag and Drop
    const [, dragConnector] = useDrag(() => ({
        type: mappingElement instanceof SetImplementation
            ? CORE_DND_TYPE.MAPPING_EXPLORER_CLASS_MAPPING
            : CORE_DND_TYPE.NONE,
        item: new MappingElementDragSource(mappingElement),
    }), [mappingElement]);
    const ref = useRef(null);
    dragConnector(ref);
    // Selection
    const isActive = currentMappingElement?.id.value === mappingElement.id.value;
    const [isSelectedFromContextMenu, setIsSelectedFromContextMenu] = useState(false);
    const onContextMenuOpen = () => setIsSelectedFromContextMenu(true);
    const onContextMenuClose = () => setIsSelectedFromContextMenu(false);
    return (_jsx(ContextMenu, { disabled: isReadOnly, content: _jsx(MappingExplorerContextMenu, { mappingElement: mappingElement, openNewMapingModal: openNewMapingModal }), menuProps: { elevation: 7 }, onOpen: onContextMenuOpen, onClose: onContextMenuClose, children: _jsx("div", { ref: ref, className: clsx('mapping-explorer__item', {
                'mapping-explorer__item--selected-from-context-menu': !isActive && isSelectedFromContextMenu,
            }, { 'mapping-explorer__item--active': isActive }), children: _jsxs("button", { className: "mapping-explorer__item__label", onClick: openMappingElement, tabIndex: -1, title: `${toSentenceCase(getMappingElementType(mappingElement)).toLowerCase()} mapping '${mappingElement.id.value}' for '${mappingElementTarget.name}'`, children: [_jsx("div", { className: "mapping-explorer__item__label__icon", children: getElementIcon(mappingElementTarget, editorStore) }), _jsx("div", { className: "mapping-explorer__item__label__text", children: getMappingElementLabel(mappingElement, editorStore).value })] }) }) }));
});
const MappingElementTreeNodeContainer = observer((props) => {
    const { node, level, stepPaddingInRem, onNodeSelect, innerProps } = props;
    const { isReadOnly, onNodeExpand } = innerProps;
    const mappingElement = node.mappingElement;
    const editorStore = useEditorStore();
    const mappingEditorState = editorStore.tabManagerState.getCurrentEditorState(MappingEditorState);
    const currentMappingElement = mappingEditorState.currentTabState instanceof MappingElementState
        ? mappingEditorState.currentTabState.mappingElement
        : undefined;
    const isExpandable = Boolean(node.childrenIds?.length);
    const nodeExpandIcon = isExpandable ? (node.isOpen ? (_jsx(ChevronDownIcon, {})) : (_jsx(ChevronRightIcon, {}))) : (_jsx("div", {}));
    const mappingElementTarget = getMappingElementTarget(mappingElement);
    const mappingElementTooltipText = mappingElement instanceof PropertyMapping && mappingElement._isEmbedded
        ? `Embedded class mapping '${mappingElement.id.value}' for property '${mappingElement.property.value.name}' (${mappingElement.property.value.genericType.value.rawType.name}) of type '${mappingElement.sourceSetImplementation.value.class.value.name}'`
        : `${toSentenceCase(getMappingElementType(mappingElement).toLowerCase())} mapping '${mappingElement.id.value}' for '${mappingElementTarget.name}'`;
    // Drag and Drop
    const [, dragConnector] = useDrag(() => ({
        type: mappingElement instanceof SetImplementation
            ? CORE_DND_TYPE.MAPPING_EXPLORER_CLASS_MAPPING
            : CORE_DND_TYPE.NONE,
        item: new MappingElementDragSource(mappingElement),
    }), [mappingElement]);
    const ref = useRef(null);
    dragConnector(ref);
    // Selection
    const selectNode = () => onNodeSelect?.(node);
    const onExpandIconClick = () => onNodeExpand(node);
    const isActive = currentMappingElement?.id.value === mappingElement.id.value;
    const [isSelectedFromContextMenu, setIsSelectedFromContextMenu] = useState(false);
    const onContextMenuOpen = () => setIsSelectedFromContextMenu(true);
    const onContextMenuClose = () => setIsSelectedFromContextMenu(false);
    return (_jsx(ContextMenu, { disabled: isReadOnly, content: _jsx(MappingExplorerContextMenu, { mappingElement: mappingElement }), menuProps: { elevation: 7 }, onOpen: onContextMenuOpen, onClose: onContextMenuClose, children: _jsx("div", { className: clsx('tree-view__node__container', {
                'mapping-explorer__item--selected-from-context-menu': !isActive && isSelectedFromContextMenu,
            }, { 'mapping-explorer__item--active': isActive }), onClick: selectNode, ref: ref, style: {
                paddingLeft: `${(level - 1) * (stepPaddingInRem ?? 1) + 0.5}rem`,
                display: 'flex',
            }, children: _jsxs("button", { className: "mapping-explorer__item__label", tabIndex: -1, title: mappingElementTooltipText, children: [_jsx("div", { className: "tree-view__node__expand-icon", onClick: onExpandIconClick, children: nodeExpandIcon }), _jsx("div", { className: "mapping-explorer__item__label__icon", children: getElementIcon(mappingElementTarget, editorStore) }), _jsx("div", { className: "mapping-explorer__item__label__text", children: getMappingElementLabel(mappingElement, editorStore).value }), mappingElement instanceof PureInstanceSetImplementation &&
                        Boolean(mappingElement.filter) && (_jsx("div", { className: "mapping-explorer__item__label__filter-icon", children: _jsx(FilterIcon, {}) }))] }) }) }));
});
const getMappingIdentitySortString = (me, type) => `${type.name}-${type.path}-${me.id.value}`;
export const MappingExplorer = observer((props) => {
    const { isReadOnly } = props;
    const editorStore = useEditorStore();
    const mappingEditorState = editorStore.tabManagerState.getCurrentEditorState(MappingEditorState);
    const mapping = mappingEditorState.mapping;
    const mappingElements = getAllMappingElements(mapping).sort((a, b) => getMappingIdentitySortString(a, getMappingElementTarget(a)).localeCompare(getMappingIdentitySortString(b, getMappingElementTarget(b))));
    const openNewMapingModal = () => mappingEditorState.createMappingElement({
        showTarget: true,
        openInAdjacentTab: false,
    });
    // Drag and Drop
    const handleDrop = useCallback((item) => isReadOnly
        ? undefined
        : mappingEditorState.createMappingElement({
            showTarget: true,
            openInAdjacentTab: false,
            target: item.data.packageableElement,
        }), [isReadOnly, mappingEditorState]);
    const [{ isDragOver }, dropConnector] = useDrop(() => ({
        accept: [
            CORE_DND_TYPE.PROJECT_EXPLORER_ENUMERATION,
            CORE_DND_TYPE.PROJECT_EXPLORER_CLASS,
            CORE_DND_TYPE.PROJECT_EXPLORER_ASSOCIATION,
        ],
        drop: (item) => handleDrop(item),
        collect: (monitor) => ({
            isDragOver: monitor.isOver({ shallow: true }),
        }),
    }), [handleDrop]);
    // Generation
    const generationParentElementPath = editorStore.graphState.graphGenerationState.findGenerationParentPath(mapping.path);
    const generationParentElement = generationParentElementPath
        ? editorStore.graphManagerState.graph.getNullableElement(generationParentElementPath)
        : undefined;
    const visitGenerationParentElement = () => {
        if (generationParentElement) {
            editorStore.graphEditorMode.openElement(generationParentElement);
        }
    };
    // explorer tree data
    const mappingElementsTreeData = mappingEditorState.mappingExplorerTreeData;
    const onNodeSelect = (node) => mappingEditorState.onMappingExplorerTreeNodeSelect(node);
    const onNodeExpand = (node) => mappingEditorState.onMappingExplorerTreeNodeExpand(node);
    const getMappingElementTreeChildNodes = (node) => mappingEditorState.getMappingExplorerTreeChildNodes(node);
    return (_jsxs("div", { "data-testid": LEGEND_STUDIO_TEST_ID.MAPPING_EXPLORER, className: "panel mapping-explorer", children: [_jsxs("div", { className: "panel__header", children: [_jsxs("div", { className: clsx('panel__header__title', {
                            'panel__header__title--with-generation-origin': generationParentElement,
                        }), children: [isReadOnly && (_jsx("div", { title: "Read Only", className: "mapping-explorer__header__lock", children: _jsx(LockIcon, {}) })), _jsx("div", { className: "panel__header__title__label", children: "mapping" }), _jsx("div", { className: "panel__header__title__content", children: mapping.name })] }), _jsxs("div", { className: "panel__header__actions", children: [!generationParentElement && (_jsx("button", { className: "panel__header__action", onClick: openNewMapingModal, disabled: isReadOnly, tabIndex: -1, title: "Create new mapping element", children: _jsx(PlusIcon, {}) })), generationParentElement && (_jsxs("button", { className: "mapping-explorer__header__generation-origin", onClick: visitGenerationParentElement, tabIndex: -1, title: `Visit generation parent '${generationParentElement.path}'`, children: [_jsx("div", { className: "mapping-explorer__header__generation-origin__label", children: _jsx(FireIcon, {}) }), _jsx("div", { className: "mapping-explorer__header__generation-origin__parent-name", children: generationParentElement.name }), _jsx("div", { className: "mapping-explorer__header__generation-origin__visit-btn", children: _jsx(StickArrowCircleRightIcon, {}) })] }))] })] }), _jsx(ContextMenu, { className: "panel__content", disabled: isReadOnly, content: _jsx(MappingExplorerContextMenu, { openNewMapingModal: openNewMapingModal }), menuProps: { elevation: 7 }, children: _jsx(PanelDropZone, { isDragOver: isDragOver && !isReadOnly, dropTargetConnector: dropConnector, children: _jsxs("div", { className: "mapping-explorer__content", children: [_jsx(TreeView, { components: {
                                    TreeNodeContainer: MappingElementTreeNodeContainer,
                                }, treeData: mappingElementsTreeData, onNodeSelect: onNodeSelect, getChildNodes: getMappingElementTreeChildNodes, innerProps: {
                                    isReadOnly,
                                    onNodeExpand,
                                } }), !isReadOnly && !mappingElements.length && (_jsx(BlankPanelPlaceholder, { text: "Add a mapping element", onClick: openNewMapingModal, clickActionType: "add", tooltipText: "Drop a class or an enumeration to start creating mappings", isDropZoneActive: isDragOver && !isReadOnly, disabled: isReadOnly, previewText: "No mapping" })), _jsx(NewMappingElementModal, {})] }) }) })] }));
});
//# sourceMappingURL=MappingExplorer.js.map