"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ClusterPage = void 0;
const test_1 = require("@playwright/test");
const sleep_1 = require("../../utils/sleep");
const clickLocatorWithRetry_1 = require("../../utils/assertionHelpers/clickLocatorWithRetry");
class ClusterPage {
    page;
    defaultClusterType = 'G3-Dev';
    belgiumRegion;
    londonRegion;
    finlandRegion;
    testInfo;
    createNewClusterButton;
    clusterNameInput;
    createClusterButton;
    clusterCreatingStatusText;
    clusterHealthyStatusText;
    clusterHealthyIndicatorSelector;
    clusterGenerationCompleteMessage;
    clustersBreadcrumb;
    deleteClusterButton;
    confirmDeleteInput;
    dangerDeleteButton;
    testClusterLink;
    connectorSecretsTab;
    createAClusterButton;
    clusterVersionOption;
    g3DevClusterType;
    alphaTab;
    devTab;
    stableTab;
    qaGenerationsTab;
    gcpRegion;
    clustersList;
    tasklistV1Api;
    tasklistV2Api;
    cluster;
    clusterHealthiness;
    clusterLink;
    dialog;
    clusterVersion;
    constructor(page, testInfo) {
        this.page = page;
        this.testInfo = testInfo;
        this.createNewClusterButton = this.createNewClusterButton = page
            .getByRole('button', {
            name: 'Create new cluster',
        })
            .or(this.page.getByRole('button', {
            name: 'Create cluster',
        }));
        this.clusterNameInput = page.getByPlaceholder('Enter a name for the cluster');
        this.createClusterButton = page.getByRole('button', {
            name: 'Create cluster',
        });
        this.clusterCreatingStatusText = page.getByText('Creating', { exact: true });
        this.clusterHealthyStatusText = page.getByText('Healthy', { exact: true });
        this.clusterHealthyIndicatorSelector = '[class="healthy healthIndicator"]';
        this.clusterGenerationCompleteMessage = page.getByText('Test Cluster is healthy and ready to use');
        this.clustersBreadcrumb = page
            .getByLabel('Breadcrumb')
            .getByText('Clusters', { exact: true });
        this.deleteClusterButton = page.getByRole('button', { name: 'Delete' });
        this.confirmDeleteInput = page.getByRole('textbox', {
            name: 'Type the word DELETE to confirm',
        });
        this.dangerDeleteButton = page.getByRole('button', { name: 'danger Delete' });
        this.testClusterLink = page.getByRole('link', { name: 'Test Cluster' });
        this.connectorSecretsTab = page.getByRole('tab', {
            name: 'Connector Secrets',
        });
        this.belgiumRegion = page.locator('label').filter({
            hasText: 'Chaos, Belgium, Europe (europe-west1)',
        });
        this.londonRegion = page.locator('label').filter({
            hasText: 'London, Europe (europe-west2)',
        });
        this.finlandRegion = page.locator('label').filter({
            hasText: 'Finland, Europe (europe-north1)',
        });
        this.createAClusterButton = page.getByRole('button', {
            name: 'Create a Cluster',
        });
        this.clusterVersionOption = page.getByText(`${process.env.CLUSTER_VERSION}`, { exact: true });
        this.g3DevClusterType = page.locator('label').filter({
            hasText: this.defaultClusterType,
        });
        this.alphaTab = page.getByRole('tab', { name: 'Alpha' });
        this.devTab = page.getByRole('tab', { name: 'Internal Dev' });
        this.stableTab = page.getByRole('tab', { name: 'Stable' });
        this.qaGenerationsTab = page.getByRole('tab', { name: 'QA Generations' });
        this.gcpRegion = page.getByRole('tab', {
            name: 'GCP',
        });
        this.clustersList = page
            .getByRole('row')
            .filter({ hasNotText: 'Generation' }); //Filter out header row
        this.tasklistV1Api = page.getByText('Tasklist API v1 (legacy)');
        this.tasklistV2Api = page.getByText('Tasklist API v2');
        this.cluster = (clusterName) => this.clustersList.filter({ hasText: clusterName });
        this.clusterHealthiness = (clusterName) => this.cluster(clusterName).getByText(`Healthy`, {
            exact: true,
        });
        this.clusterLink = (clusterName) => this.cluster(clusterName).getByRole('link', {
            name: clusterName,
        });
        this.dialog = page.getByRole('dialog');
        this.clusterVersion = (clusterName, versionText) => this.cluster(clusterName).getByText(versionText, { exact: true });
    }
    async clickClusterBanner() {
        await this.page
            .getByRole('banner')
            .getByRole('link', { name: 'Clusters' })
            .click({ timeout: 60000 });
    }
    async deleteCluster(name) {
        await this.page.waitForTimeout(1000);
        const clusterInstance = this.cluster(name).first();
        if ((await clusterInstance.count()) <= 0) {
            console.log(`No clusters found with name ${name}, skipping deletion.`);
            return;
        }
        await clusterInstance.waitFor({ timeout: 10000 });
        let deleteButtons = this.cluster(name).getByRole('button', {
            name: 'Delete',
        });
        while ((await deleteButtons.count()) > 0) {
            const deleteButton = deleteButtons.nth(0);
            if (await deleteButton.isVisible()) {
                try {
                    await deleteButton.click({ force: true, timeout: 60000 });
                }
                catch (error) {
                    await this.clickClusterBanner();
                    await (0, test_1.expect)(deleteButton).toBeVisible({ timeout: 60000 });
                    await deleteButton.click({ force: true, timeout: 60000 });
                }
                await (0, test_1.expect)(this.dialog).toBeVisible({ timeout: 60000 });
                await (0, test_1.expect)(this.confirmDeleteInput).toBeVisible({ timeout: 60000 });
                await this.confirmDeleteInput.click({ force: true, timeout: 60000 });
                await this.confirmDeleteInput.fill('DELETE', { timeout: 60000 });
                const dangerDeleteButtons = await this.dangerDeleteButton.all();
                const lastDangerDeleteButton = dangerDeleteButtons[dangerDeleteButtons.length - 1];
                await lastDangerDeleteButton.click();
                await (0, test_1.expect)(this.page.getByText('Deleting...')).not.toBeVisible({
                    timeout: 60000,
                });
                await (0, test_1.expect)(this.dialog).not.toBeVisible({ timeout: 60000 });
                // Refresh the list of delete buttons
                deleteButtons = this.cluster(name).getByRole('button', {
                    name: 'Delete',
                });
            }
        }
        await this.page.waitForTimeout(500);
    }
    async clickClusterTab() {
        await Promise.race([
            this.page.waitForURL(/\/clusters/, {
                timeout: 60000,
            }),
            this.clickClusterBanner(),
        ]);
    }
    async clickCreateNewClusterButton() {
        await (0, clickLocatorWithRetry_1.clickLocatorWithRetry)(this.page, this.createNewClusterButton, {
            visibilityTimeout: 20000,
            totalTimeout: 80000,
            postAction: async () => {
                await this.clickClusterBanner();
            },
        });
    }
    async clickClusterNameInput() {
        await (0, clickLocatorWithRetry_1.clickLocatorWithRetry)(this.page, this.clusterNameInput, {
            postAction: async () => {
                await this.clickClusterBanner();
            },
        });
    }
    async fillClusterNameInput(name) {
        await this.clusterNameInput.fill(name);
    }
    extractClusterIdFromUrl(url) {
        const clusterIdMatch = url.match(/\/cluster\/([a-f0-9-]+)/);
        return clusterIdMatch ? clusterIdMatch[1] : null;
    }
    async logClusterCreationInfo(clusterName) {
        try {
            await this.page.waitForURL(/\/cluster\/[a-f0-9-]+$/, { timeout: 60000 });
            const currentUrl = this.page.url();
            const clusterId = this.extractClusterIdFromUrl(currentUrl);
            console.log(`🎉 Cluster "${clusterName}" created successfully with ID: ${clusterId || 'Unknown'} at ${new Date().toISOString()}`);
        }
        catch (error) {
            console.warn(`⚠️ Could not extract cluster info for "${clusterName}": ${error}`);
        }
    }
    async createCluster(name, region = 'GCP', tasklistV1Api) {
        await this.clickClusterTab();
        await this.deleteCluster(name);
        await this.clickCreateNewClusterButton();
        await this.clickClusterNameInput();
        await this.fillClusterNameInput(name);
        await this.clickClusterType();
        await this.page.getByRole('tab', { name: region }).click();
        await this.selectSubRegion(region);
        await this.clickClusterOption();
        await this.determineTasklistAPI(tasklistV1Api);
        await this.clickCreateClusterButton();
        await this.logClusterCreationInfo(name);
        await this.clickClustersBreadcrumb(name);
    }
    async determineTasklistAPI(tasklistV1Api) {
        // Determine tasklistV1Api from project name if not explicitly provided
        if (tasklistV1Api === undefined) {
            const projectName = this.testInfo?.project.name || '';
            tasklistV1Api =
                projectName.includes('v1') || projectName.includes('8.10-v1');
            console.log(`Auto-detected Tasklist API version from project '${projectName}': ${tasklistV1Api ? 'V1' : 'V2'}`);
        }
        if (tasklistV1Api) {
            await this.clickTasklistV1Api();
        }
    }
    async clickClusterType() {
        if (process.env.IS_PROD !== 'true') {
            await (0, test_1.expect)(this.g3DevClusterType).toBeVisible({ timeout: 60000 });
            await this.g3DevClusterType.click();
        }
    }
    async clickCreateClusterButton() {
        await this.createClusterButton.click();
    }
    async clickClustersBreadcrumb(clusterName) {
        const creatingCheckRegex = new RegExp(`${clusterName}.*?Creatingdev`);
        const healthyCheckRegex = new RegExp(`${clusterName}.*?Healthydev`);
        const clusterRowVisible = this.page
            .getByText(creatingCheckRegex)
            .or(this.page.getByText(healthyCheckRegex));
        try {
            await (0, test_1.expect)(this.clustersBreadcrumb).toBeVisible({ timeout: 60000 });
            await this.clustersBreadcrumb.click({ timeout: 60000 });
            await (0, test_1.expect)(this.page.getByText(creatingCheckRegex)).toBeVisible({
                timeout: 30000,
            });
            return;
        }
        catch (error) {
            await this.clickClusterBanner();
            const seenViaBanner = await clusterRowVisible
                .isVisible({ timeout: 30000 })
                .catch(() => false);
            if (seenViaBanner)
                return;
            // The Clusters list sometimes serves a stale page after a create that
            // didn't propagate yet (parallel setup workers, list-fetch race).
            // Reload once and give the list a longer window before giving up.
            console.warn(`clickClustersBreadcrumb: '${clusterName}' not visible after banner nav; reloading and extending wait to 90s.`);
            await this.page.reload({ waitUntil: 'domcontentloaded' }).catch(() => { });
            await (0, test_1.expect)(clusterRowVisible).toBeVisible({ timeout: 90000 });
        }
    }
    async clickClusterLink(name) {
        await (0, clickLocatorWithRetry_1.clickLocatorWithRetry)(this.page, this.clusterLink(name), {
            postAction: async () => {
                await this.clickClusterBanner();
            },
        });
    }
    async clickConnectorSecretsTab(clusterName) {
        await (0, clickLocatorWithRetry_1.clickLocatorWithRetry)(this.page, this.connectorSecretsTab, {
            postAction: async () => {
                await this.clickClusterBanner();
                await this.clickClusterLink(clusterName);
            },
        });
    }
    async clickLatestStableForMinorVersion() {
        const minorVersion = process.env.MINOR_VERSION;
        if (!minorVersion) {
            throw new Error('MINOR_VERSION env var is required for auto-detecting the latest stable generation');
        }
        const escapedVersion = minorVersion.replace('.', '\\.');
        const stableOption = this.page
            .getByText(new RegExp(`^Camunda ${escapedVersion}\\+`))
            .first();
        await this.clickStableTab();
        await (0, test_1.expect)(stableOption).toBeVisible({ timeout: 60000 });
        console.log(`Auto-selecting latest stable generation for Camunda ${minorVersion}`);
        await stableOption.click({ timeout: 60000 });
    }
    async clickClusterOption() {
        if (!process.env.CLUSTER_VERSION) {
            await this.clickLatestStableForMinorVersion();
            return;
        }
        const tabs = [
            this.clickDevTab,
            this.clickQAGenerationsTab,
            this.clickStableTab,
            this.clickAlphaTab,
        ];
        for (const clickTab of tabs) {
            try {
                await clickTab.call(this);
                await this.clusterVersionOption.click({ timeout: 60000 });
                return;
            }
            catch (error) {
                console.error('Error clicking tab or cluster version option:', error);
            }
        }
        throw new Error('Failed to find cluster generation');
    }
    async createClusterIfNotExists(clusterName, region = 'GCP') {
        await (0, sleep_1.sleep)(15000);
        if ((await this.cluster(clusterName).isVisible({ timeout: 20000 })) &&
            (await this.clusterHealthiness(clusterName).isVisible({ timeout: 20000 }))) {
            console.log(`${clusterName} exists and is healthy.`);
        }
        else {
            await this.createCluster(clusterName, region);
            await this.assertClusterHealthyStatusWithRetry(clusterName, 120000, 600000);
        }
    }
    async assertClusterHealthyStatusWithRetry(name, visibilityTimeout = 100000, totalTimeout = 500000) {
        const unhealthy = this.cluster(name).getByText('Unhealthy', { exact: true });
        const start = Date.now();
        const maxRetries = 5;
        let attempt = 0;
        let unhealthyConsecutive = 0;
        let lastErr;
        while (Date.now() - start < totalTimeout && attempt < maxRetries) {
            attempt++;
            try {
                await this.page.reload();
                await (0, test_1.expect)(this.cluster(name)).toBeVisible({ timeout: 10000 });
                // Only treat Unhealthy as terminal if it persists across two
                // consecutive polling attempts. A cluster mid-provisioning can flash
                // through Unhealthy briefly during the Unhealthy → Creating → Healthy
                // transition and we don't want a single transient reading to abort
                // the whole helper — earlier versions gated only on `attempt > 1`
                // which still false-positived when the transient Unhealthy showed up
                // on attempt 2.
                const isUnhealthy = await unhealthy
                    .isVisible({ timeout: 2000 })
                    .catch(() => false);
                if (isUnhealthy) {
                    unhealthyConsecutive++;
                }
                else {
                    unhealthyConsecutive = 0;
                }
                if (unhealthyConsecutive >= 2) {
                    throw new Error(`Cluster "${name}" reached terminal Unhealthy state after ${Math.round((Date.now() - start) / 1000)}s (${unhealthyConsecutive} consecutive Unhealthy polls); aborting Healthy poll.`);
                }
                await this.clusterHealthiness(name).waitFor({
                    state: 'visible',
                    timeout: visibilityTimeout,
                });
                console.log('Cluster is healthy:', name);
                return;
            }
            catch (err) {
                if (err instanceof Error &&
                    err.message.includes('terminal Unhealthy state')) {
                    throw err;
                }
                lastErr = err;
                console.log(`Attempt ${attempt} failed for cluster "${name}" Healthy check, retrying...`, err instanceof Error ? err.message : err);
                await this.page.waitForTimeout(500);
            }
        }
        throw new Error(`Cluster "${name}" did not become Healthy after ${attempt} attempts. Last error: ${lastErr instanceof Error ? lastErr.message : lastErr}`);
    }
    async clickAlphaTab() {
        await this.alphaTab.click();
    }
    async clickDevTab() {
        await this.devTab.click();
    }
    async clickStableTab() {
        await this.stableTab.click();
    }
    async clickQAGenerationsTab() {
        await this.qaGenerationsTab.click();
    }
    async selectSubRegion(region) {
        if (region === 'GCP') {
            if (process.env.IS_PROD == 'true') {
                await (0, test_1.expect)(this.londonRegion).toBeVisible({ timeout: 20000 });
                await this.londonRegion.click();
            }
            else {
                const regions = [
                    { name: 'Belgium', locator: this.belgiumRegion },
                    { name: 'Finland', locator: this.finlandRegion },
                ];
                const chosen = regions[Math.floor(Math.random() * regions.length)];
                console.log(`Selected region: ${chosen.name}`);
                await (0, test_1.expect)(chosen.locator).toBeVisible({ timeout: 20000 });
                await chosen.locator.click();
            }
        }
    }
    async clickGCPRegion() {
        const regionSelectionEnabled = process.env.REGION_SELECTION === 'true';
        if (regionSelectionEnabled) {
            await (0, test_1.expect)(this.gcpRegion).toBeVisible({ timeout: 40000 });
            await this.gcpRegion.click();
        }
    }
    async clickTasklistV1Api() {
        try {
            await (0, test_1.expect)(this.tasklistV1Api).toBeVisible({ timeout: 60000 });
            await this.tasklistV1Api.click({ timeout: 30000 });
        }
        catch {
            return;
        }
    }
}
exports.ClusterPage = ClusterPage;
