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 { useApplicationStore, DEFAULT_TAB_SIZE, } from '@finos/legend-application';
import { compareLabelFn, clsx, CustomSelectorInput, TimesIcon, Dialog, ControlledDropdownMenu, CaretDownIcon, MenuContentItem, MenuContent, Modal, ModalBody, ModalFooter, ModalHeader, PanelLoadingIndicator, TreeView, ChevronDownIcon, ChevronRightIcon, ContextMenu, CompressIcon, SubjectIcon, ViewHeadlineIcon, ExpandAllIcon, BlankPanelContent, VersionsIcon, RepoIcon, ModalFooterButton, Button, Panel, PanelHeader, PanelHeaderActions, PanelHeaderActionItem, PanelContentLists, CircleNotchIcon, } from '@finos/legend-art';
import { MASTER_SNAPSHOT_ALIAS, SNAPSHOT_VERSION_ALIAS, } from '@finos/legend-server-depot';
import {} from '@finos/legend-server-sdlc';
import { ActionState, assertErrorThrown, guaranteeNonNullable, isNonNullable, LogEvent, prettyCONSTName, compareSemVerVersions, } from '@finos/legend-shared';
import { generateGAVCoordinates } from '@finos/legend-storage';
import { flowResult } from 'mobx';
import { observer } from 'mobx-react-lite';
import { forwardRef, useCallback, useEffect, useRef, useState } from 'react';
import { ProjectConfigurationEditorState } from '../../../../stores/editor/editor-state/project-configuration-editor-state/ProjectConfigurationEditorState.js';
import { ConflictTreeNodeData, ConflictVersionNodeData, buildDependencyNodeChildren, DEPENDENCY_REPORT_TAB, openAllDependencyNodesInTree, ProjectDependencyTreeNodeData, } from '../../../../stores/editor/editor-state/project-configuration-editor-state/ProjectDependencyEditorState.js';
import { LEGEND_STUDIO_APP_EVENT } from '../../../../__lib__/LegendStudioEvent.js';
import { generateViewProjectByGAVRoute, generateViewProjectRoute, generateViewVersionRoute, } from '../../../../__lib__/LegendStudioNavigation.js';
import { LEGEND_STUDIO_TEST_ID } from '../../../../__lib__/LegendStudioTesting.js';
import { useEditorStore } from '../../EditorStoreProvider.js';
const buildProjectOption = (project) => ({
    label: project.coordinates,
    value: project,
});
const ProjectDependencyActions = observer((props) => {
    const { dependencyEditorState: dependencyEditorState } = props;
    const applicationStore = useApplicationStore();
    const hasConflicts = dependencyEditorState.dependencyReport?.conflicts.length;
    const hasDependencyChanges = dependencyEditorState.hasDependencyChanges;
    const viewTree = () => {
        if (dependencyEditorState.dependencyReport) {
            dependencyEditorState.setReportTab(DEPENDENCY_REPORT_TAB.EXPLORER);
        }
    };
    const viewConflict = () => {
        if (dependencyEditorState.dependencyReport) {
            dependencyEditorState.setReportTab(DEPENDENCY_REPORT_TAB.CONFLICTS);
        }
    };
    const validateDependencies = () => {
        flowResult(dependencyEditorState.validateAndFetchDependencyReport()).catch(applicationStore.alertUnhandledError);
    };
    return (_jsxs("div", { className: "project-dependency-editor__info", children: [_jsx(Button, { onClick: viewTree, disabled: !dependencyEditorState.dependencyReport, title: "View Dependency Explorer", text: "View Dependency Explorer" }), Boolean(hasConflicts) && (_jsx(Button, { className: "project-dependency-editor__conflicts-btn", text: "View Conflicts", onClick: viewConflict, disabled: !dependencyEditorState.dependencyReport?.conflicts.length, title: "View any conflicts in your dependencies" })), hasDependencyChanges && (_jsx(Button, { className: "project-dependency-editor__validate-btn", text: "Validate Dependencies", onClick: validateDependencies, disabled: dependencyEditorState.validatingDependenciesState.isInProgress, title: "Validate dependency changes and update dependency tree" }))] }));
});
const formatOptionLabel = (option) => (_jsxs("div", { className: "project-dependency-editor__label", children: [_jsx("div", { className: "project-dependency-editor__label__tag", children: option.value.projectId }), _jsx("div", { className: "project-dependency-editor__label__name", children: option.value.coordinates })] }));
const DependencyTreeNodeContextMenu = observer(forwardRef(function DependencyTreeNodeContextMenu(props, ref) {
    const { node } = props;
    const applicationStore = useApplicationStore();
    const getViewProjectUrl = () => {
        let groupId;
        let artifactId;
        let versionId;
        if (node instanceof ConflictTreeNodeData) {
            groupId = node.conflict.groupId;
            artifactId = node.conflict.artifactId;
        }
        else if (node instanceof ConflictVersionNodeData) {
            groupId = node.versionConflict.conflict.groupId;
            artifactId = node.versionConflict.conflict.artifactId;
            versionId = node.versionConflict.version.versionId;
        }
        else if (node instanceof ProjectDependencyTreeNodeData) {
            groupId = node.value.groupId;
            artifactId = node.value.artifactId;
            versionId = node.value.versionId;
        }
        return generateViewProjectByGAVRoute(guaranteeNonNullable(groupId), guaranteeNonNullable(artifactId), versionId === MASTER_SNAPSHOT_ALIAS
            ? SNAPSHOT_VERSION_ALIAS
            : versionId);
    };
    const getSDLCProjectUrl = () => {
        if (node instanceof ConflictTreeNodeData) {
            const version = node.conflict.versions[0];
            return version
                ? generateViewProjectRoute(version.projectId)
                : undefined;
        }
        else if (node instanceof ConflictVersionNodeData) {
            return generateViewVersionRoute(node.versionConflict.version.projectId, node.versionConflict.version.artifactId);
        }
        else if (node instanceof ProjectDependencyTreeNodeData) {
            return generateViewVersionRoute(node.value.projectId, node.value.versionId);
        }
        return undefined;
    };
    const sdlcProjectUrl = getSDLCProjectUrl();
    const viewProjectUrl = getViewProjectUrl();
    const viewProject = () => {
        applicationStore.navigationService.navigator.visitAddress(applicationStore.navigationService.navigator.generateAddress(viewProjectUrl));
    };
    const viewSDLCProject = () => {
        if (sdlcProjectUrl) {
            applicationStore.navigationService.navigator.visitAddress(applicationStore.navigationService.navigator.generateAddress(sdlcProjectUrl));
        }
    };
    return (_jsxs(MenuContent, { "data-testid": LEGEND_STUDIO_TEST_ID.EXPLORER_CONTEXT_MENU, children: [_jsx(MenuContentItem, { onClick: viewProject, children: "Visit Project" }), _jsx(MenuContentItem, { disabled: !sdlcProjectUrl, onClick: viewSDLCProject, children: "Visit SDLC Project" })] }));
}));
const DependencyTreeNodeContainer = (props) => {
    const { node, level, stepPaddingInRem, onNodeSelect } = props;
    const isExpandable = Boolean(node.childrenIds?.length);
    const selectNode = () => onNodeSelect?.(node);
    const value = node.value;
    const nodeExpandIcon = isExpandable ? (node.isOpen ? (_jsx(ChevronDownIcon, {})) : (_jsx(ChevronRightIcon, {}))) : (_jsx("div", {}));
    return (_jsx(ContextMenu, { content: _jsx(DependencyTreeNodeContextMenu, { node: node }), menuProps: { elevation: 7 }, children: _jsxs("div", { className: clsx('tree-view__node__container project-dependency-explorer-tree__node__container', {
                'menu__trigger--on-menu-open': !node.isSelected,
            }, {
                'project-dependency-explorer-tree__node__container--selected': node.isSelected,
            }), style: {
                paddingLeft: `${(level - 1) * (stepPaddingInRem ?? 1)}rem`,
            }, onClick: selectNode, children: [_jsx("div", { className: "tree-view__node__icon project-dependency-explorer-tree__node__icon", children: _jsx("div", { className: "project-dependency-explorer-tree__node__icon__expand", children: nodeExpandIcon }) }), _jsx("button", { className: "tree-view__node__label project-dependency-explorer-tree__node__label", tabIndex: -1, title: value.id, children: value.artifactId }), _jsx("div", { className: "project-dependency-explorer-tree__node__version", children: _jsx("button", { className: "project-dependency-explorer-tree__node__version-btn", title: value.versionId, tabIndex: -1, children: value.versionId }) })] }) }));
};
const DependencyTreeView = (props) => {
    const { treeData, setTreeData } = props;
    const onNodeExpand = (node) => {
        if (node.childrenIds?.length) {
            node.isOpen = !node.isOpen;
            node.childrenIds
                .map((id) => treeData.nodes.get(id))
                .filter(isNonNullable)
                .forEach((c) => buildDependencyNodeChildren(c, treeData.nodes));
        }
        setTreeData({ ...treeData });
    };
    const onNodeSelect = (node) => {
        buildDependencyNodeChildren(node, treeData.nodes);
        onNodeExpand(node);
        setTreeData({ ...treeData });
    };
    const getChildNodes = (node) => {
        if (!node.childrenIds || node.childrenIds.length === 0) {
            return [];
        }
        const childrenNodes = node.childrenIds
            .map((id) => treeData.nodes.get(id))
            .filter(isNonNullable);
        return childrenNodes;
    };
    return (_jsx(TreeView, { components: {
            TreeNodeContainer: DependencyTreeNodeContainer,
        }, treeData: treeData, getChildNodes: getChildNodes, onNodeSelect: onNodeSelect, innerProps: {
            onNodeExpand,
        } }));
};
const ConflictTreeNodeContainer = (props) => {
    const { node, level, stepPaddingInRem, onNodeSelect } = props;
    const isExpandable = Boolean(node.childrenIds?.length);
    const selectNode = () => onNodeSelect?.(node);
    const nodeExpandIcon = isExpandable ? (node.isOpen ? (_jsx(ChevronDownIcon, {})) : (_jsx(ChevronRightIcon, {}))) : (_jsx("div", {}));
    return (_jsx(ContextMenu, { content: _jsx(DependencyTreeNodeContextMenu, { node: node }), menuProps: { elevation: 7 }, children: _jsxs("div", { className: clsx('tree-view__node__container project-dependency-explorer-tree__node__container', {
                'menu__trigger--on-menu-open': !node.isSelected,
            }, {
                'project-dependency-explorer-tree__node__container--selected': node.isSelected,
            }), style: {
                paddingLeft: `${(level - 1) * (stepPaddingInRem ?? 1)}rem`,
            }, onClick: selectNode, children: [_jsxs("div", { className: clsx('tree-view__node__icon project-dependency-explorer-tree__node__icon', {
                        'tree-view__node__icon project-dependency-explorer-tree__node__icon__with__type': node instanceof ConflictTreeNodeData ||
                            node instanceof ConflictVersionNodeData,
                    }), children: [_jsx("div", { className: "project-dependency-explorer-tree__node__icon__expand", children: nodeExpandIcon }), node instanceof ConflictTreeNodeData && (_jsx("div", { className: "project-dependency-explorer-tree__node__icon__type", children: _jsx(RepoIcon, {}) })), node instanceof ConflictVersionNodeData && (_jsx("div", { className: "project-dependency-explorer-tree__node__icon__type", children: _jsx(VersionsIcon, {}) }))] }), _jsx("button", { className: "tree-view__node__label project-dependency-explorer-tree__node__label", tabIndex: -1, title: node.description, children: node.label }), node instanceof ProjectDependencyTreeNodeData && (_jsx("div", { className: "project-dependency-explorer-tree__node__version", children: _jsx("button", { className: "project-dependency-explorer-tree__node__version-btn", title: node.value.versionId, tabIndex: -1, children: node.value.versionId }) }))] }) }));
};
const ConflictDependencyTreeView = (props) => {
    const { treeData, setTreeData } = props;
    const onNodeExpand = (node) => {
        if (node.childrenIds?.length) {
            node.isOpen = !node.isOpen;
        }
        setTreeData({ ...treeData });
    };
    const onNodeSelect = (node) => {
        onNodeExpand(node);
        setTreeData({ ...treeData });
    };
    const getChildNodes = (node) => {
        if (!node.childrenIds || node.childrenIds.length === 0) {
            return [];
        }
        const childrenNodes = node.childrenIds
            .map((id) => treeData.nodes.get(id))
            .filter(isNonNullable);
        return childrenNodes;
    };
    return (_jsx(TreeView, { components: {
            TreeNodeContainer: ConflictTreeNodeContainer,
        }, treeData: treeData, getChildNodes: getChildNodes, onNodeSelect: onNodeSelect, innerProps: {
            onNodeExpand,
        } }));
};
const collapseTreeData = (treeData) => {
    Array.from(treeData.nodes.values()).forEach((node) => {
        node.isOpen = false;
    });
};
export const getConflictsString = (report) => Array.from(report.conflictInfo.entries())
    .map(([k, conflictVersionPaths]) => {
    const base = `project:\n${' '.repeat(DEFAULT_TAB_SIZE) +
        generateGAVCoordinates(k.groupId, k.artifactId, undefined)}`;
    const versionConflictString = conflictVersionPaths
        .map((conflictVersion) => {
        const versions = `version: ${conflictVersion.version.versionId}\n`;
        const paths = `paths:\n${conflictVersion.pathsToVersion
            .map((p, idx) => `${' '.repeat(DEFAULT_TAB_SIZE) + (idx + 1)}:\n${p
            .map((l) => l.id)
            .join('>')}`)
            .join('\n')}`;
        return versions + paths;
    })
        .join('\n');
    return `${base}\n${versionConflictString}`;
})
    .join('\n\n');
const ProjectDependencyConflictViewer = observer((props) => {
    const { report, dependencyEditorState } = props;
    const hasConflict = Boolean(report.conflicts.length);
    const collapseTree = () => {
        dependencyEditorState.conflictStates?.forEach((c) => {
            const treeData = c.treeData;
            Array.from(treeData.nodes.values()).forEach((n) => (n.isOpen = false));
            c.setTreeData({ ...treeData });
        });
    };
    const expandAllNodes = () => {
        dependencyEditorState.expandAllConflicts();
    };
    useEffect(() => {
        if (hasConflict && !dependencyEditorState.conflictStates) {
            dependencyEditorState.buildConflictPaths();
        }
    }, [dependencyEditorState, hasConflict]);
    return (_jsxs(Panel, { className: "project-dependency-explorer", children: [_jsx(PanelHeader, { title: "conflicts", children: _jsxs(PanelHeaderActions, { children: [_jsx(PanelHeaderActionItem, { title: "Collapse Tree", disabled: !hasConflict || !dependencyEditorState.conflictStates, onClick: collapseTree, children: _jsx(CompressIcon, {}) }), _jsx(PanelHeaderActionItem, { title: "Expand All Conflict Paths", disabled: !hasConflict || !dependencyEditorState.conflictStates, onClick: expandAllNodes, children: _jsx(ExpandAllIcon, {}) })] }) }), _jsxs("div", { className: "project-dependency-explorer__content", children: [hasConflict && dependencyEditorState.conflictStates && (_jsx("div", { children: dependencyEditorState.conflictStates.map((c) => (_jsx(ConflictDependencyTreeView, { treeData: c.treeData, setTreeData: (treeData) => c.setTreeData(treeData) }, c.uuid))) })), !hasConflict && _jsx(BlankPanelContent, { children: "No Conflicts" })] })] }));
});
const ProjectDependencyResolutionViewer = observer((props) => {
    const { dependencyEditorState } = props;
    const applicationStore = dependencyEditorState.editorStore.applicationStore;
    const [versionBacktrackCount, setVersionBacktrackCount] = useState(5);
    const resolutionResult = dependencyEditorState.resolutionResult;
    const resolveConflicts = () => {
        dependencyEditorState.clearResolutionResult();
        flowResult(dependencyEditorState.resolveCompatibleDependencies(versionBacktrackCount)).catch(applicationStore.alertUnhandledError);
    };
    const applyResolution = () => {
        flowResult(dependencyEditorState.applyResolvedDependencies()).catch(applicationStore.alertUnhandledError);
    };
    const cancelResolution = () => {
        dependencyEditorState.clearResolutionResult();
    };
    return (_jsxs(Panel, { className: "project-dependency-explorer", children: [_jsx(PanelHeader, { title: "resolution", children: _jsx(PanelHeaderActions, { children: !resolutionResult?.success && (_jsx(_Fragment, { children: _jsxs("div", { className: "project-dependency-editor__resolve-controls", children: [_jsxs("div", { className: "project-dependency-editor__backtrack-control", children: [_jsx("input", { type: "number", min: "1", max: "100", value: versionBacktrackCount, onChange: (e) => setVersionBacktrackCount(Math.max(1, parseInt(e.target.value || '1', 10))), title: "Number of previous versions to check backward from the latest version when searching for compatible dependency versions", className: "input input--dark project-dependency-editor__backtrack-input", placeholder: "Backtrack versions" }), _jsx("div", { className: "project-dependency-editor__backtrack-label", children: "versions to check" })] }), _jsxs("button", { className: "btn--dark project-dependency-editor__resolve-btn", onClick: resolveConflicts, disabled: dependencyEditorState.resolvingCompatibleDependenciesState
                                        .isInProgress, title: "Automatically resolve conflicts by finding compatible versions", tabIndex: -1, children: [dependencyEditorState.resolvingCompatibleDependenciesState
                                            .isInProgress && (_jsx(CircleNotchIcon, { className: "icon--loading" })), "Resolve Compatible Dependencies"] })] }) })) }) }), _jsxs("div", { className: "project-dependency-explorer__content", children: [!resolutionResult && (_jsx(BlankPanelContent, { children: "Click \"Resolve Compatible Dependencies\" to find compatible versions for conflicting dependencies" })), resolutionResult && resolutionResult.success && (_jsxs("div", { className: "project-dependency-resolution", children: [_jsxs("div", { className: "project-dependency-resolution__header", children: [_jsx("div", { className: "project-dependency-resolution__title", children: "\u2713 Compatible Versions Found" }), _jsxs("div", { className: "project-dependency-resolution__subtitle", children: ["Found compatible versions by checking ", versionBacktrackCount, ' ', "versions back. Review and confirm to apply."] })] }), _jsx("div", { className: "project-dependency-resolution__list", children: resolutionResult.resolvedVersions.map((dep) => (_jsxs("div", { className: "project-dependency-resolution__item", children: [_jsxs("div", { className: "project-dependency-resolution__item-name", children: [dep.groupId, ":", dep.artifactId] }), _jsxs("div", { className: "project-dependency-resolution__item-version", children: ["\u2192 ", dep.versionId] })] }, `${dep.groupId}:${dep.artifactId}`))) }), _jsxs("div", { className: "project-dependency-resolution__actions", children: [_jsx("button", { className: "btn btn--dark project-dependency-resolution__action-btn project-dependency-resolution__action-btn--confirm", onClick: applyResolution, title: "Apply these resolved versions to your dependencies", children: "Confirm & Apply Changes" }), _jsx("button", { className: "btn btn--dark project-dependency-resolution__action-btn", onClick: cancelResolution, title: "Cancel and keep current versions", children: "Cancel" })] })] })), resolutionResult && !resolutionResult.success && (_jsxs("div", { className: "project-dependency-resolution project-dependency-resolution--failure", children: [_jsxs("div", { className: "project-dependency-resolution__header", children: [_jsx("div", { className: "project-dependency-resolution__title project-dependency-resolution__title--error", children: "\u2717 Unable to Resolve Conflicts" }), _jsxs("div", { className: "project-dependency-resolution__subtitle", children: [resolutionResult.failureReason ??
                                                'No compatible versions found', ' ', "within ", versionBacktrackCount, " versions."] }), _jsx("div", { className: "project-dependency-resolution__tip", children: "\uD83D\uDCA1 Tip: Try increasing the backtrack version count or manually adjust your dependency versions." })] }), _jsxs("div", { className: "project-dependency-resolution__conflicts-list", children: [_jsxs("div", { className: "project-dependency-resolution__conflicts-header", children: ["Conflicting Dependencies (", resolutionResult.conflicts.length, ")"] }), resolutionResult.conflicts.map((conflict) => {
                                        const suggestedVersionId = conflict.suggestedOverride?.versionId;
                                        return (_jsxs("div", { className: "project-dependency-resolution__conflict-item", children: [_jsxs("div", { className: "project-dependency-resolution__conflict-name", children: [conflict.groupId, ":", conflict.artifactId] }), suggestedVersionId && (_jsxs("div", { className: "project-dependency-resolution__suggested-version", children: ["\uD83D\uDCA1 Suggested Override: ", suggestedVersionId] })), _jsx("div", { className: "project-dependency-resolution__conflict-versions", children: conflict.conflictingVersions.map((versionInfo) => {
                                                        const isSuggestedVersion = versionInfo.version === suggestedVersionId;
                                                        return (_jsxs("div", { className: `project-dependency-resolution__conflict-version${isSuggestedVersion ? 'project-dependency-resolution__conflict-version--suggested' : ''}`, children: [_jsxs("span", { className: "project-dependency-resolution__version-label", children: [versionInfo.version, isSuggestedVersion && (_jsxs("span", { className: "project-dependency-resolution__suggested-badge", children: [' ', "[suggested override]"] }))] }), _jsx("span", { className: "project-dependency-resolution__required-by", children: "required by:" }), _jsxs("div", { className: "project-dependency-resolution__requirements", children: [versionInfo.requiredBy
                                                                            .slice(0, 3)
                                                                            .map((dep) => (_jsxs("div", { className: "project-dependency-resolution__requirement", children: [dep.groupId, ":", dep.artifactId, ":", dep.versionId] }, `${dep.groupId}:${dep.artifactId}:${dep.versionId}`))), versionInfo.requiredBy.length > 3 && (_jsxs("div", { className: "project-dependency-resolution__requirement-more", children: ["...and ", versionInfo.requiredBy.length - 3, ' ', "more"] }))] })] }, `${conflict.groupId}:${conflict.artifactId}:${versionInfo.version}`));
                                                    }) })] }, `${conflict.groupId}:${conflict.artifactId}`));
                                    })] }), resolutionResult.suggestedOverrides &&
                                resolutionResult.suggestedOverrides.length > 0 && (_jsxs("div", { className: "project-dependency-resolution__suggested-overrides", children: [_jsxs("div", { className: "project-dependency-resolution__suggested-overrides-header", children: [_jsxs("div", { className: "project-dependency-resolution__suggested-overrides-title", children: ["\uD83D\uDCA1 Suggested Version Overrides (", resolutionResult.suggestedOverrides.length, ")"] }), _jsx("div", { className: "project-dependency-resolution__suggested-overrides-subtitle", children: "These versions may help resolve conflicts. Consider manually updating your dependencies to these versions." })] }), _jsx("div", { className: "project-dependency-resolution__suggested-overrides-list", children: resolutionResult.suggestedOverrides.map((override) => (_jsxs("div", { className: "project-dependency-resolution__suggested-override-item", children: [_jsxs("div", { className: "project-dependency-resolution__suggested-override-name", children: [override.groupId, ":", override.artifactId] }), _jsxs("div", { className: "project-dependency-resolution__suggested-override-version", children: ["\u2192 ", override.versionId] })] }, `${override.groupId}:${override.artifactId}:${override.versionId}`))) })] }))] }))] })] }));
});
const ProjectDependencyReportModal = observer((props) => {
    const { dependencyEditorState } = props;
    const applicationStore = dependencyEditorState.editorStore.applicationStore;
    const reportTab = dependencyEditorState.reportTab;
    const tabs = Object.values(DEPENDENCY_REPORT_TAB);
    const changeTab = (tab) => () => dependencyEditorState.setReportTab(tab);
    const dependencyReport = dependencyEditorState.dependencyReport;
    const closeModal = () => dependencyEditorState.setReportTab(undefined);
    const [flattenView, setFlattenView] = useState(false);
    const [isExpandingDependencies, setIsExpandingDependencies] = useState(false);
    const setTreeData = (val) => {
        dependencyEditorState.setTreeData(val, flattenView);
    };
    const toggleViewAsListOrAsTree = () => {
        setFlattenView(!flattenView);
    };
    const treeData = flattenView
        ? dependencyEditorState.flattenDependencyTreeData
        : dependencyEditorState.dependencyTreeData;
    const collapseTree = () => {
        if (treeData) {
            collapseTreeData(treeData);
            setTreeData({ ...treeData });
        }
    };
    const openAllDependencyNodes = () => {
        if (treeData && dependencyReport) {
            setIsExpandingDependencies(true);
            openAllDependencyNodesInTree(treeData, dependencyReport.graph);
            setTreeData({ ...treeData });
            setIsExpandingDependencies(false);
        }
    };
    return (_jsx(Dialog, { open: Boolean(dependencyEditorState.reportTab), onClose: closeModal, classes: {
            root: 'editor-modal__root-container',
            container: 'editor-modal__container',
            paper: 'editor-modal__content',
        }, children: _jsxs(Modal, { darkMode: !applicationStore.layoutService.TEMPORARY__isLightColorThemeEnabled, className: "editor-modal", children: [_jsx(ModalHeader, { title: "Dependency Explorer" }), _jsx(ModalBody, { children: _jsxs("div", { className: "panel project-dependency-report", children: [_jsx(PanelLoadingIndicator, { isLoading: Boolean((isExpandingDependencies ||
                                    dependencyEditorState.expandConflictsState.isInProgress ||
                                    dependencyEditorState.buildConflictPathState
                                        .isInProgress) &&
                                    !dependencyEditorState.validatingDependenciesState
                                        .isInProgress &&
                                    !dependencyEditorState.fetchingDependencyInfoState
                                        .isInProgress) }), _jsx("div", { className: "panel__header project-dependency-report__tabs__header", children: _jsx("div", { className: "project-dependency-report__tabs", children: tabs.map((tab) => (_jsx("button", { onClick: changeTab(tab), className: clsx('project-dependency-report__tab', {
                                            'project-dependency-report__tab--active': tab === reportTab,
                                        }), children: prettyCONSTName(tab) }, tab))) }) }), reportTab === DEPENDENCY_REPORT_TAB.EXPLORER && (_jsxs(Panel, { className: "project-dependency-explorer", children: [_jsxs(PanelHeader, { children: [_jsx("div", { className: "panel__header__title", children: _jsx("div", { className: "panel__header__title__label", children: "explorer" }) }), _jsxs(PanelHeaderActions, { children: [!flattenView && (_jsxs(_Fragment, { children: [_jsx(PanelHeaderActionItem, { disabled: !treeData, onClick: collapseTree, title: "Collapse Tree", children: _jsx(CompressIcon, {}) }), _jsx(PanelHeaderActionItem, { disabled: !treeData || !dependencyReport, onClick: openAllDependencyNodes, title: "Expand All Dependencies", children: _jsx(ExpandAllIcon, {}) })] })), _jsx("div", { className: "panel__header__action query-builder__functions-explorer__custom-icon", children: flattenView ? (_jsx(SubjectIcon, { title: "View as Tree", onClick: toggleViewAsListOrAsTree })) : (_jsx(ViewHeadlineIcon, { title: "View as Flatten List", onClick: toggleViewAsListOrAsTree })) })] })] }), _jsx("div", { className: "project-dependency-explorer__content", children: treeData && (_jsx(DependencyTreeView, { treeData: treeData, setTreeData: setTreeData })) })] })), reportTab === DEPENDENCY_REPORT_TAB.CONFLICTS &&
                                dependencyReport && (_jsx(ProjectDependencyConflictViewer, { report: dependencyReport, dependencyEditorState: dependencyEditorState })), reportTab === DEPENDENCY_REPORT_TAB.RESOLUTION && (_jsx(ProjectDependencyResolutionViewer, { dependencyEditorState: dependencyEditorState }))] }) }), _jsx(ModalFooter, { children: _jsx(ModalFooterButton, { onClick: closeModal, text: "Close", type: "secondary" }) })] }) }));
});
const ProjectDependencyInlineExclusionsSelector = observer((props) => {
    const { projectDependency, isReadOnly } = props;
    const editorStore = useEditorStore();
    const applicationStore = useApplicationStore();
    const dependencyEditorState = editorStore.projectConfigurationEditorState.projectDependencyEditorState;
    const [selectedTransitiveDependency, setSelectedTransitiveDependency] = useState(null);
    const [transitiveDependencyOptions, setTransitiveDependencyOptions] = useState([]);
    const getTransitiveDependencies = useCallback(() => {
        const dependencyReport = dependencyEditorState.dependencyReport;
        if (!dependencyReport?.graph) {
            return [];
        }
        const transitiveDeps = new Map();
        const existingExclusionCoordinates = dependencyEditorState.getExclusionCoordinates(projectDependency.projectId);
        const visitedNodes = new Set();
        const traverseNode = (nodeId) => {
            if (visitedNodes.has(nodeId)) {
                return;
            }
            visitedNodes.add(nodeId);
            const node = dependencyReport.graph.nodes.get(nodeId);
            if (node?.dependencies) {
                for (let i = 0; i < node.dependencies.length; i++) {
                    const dep = node.dependencies[i];
                    if (!dep?.groupId || !dep.artifactId) {
                        continue;
                    }
                    const coordinate = generateGAVCoordinates(dep.groupId, dep.artifactId, undefined);
                    if (existingExclusionCoordinates.indexOf(coordinate) === -1 &&
                        coordinate !==
                            `${projectDependency.groupId}:${projectDependency.artifactId}`) {
                        transitiveDeps.set(coordinate, {
                            label: generateGAVCoordinates(dep.groupId, dep.artifactId, undefined),
                            value: coordinate,
                            groupId: dep.groupId,
                            artifactId: dep.artifactId,
                        });
                    }
                    traverseNode(dep.id);
                }
            }
        };
        const rootNodeId = generateGAVCoordinates(guaranteeNonNullable(projectDependency.groupId), guaranteeNonNullable(projectDependency.artifactId), guaranteeNonNullable(projectDependency.versionId));
        traverseNode(rootNodeId);
        return Array.from(transitiveDeps.values()).sort((a, b) => a.label.localeCompare(b.label));
    }, [
        dependencyEditorState,
        projectDependency.projectId,
        projectDependency.groupId,
        projectDependency.artifactId,
        projectDependency.versionId,
    ]);
    useEffect(() => {
        setTransitiveDependencyOptions(getTransitiveDependencies());
    }, [
        dependencyEditorState.dependencyReport,
        projectDependency.projectId,
        getTransitiveDependencies,
    ]);
    const addExclusionFromDropdown = (option) => {
        if (!option) {
            return;
        }
        try {
            dependencyEditorState.addExclusionByCoordinate(projectDependency.projectId, option.value);
            setSelectedTransitiveDependency(null);
            setTransitiveDependencyOptions(getTransitiveDependencies());
            applicationStore.notificationService.notifySuccess(`Exclusion added: ${option.value}. Click "Validate Dependencies" to apply changes.`);
        }
        catch (error) {
            assertErrorThrown(error);
            applicationStore.notificationService.notifyError(`Failed to add exclusion: ${error.message}`);
        }
    };
    if (isReadOnly) {
        return null;
    }
    return (_jsx("div", { className: "project-dependency-exclusions-selector", children: _jsx(CustomSelectorInput, { className: "project-dependency-exclusions-selector__dropdown", placeholder: "Add exclusion...", options: transitiveDependencyOptions, onChange: addExclusionFromDropdown, value: selectedTransitiveDependency, isClearable: true, escapeClearsValue: true, disabled: transitiveDependencyOptions.length === 0, darkMode: !applicationStore.layoutService.TEMPORARY__isLightColorThemeEnabled }) }));
});
const ProjectDependencyExclusionsList = observer((props) => {
    const { projectDependency, isReadOnly } = props;
    const editorStore = useEditorStore();
    const applicationStore = useApplicationStore();
    const dependencyEditorState = editorStore.projectConfigurationEditorState.projectDependencyEditorState;
    const [, setForceUpdate] = useState(0);
    const exclusions = dependencyEditorState.getExclusions(projectDependency.projectId);
    useEffect(() => {
        setForceUpdate((prev) => prev + 1);
    }, [
        dependencyEditorState.dependencyReport,
        projectDependency.projectId,
        dependencyEditorState,
    ]);
    useEffect(() => {
        const interval = setInterval(() => {
            const currentExclusions = dependencyEditorState.getExclusions(projectDependency.projectId);
            if (currentExclusions.length !== exclusions.length) {
                setForceUpdate((prev) => prev + 1);
            }
        }, 1000);
        return () => clearInterval(interval);
    }, [exclusions.length, dependencyEditorState, projectDependency.projectId]);
    const removeExclusion = (exclusion) => {
        try {
            dependencyEditorState.removeExclusion(projectDependency.projectId, exclusion);
            if (!dependencyEditorState.hasAnyExclusions) {
                applicationStore.notificationService.notifySuccess(`Last exclusion removed: ${exclusion.coordinate}. Refreshing dependency tree...`);
                flowResult(dependencyEditorState.fetchDependencyReport()).catch(applicationStore.alertUnhandledError);
            }
            else {
                applicationStore.notificationService.notifySuccess(`Exclusion removed: ${exclusion.coordinate}. Click "Validate Dependencies" to apply changes.`);
            }
        }
        catch (error) {
            assertErrorThrown(error);
            applicationStore.notificationService.notifyError(`Failed to remove exclusion: ${error.message}`);
        }
    };
    if (exclusions.length === 0) {
        return null;
    }
    return (_jsxs("div", { className: "project-dependency-exclusions-list", children: [_jsx("div", { className: "project-dependency-exclusions-list__header", children: _jsxs("div", { className: "project-dependency-exclusions-list__title", children: ["Exclusions (", exclusions.length, ")"] }) }), _jsx("div", { className: "project-dependency-exclusions-list__items", children: exclusions.map((exclusion) => (_jsxs("div", { className: "project-dependency-exclusions-list__item", children: [_jsx("div", { className: "project-dependency-exclusions-list__item__coordinate", children: exclusion.coordinate }), !isReadOnly && (_jsx("button", { className: "project-dependency-exclusions-list__item__remove-btn btn--dark btn--caution", onClick: () => removeExclusion(exclusion), title: "Remove exclusion", children: _jsx(TimesIcon, {}) }))] }, exclusion.coordinate))) })] }));
});
const ProjectVersionDependencyEditor = observer((props) => {
    const { projectDependency, deleteValue, isReadOnly, projects } = props;
    const projectDependencyData = projects.get(projectDependency.projectId);
    const editorStore = useEditorStore();
    const applicationStore = useApplicationStore();
    const projectSelectorRef = useRef(null);
    const versionSelectorRef = useRef(null);
    const configState = editorStore.projectConfigurationEditorState;
    const dependencyEditorState = configState.projectDependencyEditorState;
    const versions = configState.versions.get(projectDependency.projectId) ?? [];
    const [fetchSelectedProjectVersionsStatus] = useState(ActionState.create());
    // project
    const selectedProject = configState.projects.get(projectDependency.projectId);
    const selectedProjectOption = selectedProject
        ? buildProjectOption(selectedProject)
        : null;
    const projectDisabled = !configState.associatedProjectsAndVersionsFetched ||
        configState.isReadOnly;
    const projectsOptions = Array.from(configState.projects.values())
        .map(buildProjectOption)
        .sort(compareLabelFn);
    const onProjectSelectionChange = async (val) => {
        if ((val !== null || selectedProjectOption !== null) &&
            (!val ||
                !selectedProjectOption ||
                val.value !== selectedProjectOption.value)) {
            projectDependency.setProjectId(val?.value.coordinates ?? '');
            projectDependency.setVersionId('');
            if (val) {
                try {
                    fetchSelectedProjectVersionsStatus.inProgress();
                    const _versions = await editorStore.depotServerClient.getVersions(guaranteeNonNullable(projectDependency.groupId), guaranteeNonNullable(projectDependency.artifactId), true);
                    configState.versions.set(val.value.coordinates, _versions);
                    if (_versions.length) {
                        projectDependency.setVersionId(guaranteeNonNullable(_versions[_versions.length - 1]));
                        flowResult(dependencyEditorState.fetchDependencyReport()).catch(applicationStore.alertUnhandledError);
                    }
                    else {
                        projectDependency.setVersionId('');
                    }
                    fetchSelectedProjectVersionsStatus.complete();
                }
                catch (error) {
                    assertErrorThrown(error);
                    editorStore.applicationStore.notificationService.notifyError(error);
                    fetchSelectedProjectVersionsStatus.reset();
                }
            }
        }
    };
    // version
    const version = projectDependency.versionId;
    const versionOptions = versions
        .toSorted((v1, v2) => compareSemVerVersions(v2, v1))
        .map((v) => {
        if (v === MASTER_SNAPSHOT_ALIAS) {
            return { value: v, label: SNAPSHOT_VERSION_ALIAS };
        }
        return { value: v, label: v };
    });
    const selectedVersionOption = versionOptions.find((v) => v.value === version) ?? null;
    const versionDisabled = Boolean(!versions.length || !projectDependency.projectId.length) ||
        !configState.associatedProjectsAndVersionsFetched ||
        isReadOnly;
    const onVersionSelectionChange = (val) => {
        if ((val !== null || selectedVersionOption !== null) &&
            (!val ||
                !selectedVersionOption ||
                val.value !== selectedVersionOption.value)) {
            try {
                projectDependency.setVersionId(val?.value ?? '');
                flowResult(dependencyEditorState.fetchDependencyReport()).catch(applicationStore.alertUnhandledError);
            }
            catch (error) {
                assertErrorThrown(error);
                applicationStore.logService.error(LogEvent.create(LEGEND_STUDIO_APP_EVENT.SDLC_MANAGER_FAILURE), error);
            }
        }
    };
    const viewProject = () => {
        applicationStore.navigationService.navigator.visitAddress(applicationStore.navigationService.navigator.generateAddress(generateViewProjectByGAVRoute(guaranteeNonNullable(projectDependency.groupId), guaranteeNonNullable(projectDependency.artifactId), projectDependency.versionId === MASTER_SNAPSHOT_ALIAS
            ? SNAPSHOT_VERSION_ALIAS
            : projectDependency.versionId)));
    };
    // NOTE: This assumes that the dependant project is in the same studio instance as the current project
    // In the future, the studio instance may be part of the project data
    const viewSDLCProject = () => {
        if (projectDependencyData) {
            applicationStore.navigationService.navigator.visitAddress(applicationStore.navigationService.navigator.generateAddress(projectDependency.versionId === MASTER_SNAPSHOT_ALIAS
                ? generateViewProjectRoute(projectDependencyData.projectId)
                : generateViewVersionRoute(projectDependencyData.projectId, projectDependency.versionId)));
        }
    };
    const projectSelectorPlaceholder = !projectDependency.projectId.length
        ? 'Choose project'
        : versionDisabled
            ? 'No project version found. Please create a new one.'
            : 'Select version';
    return (_jsxs("div", { className: "project-dependency-editor", children: [_jsx(CustomSelectorInput, { className: "project-dependency-editor__selector", inputRef: projectSelectorRef, disabled: projectDisabled, options: projectsOptions, isClearable: true, escapeClearsValue: true, onChange: (val) => {
                    onProjectSelectionChange(val).catch(applicationStore.alertUnhandledError);
                }, value: selectedProjectOption, isLoading: configState.fetchingProjectVersionsState.isInProgress, formatOptionLabel: formatOptionLabel, darkMode: !applicationStore.layoutService.TEMPORARY__isLightColorThemeEnabled }), _jsx(CustomSelectorInput, { className: "project-dependency-editor__selector", inputRef: versionSelectorRef, options: versionOptions, isClearable: true, escapeClearsValue: true, onChange: onVersionSelectionChange, value: selectedVersionOption, disabled: versionDisabled, placeholder: fetchSelectedProjectVersionsStatus.isInProgress
                    ? 'Fetching project versions'
                    : projectSelectorPlaceholder, isLoading: editorStore.projectConfigurationEditorState
                    .fetchingProjectVersionsState.isInProgress ||
                    fetchSelectedProjectVersionsStatus.isInProgress, darkMode: !applicationStore.layoutService.TEMPORARY__isLightColorThemeEnabled }), selectedProject && selectedVersionOption && (_jsx(ProjectDependencyInlineExclusionsSelector, { projectDependency: projectDependency, isReadOnly: isReadOnly })), _jsxs(ControlledDropdownMenu, { className: "project-dependency-editor__visit-project-btn__dropdown-trigger btn--medium", content: _jsxs(MenuContent, { children: [_jsx(MenuContentItem, { disabled: !selectedProject || !selectedVersionOption, onClick: viewProject, title: "View project", children: "Project" }), _jsx(MenuContentItem, { title: "View SDLC project", disabled: !selectedProject ||
                                !selectedVersionOption ||
                                !projectDependencyData, onClick: viewSDLCProject, children: "SDLC project" })] }), children: ["Go to... ", _jsx(CaretDownIcon, { title: "Show more options..." })] }), _jsx("button", { className: "project-dependency-editor__remove-btn btn--dark btn--caution", disabled: isReadOnly, onClick: deleteValue, tabIndex: -1, title: "Close", children: _jsx(TimesIcon, {}) })] }));
});
export const ProjectDependencyEditor = observer(() => {
    const editorStore = useEditorStore();
    const applicationStore = useApplicationStore();
    const configState = editorStore.tabManagerState.getCurrentEditorState(ProjectConfigurationEditorState);
    const dependencyEditorState = configState.projectDependencyEditorState;
    const currentProjectConfiguration = configState.currentProjectConfiguration;
    const deleteProjectDependency = (val) => () => {
        currentProjectConfiguration.deleteProjectDependency(val);
        flowResult(dependencyEditorState.fetchDependencyReport()).catch(applicationStore.alertUnhandledError);
    };
    const isReadOnly = editorStore.isInViewerMode;
    const isLoading = configState.updatingConfigurationState.isInProgress ||
        configState.fetchingProjectVersionsState.isInProgress ||
        dependencyEditorState.fetchingDependencyInfoState.isInProgress ||
        dependencyEditorState.validatingDependenciesState.isInProgress;
    return (_jsxs(PanelContentLists, { children: [_jsx(PanelLoadingIndicator, { isLoading: isLoading }), isLoading && (_jsx("div", { className: "project-dependency-editor__progress-msg", children: configState.updatingConfigurationState.isInProgress
                    ? `Updating configuration...`
                    : configState.fetchingProjectVersionsState.isInProgress
                        ? `Fetching dependency versions`
                        : dependencyEditorState.validatingDependenciesState.isInProgress
                            ? 'Validating dependencies and compiling...'
                            : dependencyEditorState.fetchingDependencyInfoState.isInProgress
                                ? 'Updating project dependency tree and potential conflicts'
                                : '' })), _jsx(ProjectDependencyActions, { dependencyEditorState: dependencyEditorState }), currentProjectConfiguration.projectDependencies.map((projectDependency) => (_jsxs("div", { children: [_jsx(ProjectVersionDependencyEditor, { projectDependency: projectDependency, deleteValue: deleteProjectDependency(projectDependency), isReadOnly: isReadOnly, projects: configState.projects }), _jsx(ProjectDependencyExclusionsList, { projectDependency: projectDependency, isReadOnly: isReadOnly }, `${projectDependency.projectId}-exclusions`)] }, projectDependency._UUID))), dependencyEditorState.reportTab && (_jsx(ProjectDependencyReportModal, { tab: dependencyEditorState.reportTab, dependencyEditorState: dependencyEditorState }))] }));
});
//# sourceMappingURL=ProjectDependencyEditor.js.map