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 { useCallback, useEffect } from 'react';
import { observer } from 'mobx-react-lite';
import { useDrop } from 'react-dnd';
import { clsx, TreeView, BlankPanelPlaceholder, ChevronDownIcon, ChevronRightIcon, TimesIcon, CheckSquareIcon, SquareIcon, InfoCircleIcon, PanelDropZone, BlankPanelContent, Dialog, ModalHeader, Modal, ModalBody, Panel, PanelForm, PanelFormTextField, PanelFormBooleanField, ModalFooterButton, ModalFooter, SerializeIcon, ResizablePanel, ResizablePanelSplitter, ResizablePanelGroup, BufferIcon, CustomSelectorInput, FolderIcon, PURE_UnknownElementTypeIcon, } from '@finos/legend-art';
import { QUERY_BUILDER_TEST_ID } from '../../__lib__/QueryBuilderTesting.js';
import { deepClone, filterByType, guaranteeNonNullable, isNonNullable, prettyCONSTName, } from '@finos/legend-shared';
import { removeNodeRecursively, isGraphFetchTreeDataEmpty, } from '../../stores/fetch-structure/graph-fetch/QueryBuilderGraphFetchTreeUtil.js';
import { QUERY_BUILDER_EXPLORER_TREE_DND_TYPE, } from '../../stores/explorer/QueryBuilderExplorerState.js';
import { GraphFetchExternalFormatSerializationState, GraphFetchPureSerializationState, PureSerializationConfig, SERIALIZATION_TYPE, } from '../../stores/fetch-structure/graph-fetch/QueryBuilderGraphFetchTreeState.js';
import { QueryBuilderTextEditorMode } from '../../stores/QueryBuilderTextEditorState.js';
import { Binding, Package, getAllDescendantsOfPackage, PropertyGraphFetchTree, RootGraphFetchTree, } from '@finos/legend-graph';
import { ActionAlertActionType, ActionAlertType, } from '@finos/legend-application';
import { buildElementOption, getClassPropertyIcon, } from '@finos/legend-lego/graph-editor';
import { QueryBuilderTelemetryHelper } from '../../__lib__/QueryBuilderTelemetryHelper.js';
const getBindingFormatter = (props) => function BindingLabel(option) {
    const className = props.darkMode
        ? 'packageable-element-option-label--dark'
        : 'packageable-element-option-label';
    return (_jsxs("div", { className: className, children: [_jsx("div", { className: `${className}__name`, children: option.label }), option.value.package && (_jsx("div", { className: `${className}__tag`, children: option.value.path })), _jsx("div", { className: `${className}__tag`, children: option.value.contentType })] }));
};
export const QueryBuilderGraphFetchTreeNodeContainer = (props) => {
    const { node, level, stepPaddingInRem, onNodeSelect, innerProps } = props;
    const { isReadOnly, removeNode } = innerProps;
    let property, type, subType;
    if (node.tree instanceof PropertyGraphFetchTree) {
        property = node.tree.property.value;
        type = property.genericType.value.rawType;
        subType = node.tree.subType?.value;
    }
    else if (node.tree instanceof RootGraphFetchTree) {
        type = node.tree.class.value;
    }
    const isExpandable = Boolean(node.childrenIds.length);
    const nodeExpandIcon = isExpandable ? (node.isOpen ? (_jsx(ChevronDownIcon, {})) : (_jsx(ChevronRightIcon, {}))) : (_jsx("div", {}));
    const nodeTypeIcon = type ? (getClassPropertyIcon(type)) : (_jsx(PURE_UnknownElementTypeIcon, {}));
    const toggleExpandNode = () => onNodeSelect?.(node);
    const deleteNode = () => removeNode?.(node);
    return (_jsxs("div", { className: "tree-view__node__container query-builder-graph-fetch-tree__node__container", style: {
            paddingLeft: `${(level - 1) * (stepPaddingInRem ?? 2)}rem`,
            display: 'flex',
        }, children: [_jsxs("div", { className: "query-builder-graph-fetch-tree__node__content", children: [_jsxs("div", { className: "tree-view__node__icon query-builder-graph-fetch-tree__node__icon", children: [_jsx("div", { className: "query-builder-graph-fetch-tree__expand-icon", onClick: toggleExpandNode, children: nodeExpandIcon }), _jsx("div", { className: "query-builder-graph-fetch-tree__type-icon", onClick: toggleExpandNode, children: nodeTypeIcon })] }), _jsxs("div", { className: "tree-view__node__label query-builder-graph-fetch-tree__node__label", onClick: toggleExpandNode, children: [node.label, subType && (_jsx("div", { className: "query-builder-graph-fetch-tree__node__sub-type", children: _jsx("div", { className: "query-builder-graph-fetch-tree__node__sub-type__label", children: subType.name }) })), _jsx("div", { className: "query-builder-graph-fetch-tree__node__type", children: _jsx("div", { className: "query-builder-graph-fetch-tree__node__type__label", children: type?.name }) })] })] }), _jsx("div", { className: "query-builder-graph-fetch-tree__node__actions", children: _jsx("button", { className: "query-builder-graph-fetch-tree__node__action", title: "Remove", tabIndex: -1, onClick: deleteNode, disabled: isReadOnly, children: _jsx(TimesIcon, {}) }) })] }));
};
const PureSerializationConfigModal = observer((props) => {
    const { pureSerializationState, graphFetchState, config } = props;
    const applicationStore = graphFetchState.queryBuilderState.applicationStore;
    const toAdd = !pureSerializationState.config;
    const handleAction = () => {
        if (toAdd) {
            pureSerializationState.setConfig(config);
        }
        pureSerializationState.setConfigModal(false);
    };
    const removeConfig = () => {
        pureSerializationState.setConfig(undefined);
        pureSerializationState.setConfigModal(false);
        graphFetchState.queryBuilderState.applicationStore.notificationService.notifySuccess('Serialization config removed');
    };
    const close = () => pureSerializationState.setConfigModal(false);
    return (_jsx(Dialog, { open: pureSerializationState.configModal, onClose: close, classes: {
            root: 'editor-modal__root-container',
            container: 'editor-modal__container',
            paper: 'editor-modal__content',
        }, children: _jsxs(Modal, { darkMode: !applicationStore.layoutService.TEMPORARY__isLightColorThemeEnabled, className: "query-builder-graph-fetch-config", children: [_jsx(ModalHeader, { title: `${toAdd ? 'Add Serialization Config' : 'Edit Serialization Config'}` }), _jsx(ModalBody, { className: "query-builder-graph-fetch-config__content", children: _jsx(Panel, { children: _jsxs(PanelForm, { children: [_jsx(PanelFormTextField, { name: "Type Key Name", value: config.typeKeyName, isReadOnly: false, update: (value) => config.setTypeName(value ?? ''), errorMessage: config.typeKeyName === ''
                                        ? `Type key name can't be empty`
                                        : undefined }), _jsx(PanelFormTextField, { name: "Date Time Format", value: config.dateTimeFormat, isReadOnly: false, update: (value) => config.setDateTimeFormat(value === '' ? undefined : value) }), _jsx(PanelFormBooleanField, { name: "Include Type", value: config.includeType, isReadOnly: false, update: (value) => config.setIncludeType(Boolean(value)) }), _jsx(PanelFormBooleanField, { name: "Include Enum Type", value: config.includeEnumType, isReadOnly: false, update: (value) => config.setInclueEnumType(Boolean(value)) }), _jsx(PanelFormBooleanField, { name: "Remove Properties With Null Values", value: config.removePropertiesWithNullValues, isReadOnly: false, update: (value) => config.setRemovePropertiesWithNullValues(Boolean(value)) }), _jsx(PanelFormBooleanField, { name: "Remove properties with empty sets", value: config.removePropertiesWithEmptySets, isReadOnly: false, update: (value) => config.setRemovePropertiesWithEmptySets(Boolean(value)) }), _jsx(PanelFormBooleanField, { name: "Use Fully Qualified Type Path", value: config.fullyQualifiedTypePath, isReadOnly: false, update: (value) => config.setFullyQualifiedTypePath(Boolean(value)) }), _jsx(PanelFormBooleanField, { name: "Include Object Reference", value: config.includeObjectReference, isReadOnly: false, update: (value) => config.setIncludeObjectReference(Boolean(value)) })] }) }) }), _jsxs(ModalFooter, { children: [!toAdd && (_jsx(ModalFooterButton, { className: "btn--caution", text: "Remove Config", onClick: removeConfig })), _jsx(ModalFooterButton, { className: "modal__footer__close-btn", onClick: handleAction, type: toAdd ? 'primary' : 'secondary', children: toAdd ? 'Add Config' : 'Close' })] })] }) }));
});
export const QueryBuilderGraphFetchExternalConfig = observer((props) => {
    const { graphFetchState, serializationState, serializationTreeData, bindings, isReadOnly, } = props;
    const bindingOptions = bindings.map((result) => buildElementOption(result));
    const selectedBinding = {
        value: serializationState.targetBinding,
        label: serializationState.targetBinding.name,
    };
    const applicationStore = graphFetchState.queryBuilderState.applicationStore;
    const onBindingChange = (val) => {
        if (val !== null) {
            serializationState.setBinding(val.value);
            serializationState.setGraphFetchTree(serializationTreeData);
        }
    };
    const onNodeSelect = (node) => {
        if (node.childrenIds.length) {
            node.isOpen = !node.isOpen;
        }
    };
    const getChildNodes = (node) => node.childrenIds
        .map((id) => serializationTreeData.nodes.get(id))
        .filter(isNonNullable);
    const removeNode = (node) => {
        if (serializationTreeData.nodes.size === 1) {
            graphFetchState.queryBuilderState.applicationStore.notificationService.notifyWarning('externalize serialization tree can not be empty');
        }
        else {
            removeNodeRecursively(serializationTreeData, node);
            serializationState.setGraphFetchTree({ ...serializationTreeData });
        }
    };
    return (_jsx("div", { className: "query-builder-graph-fetch-external-format", children: _jsxs("div", { className: "service-execution-editor__configuration__items", children: [_jsxs("div", { className: "service-execution-editor__configuration__item", children: [_jsx("div", { className: "btn--sm service-execution-editor__configuration__item__label", children: _jsx(BufferIcon, {}) }), _jsx(CustomSelectorInput, { className: "panel__content__form__section__dropdown service-execution-editor__configuration__item__dropdown", disabled: isReadOnly, options: bindingOptions, onChange: onBindingChange, value: selectedBinding, formatOptionLabel: getBindingFormatter({
                                darkMode: !applicationStore.layoutService
                                    .TEMPORARY__isLightColorThemeEnabled,
                            }), darkMode: !applicationStore.layoutService
                                .TEMPORARY__isLightColorThemeEnabled })] }), _jsxs("div", { className: "service-execution-editor__configuration__item", children: [_jsx("div", { className: "btn--sm service-execution-editor__configuration__item__label", children: _jsx(FolderIcon, {}) }), _jsx(TreeView, { components: {
                                TreeNodeContainer: QueryBuilderGraphFetchTreeNodeContainer,
                            }, className: "query-builder-graph-fetch-tree__container__tree", treeData: serializationTreeData, onNodeSelect: onNodeSelect, getChildNodes: getChildNodes, innerProps: {
                                isReadOnly,
                                removeNode,
                            } })] })] }) }));
});
export const QueryBuilderGraphFetchTreeExplorer = observer((props) => {
    const { graphFetchState, serializationState, treeData, updateTreeData, isReadOnly, } = props;
    // Retrieve all bindings whose packageableElementIncludes contain the root class of main graph fetch tree
    const compatibleBindings = graphFetchState.queryBuilderState.graphManagerState.usableStores
        .filter(filterByType(Binding))
        .filter((b) => {
        const elements = b.modelUnit.packageableElementIncludes.map((p) => p.value);
        return elements
            .filter(filterByType(Package))
            .map((p) => Array.from(getAllDescendantsOfPackage(p, graphFetchState.queryBuilderState.graphManagerState.graph)))
            .flat()
            .concat(elements.filter((e) => !(e instanceof Package)))
            .includes(treeData.tree.class.value);
    });
    const onNodeSelect = (node) => {
        if (node.childrenIds.length) {
            node.isOpen = !node.isOpen;
        }
        updateTreeData({ ...treeData });
    };
    const getChildNodes = (node) => node.childrenIds
        .map((id) => treeData.nodes.get(id))
        .filter(isNonNullable);
    const removeNode = (node) => {
        removeNodeRecursively(treeData, node);
        if (treeData.nodes.size === 0) {
            graphFetchState.setSerializationState(new GraphFetchPureSerializationState(graphFetchState));
        }
        // Remove node from external format serialization tree as well
        if (serializationState instanceof
            GraphFetchExternalFormatSerializationState &&
            serializationState.treeData?.nodes.get(node.id)) {
            removeNodeRecursively(serializationState.treeData, guaranteeNonNullable(serializationState.treeData.nodes.get(node.id)));
            updateTreeData({ ...serializationState.treeData });
        }
        updateTreeData({ ...treeData });
    };
    const toggleChecked = () => graphFetchState.setChecked(!graphFetchState.isChecked);
    const openConfigModal = () => {
        if (serializationState instanceof GraphFetchPureSerializationState) {
            serializationState.setConfigModal(true);
        }
    };
    const onChangeSerializationType = (implementationType) => () => {
        if (implementationType !== serializationState.getLabel()) {
            graphFetchState.queryBuilderState.applicationStore.alertService.setActionAlertInfo({
                message: 'Current graph-fetch will be lost when switching to a different serialization mode. Do you still want to proceed?',
                type: ActionAlertType.CAUTION,
                actions: [
                    {
                        label: 'Proceed',
                        type: ActionAlertActionType.PROCEED_WITH_CAUTION,
                        handler: graphFetchState.queryBuilderState.applicationStore.guardUnhandledError(async () => {
                            switch (implementationType) {
                                case SERIALIZATION_TYPE.EXTERNAL_FORMAT:
                                    if (compatibleBindings.length > 0 &&
                                        compatibleBindings[0]) {
                                        const externalizeState = new GraphFetchExternalFormatSerializationState(graphFetchState, compatibleBindings[0], undefined);
                                        graphFetchState.setGraphFetchTree(treeData);
                                        externalizeState.setGraphFetchTree(deepClone(treeData));
                                        graphFetchState.setSerializationState(externalizeState);
                                    }
                                    else {
                                        graphFetchState.queryBuilderState.applicationStore.notificationService.notifyWarning(`Can't switch to external format serialization: No compatible bindings found`);
                                    }
                                    break;
                                case SERIALIZATION_TYPE.PURE:
                                default:
                                    graphFetchState.setSerializationState(new GraphFetchPureSerializationState(graphFetchState));
                                    break;
                            }
                        }),
                    },
                    {
                        label: 'Cancel',
                        type: ActionAlertActionType.PROCEED,
                        default: true,
                    },
                ],
            });
        }
    };
    return (_jsxs("div", { className: "query-builder-graph-fetch-tree", children: [_jsxs("div", { className: "query-builder-graph-fetch-tree__toolbar", children: [_jsx("div", { className: "query-builder__fetch__structure__modes", children: Object.values(SERIALIZATION_TYPE).map((type) => (_jsx("button", { onClick: onChangeSerializationType(type), className: clsx('query-builder__fetch__structure__mode', {
                                'query-builder__fetch__structure__mode--selected': type === serializationState.getLabel(),
                            }), children: prettyCONSTName(type) }, type))) }), _jsxs("div", { className: "query-builder-graph-fetch-tree__actions", children: [serializationState instanceof GraphFetchPureSerializationState && (_jsx("div", { className: "query-builder-graph-fetch-tree__actions__action", children: _jsxs("button", { className: "query-builder-graph-fetch-tree__actions__action-btn__label", onClick: openConfigModal, title: `${serializationState.config
                                        ? 'Edit pure serialization config'
                                        : 'Add pure serialization config'}`, tabIndex: -1, children: [_jsx(SerializeIcon, { className: "query-builder-graph-fetch-tree__actions__action-btn__label__icon" }), _jsx("div", { className: "query-builder-graph-fetch-tree__actions__action-btn__label__title", children: serializationState.config ? 'Edit Config' : 'Add Config' })] }) })), _jsxs("div", { className: clsx('panel__content__form__section__toggler'), onClick: toggleChecked, children: [_jsx("button", { className: clsx('panel__content__form__section__toggler__btn', {
                                            'panel__content__form__section__toggler__btn--toggled': graphFetchState.isChecked,
                                        }), children: graphFetchState.isChecked ? (_jsx(CheckSquareIcon, {})) : (_jsx(SquareIcon, {})) }), _jsx("div", { className: "panel__content__form__section__toggler__prompt", children: "Check graph fetch" }), _jsx("div", { className: "query-builder-graph-fetch-tree__toolbar__hint-icon", children: _jsx(InfoCircleIcon, { title: "With this enabled, while executing, violations of constraints will reported as part of the result, rather than causing a failure" }) })] })] })] }), _jsxs("div", { className: "query-builder-graph-fetch-tree__container", children: [serializationState instanceof GraphFetchPureSerializationState &&
                        serializationState.configModal && (_jsx(PureSerializationConfigModal, { pureSerializationState: serializationState, graphFetchState: graphFetchState, config: serializationState.config ??
                            PureSerializationConfig.createDefault() })), _jsxs(ResizablePanelGroup, { orientation: "horizontal", children: [_jsx(ResizablePanel, { children: _jsxs("div", { className: "query-builder-graph-fetch-external-format__config-group", children: [_jsx("div", { className: "query-builder-graph-fetch-external-format__config-group__header", children: _jsx("div", { className: "query-builder-graph-fetch-external-format__config-group__header__title", children: "Graph Fetch Tree" }) }), _jsx("div", { className: "query-builder-graph-fetch-external-format__config-group__content", children: _jsx("div", { className: "query-builder-graph-fetch-external-format__config-group__item", children: _jsx(TreeView, { components: {
                                                        TreeNodeContainer: QueryBuilderGraphFetchTreeNodeContainer,
                                                    }, className: "query-builder-graph-fetch-tree__container__tree", treeData: treeData, onNodeSelect: onNodeSelect, getChildNodes: getChildNodes, innerProps: {
                                                        isReadOnly,
                                                        removeNode,
                                                    } }) }) })] }) }), _jsx(ResizablePanelSplitter, {}), serializationState instanceof
                                GraphFetchExternalFormatSerializationState &&
                                serializationState.treeData && (_jsx(ResizablePanel, { children: _jsxs("div", { className: "query-builder-graph-fetch-external-format__config-group", children: [_jsx("div", { className: "query-builder-graph-fetch-external-format__config-group__header", children: _jsx("div", { className: "query-builder-graph-fetch-external-format__config-group__header__title", children: "Externalize" }) }), _jsx("div", { className: "query-builder-graph-fetch-external-format__config-group__content", children: _jsx("div", { className: "query-builder-graph-fetch-external-format_config-group__item", children: _jsx(QueryBuilderGraphFetchExternalConfig, { graphFetchState: graphFetchState, serializationState: serializationState, serializationTreeData: serializationState.treeData, bindings: compatibleBindings, isReadOnly: false }) }) })] }) }))] })] })] }));
});
const QueryBuilderGraphFetchTreePanel = observer((props) => {
    const { graphFetchTreeState, serializationState } = props;
    const treeData = graphFetchTreeState.treeData;
    // Deep/Graph Fetch Tree
    const updateTreeData = (data) => {
        graphFetchTreeState.setGraphFetchTree(data);
    };
    // Drag and Drop
    const handleDrop = useCallback((item) => {
        graphFetchTreeState.addProperty(item.node, { refreshTreeData: true });
        // If serializationState is GraphFetchExternalFormatSerializationState, we should add this node to
        // the external format serialization tree as well
        if (serializationState instanceof
            GraphFetchExternalFormatSerializationState) {
            serializationState.addProperty(deepClone(item.node), {
                refreshTreeData: true,
            });
        }
    }, [graphFetchTreeState, serializationState]);
    const [{ isDragOver }, dropConnector] = useDrop(() => ({
        accept: [
            QUERY_BUILDER_EXPLORER_TREE_DND_TYPE.ENUM_PROPERTY,
            QUERY_BUILDER_EXPLORER_TREE_DND_TYPE.PRIMITIVE_PROPERTY,
        ],
        drop: (item, monitor) => {
            if (!monitor.didDrop()) {
                handleDrop(item);
            } // prevent drop event propagation to accomondate for nested DnD
        },
        collect: (monitor) => ({
            isDragOver: monitor.isOver({ shallow: true }),
        }),
    }), [handleDrop]);
    useEffect(() => {
        QueryBuilderTelemetryHelper.logEvent_RenderGraphFetchPanel(graphFetchTreeState.queryBuilderState.applicationStore.telemetryService, {
            serializationType: serializationState.getLabel(),
        });
    }, [
        graphFetchTreeState.queryBuilderState.applicationStore,
        serializationState,
    ]);
    return (_jsx("div", { "data-testid": QUERY_BUILDER_TEST_ID.QUERY_BUILDER_GRAPH_FETCH, className: "panel__content", children: _jsxs(PanelDropZone, { isDragOver: isDragOver, dropTargetConnector: dropConnector, contentClassName: "query-builder-graph-fetch-panel", children: [(!treeData || isGraphFetchTreeDataEmpty(treeData)) && (_jsx(BlankPanelPlaceholder, { text: "Add a graph fetch property", tooltipText: "Drag and drop properties here" })), treeData && !isGraphFetchTreeDataEmpty(treeData) && (_jsx(QueryBuilderGraphFetchTreeExplorer, { graphFetchState: graphFetchTreeState, serializationState: serializationState, treeData: treeData, isReadOnly: false, updateTreeData: updateTreeData }))] }) }));
});
export const QueryBuilderGraphFetchPanel = observer((props) => {
    const { graphFetchTreeState } = props;
    const serializationState = graphFetchTreeState.serializationState;
    const handleTextModeClick = () => graphFetchTreeState.queryBuilderState.textEditorState.openModal(QueryBuilderTextEditorMode.TEXT);
    if (serializationState instanceof GraphFetchPureSerializationState ||
        serializationState instanceof GraphFetchExternalFormatSerializationState) {
        return (_jsx(QueryBuilderGraphFetchTreePanel, { graphFetchTreeState: graphFetchTreeState, serializationState: serializationState }));
    }
    return (_jsx("div", { "data-testid": QUERY_BUILDER_TEST_ID.QUERY_BUILDER_GRAPH_FETCH, children: _jsx(BlankPanelContent, { children: _jsxs("div", { className: "unsupported-element-editor__main", children: [_jsx("div", { className: "unsupported-element-editor__summary", children: "Unsupported Graph Fetch Serialization Type" }), _jsx("button", { className: "btn--dark unsupported-element-editor__to-text-mode__btn", onClick: handleTextModeClick, children: "Edit in text mode" })] }) }) }));
});
//# sourceMappingURL=QueryBuilderGraphFetchTreePanel.js.map