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 { clsx, PanelLoadingIndicator, PlayIcon, TreeView, ChevronDownIcon, ChevronRightIcon, RefreshIcon, TimesCircleIcon, CheckCircleIcon, ContextMenu, MenuContent, MenuContentItem, Dialog, WarningIcon, CircleNotchIcon, EmptyCircleIcon, PanelContent, Modal, ModalBody, ModalFooter, ModalHeader, ModalFooterButton, Panel, PanelHeader, OffIcon, ControlledDropdownMenu, MenuContentItemIcon, CheckIcon, MenuContentItemLabel, MoreVerticalIcon, ResizablePanelGroup, ResizablePanel, ResizablePanelSplitter, } from '@finos/legend-art';
import { AssertFail, AssertionStatus, EqualToJsonAssertFail, PackageableElement, TestError, } from '@finos/legend-graph';
import { isNonNullable, prettyCONSTName, } from '@finos/legend-shared';
import { observer } from 'mobx-react-lite';
import React, { forwardRef, useEffect, useState } from 'react';
import { TEST_RUNNER_TABS } from '../../../../stores/editor/EditorConfig.js';
import { TESTABLE_RESULT, AtomicTestTreeNodeData, AssertionTestTreeNodeData, TestableTreeNodeData, TestTreeNodeData, getNodeTestableResult, getAtomicTest_TestResult, getAssertionStatus, } from '../../../../stores/editor/sidebar-state/testable/GlobalTestRunnerState.js';
import { LEGEND_STUDIO_TEST_ID } from '../../../../__lib__/LegendStudioTesting.js';
import { getElementTypeIcon } from '../../../ElementIconUtils.js';
import { UnsupportedEditorPanel } from '../../editor-group/UnsupportedElementEditor.js';
import { useEditorStore } from '../../EditorStoreProvider.js';
import { CODE_EDITOR_LANGUAGE } from '@finos/legend-code-editor';
import { CodeEditor, CodeDiffView } from '@finos/legend-lego/code-editor';
export const getTestableResultIcon = (testableResult) => {
    switch (testableResult) {
        case TESTABLE_RESULT.PASSED:
            return (_jsx("div", { title: "Test passed", className: "global-test-runner__item__link__content__status__indicator global-test-runner__item__link__content__status__indicator--succeeded", children: _jsx(CheckCircleIcon, {}) }));
        case TESTABLE_RESULT.IN_PROGRESS:
            return (_jsx("div", { title: "Test is running", className: "global-test-runner__item__link__content__status__indicator workflow-manager__item__link__content__status__indicator--in-progress", children: _jsx(CircleNotchIcon, {}) }));
        case TESTABLE_RESULT.FAILED:
            return (_jsx("div", { title: "Test Failed", className: "global-test-runner__item__link__content__status__indicator global-test-runner__item__link__content__status__indicator--failed", children: _jsx(TimesCircleIcon, {}) }));
        case TESTABLE_RESULT.ERROR:
            return (_jsx("div", { title: "Test has an error", className: "global-test-runner__item__link__content__status__indicator global-test-runner__item__link__content__status__indicator--failed", children: _jsx(WarningIcon, {}) }));
        case TESTABLE_RESULT.NO_TESTS:
            return (_jsx("div", { title: "No Tests to Run", className: "global-test-runner__item__link__content__status__indicator global-test-runner__item__link__content__status__indicator--unknown", children: _jsx(OffIcon, {}) }));
        default:
            return (_jsx("div", { title: "Test did not run", className: "global-test-runner__item__link__content__status__indicator global-test-runner__item__link__content__status__indicator--unknown", children: _jsx(EmptyCircleIcon, {}) }));
    }
};
const getOptionalError = (node, testableState) => {
    if (node instanceof AtomicTestTreeNodeData) {
        const result = getAtomicTest_TestResult(node.atomicTest, testableState.results);
        if (result instanceof TestError) {
            return result;
        }
    }
    else if (node instanceof AssertionTestTreeNodeData) {
        const status = getAssertionStatus(node.assertion, testableState.results);
        if (status instanceof AssertFail) {
            return status;
        }
        else if (status && !(status instanceof AssertionStatus)) {
            const errorState = new Map();
            Array.from(status.entries()).forEach(([key, assertionStatus]) => {
                if (assertionStatus instanceof AssertFail) {
                    errorState.set(key, assertionStatus);
                }
            });
            return errorState;
        }
    }
    return undefined;
};
const TestFailViewer = observer((props) => {
    const { globalTestRunnerState, failure } = props;
    const applicationStore = globalTestRunnerState.editorStore.applicationStore;
    const id = failure instanceof TestError
        ? failure.atomicTest.id
        : failure.assertion.id;
    const closeLogViewer = () => globalTestRunnerState.setFailureViewing(undefined);
    return (_jsx(Dialog, { open: Boolean(failure), onClose: closeLogViewer, 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(PanelLoadingIndicator, { isLoading: globalTestRunnerState.isDispatchingOwnProjectAction }), _jsx(ModalHeader, { title: id }), _jsxs(ModalBody, { children: [failure instanceof TestError && (_jsx(CodeEditor, { inputValue: failure.error, isReadOnly: true, language: CODE_EDITOR_LANGUAGE.TEXT })), failure instanceof EqualToJsonAssertFail && (_jsx(CodeDiffView, { language: CODE_EDITOR_LANGUAGE.JSON, from: failure.expected, to: failure.actual })), failure instanceof AssertFail &&
                            !(failure instanceof EqualToJsonAssertFail) && (_jsx(CodeEditor, { inputValue: failure.message ?? '', isReadOnly: true, language: CODE_EDITOR_LANGUAGE.TEXT }))] }), _jsx(ModalFooter, { children: _jsx(ModalFooterButton, { text: "Close", onClick: closeLogViewer, type: "secondary" }) })] }) }));
});
const TestableExplorerContextMenu = observer(forwardRef(function TestableExplorerContextMenu(props, ref) {
    const { node, error, globalTestRunnerState, testableState } = props;
    const runTest = () => {
        testableState.run(node);
    };
    const viewError = (err) => globalTestRunnerState.setFailureViewing(err);
    const visitTestable = () => {
        globalTestRunnerState.visitTestable(testableState.testableMetadata.testable);
    };
    return (_jsxs(MenuContent, { "data-testid": LEGEND_STUDIO_TEST_ID.EXPLORER_CONTEXT_MENU, children: [_jsx(MenuContentItem, { disabled: globalTestRunnerState.isDispatchingOwnProjectAction, onClick: runTest, children: "Run" }), _jsx(MenuContentItem, { onClick: visitTestable, children: "Open Testable" }), error &&
                (error instanceof TestError || error instanceof AssertFail) && (_jsx(MenuContentItem, { onClick: () => viewError(error), children: error instanceof TestError ? 'View Error' : 'View assert fail' })), error &&
                !(error instanceof TestError || error instanceof AssertFail) &&
                Array.from(error.entries()).map(([key, testError]) => (_jsx(MenuContentItem, { onClick: () => viewError(testError), children: testError instanceof TestError
                        ? `View Error for ${key}`
                        : `View assert fail for ${key}` }, key)))] }));
}));
const TestableTreeNodeContainer = (props) => {
    const { node, level, stepPaddingInRem, onNodeSelect } = props;
    const { treeData, testableState, globalTestRunnerState, isDependency } = props.innerProps;
    const editorStore = useEditorStore();
    const results = testableState.results;
    const expandIcon = node instanceof AssertionTestTreeNodeData ? (_jsx("div", {})) : node.isOpen ? (_jsx(ChevronDownIcon, {})) : (_jsx(ChevronRightIcon, {}));
    const nodeIcon = node instanceof TestableTreeNodeData
        ? node.testableMetadata.testable instanceof PackageableElement
            ? getElementTypeIcon(editorStore.graphState.getPackageableElementType(node.testableMetadata.testable), editorStore)
            : null
        : null;
    const resultIcon = getTestableResultIcon(getNodeTestableResult(node, isDependency
        ? testableState.globalTestRunnerState.isRunningDependencyTests
            .isInProgress
        : testableState.globalTestRunnerState.isRunningTests.isInProgress, results));
    const optionalError = getOptionalError(node, testableState);
    const selectNode = (event) => onNodeSelect?.(node);
    const openTestable = (event) => {
        event.stopPropagation();
        event.preventDefault();
        globalTestRunnerState.visitTestable(testableState.testableMetadata.testable);
    };
    const dblClick = () => {
        if (optionalError instanceof TestError ||
            optionalError instanceof AssertFail) {
            globalTestRunnerState.setFailureViewing(optionalError);
        }
    };
    return (_jsx(ContextMenu, { content: _jsx(TestableExplorerContextMenu, { globalTestRunnerState: globalTestRunnerState, testableState: testableState, treeData: treeData, node: node, error: optionalError }), menuProps: { elevation: 7 }, children: _jsxs("div", { className: clsx('tree-view__node__container global-test-runner__explorer__testable-tree__node__container'), onClick: selectNode, style: {
                paddingLeft: `${level * (stepPaddingInRem ?? 1)}rem`,
                display: 'flex',
            }, children: [_jsxs("div", { className: "tree-view__node__icon global-test-runner__explorer__testable-tree__node__icon", children: [_jsx("div", { className: "global-test-runner__explorer__testable-tree__node__icon__expand", children: expandIcon }), _jsx("div", { className: "global-test-runner__explorer__testable-tree__node__icon__type", children: resultIcon }), nodeIcon && (_jsx("div", { className: "global-test-runner__explorer__testable-tree__node__result__icon__type", children: nodeIcon }))] }), node instanceof TestableTreeNodeData && (_jsx("div", { onClick: openTestable, className: "global-test-runner__item__link__content", children: _jsx("span", { className: "global-test-runner__item__link__content__id", children: node.testableMetadata.name }) })), node instanceof TestTreeNodeData && (_jsx("div", { onDoubleClick: dblClick, className: "global-test-runner__item__link__content", children: _jsx("span", { className: "global-test-runner__item__link__content__id", children: node.label }) })), node instanceof AssertionTestTreeNodeData && (_jsx("div", { onDoubleClick: dblClick, className: "global-test-runner__item__link__content", children: _jsx("span", { className: "global-test-runner__item__link__content__id", children: node.label }) }))] }) }));
};
export const TestablePanelRunner = observer((props) => {
    const { globalTestRunnerState } = props;
    const isDispatchingAction = globalTestRunnerState.isDispatchingOwnProjectAction;
    const showDependencyPanel = globalTestRunnerState.showDependencyPanel;
    const toggleShowDependencyTests = () => {
        globalTestRunnerState.setShowDependencyPanel(!showDependencyPanel);
    };
    const runAllTests = () => globalTestRunnerState.runAllTests();
    const reset = () => globalTestRunnerState.initOwnTestables(true);
    //
    const runDependencyTests = () => globalTestRunnerState.runDependenciesTests();
    const isDispatchingDependencyAction = globalTestRunnerState.isDispatchingDependencyAction;
    const renderTestables = () => (_jsxs(_Fragment, { children: [_jsxs(PanelHeader, { children: [_jsx("div", { className: "panel__header__title", children: _jsx("div", { className: "panel__header__title__content", children: "TESTABLES" }) }), _jsxs("div", { className: "panel__header__actions side-bar__header__actions", children: [_jsx("button", { className: clsx('panel__header__action side-bar__header__action global-test-runner__refresh-btn', {
                                    'global-test-runner__refresh-btn--loading': isDispatchingAction,
                                }), disabled: isDispatchingAction, onClick: reset, tabIndex: -1, title: "Reset", children: _jsx(RefreshIcon, {}) }), _jsx("button", { className: "panel__header__action side-bar__header__action global-test-runner__refresh-btn", disabled: isDispatchingAction, onClick: runAllTests, tabIndex: -1, title: "Run All Tests", children: _jsx(PlayIcon, {}) }), _jsx("div", { className: "global-test-runner__count", "data-testid": LEGEND_STUDIO_TEST_ID.SIDEBAR_PANEL_HEADER__CHANGES_COUNT, children: globalTestRunnerState.testableStates?.length ?? '0' }), _jsx(ControlledDropdownMenu, { className: "panel__header__action", title: "Show Options Menu...", content: _jsx(MenuContent, { children: _jsxs(MenuContentItem, { onClick: toggleShowDependencyTests, children: [_jsx(MenuContentItemIcon, { children: showDependencyPanel ? _jsx(CheckIcon, {}) : null }), _jsx(MenuContentItemLabel, { children: "Show from dependency tests" })] }) }), menuProps: {
                                    anchorOrigin: {
                                        vertical: 'bottom',
                                        horizontal: 'left',
                                    },
                                    transformOrigin: {
                                        vertical: 'top',
                                        horizontal: 'left',
                                    },
                                    elevation: 7,
                                }, children: _jsx(MoreVerticalIcon, { className: "query-builder__icon__more-options" }) })] })] }), (globalTestRunnerState.testableStates ?? []).map((testableState) => {
                const onNodeSelect = (node) => {
                    testableState.onTreeNodeSelect(node, testableState.treeData);
                };
                const getChildNodes = (node) => {
                    if (node.childrenIds) {
                        return node.childrenIds
                            .map((id) => testableState.treeData.nodes.get(id))
                            .filter(isNonNullable);
                    }
                    return [];
                };
                return (_jsx(TreeView, { components: {
                        TreeNodeContainer: TestableTreeNodeContainer,
                    }, treeData: testableState.treeData, onNodeSelect: onNodeSelect, getChildNodes: getChildNodes, innerProps: {
                        globalTestRunnerState,
                        testableState,
                        treeData: testableState.treeData,
                    } }, testableState.uuid));
            })] }));
    const renderDependenciesTestables = () => (_jsxs(_Fragment, { children: [_jsxs(PanelHeader, { children: [_jsx("div", { className: "panel__header__title", children: _jsx("div", { className: "panel__header__title__content", children: "DEPENDENCY TESTABLES" }) }), _jsxs("div", { className: "panel__header__actions side-bar__header__actions", children: [_jsx("button", { className: "panel__header__action side-bar__header__action global-test-runner__refresh-btn", disabled: isDispatchingDependencyAction, onClick: runDependencyTests, tabIndex: -1, title: "Run Dependency Tests", children: _jsx(PlayIcon, {}) }), _jsx("div", { className: "global-test-runner__count", "data-testid": LEGEND_STUDIO_TEST_ID.SIDEBAR_PANEL_HEADER__CHANGES_COUNT, children: globalTestRunnerState.dependencyTestableStates?.length ?? '0' })] })] }), (globalTestRunnerState.dependencyTestableStates ?? []).map((testableState) => {
                const onNodeSelect = (node) => {
                    testableState.onTreeNodeSelect(node, testableState.treeData);
                };
                const getChildNodes = (node) => {
                    if (node.childrenIds) {
                        return node.childrenIds
                            .map((id) => testableState.treeData.nodes.get(id))
                            .filter(isNonNullable);
                    }
                    return [];
                };
                return (_jsx(TreeView, { components: {
                        TreeNodeContainer: TestableTreeNodeContainer,
                    }, treeData: testableState.treeData, onNodeSelect: onNodeSelect, getChildNodes: getChildNodes, innerProps: {
                        globalTestRunnerState,
                        testableState,
                        treeData: testableState.treeData,
                        isDependency: true,
                    } }, testableState.uuid));
            })] }));
    return (_jsxs(Panel, { className: "side-bar__panel", children: [!showDependencyPanel && (_jsx(PanelContent, { children: renderTestables() })), showDependencyPanel && (_jsxs(ResizablePanelGroup, { children: [_jsx(ResizablePanel, { children: _jsx(PanelContent, { children: renderTestables() }) }), _jsx(ResizablePanelSplitter, {}), _jsx(ResizablePanel, { children: _jsx(PanelContent, { children: renderDependenciesTestables() }) })] })), globalTestRunnerState.failureViewing && (_jsx(TestFailViewer, { globalTestRunnerState: globalTestRunnerState, failure: globalTestRunnerState.failureViewing }))] }));
});
// TODO:
// - Handle Multi Execution Test Results
export const GlobalTestRunner = observer((props) => {
    const editorStore = useEditorStore();
    const globalTestRunnerState = props.globalTestRunnerState;
    const isDispatchingAction = globalTestRunnerState.isDispatchingOwnProjectAction;
    const [selectedTab, setSelectedTab] = useState(TEST_RUNNER_TABS.TEST_RUNNER.valueOf());
    const extractTestRunnerTabConfigurations = editorStore.pluginManager
        .getApplicationPlugins()
        .flatMap((plugin) => plugin.getExtraTestRunnerViewConfigurations?.() ?? [])
        .filter((configuration) => Boolean(configuration.renderer(editorStore)));
    const testRunnerTabs = Object.values(TEST_RUNNER_TABS)
        .map((e) => ({
        value: e,
        label: prettyCONSTName(e),
    }))
        .concat(extractTestRunnerTabConfigurations.map((config) => ({
        value: config.key,
        label: config.title,
    })));
    const changeTab = (tab) => {
        setSelectedTab(tab);
    };
    useEffect(() => {
        editorStore.globalTestRunnerState.initOwnTestables();
    }, [editorStore.globalTestRunnerState]);
    const renderSelectedTab = () => {
        if (selectedTab === TEST_RUNNER_TABS.TEST_RUNNER) {
            return (_jsxs(_Fragment, { children: [_jsx(PanelLoadingIndicator, { isLoading: isDispatchingAction }), _jsx(TestablePanelRunner, { globalTestRunnerState: globalTestRunnerState })] }));
        }
        for (const testRunnerTabConfiguration of extractTestRunnerTabConfigurations) {
            if (testRunnerTabConfiguration.key === selectedTab) {
                return testRunnerTabConfiguration.renderer(editorStore);
            }
        }
        return (_jsx(UnsupportedEditorPanel, { text: "Can't display this tab", isReadOnly: true }));
    };
    return (_jsxs("div", { "data-testid": LEGEND_STUDIO_TEST_ID.TEST_RUNNER, className: "panel global-test-runner", children: [_jsx(PanelHeader, { className: "panel__header side-bar__header", children: _jsx("div", { className: "panel__header__title global-test-runner__header__title", children: _jsx("div", { className: "panel__header__title__content side-bar__header__title__content", children: "GLOBAL TEST RUNNER" }) }) }), _jsx("div", { className: "panel__header__tabs panel__header__test__runner__tabs", children: testRunnerTabs.map((tab) => (_jsx("div", { onClick: () => changeTab(tab.value), className: clsx('panel__header__tab', {
                        ['panel__header__tab--active']: tab.value === selectedTab,
                    }), children: tab.label }, tab.value))) }), _jsx(PanelContent, { className: "side-bar__content", children: renderSelectedTab() })] }));
});
//# sourceMappingURL=GlobalTestRunner.js.map