"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.BaseGridPage = exports.DEFAULT_GRID_ROW_MENU_ACTION_TEST_IDS = void 0;
const BasePage_1 = require("../BasePage/BasePage");
const logger_1 = require("../../utils/logger");
const test_role_utils_1 = require("../../utils/test-role-utils");
const services_1 = require("./services");
const FormDataFilterUtils_1 = require("../shared/form/FormDataFilterUtils");
const TableRowService_1 = require("../shared/table/TableRowService");
const table_scope_1 = require("../shared/table/table-scope");
const FormFlowService_1 = require("../shared/form/FormFlowService");
const ConfigResolver_1 = require("../shared/config/ConfigResolver");
const visibility_1 = require("../shared/visibility");
/** Общий testId кнопки меню действий строки грида (используется по умолчанию для страниц с type: 'menu'). */
exports.DEFAULT_GRID_ROW_MENU_ACTION_TEST_IDS = { RowMenuButton: 'actionMenu' };
class BaseGridPage extends BasePage_1.BasePage {
    constructor(page) {
        super(page);
        this.tableRowService = new TableRowService_1.TableRowService(this.page, this.textUtils);
        this.formFlowService = this.createFormFlowService();
        this.gridRowActionsService = this.createGridRowActionsService();
    }
    createFormFlowService() {
        return new FormFlowService_1.FormFlowService({
            getRole: () => this.getRoleOrThrow(),
            findModalConfig: (modalWindowKey) => this.findModalWindowConfig(modalWindowKey),
            openModal: (modalWindowKey) => this.openModalWindow(modalWindowKey),
            getModalFormLocators: (modalWindowKey) => this.getModalWindowFormLocators(modalWindowKey),
            getModalButtons: (modalWindowKey) => this.getModalWindowButtons(modalWindowKey),
            getSaveButtonKey: (modalWindowKey) => this.findModalWindowConfig(modalWindowKey).saveButtonKey,
            performSave: (button, stepName) => this.performModalTerminalActionToCloseModalWindow(button, stepName),
            formHelperFillFormFields: (data, locators, context) => this.formHelper.fillFormFields(data, locators, context),
            verifyTableRow: (rowKey, expectedRowData, isFirstRow, onRowFound) => this.verifyTableRow(rowKey, expectedRowData, isFirstRow, onRowFound),
        }, `${this.constructor.name}.formFlow`);
    }
    /**
     * Создает сервис для работы с действиями над строками грида.
     * @returns GridRowActionsService
     */
    createGridRowActionsService() {
        return new services_1.GridRowActionsService({
            getRole: () => this.getRoleOrThrow(),
            validateRole: test_role_utils_1.validateRole,
            getConfig: (role) => this.getGridRowActionsConfiguration(role),
            getRowActionButtons: (row) => this.getGridRowActionButtons(row),
            getMenuItems: () => this.GridRowActionMenuItems || null,
            orchestrator: this.rowActionsOrchestrator,
            findModalWindowConfig: (modalWindowKey) => this.findModalWindowConfig(modalWindowKey),
            confirmAction: () => this.confirmAction()
        });
    }
    /**
    * Метод для получения конфигурации модальных окон в зависимости от роли
    * Должен быть переопределен в наследниках, если страница имеет модальные окна
    * По умолчанию возвращает пустой массив
    * @param role - роль пользователя
    * @returns Массив конфигураций модальных окон (может быть пустым, если модальных окон нет)
    */
    getModalWindowConfigurations(_role) {
        return [];
    }
    /**
     * Метод для получения конфигурации действий строки в зависимости от роли
     * Должен быть переопределен в наследниках, если страница имеет действия строки
     * По умолчанию возвращает null
     * @param role - роль пользователя
     * @returns Конфигурация действий строки или null, если действия не настроены
     */
    getGridRowActionsConfiguration(_role) {
        return null;
    }
    // ------------------------------------------------------------------------------------------
    // ------------------------------------------------------------------------------------------
    // Геттеры полей, кнопок
    /**
    * Метод для получения Record кнопок модального окна
    * Должен быть переопределен в наследниках, если страница имеет модальные окна
    * По умолчанию возвращает null
    * @param modalWindowKey - ключ модального окна
    */
    getModalWindowButtons(_modalWindowKey) {
        return null;
    }
    /**
    * Метод для получения Record полей ввода модального окна
    * Должен быть переопределен в наследниках, если страница имеет модальные окна
    * По умолчанию возвращает null
    * @param _modalWindowKey - ключ модального окна
    */
    getModalWindowTextFields(_modalWindowKey) {
        return null;
    }
    /**
    * Метод для получения Record radiobutton модального окна
    * Должен быть переопределен в наследниках, если модальное окно имеет вариативное содержание
    * По умолчанию возвращает null
    * @param _modalWindowKey - ключ модального окна
    */
    getModalWindowRadioButtons(_modalWindowKey) {
        return null;
    }
    /**
    * Метод для получения Record чекбоксов модального окна
    * Должен быть переопределен в наследниках, если модальное окно имеет чекбоксы
    * По умолчанию возвращает null
    * @param _modalWindowKey - ключ модального окна
    */
    getModalWindowCheckboxes(_modalWindowKey) {
        return null;
    }
    /**
    * Метод для получения Record слайдеров модального окна
    * Должен быть переопределен в наследниках, если модальное окно имеет слайдеры
    * По умолчанию возвращает null
    * @param _modalWindowKey - ключ модального окна
    */
    getModalWindowSliders(_modalWindowKey) {
        return null;
    }
    /**
    * Метод для получения Record кнопок Toggle модального окна
    * Должен быть переопределен в наследниках, если модальное окно имеет кнопки Toggle
    * По умолчанию возвращает null
    * @param _modalWindowKey - ключ модального окна
    */
    getModalWindowToggleButtons(_modalWindowKey) {
        return null;
    }
    /**
    * Метод для получения Record combobox полей модального окна
    * Должен быть переопределен в наследниках, если модальное окно имеет поля combobox
    * По умолчанию возвращает null
    * @param _modalWindowKey - ключ модального окна
    */
    getModalWindowComboboxFields(_modalWindowKey) {
        return null;
    }
    /**
    * Метод для получения Record полей DaData модального окна
    * Должен быть переопределен в наследниках, если модальное окно имеет поля DaData
    * По умолчанию возвращает null
    * @param _modalWindowKey - ключ модального окна
    */
    getModalWindowDaDataFields(_modalWindowKey) {
        return null;
    }
    /**
    * Метод для получения Record полей multiCombobox модального окна
    * Должен быть переопределен в наследниках, если модальное окно имеет поля multiCombobox
    * По умолчанию возвращает null
    * @param _modalWindowKey - ключ модального окна
    */
    getModalWindowMultiComboboxFields(_modalWindowKey) {
        return null;
    }
    /**
    * Метод для получения Record полей для загрузки файлов модального окна
    * Должен быть переопределен в наследниках, если модальное окно имеет поля для загрузки файлов
    * По умолчанию возвращает null
    * @param _modalWindowKey - ключ модального окна
    */
    getModalWindowFileFields(_modalWindowKey) {
        return null;
    }
    // -------------------------------------------------------------------------------------------------------------------------
    /**
    * Возвращает Record testId → ключ для кнопок действий строки.
    * Переопределите в наследнике и верните объект из локаторов, чтобы использовать стандартную реализацию getGridRowActionButtons.
    * По умолчанию возвращает кнопку меню (RowMenuButton: 'actionMenu') для страниц с действиями через меню.
    */
    getGridRowActionTestIds() {
        return exports.DEFAULT_GRID_ROW_MENU_ACTION_TEST_IDS;
    }
    /**
     * Получает Record локаторов кнопок действий строки для конкретной строки.
     * Стандартная реализация строит локаторы по getGridRowActionTestIds(). Можно переопределить в наследниках при другой разметке.
     * @param rowLocator - локатор строки, для которой нужно получить кнопки
     * @returns Record с локаторами кнопок действий строки или null
     */
    getGridRowActionButtons(rowLocator) {
        const testIds = this.getGridRowActionTestIds();
        if (!testIds || Object.keys(testIds).length === 0) {
            return null;
        }
        const rowActionButtons = {};
        for (const [key, testId] of Object.entries(testIds)) {
            rowActionButtons[key] = rowLocator.getByTestId(testId).first();
        }
        return rowActionButtons;
    }
    /**
     * Собирает все локаторы полей формы модального окна
     * @param modalWindowKey - ключ модального окна
     * @returns объект локаторов для formHelper.fillFormFields
     */
    getModalWindowFormLocators(modalWindowKey) {
        return {
            textFieldsLocators: this.getModalWindowTextFields(modalWindowKey),
            comboboxFieldsLocators: this.getModalWindowComboboxFields(modalWindowKey),
            fileFieldsLocators: this.getModalWindowFileFields(modalWindowKey),
            radioButtonsLocators: this.getModalWindowRadioButtons(modalWindowKey),
            checkboxesLocators: this.getModalWindowCheckboxes(modalWindowKey),
            toggleButtonsLocators: this.getModalWindowToggleButtons(modalWindowKey),
            daDataFieldsLocators: this.getModalWindowDaDataFields(modalWindowKey),
            multiComboboxFieldsLocators: this.getModalWindowMultiComboboxFields(modalWindowKey)
        };
    }
    /**
    * Проверяет видимость всех кнопок грида согласно конфигурации для текущей роли
    * @throws {Error} Если какая-либо кнопка не соответствует ожидаемому состоянию видимости
    */
    async checkGridButtons() {
        const role = this.getRoleOrThrow();
        (0, test_role_utils_1.validateRole)(role);
        const config = this.getGridPageConfiguration(role);
        await this.visibilityChecker.checkElementsVisibility({
            locators: this.GridButtons,
            visibleKeys: config.buttons,
            hiddenKeys: config.notVisibleButtons,
            logContext: `${this.constructor.name}.checkGridButtons`,
            stepTitle: `Проверка кнопок грида для роли ${role}`,
            elementLabel: 'кнопки',
        });
    }
    /**
     * Проверяет видимость полей фильтрации грида (text / combobox / daData).
     * Локаторы берутся из {@link GridFilters}.
     * @throws {Error} Если в конфигурации не задана ни одна группа фильтров
     */
    async checkGridFilters() {
        const role = this.getRoleOrThrow();
        (0, test_role_utils_1.validateRole)(role);
        const config = this.getGridPageConfiguration(role);
        const logContext = `${this.constructor.name}.checkGridFilters`;
        if (!visibility_1.FilterVisibilityUtils.hasAnyConfiguredFilters(config)) {
            logger_1.logger.warn('В конфигурации грида не было найдено полей фильтров', logContext);
            throw new Error('В конфигурации грида не было найдена полей фильтров');
        }
        const groups = visibility_1.FilterVisibilityUtils.buildGroups(config, {
            text: this.GridFilters,
            combobox: this.GridFilters,
            daData: this.GridFilters,
        });
        const checkedGroups = await visibility_1.FilterVisibilityUtils.checkGroups(this.visibilityChecker, groups, {
            logContext,
            stepTitlePrefix: `Проверка полей фильтрации грида для роли ${role}`,
        });
        if (checkedGroups === 0) {
            throw new Error('В конфигурации грида не было найдена полей фильтров');
        }
    }
    // =========================
    // Методы для работы со строками таблицы
    // =========================
    /**
     * Проверяет наличие необходимых колонок в таблице по их названиям.
     * @param columnNames - массив названий колонок для проверки (например, ['Фамилия', 'Имя', 'Email'])
     */
    async verifyRequiredColumns(columnNames) {
        await this.tableRowService.verifyRequiredColumns(table_scope_1.PAGE_TABLE_SCOPE, columnNames, this.constructor.name);
    }
    /**
    * Поиск строки по ключевому полю
    * @param rowKey - название ключевого поля (колонки) для поиска (например, 'email', 'id')
    * @param keyValue - значение ключевого поля для поиска строки
    * @param isNewlyAdded - если true, проверяет только что добавленную строку (использует меньший таймаут)
    * @param customTimeout - кастомные настройки таймаута и интервалов проверки (необязательно)
    * @returns Locator строки или null, если строка не найдена
    * @throws {Error} При ошибках в процессе поиска
    */
    async findTableRow(rowKey, keyValue, isNewlyAdded = false, customTimeout) {
        return this.tableRowService.findTableRow(table_scope_1.PAGE_TABLE_SCOPE, rowKey, keyValue, isNewlyAdded, this.constructor.name, customTimeout);
    }
    /**
    * Кликает по первой ссылке в найденной строке таблицы
    * @param key - объект с ключом и значением для поиска строки
    * @param itemToClick - кликабельный текст в строке таблицы
    * @param keyValue - значение ключевого поля для поиска строки
    * @param isNewlyAdded - если true, ищет среди только что добавленных строк
    * @throws {Error} Если строка не найдена или в строке нет ссылок для клика
    */
    async clickInTableRow(key, itemToClick, isNewlyAdded) {
        await this.tableRowService.clickInTableRow(table_scope_1.PAGE_TABLE_SCOPE, key, itemToClick, isNewlyAdded);
    }
    /**
     * Ищет строку в таблице без выброса ошибки (для проверки отсутствия строки)
     * @param rowKey - ключевое поле строки
     * @param keyValue - Значение в ключевом поле строки
     * @returns Locator строки или null, если строка не найдена
     */
    async findTableRowSilent(rowKey, keyValue) {
        return this.tableRowService.findTableRowSilent(table_scope_1.PAGE_TABLE_SCOPE, rowKey, keyValue, this.constructor.name);
    }
    /**
    * Проверяет строку в таблице на соответствие ожидаемым данным
    * @param rowKey - ключевое поле строки (уникальный идентификатор)
    * @param rowData - объект с ожидаемыми данными строки, где ключ - название колонки, значение - ожидаемый текст
    * @param isNewlyAdded - если true, проверяет только что добавленную строку
    * @param onRowFound - колбэк, вызываемый после нахождения строки (даже при ошибках проверки)
    * @throws {Error} Если строка не найдена или данные не соответствуют ожидаемым
    */
    async verifyTableRow(rowKey, rowData, isNewlyAdded, onRowFound) {
        await this.tableRowService.verifyTableRow(table_scope_1.PAGE_TABLE_SCOPE, rowKey, rowData, isNewlyAdded, this.constructor.name, onRowFound);
    }
    async waitForTableRowWithRefresh(rowKey, rowData, isNewlyAdded, options) {
        await this.tableRowService.waitForTableRowWithRefresh(table_scope_1.PAGE_TABLE_SCOPE, rowKey, rowData, isNewlyAdded, this.constructor.name, options);
    }
    /**
    * Открывает модальное окно ,заполняет форму и сохраняет.
    * @param modalWindowKey - ключ конфигурации модального окна
    * @param formFields - данные для заполнения полей формы
    * @throws {Error} Если не удается заполнить форму или сохранить
    */
    async fillModalForm(modalWindowKey, formFields) {
        await this.formFlowService.fillModalForm(modalWindowKey, formFields);
    }
    /**
     * Заполняет поля уже открытой модальной формы (без open/save).
     */
    async fillModalFormFields(modalWindowKey, formFields) {
        await this.formFlowService.fillOpenModalFormFields(modalWindowKey, formFields);
    }
    /**
     * Проверяет все элементы грида (кнопки и поля)
     */
    async checkAllGridElements() {
        const role = this.getRoleOrThrow();
        (0, test_role_utils_1.validateRole)(role);
        logger_1.logger.info(`Проверка всех элементов грида для роли ${role}`, `${this.constructor.name}.checkAllGridElements`);
        // Параллельный запуск
        await Promise.all([
            this.checkGridButtons(),
            this.checkGridFilters()
        ]);
        logger_1.logger.info(`Проверка всех элементов грида завершена`, `${this.constructor.name}.checkAllGridElements`);
    }
    /**
    * Открывает модальное окно по кнопке-триггеру
    * @param modalWindowKey - Ключ модального окна из конфигурации
    * @throws {Error} Если модальное окно не открылось
    */
    async openModalWindow(modalWindowKey) {
        const modalConfig = this.findModalWindowConfig(modalWindowKey);
        const triggerButton = this.GridButtons[modalConfig.triggerButtonKey];
        if (!triggerButton) {
            throw new Error('Кнопка, открывающая модальное окно, не найдена. Проверьте конфигурацию.');
        }
        await this.modalWindowService.openModal({
            triggerButton,
            modalDialog: this.modalDialog,
        }, this.constructor.name);
    }
    /**
     * Открывает подразделы модального окна с полями
     * @param name - Наименование подраздел(а/ов)
     */
    async openModalCategories(name) {
        await this.modalWindowService.openModalCategories(name, this.constructor.name);
    }
    /**
     * Находит конфигурацию модального окна по ключу.
     * @param role - роль пользователя
     * @param modalWindowKey - ключ модального окна
     * @returns найденная конфигурация
     */
    findModalWindowConfig(modalWindowKey) {
        const role = this.getRoleOrThrow();
        return (0, ConfigResolver_1.resolveModalWindowConfig)(this.getModalWindowConfigurations(role), modalWindowKey);
    }
    // =========================
    // Методы работы с действиями над строкой грида
    // =========================
    /**
     * Проверяет, доступно ли действие для текущей роли.
     * @param actionKey - ключ действия
     * @returns true если действие доступно
     */
    isRowActionAvailable(actionKey) {
        return this.gridRowActionsService.isRowActionAvailable(actionKey, this.constructor.name);
    }
    /**
     * Открывает меню действий над строкой.
     * @param rowLocator - локатор строки
     */
    async openRowActionsMenu(rowLocator) {
        await this.gridRowActionsService.openRowActionsMenu(rowLocator, this.constructor.name);
    }
    /**
     * Закрывает меню действий над строкой
     */
    async closeRowActionsMenu() {
        await this.gridRowActionsService.closeRowActionsMenu();
    }
    /**
     * Универсальный метод для выполнения действия со строкой.
     * @param rowLocator - локатор строки
     * @param actionKey - ключ действия (например, 'MenuItemEdit' или 'RowEditButton')
     */
    async clickRowAction(rowLocator, actionKey) {
        await this.gridRowActionsService.clickRowAction(rowLocator, actionKey, this.constructor.name);
    }
    /**
     * Проверяет видимость кнопок действий строки.
     * @param rowLocator - локатор строки
     */
    async checkGridRowActions(rowLocator) {
        await this.gridRowActionsService.checkRowActions(rowLocator, this.constructor.name);
    }
    /**
     * Открывает строку на редактирование.
     * @param rowLocator - локатор строки
     * @returns true если модальное окно успешно открыто
     */
    async editRow(rowLocator) {
        return this.gridRowActionsService.editRow(rowLocator, this.constructor.name);
    }
    /**
     * Открывает строку на просмотр (readonly).
     * @param rowLocator - локатор строки
     * @returns true если модальное окно успешно открыто
     */
    async viewRow(rowLocator) {
        return this.gridRowActionsService.viewRow(rowLocator, this.constructor.name);
    }
    /**
     * Удаляет строку (с подтверждением, если требуется).
     * @param rowLocator - локатор строки
     * @param confirmDelete - нужно ли подтверждение удаления (по умолчанию true)
     * @returns true если удаление успешно
     */
    async deleteRow(rowLocator, confirmDelete = true) {
        return this.gridRowActionsService.deleteRow(rowLocator, confirmDelete, this.constructor.name);
    }
    /**
    * Редактирует строку с заполнением формы, сохранением и проверкой результата.
    * @param modalWindowKey - ключ модального окна (из конфигурации страницы)
    * @param rowLocator - локатор строки для редактирования
    * @param expectedRowData - ожидаемые данные после сохранения, где ключ - название колонки, значение - ожидаемый текст
    * @param formFields - объект с полями для заполнения в форме редактирования
    * @param onRowVerified - колбэк, вызываемый после нахождения строки (даже при ошибках проверки)
    * @throws {Error} Если не удалось открыть форму, заполнить, сохранить или данные не совпадают
    */
    async editRowAndVerify(modalWindowKey, rowLocator, expectedRowData, formFields, onRowVerified) {
        const role = this.getRoleOrThrow();
        logger_1.logger.info(`Начало редактирования строки с проверкой для роли ${role}`, `${this.constructor.name}.editRowAndVerify`);
        const modalConfig = this.findModalWindowConfig(modalWindowKey);
        const fieldsToFill = (0, FormDataFilterUtils_1.filterFormFieldsByConfig)(formFields, modalConfig, `${this.constructor.name}.editRowAndVerify`);
        const verifyFn = async () => {
            const rowKey = Object.keys(expectedRowData)[0];
            await this.verifyTableRow(rowKey, expectedRowData, false, onRowVerified);
        };
        const getModalButtons = (key) => {
            const modalButtons = this.getModalWindowButtons(key);
            if (!modalButtons) {
                throw new Error(`Кнопки модального окна ${key} не найдены`);
            }
            return modalButtons;
        };
        await this.rowActionsOrchestrator.editRowAndVerifyBase(() => this.editRow(rowLocator), modalWindowKey, fieldsToFill, (key) => this.getModalWindowFormLocators(key), getModalButtons, modalConfig.saveButtonKey, (btn, name) => this.performModalTerminalActionToCloseModalWindow(btn, name), verifyFn);
    }
    /**
     * Просматривает строку и проверяет данные.
     * @param rowLocator - локатор строки
     * @param expectedValues - ожидаемые значения полей
     */
    async viewRowAndVerify(modalWindowKey, rowLocator, expectedValues) {
        const role = this.getRoleOrThrow();
        logger_1.logger.info(`Начало просмотра строки с проверкой для роли ${role}`, `${this.constructor.name}.viewRowAndVerify`);
        const modalConfig = this.findModalWindowConfig(modalWindowKey);
        await this.rowActionsOrchestrator.viewRowAndVerifyBase(modalWindowKey, () => this.viewRow(rowLocator), expectedValues, (key) => this.getModalWindowFormLocators(key), (key) => this.getModalWindowButtons(key), modalConfig.closeButtonKey, (btn, name) => this.performModalTerminalActionToCloseModalWindow(btn, name));
    }
    /**
     * Удаляет строку и проверяет, что она исчезла.
     * @param rowKey - ключевое поле строки
     * @param rowData - данные строки для поиска
     * @param confirmDelete - нужно ли подтверждение
     */
    async deleteRowAndVerify(rowKey, rowData, confirmDelete = true) {
        const role = this.getRoleOrThrow();
        logger_1.logger.info(`Начало удаления строки с проверкой для роли ${role} (rowKey: ${rowKey})`, `${this.constructor.name}.deleteRowAndVerify`);
        await this.rowActionsOrchestrator.deleteRowAndVerifyBase(() => this.findTableRow(rowKey, rowData[rowKey], false), async (row) => {
            const _result = await this.deleteRow(row, confirmDelete);
            if (!_result) {
                throw new Error('Строка не была удалена');
            }
        }, () => this.findTableRowSilent(rowKey, rowData[rowKey]), rowData);
    }
    /**
     * Реализация метода активации/деактивации для гридов.
     */
    async toggleActivationForGrid(rowLocator, action, actionType, requireConfirmation) {
        await this.gridRowActionsService.toggleActivationForGrid(rowLocator, action, actionType, requireConfirmation, this.constructor.name);
    }
    /**
    * Активирует или деактивирует строку в гриде.
    * @param rowLocator - локатор строки
    * @param action - действие: 'activate' или 'deactivate'
    * @param actionType - тип действия: 'menu' или 'button' (опционально)
    * @param requireConfirmation - требуется ли подтверждение (опционально)
    */
    async toggleActivation(rowLocator, action, actionType, requireConfirmation) {
        return this.toggleActivationForGrid(rowLocator, action, actionType, requireConfirmation);
    }
    async deleteGridTrashAfterFail(rowLocator, confirmDelete) {
        try {
            logger_1.logger.info(`Зачистка мусора после падения теста по ключу строки`, `${this.constructor.name}.deleteTrashAfterFail`);
            const modalIsVisible = await this.modalDialog.isVisible();
            if (modalIsVisible) {
                await this.page.keyboard.press('Escape');
            }
            await this.deleteRow(rowLocator, confirmDelete);
            logger_1.logger.info(`Мусор после теста зачищен. Строка удалена`, `${this.constructor.name}.deleteTrashAfterFail`);
        }
        catch (error) {
            logger_1.logger.info(`Мусор не был зачищен, т.к не был найден по ключу`, `${this.constructor.name}.deleteTrashAfterFail`);
        }
    }
}
exports.BaseGridPage = BaseGridPage;
