import {test, expect, Browser, Page, BrowserContext} from "@playwright/test";
import {RegistrationPage} from "../../pages/RegistrationPage";
import {waitForActivationCode} from "../../utils/ActivationCodeUtils";
import {ChatPage} from "../../pages/ChatPage";
import {ModelCreationProcess} from "../../pages/modelCreationProcess";
import * as path from "node:path";

let page: Page;
let context: BrowserContext;
let onboardingFullName: string;
let onboardingEmail: string;
let onboardingPassword: string;
let onboardingSubdomain: string;
let onboardingWalletAddress: string;
let chatPage: ChatPage;

function generateRandomEthWalletAddress() {
    const hex = "0123456789abcdef";
    let wallet = "0x";
    for (let i = 0; i < 40; i++) {
        wallet += hex[Math.floor(Math.random() * hex.length)];
    }
    return wallet;
}


async function dismissWelcomeChooser(page: Page) {
    const welcomeHeading = page.getByText("Welcome to RoboCorp!").first();
    const verifyBanner = page.getByText(/Verify your business profile and unlock advanced features/i).first();
    const legacyWelcomeBanner = page.locator("#welcome-cards-banner");
    const welcomeModal = page.locator(
        "xpath=//div[.//*[contains(normalize-space(.), 'Welcome to RoboCorp!')] and (.//input[@type='checkbox'] or .//*[@role='checkbox'])]"
    ).first();
    const primaryCloseButton = page.locator('#welcome-cards-banner button.absolute').first();
    const fallbackCloseButton = welcomeModal.locator('button.absolute').first();
    const structuralCloseButton = page.locator(
        "xpath=//div[.//*[contains(normalize-space(.), 'Welcome to RoboCorp!')]]//button[not(normalize-space()) or .//img]"
    ).last();

    if (await legacyWelcomeBanner.isVisible({timeout: 3000}).catch(() => false)) {
        const legacyCheckbox = legacyWelcomeBanner.getByRole("checkbox").first();
        if (await legacyCheckbox.isVisible({timeout: 2000}).catch(() => false)) {
            await legacyCheckbox.click({force: true}).catch(() => {
            });
        }
        await Promise.race([
            verifyBanner.waitFor({state: "visible", timeout: 10000}).catch(() => null),
            legacyWelcomeBanner.waitFor({state: "hidden", timeout: 10000}).catch(() => null)
        ]);
        return;
    }

    if (!(await welcomeHeading.isVisible({timeout: 5000}).catch(() => false))) {
        return;
    }

    for (const closeButton of [primaryCloseButton, fallbackCloseButton, structuralCloseButton]) {
        if (!(await closeButton.isVisible({timeout: 1500}).catch(() => false))) continue;

        await closeButton.click({force: true}).catch(() => {
        });
        const progressed = await Promise.race([
            verifyBanner.waitFor({state: "visible", timeout: 5000}).then(() => true).catch(() => false),
            welcomeHeading.waitFor({state: "hidden", timeout: 5000}).then(() => true).catch(() => false)
        ]);

        if (progressed) return;
    }

    await page.keyboard.press('Escape').catch(() => {
    });
    const progressed = await Promise.race([
        verifyBanner.waitFor({state: "visible", timeout: 3000}).then(() => true).catch(() => false),
        welcomeHeading.waitFor({state: "hidden", timeout: 3000}).then(() => true).catch(() => false)
    ]);
    if (progressed) return;

    throw new Error("Welcome chooser did not dismiss to the business verification banner.");
}

async function dismissSequentialDontShowAgainPopups(page: Page) {
    for (let i = 0; i < 2; i++) {
        const dontShowAgain = page.getByRole("checkbox", {name: /Don't show again/i}).first();
        if (!(await dontShowAgain.isVisible({timeout: 3000}).catch(() => false))) {
            break;
        }

        await dontShowAgain.click({force: true}).catch(() => {
        });

        const popupCloseButton = page.locator(
            "xpath=//div[.//*[@role='checkbox' or self::input[@type='checkbox']][.//*[contains(., \"Don't show again\")] or contains(normalize-space(.), \"Don't show again\")]]//button[not(normalize-space()) or .//img]"
        ).last();

        if (await popupCloseButton.isVisible({timeout: 1500}).catch(() => false)) {
            await popupCloseButton.click({force: true}).catch(() => {
            });
        } else {
            await page.keyboard.press('Escape').catch(() => {
            });
        }

        await page.waitForTimeout(500);
    }
}

async function closeOverlayToReturnToChat(page: Page) {
    const overlayContainer = page.locator('.extend-beyond-borders.flex').first();
    if (await overlayContainer.isVisible({timeout: 3000}).catch(() => false)) {
        await overlayContainer.click({force: true}).catch(() => {
        });
        await page.waitForTimeout(300);
    }
}


async function getAccessToken() {
    await expect
        .poll(
            async () => {
                const cookies = await page.context().cookies();
                return cookies.find((cookie) => cookie.name === 'access_token')?.value ?? null;
            },
            {
                timeout: 30000,
                intervals: [500, 1000, 2000]
            }
        )
        .not.toBeNull();

    const cookies = await page.context().cookies();
    const token = cookies.find((cookie) => cookie.name === 'access_token')?.value;
    if (!token) {
        throw new Error('access_token not found in cookies.');
    }
    return token;
}

async function createWalletAndDeposit(amount: string) {
    const token = await getAccessToken();

    const createWalletResponse = await page.request.post("https://promptify-exchange-be-dev.promptyf.cloud/api/v1/wallet/", {
        headers: {
            accept: 'application/json',
            Authorization: `Bearer ${token}`,
            'Content-Type': 'application/json'
        },
        data: {
            currency_type: 'DAAC',
            address: onboardingWalletAddress,
            make_active: true
        }
    });
    expect(createWalletResponse.ok()).toBeTruthy();

    const depositUrl = new URL(
        `https://promptify-exchange-be-dev.promptyf.cloud/api/v1/wallet/${onboardingWalletAddress}/deposit`
    );
    depositUrl.searchParams.set('amount', amount);
    depositUrl.searchParams.set('description', `Add ${amount} DAAC manually for testing`);
    depositUrl.searchParams.set('perform_blockchain_transfer', 'false');

    const depositResponse = await page.request.post(depositUrl.toString(), {
        headers: {
            accept: 'application/json',
            Authorization: `Bearer ${token}`
        }
    });
    expect(depositResponse.ok()).toBeTruthy();
}

async function ensureChatSurfaceVisible() {
    const chatInput = page.getByRole('textbox', {name: 'Ask anything...'}).first();
    const skipVerifyButton = page.getByRole("button", {name: /Skip for now/i}).first();

    if (await skipVerifyButton.isVisible({timeout: 3000}).catch(() => false)) {
        await skipVerifyButton.click().catch(() => {
        });
    }

    await chatPage.dismissChatOverlays().catch(() => {
    });
    await closeOverlayToReturnToChat(page);
    if (await chatInput.isVisible({timeout: 3000}).catch(() => false)) {
        return;
    }

    await page.keyboard.press('Escape').catch(() => {
    });
    await chatPage.dismissChatOverlays().catch(() => {
    });
    await closeOverlayToReturnToChat(page);
    if (await chatInput.isVisible({timeout: 3000}).catch(() => false)) {
        return;
    }

    await chatPage.refreshChat().catch(() => {
    });
    await closeOverlayToReturnToChat(page);
    await chatPage.waitForChatReady().catch(async () => {
        const refreshChatButton = page.locator('#btn-refresh-chat').first();
        if (await refreshChatButton.isVisible({timeout: 3000}).catch(() => false)) {
            await refreshChatButton.click({force: true}).catch(() => {
            });
        }
    });

    await closeOverlayToReturnToChat(page);
    await expect(chatInput).toBeVisible({timeout: 30000});
    await expect(chatInput).toBeEditable({timeout: 30000});
}

async function setupOnboardedBusinessUser(browser: Browser) {
    context = await browser.newContext();
    page = await context.newPage();
    const registrationPage = new RegistrationPage(page);
    chatPage = new ChatPage(page);

    onboardingEmail = `automation.general+${Date.now()}@datafab.ai`;
    onboardingPassword = process.env.USER_PASSWORD?.trim() || "User@123";
    onboardingFullName = "Automation General Business User";
    onboardingSubdomain =
        process.env.USER_USERNAME?.trim() ||
        onboardingEmail
            .split("@")[0]
            .replace(/[^a-zA-Z0-9-]/g, "-")
            .slice(0, 32);
    onboardingWalletAddress = generateRandomEthWalletAddress();

    await registrationPage.gotoSignIn();
    await registrationPage.openSignUpFromSignIn();
    await registrationPage.submitSignUpCredentials(onboardingEmail, onboardingPassword);
    await registrationPage.submitFullName(onboardingFullName);

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

    await expect(page).toHaveURL(/\/sign-in\/account/i, {timeout: 30000});
    await registrationPage.completeBusinessUserConsumerFlow();
    await registrationPage.waitForTipsOrOnboarding();

    for (let i = 0; i < 4; i++) {
        const tipsVisible = await page
            .locator("body")
            .getByText(/Here is tips how to use our platform/i)
            .first()
            .isVisible({timeout: 2000})
            .catch(() => false);
        if (!tipsVisible) {
            break;
        }

        const continueBtn = page.getByRole("button", {name: /^Continue$/i}).first();
        if (await continueBtn.isVisible({timeout: 2000}).catch(() => false)) {
            await continueBtn.click();
            await page.waitForTimeout(400);
            continue;
        }

        const skipBtn = page.getByRole("button", {name: /Skip for now/i}).first();
        if (await skipBtn.isVisible({timeout: 2000}).catch(() => false)) {
            await skipBtn.click();
            await page.waitForTimeout(400);
        }
    }

    await page.waitForTimeout(2500);
    await chatPage.dismissChatOverlays();
    await dismissSequentialDontShowAgainPopups(page);
    await dismissWelcomeChooser(page);
    await page.waitForTimeout(2500);

    const legacyBusinessBanner = page.locator("#business-verification-banner");
    await Promise.race([
        legacyBusinessBanner.waitFor({state: "visible", timeout: 10000}).catch(() => null),
        page.getByText(/Verify your business profile and unlock advanced features/i).first().waitFor({
            state: "visible",
            timeout: 10000
        }).catch(() => null)
    ]);

    const skipVerifyButton = page.getByRole("button", {name: /Skip for now/i}).first();
    if (await skipVerifyButton.isVisible({timeout: 5000}).catch(() => false)) {
        await skipVerifyButton.click();
    }

    await ensureChatSurfaceVisible();
}

test.describe.serial("Business Users / General Business User", () => {
    test.describe.configure({timeout: 480000});

    test("should fund wallet with 100 DAAC", async ({browser}) => {
        await setupOnboardedBusinessUser(browser);
        await createWalletAndDeposit('100');
        await chatPage.refreshChat();
        await page.keyboard.press('Escape').catch(() => {
        });
        await ensureChatSurfaceVisible();
    });

    // 309 Can create asset
    test("can create asset", async () => {
        const chatInput = page.getByRole('textbox', {name: 'Ask anything...'});
        const sendButton = page.locator('#chat-input').getByRole('button').first();
        const modelImagePath = path.resolve(__dirname, '../../asset-creation-files/asset-image.jpg');
        const modelFilePath = path.resolve(__dirname, '../../asset-creation-files/rf.pkl');
        const body = page.locator('body');
        const model = new ModelCreationProcess(page);

        async function createModel(modelName: string, modelDescription: string) {
            await expect(chatInput).toBeVisible({timeout: 30000});
            await chatInput.click();
            await chatInput.fill('I need to create new model without domain');
            await sendButton.click();

            await expect(body).toContainText(/name of your new model|provide the name of your new model/i, {timeout: 120000});
            await chatInput.fill(modelName);
            await sendButton.click();

            await expect(body).toContainText(/describe|description/i, {timeout: 120000});
            await chatInput.fill(modelDescription);
            await sendButton.click();

            await expect(body).toContainText(/upload a thumbnail|thumbnail image uploader/i, {timeout: 120000});
            await model.uploadThumbnail(modelImagePath);
            await expect(body).toContainText(/please upload the files that contain the model details|model file\/s/i, {timeout: 120000});
            await model.uploadModelFile(modelFilePath);

            const createModelButton = page.getByRole('button', {name: /Create Model/i}).first();
            await expect(createModelButton).toBeVisible({timeout: 120000});
            await createModelButton.click();

            const maximizeWidgetButton = page.getByRole('button', {name: 'Maximize widget'}).first();
            await expect(maximizeWidgetButton).toBeVisible({timeout: 120000});
            await maximizeWidgetButton.click();
        }

        await createModel(`Automation model ${Date.now()}`, 'Model for automation');
        await page.getByText('DRAFT').click();
    });

    // 311 Can save an asset before publishing and the asset will appear in Draft status in the inventory
    test("can save an asset before publishing and the asset appears in Draft status in inventory", async () => {
        await page.getByRole('button', {name: 'Open inventory'}).click();
        await page.getByRole('button', {name: 'Models'}).click();
        await page.getByRole('button', {name: 'See details...', exact: true}).first().click();
        await page.getByRole('heading', {name: /MODEL:\s*Automation model/i}).click();
        await page.getByRole('button', {name: 'DRAFT'}).click();
    });

    // 313 Can update all asset fields before it is sold at least once
    test("can update all asset fields before it is sold at least once", async () => {
        const modelImagePath = path.resolve(__dirname, '../../asset-creation-files/asset-image.jpg');

        await page.getByRole('button', {name: 'Open inventory'}).click();
        await page.getByRole('button', {name: 'Models'}).click();
        await page.getByRole('button', {name: 'See details...', exact: true}).first().click();
        await page.getByRole('heading', {name: /MODEL:\s*Automation model/i}).click();
        await page.getByRole('button', {name: 'DRAFT'}).click();
        await page.getByRole('button', {name: 'DRAFT'}).press('Escape');

        await page.getByRole('button', {name: 'See details...', exact: true}).first().click();
        await page.getByRole('button').nth(1).click();
        await page.getByRole('button', {name: 'Maximize widget'}).click();

        const modelNameInput = page.getByRole('textbox', {name: /Name Your Model/i});
        await expect(modelNameInput).toBeVisible({timeout: 30000});
        await modelNameInput.fill('Automation model Updated');

        const modelDescriptionInput = page.getByRole('textbox', {name: /Describe/i});
        await expect(modelDescriptionInput).toBeVisible({timeout: 30000});
        await modelDescriptionInput.fill('Model for automation updated');

        const saveButton = page.getByRole('button', {name: 'Save'}).first();
        await expect(saveButton).toBeVisible({timeout: 30000});
        await saveButton.click();

        await page.getByRole('button', {name: 'Remove thumbnail'}).click();
        await page.getByRole('button', {name: 'Thumbnail'}).click();
        await page.locator('input[type="file"]').last().setInputFiles(modelImagePath);

        await expect(saveButton).toBeVisible({timeout: 30000});
        await saveButton.click();

        await page.getByRole('tab', {name: 'Purpose'}).click();
    });

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