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 { observer } from 'mobx-react-lite';
import { flowResult } from 'mobx';
import { BlankPanelPlaceholder, BlankPanelContent, clsx, ContextMenu, CustomSelectorInput, Dialog, ErrorWarnIcon, MenuContent, MenuContentItem, Modal, ModalBody, ModalFooter, ModalFooterButton, ModalHeader, ModalTitle, Panel, PanelFormTextField, PanelHeader, PanelHeaderActionItem, PanelHeaderActions, PanelLoadingIndicator, PlayIcon, PlusIcon, ResizablePanel, ResizablePanelGroup, ResizablePanelSplitter, ResizablePanelSplitterLine, RunAllIcon, TimesIcon, } from '@finos/legend-art';
import {} from '../../../../../stores/editor/editor-state/element-editor-state/dataProduct/testable/DataProductTestableState.js';
import { forwardRef, useEffect, useRef, useState } from 'react';
import { getTestableResultIcon } from '../../../side-bar/testable/GlobalTestRunner.js';
import { TESTABLE_RESULT, getTestableResultFromTestResult, } from '../../../../../stores/editor/sidebar-state/testable/GlobalTestRunnerState.js';
import { RelationElementsDataEditor } from '../../data-editor/RelationElementsDataEditor.js';
import { validateTestableId } from '../../../../../stores/editor/utils/TestableUtils.js';
import { useEditorStore } from '../../../EditorStoreProvider.js';
import { guaranteeNonNullable } from '@finos/legend-shared';
import { RenameModal, TestAssertionEditor, } from '../../testable/TestableSharedComponents.js';
import { testSuite_setId } from '../../../../../stores/graph-modifier/Testable_GraphModifierHelper.js';
const CreateSuiteModal = observer((props) => {
    const { testableState, onClose } = props;
    const editorStore = useEditorStore();
    const applicationStore = editorStore.applicationStore;
    const inputRef = useRef(null);
    const [testName, setTestName] = useState(undefined);
    const [selectedAccessPointId, setSelectedAccessPointId] = useState(undefined);
    // Auto-generate suite name
    const existingIds = testableState.dataProduct.tests.map((s) => s.id);
    const generateSuiteName = () => {
        let idx = 1;
        while (existingIds.includes(`suite_${idx}`)) {
            idx++;
        }
        return `suite_${idx}`;
    };
    const testError = validateTestableId(testName, undefined);
    // Access points on the current DataProduct (for the test target)
    const accessPointOptions = testableState.ownAccessPoints.map((ap) => ({
        value: ap.id,
        label: ap.id,
    }));
    const selectedApOption = accessPointOptions.find((o) => o.value === selectedAccessPointId) ?? null;
    const isValid = testName && !testError && selectedAccessPointId;
    const create = () => {
        if (!testName || !selectedAccessPointId) {
            return;
        }
        flowResult(testableState.createSuite(generateSuiteName(), testName, selectedAccessPointId))
            .then((err) => {
            if (err) {
                applicationStore.notificationService.notifyError(err);
            }
            else {
                onClose();
            }
        })
            .catch(applicationStore.alertUnhandledError);
    };
    return (_jsx(Dialog, { open: true, onClose: onClose, classes: { container: 'search-modal__container' }, slotProps: {
            transition: { onEnter: () => inputRef.current?.focus() },
            paper: { classes: { root: 'search-modal__inner-container' } },
        }, children: _jsxs(Modal, { darkMode: !applicationStore.layoutService.TEMPORARY__isLightColorThemeEnabled, children: [_jsx(ModalHeader, { children: _jsx(ModalTitle, { title: "Create Test Suite" }) }), _jsxs(ModalBody, { children: [_jsx(PanelFormTextField, { ref: inputRef, name: "Test Name", prompt: "Name for the first test in this suite", placeholder: "e.g. test_1", value: testName, update: (value) => setTestName(value ?? ''), errorMessage: testError }), _jsxs("div", { className: "panel__content__form__section", children: [_jsx("div", { className: "panel__content__form__section__header__label", children: "Access Point to Test" }), _jsx("div", { className: "panel__content__form__section__header__prompt", children: "Select the access point of the current DataProduct that the first test in this suite will verify" }), _jsx(CustomSelectorInput, { options: accessPointOptions, onChange: (opt) => setSelectedAccessPointId(opt?.value), value: selectedApOption, placeholder: "Select access point...", isClearable: false, darkMode: true, disabled: accessPointOptions.length === 0 })] })] }), _jsxs(ModalFooter, { children: [_jsx(ModalFooterButton, { disabled: !isValid, title: !isValid ? 'Fill in all required fields' : 'Create Suite', onClick: create, text: "Create" }), _jsx(ModalFooterButton, { onClick: onClose, text: "Close", type: "secondary" })] })] }) }));
});
// ──────────────────────────────────────────────────────────────────────────────
// Create Test Modal — asks for test name + access point to test
// ──────────────────────────────────────────────────────────────────────────────
const CreateTestModal = observer((props) => {
    const { suiteState, onClose } = props;
    const editorStore = suiteState.editorStore;
    const applicationStore = editorStore.applicationStore;
    const inputRef = useRef(null);
    const existingIds = suiteState.suite.tests.map((t) => t.id);
    const [testName, setTestName] = useState(undefined);
    const [selectedAccessPointId, setSelectedAccessPointId] = useState(undefined);
    const testNameError = validateTestableId(testName, existingIds);
    const accessPointOptions = suiteState.testableState.ownAccessPoints.map((ap) => ({
        value: ap.id,
        label: ap.id,
    }));
    const selectedApOption = accessPointOptions.find((o) => o.value === selectedAccessPointId) ?? null;
    const isValid = testName && !testNameError && selectedAccessPointId;
    const create = () => {
        if (!testName || !selectedAccessPointId) {
            return;
        }
        flowResult(suiteState.addNewTest(testName, selectedAccessPointId))
            .then((err) => {
            if (err) {
                applicationStore.notificationService.notifyError(err);
            }
            else {
                onClose();
            }
        })
            .catch(applicationStore.alertUnhandledError);
    };
    return (_jsx(Dialog, { open: true, onClose: onClose, classes: { container: 'search-modal__container' }, slotProps: {
            transition: { onEnter: () => inputRef.current?.focus() },
            paper: { classes: { root: 'search-modal__inner-container' } },
        }, children: _jsxs(Modal, { darkMode: !applicationStore.layoutService.TEMPORARY__isLightColorThemeEnabled, children: [_jsx(ModalHeader, { children: _jsx(ModalTitle, { title: `Add Test to "${suiteState.suite.id}"` }) }), _jsxs(ModalBody, { children: [_jsx(PanelFormTextField, { ref: inputRef, name: "Test Name", prompt: "Unique identifier for the test", placeholder: "e.g. test_1", value: testName, update: (value) => setTestName(value ?? ''), errorMessage: testNameError }), _jsxs("div", { className: "panel__content__form__section", children: [_jsx("div", { className: "panel__content__form__section__header__label", children: "Access Point to Test" }), _jsx("div", { className: "panel__content__form__section__header__prompt", children: "Select which access point of the DataProduct this test will verify" }), _jsx(CustomSelectorInput, { options: accessPointOptions, onChange: (opt) => setSelectedAccessPointId(opt?.value), value: selectedApOption, placeholder: "Select access point...", isClearable: false, darkMode: true, disabled: accessPointOptions.length === 0 })] })] }), _jsxs(ModalFooter, { children: [_jsx(ModalFooterButton, { disabled: !isValid, title: !isValid ? 'Fill in all required fields' : 'Create Test', onClick: create, text: "Create" }), _jsx(ModalFooterButton, { onClick: onClose, text: "Close", type: "secondary" })] })] }) }));
});
// ──────────────────────────────────────────────────────────────────────────────
// Element Test Data Item (sidebar list row in test data panel)
// ──────────────────────────────────────────────────────────────────────────────
const ElementTestDataItem = observer((props) => {
    const { elementState, testDataState, isReadOnly } = props;
    const isActive = testDataState.selectedElementTestDataState === elementState;
    const select = () => testDataState.setSelectedElementTestDataState(elementState);
    return (_jsx("div", { className: clsx('testable-test-explorer__item', {
            'testable-test-explorer__item--active': isActive,
        }), children: _jsxs("div", { className: "testable-test-explorer__item__label", onClick: select, tabIndex: -1, children: [_jsx("div", { className: "testable-test-explorer__item__label__text", children: _jsx("span", { title: elementState.element.path, children: elementState.element.name }) }), !isReadOnly && (_jsx("div", { className: "mapping-test-explorer__item__actions", children: _jsx("button", { className: "mapping-test-explorer__item__action", onClick: (e) => {
                            e.stopPropagation();
                            testDataState.deleteElement(elementState);
                        }, tabIndex: -1, title: "Delete", children: _jsx(TimesIcon, {}) }) }))] }) }));
});
const ElementTestDataEditor = observer((props) => {
    const { elementState, isReadOnly } = props;
    const dataState = elementState.relationElementsDataState;
    if (!dataState) {
        return (_jsx(BlankPanelContent, { children: "No relation data for this element" }));
    }
    return (_jsx(RelationElementsDataEditor, { dataState: dataState, isReadOnly: isReadOnly, hideColumnDefinitions: true }));
});
// ──────────────────────────────────────────────────────────────────────────────
// Test Data Editor (top panel) — always-visible elements sidebar + per-element editor
// ──────────────────────────────────────────────────────────────────────────────
const AddElementModal = observer((props) => {
    const { testDataState } = props;
    const applicationStore = testDataState.editorStore.applicationStore;
    const options = testDataState.availableElementsToAdd.map((e) => ({
        value: e.path,
        label: e.path,
    }));
    const [selectedPath, setSelectedPath] = useState(options[0]?.value);
    const close = () => testDataState.setShowAddElementModal(false);
    const add = () => {
        if (selectedPath) {
            testDataState.addElement(selectedPath);
            close();
        }
    };
    const onChange = (val) => {
        setSelectedPath(val?.value);
    };
    return (_jsx(Dialog, { open: testDataState.showAddElementModal, onClose: close, classes: { container: 'search-modal__container' }, slotProps: {
            paper: {
                classes: { root: 'search-modal__inner-container' },
            },
        }, children: _jsxs(Modal, { darkMode: !applicationStore.layoutService.TEMPORARY__isLightColorThemeEnabled, children: [_jsx(ModalHeader, { children: _jsx(ModalTitle, { title: "Add Element" }) }), _jsx(ModalBody, { children: _jsx(CustomSelectorInput, { className: "panel__content__form__section__dropdown", options: options, onChange: onChange, value: selectedPath
                            ? { value: selectedPath, label: selectedPath }
                            : null, placeholder: "Select element...", darkMode: !applicationStore.layoutService
                            .TEMPORARY__isLightColorThemeEnabled }) }), _jsxs(ModalFooter, { children: [_jsx(ModalFooterButton, { disabled: !selectedPath, onClick: add, text: "Add" }), _jsx(ModalFooterButton, { onClick: close, text: "Close", type: "secondary" })] })] }) }));
});
const DataProductTestDataEditor = observer((props) => {
    const { testDataState, isReadOnly } = props;
    const addElement = () => {
        if (testDataState.availableElementsToAdd.length === 0) {
            testDataState.editorStore.applicationStore.notificationService.notifyWarning('No elements available to add');
            return;
        }
        testDataState.setShowAddElementModal(true);
    };
    const hasTestData = testDataState.elementTestDataStates.length > 0;
    return (_jsxs("div", { className: clsx('service-test-data-editor panel', {
            'service-test-data-editor--no-data': !hasTestData,
        }), children: [_jsx("div", { className: "service-test-data-editor__data", children: _jsxs(ResizablePanelGroup, { orientation: "vertical", children: [_jsxs(ResizablePanel, { minSize: 100, size: 180, children: [_jsxs("div", { className: "binding-editor__header", children: [_jsx("div", { className: "binding-editor__header__title", children: _jsx("div", { className: "panel__header__title__content", children: "Test Data" }) }), !isReadOnly && (_jsx("div", { className: "panel__header__actions", children: _jsx("button", { className: "panel__header__action", tabIndex: -1, onClick: addElement, title: "Add Element", children: _jsx(PlusIcon, {}) }) }))] }), !hasTestData ? (_jsxs("div", { className: "service-test-data-editor__warning", children: [_jsx(ErrorWarnIcon, {}), _jsx("span", { children: "Add an element to configure test data" })] })) : (_jsx("div", { children: testDataState.elementTestDataStates.map((elementState) => (_jsx(ElementTestDataItem, { elementState: elementState, testDataState: testDataState, isReadOnly: isReadOnly }, elementState.element.path))) }))] }), _jsx(ResizablePanelSplitter, { children: _jsx(ResizablePanelSplitterLine, { color: "var(--color-dark-grey-200)" }) }), _jsx(ResizablePanel, { minSize: 200, children: testDataState.selectedElementTestDataState ? (_jsx(ElementTestDataEditor, { elementState: testDataState.selectedElementTestDataState, isReadOnly: isReadOnly })) : (_jsx(BlankPanelContent, { children: "Select an element to configure its test data" })) })] }) }), testDataState.showAddElementModal && (_jsx(AddElementModal, { testDataState: testDataState }))] }));
});
// ──────────────────────────────────────────────────────────────────────────────
// Test Item (inside tests panel)
// ──────────────────────────────────────────────────────────────────────────────
const TestItem = observer((props) => {
    const { testState, suiteState, isReadOnly } = props;
    const isActive = suiteState.selectTestState === testState;
    const isRunning = testState.runningTestAction.isInProgress;
    const _testableResult = getTestableResultFromTestResult(testState.testResultState.result);
    const testResult = isRunning
        ? TESTABLE_RESULT.IN_PROGRESS
        : _testableResult;
    const select = () => suiteState.changeTest(testState.test);
    const runTest = () => {
        flowResult(testState.runTest()).catch(testState.editorStore.applicationStore.alertUnhandledError);
    };
    const deleteTest = () => {
        if (!isReadOnly) {
            suiteState.deleteTest(testState.test);
        }
    };
    return (_jsxs("div", { className: clsx('testable-test-explorer__item', {
            'testable-test-explorer__item--active': isActive,
        }), children: [_jsxs("div", { className: "testable-test-explorer__item__label", onClick: select, tabIndex: -1, children: [_jsx("div", { className: "testable-test-explorer__item__label__icon", children: getTestableResultIcon(testResult) }), _jsx("div", { className: "testable-test-explorer__item__label__text", children: testState.test.id })] }), _jsxs("div", { className: "mapping-test-explorer__item__actions", children: [_jsx("button", { className: "mapping-test-explorer__item__action mapping-test-explorer__run-test-btn", onClick: runTest, disabled: isRunning, tabIndex: -1, title: `Run test ${testState.test.id}`, children: _jsx(PlayIcon, {}) }), !isReadOnly && (_jsx("button", { className: "mapping-test-explorer__item__action mapping-test-explorer__run-test-btn", onClick: deleteTest, tabIndex: -1, title: `Delete test ${testState.test.id}`, children: _jsx(TimesIcon, {}) }))] })] }));
});
// ──────────────────────────────────────────────────────────────────────────────
// Test Editor — shows SETUP (input data) and ASSERTION (expected + result) tabs
// ──────────────────────────────────────────────────────────────────────────────
const DataProductTestEditor = observer((props) => {
    const { testState } = props;
    const selectedAssertion = testState.selectedAsertionState;
    return (_jsxs("div", { className: "function-test-editor panel", children: [_jsx("div", { className: "panel__header", children: _jsx("div", { className: "panel__header service-test-editor__header--with-tabs", children: _jsx("div", { className: "panel__header__title__content", children: "Assertion" }) }) }), _jsxs("div", { className: "panel", children: [selectedAssertion && (_jsx(TestAssertionEditor, { testAssertionState: selectedAssertion })), !selectedAssertion && (_jsx(BlankPanelPlaceholder, { text: "No assertion", tooltipText: "No assertion configured for this test" }))] })] }));
});
// ──────────────────────────────────────────────────────────────────────────────
// Tests Editor (bottom panel) — tests list + test detail
// ──────────────────────────────────────────────────────────────────────────────
const DataProductTestsEditor = observer((props) => {
    const { suiteState, testableState, isReadOnly } = props;
    const selectedTest = suiteState.selectTestState;
    return (_jsx("div", { className: "panel service-test-editor", children: _jsx("div", { className: "service-test-editor__content", children: _jsxs(ResizablePanelGroup, { orientation: "vertical", children: [_jsxs(ResizablePanel, { minSize: 100, size: 200, children: [_jsxs("div", { className: "binding-editor__header", children: [_jsx("div", { className: "binding-editor__header__title", children: _jsx("div", { className: "panel__header__title__content", children: "Tests" }) }), _jsxs("div", { className: "panel__header__actions", children: [_jsx("button", { className: "panel__header__action testable-test-explorer__play__all__icon", tabIndex: -1, onClick: () => {
                                                    flowResult(suiteState.runSuite()).catch(testableState.editorStore.applicationStore
                                                        .alertUnhandledError);
                                                }, disabled: suiteState.runningSuiteState.isInProgress ||
                                                    suiteState.suite.tests.length === 0, title: "Run all tests in this suite", children: _jsx(RunAllIcon, {}) }), !isReadOnly && (_jsx("button", { className: "panel__header__action", tabIndex: -1, onClick: () => testableState.setShowCreateTestModal(true), title: "Add test to this suite", children: _jsx(PlusIcon, {}) }))] })] }), _jsx("div", { children: suiteState.testStates.map((ts) => (_jsx(TestItem, { testState: ts, suiteState: suiteState, isReadOnly: isReadOnly }, ts.test.id))) })] }), _jsx(ResizablePanelSplitter, { children: _jsx(ResizablePanelSplitterLine, { color: "var(--color-dark-grey-200)" }) }), _jsx(ResizablePanel, { minSize: 56, children: selectedTest ? (_jsx(DataProductTestEditor, { testState: selectedTest, isReadOnly: isReadOnly })) : (_jsx(BlankPanelPlaceholder, { text: "Select a test", tooltipText: "Select a test from the list above" })) })] }) }) }));
});
// ──────────────────────────────────────────────────────────────────────────────
// Suite Editor — horizontal split: test data (top) + tests (bottom)
// ──────────────────────────────────────────────────────────────────────────────
const DataProductTestSuiteEditor = observer((props) => {
    const { suiteState } = props;
    const testableState = suiteState.testableState;
    const isReadOnly = testableState.dataProductEditorState.isReadOnly;
    return (_jsx("div", { className: "service-test-suite-editor", children: _jsxs(ResizablePanelGroup, { orientation: "horizontal", children: [_jsx(ResizablePanel, { size: 580, minSize: 28, children: _jsx(DataProductTestDataEditor, { testDataState: suiteState.testDataState, isReadOnly: isReadOnly }) }), _jsx(ResizablePanelSplitter, { children: _jsx(ResizablePanelSplitterLine, { color: "var(--color-dark-grey-200)" }) }), _jsx(ResizablePanel, { minSize: 56, children: _jsx(DataProductTestsEditor, { suiteState: suiteState, testableState: testableState, isReadOnly: isReadOnly }) })] }) }));
});
// ──────────────────────────────────────────────────────────────────────────────
// Suite Tab Context Menu (rename/delete)
// ──────────────────────────────────────────────────────────────────────────────
const SuiteHeaderTabContextMenu = observer(forwardRef(function SuiteHeaderTabContextMenu(props, ref) {
    const { testSuite, testableState } = props;
    const deleteSuite = () => {
        const suiteState = testableState.suiteStates.find((s) => s.suite === testSuite);
        if (suiteState) {
            testableState.deleteSuite(suiteState);
        }
    };
    const rename = () => testableState.setSuiteToRename(testSuite);
    return (_jsxs(MenuContent, { ref: ref, children: [_jsx(MenuContentItem, { onClick: rename, children: "Rename" }), _jsx(MenuContentItem, { onClick: deleteSuite, children: "Delete" })] }));
}));
// ──────────────────────────────────────────────────────────────────────────────
// Main Testing Tab — suite tabs at top, suite editor below
// ──────────────────────────────────────────────────────────────────────────────
export const DataProductTestableEditor = observer((props) => {
    const { dataProductEditorState, isReadOnly } = props;
    const testableState = dataProductEditorState.testableState;
    const selectedSuiteState = testableState.selectedSuiteState;
    const dp = testableState.dataProduct;
    useEffect(() => {
        testableState.init();
    }, [testableState]);
    const addSuite = () => {
        testableState.setShowCreateSuiteModal(true);
    };
    const changeSuite = (suite) => {
        testableState.changeSuite(suite);
    };
    const renameSuite = (val) => testSuite_setId(guaranteeNonNullable(testableState.suiteToRename), val);
    return (_jsxs(Panel, { className: "service-test-suite-editor", children: [_jsx(PanelLoadingIndicator, { isLoading: testableState.runningAllTestsState.isInProgress }), testableState.showCreateSuiteModal && (_jsx(CreateSuiteModal, { testableState: testableState, onClose: () => testableState.setShowCreateSuiteModal(false) })), testableState.showCreateTestModal && selectedSuiteState && (_jsx(CreateTestModal, { suiteState: selectedSuiteState, onClose: () => testableState.setShowCreateTestModal(false) })), _jsxs(PanelHeader, { children: [dp.tests.length ? (_jsx(PanelHeader, { className: "service-test-suite-editor__header service-test-suite-editor__header--with-tabs", children: _jsx("div", { className: "uml-element-editor__tabs", children: dp.tests.map((suite) => (_jsx("div", { onClick: () => changeSuite(suite), className: clsx('service-test-suite-editor__tab', {
                                    'service-test-suite-editor__tab--active': selectedSuiteState?.suite === suite,
                                }), children: _jsx(ContextMenu, { className: "mapping-editor__header__tab__content", content: _jsx(SuiteHeaderTabContextMenu, { testableState: testableState, testSuite: suite }), children: suite.id }) }, suite.id))) }) })) : (_jsx("div", {})), _jsx(PanelHeaderActions, { children: _jsx(PanelHeaderActionItem, { onClick: addSuite, title: "Add Test Suite", children: _jsx(PlusIcon, {}) }) })] }), _jsxs(Panel, { className: "service-test-suite-editor", children: [selectedSuiteState && (_jsx(DataProductTestSuiteEditor, { suiteState: selectedSuiteState })), !dp.tests.length && (_jsx(BlankPanelPlaceholder, { text: "Add Test Suite", onClick: addSuite, clickActionType: "add", tooltipText: "Click to add test suite" })), testableState.suiteToRename && (_jsx(RenameModal, { val: testableState.suiteToRename.id, isReadOnly: isReadOnly, showModal: true, closeModal: () => testableState.setSuiteToRename(undefined), setValue: renameSuite }))] })] }));
});
//# sourceMappingURL=DataProductTestableEditor.js.map