import {expect} from '@playwright/test';

import datalensTest from '../../utils/playwright/globalTestDefinition';
import {openTestPage, slct} from '../../utils';
import {
    DatasetFieldsTabQa,
    DatasetEditorTableSettingsItems,
    EditHistoryQA,
    DatasetFieldContextMenuItemsQA,
    FieldEditorQa,
    DatasetFieldTabBatchPanelQa,
    DialogConfirmQA,
    DATASET_TAB,
    DialogParameterQA,
    ActionPanelQA,
    DialogRelatedEntitiesQA,
} from '../../../src/shared';
import DatasetPage from '../../page-objects/dataset/DatasetPage';
import FieldEditor from '../../page-objects/wizard/FieldEditor';
import {VALIDATE_DATASET_URL} from '../../page-objects/dataset/constants';
import {RobotChartsDatasetUrls} from '../../utils/constants';
import {getValidatePromise} from '../../page-objects/dataset/utils';
import {ActionPanelEntryContextMenuQa} from '../../../src/shared/constants/qa/action-panel';

datalensTest.describe('Dataset history', () => {
    const url = RobotChartsDatasetUrls.DatasetWithCsvConnection;
    let datasetPage: DatasetPage;

    datalensTest.beforeEach(async ({page}) => {
        datasetPage = new DatasetPage({page});
        const validatePromise = page.waitForResponse(
            (response) => response.url().includes('/validateDataset') && response.ok(),
        );
        await openTestPage(page, url);
        await page.waitForSelector(slct(DatasetFieldsTabQa.FieldNameColumnInput));
        // Wait for initial dataset validation to complete, ensuring the edit history is initialized
        await validatePromise;
    });

    datalensTest('Undo button is disabled when no changes were made', async ({page}) => {
        const undoBtn = page.locator(slct(EditHistoryQA.UndoBtn));
        await expect(undoBtn).toBeVisible();
        await expect(undoBtn).toBeDisabled();
    });

    datalensTest('Redo button is disabled when no changes were made', async ({page}) => {
        const redoBtn = page.locator(slct(EditHistoryQA.RedoBtn));
        await expect(redoBtn).toBeVisible();
        await expect(redoBtn).toBeDisabled();
    });

    datalensTest('Undo and redo reverts a field rename', async ({page}) => {
        const undoBtn = page.locator(slct(EditHistoryQA.UndoBtn));
        await expect(undoBtn).toBeDisabled();
        const fieldInput = datasetPage.datasetFieldsTable.getFieldNameInput();
        const {newValue: changedValue, originalValue} =
            (await datasetPage.renameFirstField()) ?? {};

        await expect(undoBtn).toBeEnabled();
        await undoBtn.click();

        const undoValue = await fieldInput.inputValue();
        expect(undoValue).toBe(originalValue);

        const redoBtn = page.locator(slct(EditHistoryQA.RedoBtn));
        await expect(redoBtn).toBeEnabled();
        await redoBtn.click();

        const redoValue = await fieldInput.inputValue();
        expect(redoValue).toBe(changedValue);
    });

    datalensTest('Redo is disabled after undo and making a new change', async ({page}) => {
        await datasetPage.renameFirstField();

        // Undo
        const undoBtn = page.locator(slct(EditHistoryQA.UndoBtn));
        await expect(undoBtn).toBeEnabled();
        await undoBtn.click();

        const redoBtn = page.locator(slct(EditHistoryQA.RedoBtn));
        await expect(redoBtn).toBeEnabled();

        await datasetPage.renameFirstField({value: 'new_value'});
        await expect(redoBtn).toBeDisabled();
    });

    datalensTest('Undo reverts field visibility toggle', async ({page}) => {
        // Disable "Show hidden" so we cant observe the hidden field
        const settingsBtn = page.locator(slct(DatasetFieldsTabQa.TableSettingsBtn));
        await settingsBtn.click();
        const showHiddenMenuItem = await page.waitForSelector(
            slct(DatasetEditorTableSettingsItems.ShowHidden),
        );
        await showHiddenMenuItem.click();

        const rowsCountBefore = await page
            .locator(slct(DatasetFieldsTabQa.FieldNameColumnInput))
            .count();

        // Hide first field
        const hiddenBtn = page.locator(slct(DatasetFieldsTabQa.FieldVisibleColumnIcon)).first();
        await hiddenBtn.click();

        const rowsCountAfterHide = await page
            .locator(slct(DatasetFieldsTabQa.FieldNameColumnInput))
            .count();
        expect(rowsCountAfterHide).toBe(rowsCountBefore - 1);

        // Undo the hide
        const undoBtn = page.locator(slct(EditHistoryQA.UndoBtn));
        await expect(undoBtn).toBeEnabled();
        await undoBtn.click();

        const rowsCountAfterUndo = await page
            .locator(slct(DatasetFieldsTabQa.FieldNameColumnInput))
            .count();
        expect(rowsCountAfterUndo).toBe(rowsCountBefore);
    });

    datalensTest('Redo restores field visibility toggle', async ({page}) => {
        // Get the visibility state of the first field before hiding
        const firstFieldVisibleIcon = page
            .locator(slct(DatasetFieldsTabQa.FieldVisibleColumnIcon))
            .first();
        const visibleClassBefore = await firstFieldVisibleIcon.getAttribute('title');

        // Hide first field
        await firstFieldVisibleIcon.click();

        const visibleClassAfterHide = await firstFieldVisibleIcon.getAttribute('title');
        expect(visibleClassAfterHide).not.toBe(visibleClassBefore);

        // Undo the hide
        const undoBtn = page.locator(slct(EditHistoryQA.UndoBtn));
        await expect(undoBtn).toBeEnabled();
        await undoBtn.click();

        // Redo the hide
        const redoBtn = page.locator(slct(EditHistoryQA.RedoBtn));
        await expect(redoBtn).toBeEnabled();
        await redoBtn.click();

        const visibleClassAfterRedo = await firstFieldVisibleIcon.getAttribute('title');
        expect(visibleClassAfterRedo).toBe(visibleClassAfterHide);
    });

    datalensTest('Undo and Redo restores field duplication', async ({page}) => {
        const rowsCountBefore = await page
            .locator(slct(DatasetFieldsTabQa.FieldNameColumnInput))
            .count();
        const fieldInput = datasetPage.datasetFieldsTable.getFieldNameInput();
        await fieldInput.hover();
        // Duplicate field via context menu
        const contextMenuBtn = page.locator(slct(DatasetFieldsTabQa.FieldContextMenuBtn)).first();
        await contextMenuBtn.click();

        const duplicateItem = page.locator(slct(DatasetFieldContextMenuItemsQA.DUPLICATE));
        const duplicatePromise = datasetPage.waitForSuccessfulResponse(VALIDATE_DATASET_URL);
        await duplicateItem.click();
        await duplicatePromise;

        const rowsCountAfterDuplicate = await page
            .locator(slct(DatasetFieldsTabQa.FieldNameColumnInput))
            .count();
        expect(rowsCountAfterDuplicate).toBe(rowsCountBefore + 1);

        // Undo the duplication
        const undoBtn = page.locator(slct(EditHistoryQA.UndoBtn));
        await expect(undoBtn).toBeEnabled();
        await undoBtn.click();

        const rowsCountAfterUndo = await page
            .locator(slct(DatasetFieldsTabQa.FieldNameColumnInput))
            .count();
        expect(rowsCountAfterUndo).toBe(rowsCountBefore);

        // Redo the duplication
        const redoBtn = page.locator(slct(EditHistoryQA.RedoBtn));
        await expect(redoBtn).toBeEnabled();
        await redoBtn.click();

        const rowsCountAfterRedo = await page
            .locator(slct(DatasetFieldsTabQa.FieldNameColumnInput))
            .count();
        expect(rowsCountAfterRedo).toBe(rowsCountBefore + 1);
    });

    datalensTest('Undo and Redo restores field removal', async ({page}) => {
        const rowsCountBefore = await page
            .locator(slct(DatasetFieldsTabQa.FieldNameColumnInput))
            .count();
        const fieldInput = datasetPage.datasetFieldsTable.getFieldNameInput();
        await fieldInput.hover();
        // Remove field via context menu
        const contextMenuBtn = page.locator(slct(DatasetFieldsTabQa.FieldContextMenuBtn)).first();
        await contextMenuBtn.click();

        const removeItem = page.locator(slct(DatasetFieldContextMenuItemsQA.REMOVE));
        const removePromise = datasetPage.waitForSuccessfulResponse(VALIDATE_DATASET_URL);
        await removeItem.click();
        await removePromise;

        const rowsCountAfterRemove = await page
            .locator(slct(DatasetFieldsTabQa.FieldNameColumnInput))
            .count();
        expect(rowsCountAfterRemove).toBe(rowsCountBefore - 1);

        // Undo the removal
        const undoBtn = page.locator(slct(EditHistoryQA.UndoBtn));
        await expect(undoBtn).toBeEnabled();
        await undoBtn.click();

        const rowsCountAfterUndo = await page
            .locator(slct(DatasetFieldsTabQa.FieldNameColumnInput))
            .count();
        expect(rowsCountAfterUndo).toBe(rowsCountBefore);

        // Redo the removal
        const redoBtn = page.locator(slct(EditHistoryQA.RedoBtn));
        await expect(redoBtn).toBeEnabled();
        await redoBtn.click();

        const rowsCountAfterRedo = await page
            .locator(slct(DatasetFieldsTabQa.FieldNameColumnInput))
            .count();
        expect(rowsCountAfterRedo).toBe(rowsCountBefore - 1);
    });

    datalensTest('Undo reverts field description change', async ({page}) => {
        const descriptionInput = page
            .locator(slct(DatasetFieldsTabQa.FieldDescriptionColumnInput))
            .first()
            .locator('input');
        const originalValue = await descriptionInput.inputValue();

        await descriptionInput.fill('test description for history');
        await page.keyboard.press('Enter');

        const updatedValue = await descriptionInput.inputValue();
        expect(updatedValue).toBe('test description for history');

        // Undo the description change
        const undoBtn = page.locator(slct(EditHistoryQA.UndoBtn));
        await expect(undoBtn).toBeEnabled();
        await undoBtn.click();

        const restoredValue = await descriptionInput.inputValue();
        expect(restoredValue).toBe(originalValue);
    });

    datalensTest('Redo restores field description change', async ({page}) => {
        const descriptionInput = page
            .locator(slct(DatasetFieldsTabQa.FieldDescriptionColumnInput))
            .first()
            .locator('input');
        const originalValue = await descriptionInput.inputValue();
        const newDescription = 'test description for redo';

        await descriptionInput.fill(newDescription);
        await page.keyboard.press('Enter');

        // Undo
        const undoBtn = page.locator(slct(EditHistoryQA.UndoBtn));
        await expect(undoBtn).toBeEnabled();
        await undoBtn.click();

        const afterUndo = await descriptionInput.inputValue();
        expect(afterUndo).toBe(originalValue);

        // Redo
        const redoBtn = page.locator(slct(EditHistoryQA.RedoBtn));
        await expect(redoBtn).toBeEnabled();
        await redoBtn.click();

        const afterRedo = await descriptionInput.inputValue();
        expect(afterRedo).toBe(newDescription);
    });

    datalensTest('Undo reverts field type change', async ({page}) => {
        const typeSelect = page.locator(slct(DatasetFieldsTabQa.FieldTypeColumnBtn));
        const {
            targetOption,
            selectBtn: typeSelectBtn,
            originalSelectText: originalTypeText,
        } = await datasetPage.datasetFieldsTable.changeFieldSelect(typeSelect);

        if (!targetOption || !typeSelectBtn) {
            return;
        }

        const validatePromise = datasetPage.waitForSuccessfulResponse(VALIDATE_DATASET_URL);
        await targetOption.click();
        await validatePromise;

        const changedTypeText = await typeSelectBtn.textContent();
        expect(changedTypeText).not.toBe(originalTypeText);

        // Undo the type change
        const undoBtn = page.locator(slct(EditHistoryQA.UndoBtn));
        await expect(undoBtn).toBeEnabled();
        await undoBtn.click();

        const restoredTypeText = await typeSelectBtn.textContent();
        expect(restoredTypeText).toBe(originalTypeText);
    });

    datalensTest('Redo restores field type change', async ({page}) => {
        const typeSelect = page.locator(slct(DatasetFieldsTabQa.FieldTypeColumnBtn));
        const {
            targetOption,
            targetOptionText,
            originalSelectText: originalTypeText,
            selectBtn: typeSelectBtn,
        } = await datasetPage.datasetFieldsTable.changeFieldSelect(typeSelect);
        if (!targetOption || !typeSelectBtn) {
            return;
        }

        const validatePromise = datasetPage.waitForSuccessfulResponse(VALIDATE_DATASET_URL);
        await targetOption.click();
        await validatePromise;

        // Undo
        const undoBtn = page.locator(slct(EditHistoryQA.UndoBtn));
        await expect(undoBtn).toBeEnabled();
        await undoBtn.click();

        const afterUndo = await typeSelectBtn.textContent();
        expect(afterUndo).toBe(originalTypeText);

        // Redo
        const redoBtn = page.locator(slct(EditHistoryQA.RedoBtn));
        await expect(redoBtn).toBeEnabled();
        await redoBtn.click();

        const afterRedo = await typeSelectBtn.textContent();
        expect(afterRedo).toBe(targetOptionText);
    });

    datalensTest('Undo reverts field aggregation change', async ({page}) => {
        const aggregationSelectBtns = page.locator(
            slct(DatasetFieldsTabQa.FieldAggregationColumnBtn),
        );
        const {
            targetOption,
            selectBtn: aggregationSelectBtn,
            originalSelectText: originalAggregationText,
        } = await datasetPage.datasetFieldsTable.changeFieldSelect(aggregationSelectBtns);

        if (!targetOption || !aggregationSelectBtn) {
            return;
        }

        const validatePromise = getValidatePromise(page);
        await targetOption.click();
        await validatePromise;

        const changedAggregationText = await aggregationSelectBtn.textContent();
        expect(changedAggregationText).not.toBe(originalAggregationText);

        // Undo
        const undoBtn = page.locator(slct(EditHistoryQA.UndoBtn));
        await expect(undoBtn).toBeEnabled();
        await undoBtn.click();

        const restoredAggregationText = await aggregationSelectBtn.textContent();
        expect(restoredAggregationText).toBe(originalAggregationText);
    });

    datalensTest('Redo restores field aggregation change', async ({page}) => {
        const aggregationSelectBtns = page.locator(
            slct(DatasetFieldsTabQa.FieldAggregationColumnBtn),
        );
        const {
            targetOption,
            targetOptionText,
            originalSelectText: originalAggregationText,
            selectBtn: aggregationSelectBtn,
        } = await datasetPage.datasetFieldsTable.changeFieldSelect(aggregationSelectBtns);

        if (!targetOption || !aggregationSelectBtn) {
            return;
        }

        const validatePromise = getValidatePromise(page);
        await targetOption.click();
        await validatePromise;

        // Undo
        const undoBtn = page.locator(slct(EditHistoryQA.UndoBtn));
        await expect(undoBtn).toBeEnabled();
        await undoBtn.click();

        const afterUndo = await aggregationSelectBtn.textContent();
        expect(afterUndo).toBe(originalAggregationText);

        // Redo
        const redoBtn = page.locator(slct(EditHistoryQA.RedoBtn));
        await expect(redoBtn).toBeEnabled();
        await redoBtn.click();

        const afterRedo = await aggregationSelectBtn.textContent();
        expect(afterRedo).toBe(targetOptionText);
    });

    datalensTest('Undo and redo restore field edit via field editor dialog', async ({page}) => {
        const fieldInput = datasetPage.datasetFieldsTable.getFieldNameInput();
        const originalName = await fieldInput.inputValue();
        await fieldInput.hover();

        // Open field editor via context menu
        const contextMenuBtn = page.locator(slct(DatasetFieldsTabQa.FieldContextMenuBtn)).first();
        await contextMenuBtn.click();

        const editItem = page.locator(slct(DatasetFieldContextMenuItemsQA.EDIT));
        await editItem.click();

        const fieldEditorDialog = page.locator(slct(FieldEditorQa.Dialog));
        await expect(fieldEditorDialog).toBeVisible();
        const fieldEditor = new FieldEditor(page);
        const changedName = `${originalName}_edited`;
        await fieldEditor.changeName(changedName);

        const applyPromise = datasetPage.waitForSuccessfulResponse(VALIDATE_DATASET_URL);
        await fieldEditor.clickToApplyButton();
        await applyPromise;

        // Undo
        const undoBtn = page.locator(slct(EditHistoryQA.UndoBtn));
        await expect(undoBtn).toBeEnabled();
        await undoBtn.click();

        const afterUndo = await fieldInput.inputValue();
        expect(afterUndo).toBe(originalName);

        // Redo
        const redoBtn = page.locator(slct(EditHistoryQA.RedoBtn));
        await expect(redoBtn).toBeEnabled();
        await redoBtn.click();

        const afterRedo = await fieldInput.inputValue();
        expect(afterRedo).toBe(changedName);
    });

    datalensTest('Undo reverts batch deletion of selected fields', async ({page}) => {
        const rowsCountBefore = await page
            .locator(slct(DatasetFieldsTabQa.FieldNameColumnInput))
            .count();
        await datasetPage.datasetFieldsTable.selectTwoCheckboxes();

        // Click batch delete button
        const actionsPanel = page.locator(slct(DatasetFieldsTabQa.BatchActionsPanel));
        await expect(actionsPanel).toBeVisible();

        const deleteBtn = actionsPanel.locator(slct(DatasetFieldTabBatchPanelQa.BatchDelete));
        await deleteBtn.click();
        const applyDelete = await page.waitForSelector(slct(DialogConfirmQA.ApplyButton));
        const deletePromise = datasetPage.waitForSuccessfulResponse(VALIDATE_DATASET_URL);
        await applyDelete.click();
        await deletePromise;

        const rowsCountAfterDelete = await page
            .locator(slct(DatasetFieldsTabQa.FieldNameColumnInput))
            .count();
        expect(rowsCountAfterDelete).toBe(rowsCountBefore - 2);

        const undoBtn = page.locator(slct(EditHistoryQA.UndoBtn));
        await expect(undoBtn).toBeEnabled();
        await undoBtn.click();

        const rowsCountAfterUndo = await page
            .locator(slct(DatasetFieldsTabQa.FieldNameColumnInput))
            .count();
        expect(rowsCountAfterUndo).toBe(rowsCountBefore);

        const redoBtn = page.locator(slct(EditHistoryQA.RedoBtn));
        await expect(redoBtn).toBeEnabled();
        await redoBtn.click();

        const rowsCountAfterRedo = await page
            .locator(slct(DatasetFieldsTabQa.FieldNameColumnInput))
            .count();
        expect(rowsCountAfterRedo).toBe(rowsCountBefore - 2);
    });

    datalensTest('Undo reverts batch hide of selected fields', async ({page}) => {
        const settingsBtn = page.locator(slct(DatasetFieldsTabQa.TableSettingsBtn));
        await settingsBtn.click();
        const showHiddenMenuItem = await page.waitForSelector(
            slct(DatasetEditorTableSettingsItems.ShowHidden),
        );
        await showHiddenMenuItem.click();
        const rowsCountBefore = await page
            .locator(slct(DatasetFieldsTabQa.FieldNameColumnInput))
            .count();
        await datasetPage.datasetFieldsTable.selectTwoCheckboxes();

        const actionsPanel = page.locator(slct(DatasetFieldsTabQa.BatchActionsPanel));
        await expect(actionsPanel).toBeVisible();

        const hideBtn = actionsPanel.locator('button').nth(1);
        await hideBtn.click();
        const applyHide = await page.waitForSelector(slct(DialogConfirmQA.ApplyButton));
        await applyHide.click();

        // After hiding, the hidden fields should disappear (Show hidden is off by default)
        const rowsCountAfterHide = await page
            .locator(slct(DatasetFieldsTabQa.FieldNameColumnInput))
            .count();
        expect(rowsCountAfterHide).toBe(rowsCountBefore - 2);

        // Undo the batch hide
        const undoBtn = page.locator(slct(EditHistoryQA.UndoBtn));
        await expect(undoBtn).toBeEnabled();
        await undoBtn.click();

        const rowsCountAfterUndo = await page
            .locator(slct(DatasetFieldsTabQa.FieldNameColumnInput))
            .count();
        expect(rowsCountAfterUndo).toBe(rowsCountBefore);

        const redoBtn = page.locator(slct(EditHistoryQA.RedoBtn));
        await expect(redoBtn).toBeEnabled();
        await redoBtn.click();

        const rowsCountAfterRedo = await page
            .locator(slct(DatasetFieldsTabQa.FieldNameColumnInput))
            .count();
        expect(rowsCountAfterRedo).toBe(rowsCountBefore - 2);
    });

    datalensTest('Multiple redo steps restore multiple changes', async ({page}) => {
        const fieldInput = datasetPage.datasetFieldsTable.getFieldNameInput();
        const step1Value = 'step1';
        const step2Value = 'step2';

        // Two changes
        const {originalValue} = (await datasetPage.renameFirstField({value: step1Value})) ?? {};
        await datasetPage.renameFirstField({value: step2Value});

        const undoBtn = page.locator(slct(EditHistoryQA.UndoBtn));
        const redoBtn = page.locator(slct(EditHistoryQA.RedoBtn));

        await expect(undoBtn).toBeEnabled();
        await undoBtn.click();

        const afterFirstUndo = await fieldInput.inputValue();
        expect(afterFirstUndo).toBe(step1Value);
        await expect(undoBtn).toBeEnabled();
        await undoBtn.click();

        const afterUndoAll = await fieldInput.inputValue();
        expect(afterUndoAll).toBe(originalValue);

        // Redo first
        await expect(redoBtn).toBeEnabled();
        await redoBtn.click();

        const afterFirstRedo = await fieldInput.inputValue();
        expect(afterFirstRedo).toBe(step1Value);

        // Redo second
        await expect(redoBtn).toBeEnabled();
        await redoBtn.click();

        const afterSecondRedo = await fieldInput.inputValue();
        expect(afterSecondRedo).toBe(step2Value);
    });

    datalensTest('Undo button becomes disabled at the beginning of history', async ({page}) => {
        await datasetPage.renameFirstField();
        const undoBtn = page.locator(slct(EditHistoryQA.UndoBtn));
        await expect(undoBtn).toBeEnabled();
        await undoBtn.click();
        await expect(undoBtn).toBeDisabled();
    });

    datalensTest('Redo button becomes disabled at the end of history', async ({page}) => {
        await datasetPage.renameFirstField();
        const undoBtn = page.locator(slct(EditHistoryQA.UndoBtn));
        await expect(undoBtn).toBeEnabled();
        await undoBtn.click();

        const redoBtn = page.locator(slct(EditHistoryQA.RedoBtn));
        await expect(redoBtn).toBeEnabled();
        await redoBtn.click();
        await expect(redoBtn).toBeDisabled();
    });

    datalensTest('Keyboard shortcut Cmd/Ctrl+Z triggers undo', async ({page}) => {
        const fieldInput = datasetPage.datasetFieldsTable.getFieldNameInput();
        const {originalValue} = (await datasetPage.renameFirstField()) ?? {};
        const undoBtn = page.locator(slct(EditHistoryQA.UndoBtn));
        await expect(undoBtn).toBeEnabled();
        // Click outside the input to unfocus before using hotkey
        await page.locator(slct(DatasetFieldsTabQa.DatasetEditor)).click();
        await page.keyboard.press(`ControlOrMeta+z`);

        const restoredValue = await fieldInput.inputValue();
        expect(restoredValue).toBe(originalValue);
    });

    datalensTest('Keyboard shortcut Cmd/Ctrl+Shift+Z triggers redo', async ({page}) => {
        const fieldInput = datasetPage.datasetFieldsTable.getFieldNameInput();
        const {newValue: changedValue} = (await datasetPage.renameFirstField()) ?? {};

        // Undo via button
        const undoBtn = page.locator(slct(EditHistoryQA.UndoBtn));
        await expect(undoBtn).toBeEnabled();
        await undoBtn.click();

        const redoBtn = page.locator(slct(EditHistoryQA.RedoBtn));
        await expect(redoBtn).toBeEnabled();
        // Click outside the input to unfocus before using hotkey
        await page.locator(slct(DatasetFieldsTabQa.DatasetEditor)).click();
        await page.keyboard.press(`ControlOrMeta+Shift+z`);

        const restoredValue = await fieldInput.inputValue();
        expect(restoredValue).toBe(changedValue);
    });

    datalensTest('Ctrl+Z hotkey does not trigger undo when dialog is open', async ({page}) => {
        const fieldInput = datasetPage.datasetFieldsTable.getFieldNameInput();
        const {newValue: changedValue} = (await datasetPage.renameFirstField()) ?? {};

        const undoBtn = page.locator(slct(EditHistoryQA.UndoBtn));
        const redoBtn = page.locator(slct(EditHistoryQA.RedoBtn));
        await expect(undoBtn).toBeEnabled();

        await datasetPage.openTab(DATASET_TAB.PARAMETERS);
        await datasetPage.datasetTabSection.clickAddButton();
        await datasetPage.dialogParameter.waitForDialogParameter();

        await page.waitForSelector(slct(DialogParameterQA.Dialog));
        const parametersDialog = page.locator(slct(DialogParameterQA.Dialog));
        await expect(parametersDialog).toBeVisible();

        await expect(undoBtn).toBeHidden();
        await expect(redoBtn).toBeHidden();

        // Try to undo via hotkey — should have no effect
        await page.keyboard.press('ControlOrMeta+z');

        // Close the dialog
        const cancelBtn = page.locator(slct(DialogParameterQA.Cancel));
        await cancelBtn.click();
        await expect(parametersDialog).toBeHidden();

        await datasetPage.openTab(DATASET_TAB.DATASET);
        // The field value should still be the changed value (undo was blocked)
        const currentValue = await fieldInput.inputValue();
        expect(currentValue).toBe(changedValue);

        // Undo button should be visible and enabled again
        await expect(undoBtn).toBeVisible();
        await expect(undoBtn).toBeEnabled();
    });

    datalensTest(
        'Ctrl+Shift+Z hotkey does not trigger redo when dialog is open',
        async ({page}) => {
            const fieldInput = datasetPage.datasetFieldsTable.getFieldNameInput();
            const {originalValue} = (await datasetPage.renameFirstField()) ?? {};

            // Undo the change first
            const undoBtn = page.locator(slct(EditHistoryQA.UndoBtn));
            await expect(undoBtn).toBeEnabled();
            await undoBtn.click();

            const redoBtn = page.locator(slct(EditHistoryQA.RedoBtn));
            await expect(redoBtn).toBeEnabled();

            const entryContextBtn = page.locator(slct(ActionPanelQA.MoreBtn));
            await entryContextBtn.click();
            const relatedEntitiesBtn = await page.waitForSelector(
                slct(ActionPanelEntryContextMenuQa.RelatedEntities),
            );
            await relatedEntitiesBtn.click();

            await page.waitForSelector(slct(DialogRelatedEntitiesQA.Dialog));
            const relatedEntitiesDialog = page.locator(slct(DialogRelatedEntitiesQA.Dialog));
            await expect(relatedEntitiesDialog).toBeVisible();

            await expect(undoBtn).toBeHidden();
            await expect(redoBtn).toBeHidden();

            // Try to redo via hotkey — should have no effect
            await page.keyboard.press('ControlOrMeta+Shift+z');
            await page.keyboard.press('Escape');

            await expect(relatedEntitiesDialog).toBeHidden();

            // The field value should still be the original value (redo was blocked)
            const currentValue = await fieldInput.inputValue();
            expect(currentValue).toBe(originalValue);

            // Redo button should be visible and enabled again
            await expect(redoBtn).toBeVisible();
            await expect(redoBtn).toBeEnabled();
        },
    );

    datalensTest('Undo reverts mixed action types in correct order', async ({page}) => {
        const rowsCountBefore = await page
            .locator(slct(DatasetFieldsTabQa.FieldNameColumnInput))
            .count();

        const fieldInput = datasetPage.datasetFieldsTable.getFieldNameInput();

        // // Step 1: Rename a field
        const {newValue, originalValue} = (await datasetPage.renameFirstField()) ?? {};

        // Step 2: Duplicate a field via context menu
        await fieldInput.hover();
        const contextMenuBtn = page.locator(slct(DatasetFieldsTabQa.FieldContextMenuBtn)).first();
        await contextMenuBtn.click();

        const duplicateItem = page.locator(slct(DatasetFieldContextMenuItemsQA.DUPLICATE));
        const dupPromise = datasetPage.waitForSuccessfulResponse(VALIDATE_DATASET_URL);
        await duplicateItem.click();
        await dupPromise;

        const rowsCountAfterDup = await page
            .locator(slct(DatasetFieldsTabQa.FieldNameColumnInput))
            .count();
        expect(rowsCountAfterDup).toBe(rowsCountBefore + 1);

        const undoBtn = page.locator(slct(EditHistoryQA.UndoBtn));

        // Undo step 2 (duplicate)
        await expect(undoBtn).toBeEnabled();
        await undoBtn.click();

        const rowsCountAfterUndoDup = await page
            .locator(slct(DatasetFieldsTabQa.FieldNameColumnInput))
            .count();
        expect(rowsCountAfterUndoDup).toBe(rowsCountBefore);
        const afterUndoFirst = await fieldInput.inputValue();
        expect(afterUndoFirst).toBe(newValue);

        // Undo step 1 (rename)
        await expect(undoBtn).toBeEnabled();
        await undoBtn.click();
        const afterUndoAll = await fieldInput.inputValue();
        expect(afterUndoAll).toBe(originalValue);
    });
});
