import { test, expect, type BrowserContext, type Page } from '@playwright/test';
import { RegistrationPage } from '../../pages/RegistrationPage';
import { waitForActivationCode } from '../../utils/ActivationCodeUtils';
import { ChatPage } from '../../pages/ChatPage';
import process from 'process';

let context: BrowserContext;
let page: Page;
let registrationPage: RegistrationPage;
let email: string;
let password: string;
let chatPage: ChatPage;
const ASSET_CREATION_TIMEOUT_MS = 120000;

const SEARCH_LIMIT_MESSAGE = /Your search query limit of 10 has reached\./i;
const NO_ACTIVE_WALLET_MESSAGE = /No active wallet found\./i;
const B1_CHAT_LIMIT_PROMPTS = {
    agent: 'I need to create new agent without domain',
    widget: 'I need to create a widget without domain',
    curatedData: 'I need to create a dataset without domain',
    utility: 'I need to create a utility without domain',
    chain: 'I need to create a chain of agents without domain',
    media: 'I need to create media without domain',
    model: 'I need to create a model without domain',
    marketplace: 'I need a case management system',
    legalCaseAnalyser: 'I need legal case analyser',
    knowledgeFabric: 'Show me knowledge fabric'
};

async function isVisible(locator: ReturnType<Page['locator']> | any, timeout = 1500): Promise<boolean> {
    return locator.isVisible({ timeout }).catch(() => false);
}

async function closeWelcomePopupOnly(page: Page, appearanceTimeout = 10000) {
    const welcomePopup = page.getByText(/Welcome to RoboCorp!/i).first();

    const deadline = Date.now() + appearanceTimeout;
    while (Date.now() < deadline) {
        if (await isVisible(welcomePopup, 1000)) break;
        await page.waitForTimeout(250);
    }

    if (!(await isVisible(welcomePopup, 5000))) return;

    if (await isVisible(welcomePopup, 5000)) {
        await page.click('body', { position: { x: 0, y: 0 } }).catch(() => {});
        await welcomePopup.waitFor({ state: 'hidden', timeout: 10000 }).catch(() => {});
    }

    await expect(welcomePopup).toBeHidden({ timeout: 10000 });
}

async function sendSearchAndWaitForResponse(query: string) {
    const previousCount = await chatPage.getReceivedMessageCount();
    await chatPage.sendMessage(query);
    await chatPage.waitForNewReceivedMessage(previousCount, ASSET_CREATION_TIMEOUT_MS);
    return chatPage.getLastReceivedMessage();
}

async function expectPromptDoesNotHitLimit(prompt: string) {
    const response = await sendSearchAndWaitForResponse(prompt);
    expect(response).not.toMatch(SEARCH_LIMIT_MESSAGE);
    await chatPage.refreshChat();
}

async function expectPromptHitsWalletGate(prompt: string) {
    await chatPage.sendMessage(prompt);
    await expect(page.getByText(NO_ACTIVE_WALLET_MESSAGE).last()).toBeVisible({ timeout: ASSET_CREATION_TIMEOUT_MS });

    const closeWidgetButton = page.getByRole('button', { name: /Close widget/i }).first();
    if (await closeWidgetButton.isVisible({ timeout: 3000 }).catch(() => false)) {
        await closeWidgetButton.click({ force: true }).catch(() => {});
    }

    const continueButton = page.getByRole('button', { name: /^Continue$/i }).first();
    if (await continueButton.isVisible({ timeout: 3000 }).catch(() => false)) {
        await continueButton.click({ force: true }).catch(() => {});
    }

    await expect(chatPage.chatInput).toBeEditable({ timeout: ASSET_CREATION_TIMEOUT_MS });
    await chatPage.refreshChat();
}

async function advanceTipsToChat(page: Page) {
    const continueButton = page.getByRole('button', { name: /^Continue$/i }).first();
    const tipsText = page.getByText(/Here is tips how to use our platform|Traditional search engines return links/i).first();
    const chatInput = page.getByPlaceholder('Ask anything...').first();
    const welcomeBanner = page.locator('#welcome-cards-banner');

    while (await continueButton.isVisible({ timeout: 20000 }).catch(() => false)) {
        page.waitForTimeout(2000);
        await continueButton.click({ timeout: 10000 }).catch(() => {});
    }

    while (!welcomeBanner.isVisible({ timeout: 1000 }).catch(() => false)) {
        const chatReady = await chatInput.isVisible({ timeout: 1000 }).catch(() => false);
        const tipsVisible = await tipsText.isVisible({ timeout: 1000 }).catch(() => false);
        if (chatReady && !tipsVisible) {
            return;
        }
        
        await Promise.race([
            welcomeBanner.waitFor({ state: 'visible', timeout: 3000 }).catch(() => null),
            page.waitForTimeout(800)
        ]);
    }
}

test.describe.serial('Business Users / B1 User', () => {
    test.describe.configure({ timeout: 180000 });

    test.beforeAll(async ({ browser }) => {
        context = await browser.newContext();
        page = await context.newPage();
        registrationPage = new RegistrationPage(page);
        chatPage = new ChatPage(page);

        email = `automation.b1+${Date.now()}@datafab.ai`;
        password = process.env.USER_PASSWORD?.trim() || 'Password123!';

        await registrationPage.gotoSignIn();
        await registrationPage.openSignUpFromSignIn();
        await registrationPage.submitSignUpCredentials(email, password);
        await registrationPage.submitFullName('Automation B One User');

        await registrationPage.waitForVerificationScreen();
        const code = await waitForActivationCode(email, { timeoutMs: 90000, intervalMs: 3000 });
        await registrationPage.enterVerificationCode(code);
        await registrationPage.clickVerify();
    });

    test.afterAll(async () => {
        await context?.close();
    });

    test('should register and activate successfully', async () => {
        await expect(page).toHaveURL(/\/sign-in\/account/i, { timeout: 30000 });
        await expect(page.locator('body')).not.toContainText(/please check your email/i, { timeout: 10000 });
    });

    test('should keep the user on the account selection milestone', async () => {
        await expect(page).toHaveURL(/\/sign-in\/account/i, { timeout: 10000 });
        await expect(page.locator('body')).toContainText(/What best describes you\?/i, { timeout: 30000 });
    });

    test('should show both account type options', async () => {
        await expect(registrationPage.individualUserOption).toBeVisible({ timeout: 30000 });
        await expect(registrationPage.businessUserOption).toBeVisible({ timeout: 30000 });
    });

    test('should not proceed to tips', async () => {
        await expect(page.locator('body')).not.toContainText(/Here is tips how to use our platform|Welcome to RoboCorp!/i, {
            timeout: 5000
        });
    });

    test('should clear the session and log in again with the same user', async () => {
        const browser = context.browser();
        if (!browser) {
            throw new Error('Browser instance is not available for creating a new session.');
        }
        await context.close();
        context = await browser.newContext();
        page = await context.newPage();
        registrationPage = new RegistrationPage(page);
        chatPage = new ChatPage(page);

        await registrationPage.gotoSignIn();
        await registrationPage.loginFromSignIn(email, password);

        await expect(page).toHaveURL(/\/sign-in\/account/i, { timeout: 30000 });
        await expect(page.locator('body')).toContainText(/What best describes you\?/i, { timeout: 30000 });
    });

    test('should not make the chat view available after logging in again', async () => {
        await expect(page.getByPlaceholder('Ask anything...')).not.toBeVisible({ timeout: 5000 });
    });

    test('should complete business user type selection after logging in again', async () => {
        await registrationPage.completeBusinessUserConsumerFlow();
        await registrationPage.waitForTipsOrOnboarding();
        await expect(page.locator('body')).toContainText(/Here is tips how to use our platform|Traditional search engines return links/i, {
            timeout: 30000
        });
    });

    test('should close pop up boxes and leave the user on the chat ui', async () => {
        await advanceTipsToChat(page);
        await closeWelcomePopupOnly(page);

        const verifyBusinessProfileBannerSkipButton = page.locator('#skip-button');

        if (await verifyBusinessProfileBannerSkipButton.isVisible({ timeout: 20000 }).catch(() => false)) {
            await verifyBusinessProfileBannerSkipButton.click();
            page.waitForTimeout(2000);
            await expect(verifyBusinessProfileBannerSkipButton).not.toBeVisible({ timeout: 20000 });
        }

        await expect(page.getByPlaceholder('Ask anything...')).toBeVisible({ timeout: 30000 });
    });

    test('Cannot create agent', async () => {
        await expect(page.getByPlaceholder('Ask anything...')).toBeVisible({ timeout: 30000 });
        await expectPromptHitsWalletGate(B1_CHAT_LIMIT_PROMPTS.agent);
    });

    test('Cannot create widget', async () => {
        await expectPromptHitsWalletGate(B1_CHAT_LIMIT_PROMPTS.widget);
    });

    test('Cannot create curated data', async () => {
        await expectPromptHitsWalletGate(B1_CHAT_LIMIT_PROMPTS.curatedData);
    });

    test('Cannot create utility', async () => {
        await expectPromptHitsWalletGate(B1_CHAT_LIMIT_PROMPTS.utility);
    });

    test('Cannot create chain of agents', async () => {
        await expectPromptHitsWalletGate(B1_CHAT_LIMIT_PROMPTS.chain);
    });

    test('Cannot create Media', async () => {
        await expectPromptHitsWalletGate(B1_CHAT_LIMIT_PROMPTS.media);
    });

    test('Cannot create Model', async () => {
        await expectPromptHitsWalletGate(B1_CHAT_LIMIT_PROMPTS.model);
    });

    test('should allow chat attempt 8 for marketplace search query', async () => {
        await expectPromptDoesNotHitLimit(B1_CHAT_LIMIT_PROMPTS.marketplace);
    });

    test('should allow chat attempt 9 for legal case analyser', async () => {
        await expectPromptDoesNotHitLimit(B1_CHAT_LIMIT_PROMPTS.legalCaseAnalyser);
    });

    test('should allow chat attempt 10 for knowledge fabric', async () => {
        await expectPromptDoesNotHitLimit(B1_CHAT_LIMIT_PROMPTS.knowledgeFabric);
    });

    test('should receive a failure message after more than 10 chat attempts', async () => {
        const limitResponse = await sendSearchAndWaitForResponse('I need to create another automation asset');
        expect(limitResponse).toMatch(SEARCH_LIMIT_MESSAGE);
    });
});
