"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PlayPage = void 0;
const test_1 = require("@playwright/test");
const sleep_1 = require("../../utils/sleep");
const expectLocatorWithRetry_1 = require("../../utils/assertionHelpers/expectLocatorWithRetry");
const env_1 = require("../../utils/env");
const maxWaitTimeSeconds = 180000;
// OpenSearch indexes process instances more slowly than Elasticsearch, so
// "Loading instance details..." can stay visible longer than the default 60s.
const loadingDetailsHideTimeout = env_1.isOpenSearch ? 180000 : 60000;
class PlayPage {
    page;
    completeJobButton;
    saveScenarioButton;
    viewScenarioButton;
    runAllScenariosButton;
    confirmSaveScenarioButton;
    enterScenarioNameInput;
    confirmDeleteScenarioButton;
    viewAllScenariosButton;
    getScenarioRowByName;
    getScenarioRow;
    diagram;
    startInstanceButton;
    notifications;
    saveScenarioModal;
    dialog;
    loadingInstanceDetailsText;
    retryButton;
    restartProcess;
    constructor(page) {
        this.page = page;
        this.completeJobButton = page
            .getByTestId('diagram')
            .getByLabel('Complete job');
        this.saveScenarioButton = page.getByRole('button', { name: 'Save scenario' });
        this.viewScenarioButton = page.getByRole('button', { name: 'View scenario' });
        this.runAllScenariosButton = page.getByRole('button', {
            name: 'Run all scenarios',
        });
        this.dialog = page.getByRole('dialog');
        this.confirmSaveScenarioButton = page.getByRole('button', {
            name: 'Save',
            exact: true,
        });
        this.enterScenarioNameInput = page
            .getByTestId('save-scenario-modal')
            .first()
            .locator('input#scenario-name')
            .first();
        this.confirmDeleteScenarioButton = page.getByRole('button', {
            name: 'danger Delete',
        });
        this.viewAllScenariosButton = page
            .getByTestId('instance-header')
            .getByRole('button', { name: '(View all)' });
        this.getScenarioRowByName = (scenarioName) => page.locator('tr', { hasText: scenarioName });
        this.getScenarioRow = (scenarioName) => page.locator('tr', { hasText: scenarioName });
        this.diagram = page.getByTestId('diagram');
        this.startInstanceButton = page.getByTestId('undefined-Start instance');
        this.notifications = page.locator('.cds--toast-notification');
        this.saveScenarioModal = page.getByTestId('save-scenario-modal').first();
        this.loadingInstanceDetailsText = this.page.getByText('Loading instance details...');
        this.retryButton = this.page.getByRole('button', { name: 'Retry' });
        this.restartProcess = this.page.getByRole('button', {
            name: 'Restart process',
        });
    }
    async waitForCompleteJobButtonToBeAvailable() {
        await (0, test_1.expect)(this.completeJobButton).toBeVisible({
            timeout: maxWaitTimeSeconds,
        });
    }
    async clickCompleteJobButton() {
        await (0, test_1.expect)(this.completeJobButton).toBeVisible({ timeout: 30000 });
        await (0, test_1.expect)(this.completeJobButton).toBeEnabled({ timeout: 30000 });
        await this.completeJobButton.click();
    }
    async clickStartInstanceButton() {
        const maxRetries = 3;
        let attempts = 0;
        while (attempts < maxRetries) {
            try {
                await this.diagram.waitFor({ state: 'visible', timeout: 30000 });
                await this.startInstanceButton.click({ timeout: 30000 });
                await (0, test_1.expect)(this.loadingInstanceDetailsText).toBeVisible();
                await (0, test_1.expect)(this.loadingInstanceDetailsText).not.toBeVisible({
                    timeout: loadingDetailsHideTimeout,
                });
                await (0, test_1.expect)(this.dialog).not.toBeVisible({ timeout: 15000 });
                return;
            }
            catch (error) {
                if (attempts >= maxRetries - 1)
                    throw error;
                await this.page.reload();
                await (0, sleep_1.sleep)(2000);
                attempts++;
            }
        }
    }
    async dismissStartModal() {
        const buttonVariations = [
            'Start a process instance',
            'Start another instance',
            'Start new instance',
            'Start instance',
        ];
        for (const buttonName of buttonVariations) {
            const button = this.page
                .getByRole('dialog')
                .getByRole('button', { name: buttonName });
            const count = await button.count();
            if (count > 0) {
                await button.click();
                return;
            }
        }
        // No modal present — Play loaded directly without a start modal (env already provisioned)
    }
    async waitForInstanceDetailsToBeLoaded() {
        const maxRetries = 3;
        let attempts = 0;
        while (attempts < maxRetries) {
            try {
                await (0, test_1.expect)(this.page.getByText(/process instance key/i)).toBeVisible({
                    timeout: maxWaitTimeSeconds,
                });
                await (0, test_1.expect)(this.page.getByText(/This process instance has no variables/i)).toBeVisible({
                    timeout: maxWaitTimeSeconds,
                });
                return;
            }
            catch (error) {
                if (attempts >= maxRetries - 1)
                    throw error;
                await this.page.reload();
                const startButton = this.restartProcess.or(this.startInstanceButton);
                await startButton.waitFor({ state: 'visible', timeout: 30000 });
                await startButton.click();
                attempts++;
            }
        }
    }
    async waitForNextElementToBeActive(historyItem) {
        const locator = this.page.getByText(new RegExp(`^${historyItem}`, 'i'));
        const maxRetries = 6; // ~1 min with the waits below
        for (let attempt = 1; attempt <= maxRetries; attempt++) {
            try {
                await (0, test_1.expect)(locator.first()).toBeVisible({ timeout: 20000 });
                return;
            }
            catch (e) {
                if (attempt === maxRetries)
                    throw e;
                // force UI to update
                await this.page.reload();
                await this.diagram.waitFor({ state: 'visible', timeout: 30000 });
                await this.page.waitForTimeout(5000);
            }
        }
    }
    async waitForProcessToBeCompleted() {
        await (0, test_1.expect)(this.page.getByText('Completed')).toBeVisible({
            timeout: maxWaitTimeSeconds,
        });
    }
    // The "<task> completed manually" success toast renders in the top-right
    // corner, directly on top of the "Save scenario" button. These Play toasts
    // do not auto-dismiss, so passively waiting for them to hide times out and a
    // force-click then lands on the toast instead of the button — leaving the
    // save-scenario modal closed. Actively close every open toast before
    // interacting with the button.
    async dismissNotifications() {
        const closeButtons = this.page.getByLabel('Close notification');
        for (let i = 0; i < 5; i++) {
            const count = await closeButtons.count().catch(() => 0);
            if (count === 0) {
                break;
            }
            await closeButtons
                .first()
                .click({ timeout: 5000 })
                .catch(() => { });
            await this.page.waitForTimeout(300);
        }
        await this.notifications
            .first()
            .waitFor({ state: 'hidden', timeout: 10000 })
            .catch(() => { });
    }
    async clickSaveScenarioButton() {
        await this.dismissNotifications();
        const isModalVisible = await this.saveScenarioModal
            .isVisible()
            .catch(() => false);
        if (!isModalVisible) {
            await (0, sleep_1.sleep)(3000);
            await (0, test_1.expect)(this.saveScenarioButton).toBeVisible({ timeout: 30000 });
            await (0, test_1.expect)(this.saveScenarioButton).toBeEnabled({ timeout: 30000 });
            await this.saveScenarioButton.click({ force: true, timeout: 30000 });
            // Wait for the save-scenario modal to actually open before returning so
            // that enterScenarioName finds the dialog immediately on the first attempt.
            // Trace evidence shows the modal can take up to ~20 s to appear after the
            // button click, so use a 30 s window here.
            await this.saveScenarioModal
                .waitFor({ state: 'visible', timeout: 30000 })
                .catch(() => { });
        }
    }
    async clickViewScenarioButton() {
        await this.viewScenarioButton.click();
    }
    async enterScenarioName(scenarioName) {
        await (0, expectLocatorWithRetry_1.expectLocatorWithRetry)(this.page, this.dialog, {
            postAction: async () => {
                // A lingering success toast can cover the "Save scenario" button and
                // swallow the recovery click, so clear it before re-clicking.
                await this.dismissNotifications();
                await this.saveScenarioButton.click({ timeout: 5000 }).catch(() => { });
            },
        });
        const input = this.dialog.locator('input#scenario-name');
        await input.waitFor({ state: 'visible', timeout: 10000 });
        await input.clear();
        await input.fill(scenarioName);
    }
    async updateScenarioName(newScenarioName) {
        await this.enterScenarioNameInput.fill(newScenarioName);
    }
    async clickViewAllScenariosButton() {
        // After saving a scenario Play shows an "instance-details" view (Instance
        // History + Variables) with a compact scenario header.  The "(View all)"
        // button expands that header into the full scenarios-list view where
        // "Run all scenarios" lives.
        //
        // If the button is not present we are NOT in the full-list view — we are in
        // the instance-details view.  Reloading the page resets Play to the
        // scenarios-list view reliably (confirmed by screenshots from past failures).
        await this.notifications
            .first()
            .waitFor({ state: 'hidden', timeout: 30000 })
            .catch(() => { });
        const buttonVisible = await this.viewAllScenariosButton
            .waitFor({ state: 'visible', timeout: 10000 })
            .then(() => true)
            .catch(() => false);
        if (!buttonVisible) {
            // Reload to switch from instance-details view to scenarios-list view.
            await this.page.reload();
            await this.diagram
                .waitFor({ state: 'visible', timeout: 30000 })
                .catch(() => { });
            return;
        }
        // Button found — click it to expand the compact view into the full list.
        await (0, expectLocatorWithRetry_1.expectLocatorWithRetry)(this.page, this.viewAllScenariosButton, {
            totalTimeout: 60000,
            visibilityTimeout: 10000,
            maxRetries: 3,
            preAction: async () => {
                await this.notifications
                    .first()
                    .waitFor({ state: 'hidden', timeout: 30000 })
                    .catch(() => { });
            },
        });
        await this.viewAllScenariosButton.click({ force: true, timeout: 10000 });
    }
    async confirmSaveScenario() {
        await this.confirmSaveScenarioButton.click({ timeout: 30000 });
    }
    async getDeleteIconForScenario(scenarioName) {
        const scenarioRow = this.page
            .locator('tr')
            .filter({ has: this.page.getByText(scenarioName, { exact: true }) });
        return scenarioRow.getByLabel('Delete scenario');
    }
    async deleteScenario(scenarioName) {
        const deleteIcon = await this.getDeleteIconForScenario(scenarioName);
        await deleteIcon.click();
    }
    async confirmDeleteScenario() {
        await this.confirmDeleteScenarioButton.click();
    }
    async clickRunAllScenariosButton() {
        // Trace evidence: after clicking "(View all)" the Scenarios panel sometimes
        // fails to render (view stays on instance-details) and a stray "Add variable"
        // dialog can appear and block the button.  Retry loop:
        //   1. Dismiss any open dialog (Escape).
        //   2. Wait up to 10 s for the button.
        //   3. If still absent, reload — a fresh page load always lands on the
        //      Scenarios-list view where the button is immediately available.
        const maxRetries = 3;
        for (let attempt = 1; attempt <= maxRetries; attempt++) {
            // Dismiss any blocking dialog (e.g. "Add variable") with Escape.
            const anyDialog = this.page.getByRole('dialog');
            if (await anyDialog
                .first()
                .isVisible()
                .catch(() => false)) {
                await this.page.keyboard.press('Escape');
                await anyDialog
                    .first()
                    .waitFor({ state: 'hidden', timeout: 5000 })
                    .catch(() => { });
            }
            const found = await this.runAllScenariosButton
                .waitFor({ state: 'visible', timeout: 10000 })
                .then(() => true)
                .catch(() => false);
            if (found) {
                await this.runAllScenariosButton.click({ timeout: 15000 });
                return;
            }
            if (attempt < maxRetries) {
                // Scenarios-list view didn't render after (View all) click — reload to
                // reset Play to the default scenarios-list view.
                await this.page.reload();
                await this.diagram
                    .waitFor({ state: 'visible', timeout: 30000 })
                    .catch(() => { });
            }
        }
        // Final attempt after exhausting retries.
        await this.runAllScenariosButton.click({ timeout: 30000 });
    }
    async clickmessagePublishButton() {
        await this.diagram.waitFor({ state: 'visible', timeout: 30000 });
        const publishIcon = this.diagram
            .locator('div.djs-overlays')
            .getByLabel('Publish message', { exact: true });
        await publishIcon.click({ timeout: 30000 });
    }
    async assertScenarioCompleted(scenarioName) {
        await (0, expectLocatorWithRetry_1.expectLocatorWithRetry)(this.page, this.page
            .locator('tr', { hasText: scenarioName })
            .locator('text=Completed'), {
            totalTimeout: 90000,
            visibilityTimeout: 20000,
            postAction: async () => {
                if (await this.retryButton.first().isVisible()) {
                    await this.retryButton.first().click();
                }
            },
        });
    }
}
exports.PlayPage = PlayPage;
