import { Page, Locator, expect } from '@playwright/test';

export class ChatPage {
    readonly page: Page;
    readonly newChatButton: Locator;
    readonly chatInput: Locator;
    readonly sendButton: Locator;
    readonly messageBubbles: Locator;
    readonly sentMessageBubbles: Locator;
    readonly receivedMessageBubbles: Locator;

    constructor(page: Page) {
        this.page = page;

        // New Chat (Refresh Icon in Sidebar Header - usually the last button in that row)
        this.newChatButton = page.locator('div.flex.items-center.justify-between button').last();

        // Input (scoped to main chat composer to avoid auth-modal fields)
        this.chatInput = page
            .locator('main textarea[placeholder="Ask anything..."], main input[placeholder="Ask anything..."]')
            .first();

        // Send Button
        this.sendButton = page.locator('button.mr-3');

        // Messages
        this.messageBubbles = page.locator('div.group\\/message');

        // Specific types based on classes
        this.sentMessageBubbles = page.locator('div.group\\/message.bg-marine-0');
        this.receivedMessageBubbles = page.locator('div.group\\/message.bg-green-blue-8');
    }

    async startNewChat() {
        await this.dismissChatOverlays();

        const roleBasedNewChat = this.page.getByRole('button', {
            name: /new chat|start new|new conversation|create new/i
        });
        const candidates = [roleBasedNewChat.first(), this.newChatButton];

        for (const candidate of candidates) {
            if (await candidate.isVisible({ timeout: 2000 }).catch(() => false)) {
                await candidate.click({ timeout: 5000 });
                return;
            }
        }

        // Some UI states are already at an empty conversation with input ready.
        if (await this.chatInput.isVisible({ timeout: 2000 }).catch(() => false)) {
            return;
        }

        throw new Error('Unable to start a new chat: no new-chat control found.');
    }

    async sendMessage(text: string) {
        await this.dismissChatOverlays();
        const loginButton = this.page.getByRole('button', { name: /Log In\/ Sign Up/i }).first();
        const loggedOut = await loginButton.isVisible({ timeout: 1000 }).catch(() => false);
        if (loggedOut) {
            throw new Error('Cannot send chat message while logged out (Log In/Sign Up visible).');
        }
        const authDialog = this.page.getByRole('dialog').first();
        if (await authDialog.isVisible({ timeout: 1000 }).catch(() => false)) {
            await this.page.press('body', 'Escape').catch(() => { });
            await this.page.waitForTimeout(300);
        }
        await expect(this.chatInput).toBeVisible({ timeout: 30000 });
        await expect(this.chatInput).toBeEditable({ timeout: 60000 });
        await this.chatInput.fill(text);
        await this.page.waitForTimeout(500);
        await this.chatInput.press('Enter');
    }

    async getLastSentMessage(): Promise<string> {
        await this.sentMessageBubbles.last().waitFor({ state: 'visible' });
        return await this.sentMessageBubbles.last().innerText();
    }

    async getLastReceivedMessage(): Promise<string> {
        await this.receivedMessageBubbles.last().waitFor({ state: 'visible', timeout: 30000 });
        return await this.receivedMessageBubbles.last().innerText();
    }

    async getMessageCount(): Promise<number> {
        return await this.messageBubbles.count();
    }

    async getMarketplaceWidget(): Promise<Locator> {
        // Fallback to checking the Heading
        return this.page.getByRole('heading', { name: 'Marketplace' });
    }

    async getReceivedMessageCount(): Promise<number> {
        return await this.receivedMessageBubbles.count();
    }

    async waitForNewReceivedMessage(previousCount: number, timeout = 90000) {
        await expect(async () => {
            const currentCount = await this.receivedMessageBubbles.count();
            console.log(`Waiting for BOT response... Previous: ${previousCount}, Current: ${currentCount}`);
            // New chat states can replace/reset message groups; treat any count
            // change as progress instead of requiring monotonic growth.
            expect(currentCount).not.toBe(previousCount);
        }).toPass({ timeout }); // Increased timeout for bot latency
    }

    async waitForNewResponse(previousCount: number, timeout = 90000) {
        await this.waitForNewReceivedMessage(previousCount, timeout);
    }

    async dismissChatOverlays() {
        const accountTypePrompt = this.page.getByText(/What best describes you\?/i).first();
        if (await accountTypePrompt.isVisible({ timeout: 2000 }).catch(() => false)) {
            const businessUserBtn = this.page.getByRole('button', { name: /Business User/i }).first();
            const continueBtn = this.page.getByRole('button', { name: /^Continue$/i }).first();
            if (await businessUserBtn.isVisible({ timeout: 2000 }).catch(() => false)) {
                await businessUserBtn.click({ force: true, timeout: 2000 }).catch(() => { });
            }
            if (await continueBtn.isVisible({ timeout: 2000 }).catch(() => false)) {
                await continueBtn.click({ force: true, timeout: 2000 }).catch(() => { });
            }

            const consumerBtn = this.page.getByRole('button', { name: /Consumer/i }).first();
            if (await consumerBtn.isVisible({ timeout: 2000 }).catch(() => false)) {
                await consumerBtn.click({ force: true, timeout: 2000 }).catch(() => { });
                if (await continueBtn.isVisible({ timeout: 2000 }).catch(() => false)) {
                    await continueBtn.click({ force: true, timeout: 2000 }).catch(() => { });
                }
            }
            await this.page.waitForTimeout(800);
        }

        const tipsHeader = this.page.getByText(/Here is tips how to use our platform/i).first();
        const tipsSkipButton = this.page.getByRole('button', { name: /^Skip for now$/i }).first();
        if (await tipsHeader.isVisible({ timeout: 3000 }).catch(() => false)) {
            if (await tipsSkipButton.isVisible({ timeout: 2000 }).catch(() => false)) {
                await tipsSkipButton.click({ force: true, timeout: 2000 }).catch(() => { });
            } else {
                const continueBtn = this.page.getByRole('button', { name: /^Continue$/i }).first();
                if (await continueBtn.isVisible({ timeout: 2000 }).catch(() => false)) {
                    await continueBtn.click({ force: true, timeout: 2000 }).catch(() => { });
                }
            }
            await this.page.waitForTimeout(800);
        }

        const welcomeCardHeader = this.page.getByText(/Welcome to RoboCorp!/i).first();
        if (await welcomeCardHeader.isVisible({ timeout: 2000 }).catch(() => false)) {
            const closeButton = this.page
                .locator('button')
                .filter({ has: this.page.locator('img') })
                .last();
            if (await closeButton.isVisible({ timeout: 2000 }).catch(() => false)) {
                await closeButton.click({ force: true });
            }
        }

        const campaignSkipButton = this.page.getByRole('button', { name: /^Skip for now$/i }).first();
        if (await campaignSkipButton.isVisible({ timeout: 2000 }).catch(() => false)) {
            await campaignSkipButton.click({ force: true, timeout: 2000 }).catch(() => { });
        }

        const startBuildingButton = this.page.getByRole('button', { name: /Start building/i }).first();
        if (await startBuildingButton.isVisible({ timeout: 2000 }).catch(() => false)) {
            await startBuildingButton.click({ force: true, timeout: 2000 }).catch(() => { });
        }

        const dontShowAgain = this.page.getByText(/Don't show again/i).first();
        if (await dontShowAgain.isVisible({ timeout: 2000 }).catch(() => false)) {
            await dontShowAgain.click({ force: true, timeout: 2000 }).catch(() => { });
        }
    }

    async waitForChatReady() {
        const deadline = Date.now() + 30000;
        const loginButton = this.page.getByRole('button', { name: /Log In\/ Sign Up/i }).first();
        const userDropdown = this.page.getByRole('button', { name: /Toggle User Dropdown/i }).first();
        const walletToggle = this.page.getByRole('button', { name: /Toggle Wallet/i }).first();
        while (Date.now() < deadline) {
            await this.dismissChatOverlays();

            const hasLoginButton = await loginButton.isVisible({ timeout: 1000 }).catch(() => false);
            const hasChatInput = await this.chatInput.isVisible({ timeout: 1000 }).catch(() => false);
            const hasUserDropdown = await userDropdown.isVisible({ timeout: 1000 }).catch(() => false);
            const hasWalletToggle = await walletToggle.isVisible({ timeout: 1000 }).catch(() => false);

            if (!hasLoginButton && hasChatInput && (hasUserDropdown || hasWalletToggle)) {
                return;
            }

            await this.page.waitForTimeout(500);
        }

        await expect(this.chatInput).toBeVisible({ timeout: 10000 });
        await expect(loginButton).toHaveCount(0, { timeout: 10000 });
        const dropdownVisible = await userDropdown.isVisible({ timeout: 1000 }).catch(() => false);
        const walletVisible = await walletToggle.isVisible({ timeout: 1000 }).catch(() => false);
        if (!dropdownVisible && !walletVisible) {
            throw new Error('Authenticated chat indicators are missing (no user dropdown or wallet toggle).');
        }
    }

    async refreshChat() {
        const refreshButton = this.page.locator('#btn-refresh-chat');
        if (!(await refreshButton.isVisible({ timeout: 5000 }).catch(() => false))) {
            return;
        }

        await refreshButton.click({ force: true });
        await this.page.waitForTimeout(1000);
        await this.dismissChatOverlays();
        await expect(this.chatInput).toBeVisible({ timeout: 30000 });
        await expect(this.chatInput).toBeEditable({ timeout: 60000 });
    }

    async uploadFile(filePath: string) {
        // Handle hidden file input
        const fileInput = this.page.locator('input[type="file"]');
        await fileInput.waitFor({ state: 'attached', timeout: 10000 });
        await fileInput.setInputFiles(filePath);
    }

    async clearChatAndCanvas() {
        await this.dismissChatOverlays();

        const clickFirstVisible = async (candidates: Locator[]) => {
            for (const candidate of candidates) {
                if (await candidate.isVisible({ timeout: 1500 }).catch(() => false)) {
                    await candidate.click({ force: true, timeout: 5000 }).catch(() => {});
                    await this.page.waitForTimeout(300);
                    return true;
                }
            }
            return false;
        };

        const canvasCandidates = [
            this.page.getByRole('button', { name: /canvas/i }).first(),
            this.page.locator('button[aria-label*="Canvas" i]').first(),
            this.page.locator('button[title*="Canvas" i]').first()
        ];

        await clickFirstVisible(canvasCandidates);

        const refreshChat = this.page.locator('#btn-refresh-chat').first();
        if (await refreshChat.isVisible({ timeout: 2000 }).catch(() => false)) {
            await refreshChat.click({ force: true, timeout: 5000 }).catch(() => {});
            return;
        }

        const clearChatCandidates = [
            this.page.getByRole('button', { name: /clear chat|clear conversation|clear history/i }).first(),
            this.page.locator('button[aria-label*="Clear chat" i]').first(),
            this.page.locator('button[aria-label*="Clear conversation" i]').first(),
            this.page.locator('button[title*="Clear chat" i]').first()
        ];

        const clickedClear = await clickFirstVisible(clearChatCandidates);
        if (clickedClear) {
            const confirmButton = this.page
                .getByRole('button', { name: /clear|confirm|yes|ok/i })
                .first();
            if (await confirmButton.isVisible({ timeout: 1500 }).catch(() => false)) {
                await confirmButton.click({ force: true, timeout: 5000 }).catch(() => {});
            }
        }
    }
}
