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

async function isVisible(locator: Locator, timeout = 1000): Promise<boolean> {
    return locator.isVisible({ timeout }).catch(() => false);
}

export class ModelCreationProcess {

    readonly page: Page;
    readonly chatInput: Locator;
    readonly welcomePopupBanner: Locator;
    readonly welcomePopup: Locator;
    readonly dontShowAgainCheckbox: Locator;
    readonly modelNamePrompt: Locator;
    readonly descriptionPrompt: Locator;
    readonly thumbnailUploaderTitle: Locator;
    readonly modelFilesUploaderTitle: Locator;
    readonly createModelButton: Locator;

    constructor(page: Page) {

        this.page = page;

        this.chatInput = page.getByPlaceholder('Ask anything...').first();
        this.welcomePopupBanner = page.locator('#welcome-cards-banner').first();

        this.welcomePopup = page.getByText('Welcome to RoboCorp!').first();

        this.dontShowAgainCheckbox = page.getByRole('checkbox', {
            name: /don't show again/i
        }).first();
        this.modelNamePrompt = page.getByText(
            /Please provide the name of your new model/i
        ).first();
        this.descriptionPrompt = page.getByText(/please describe/i).first();
        this.thumbnailUploaderTitle = page.getByText(/Thumbnail Image Uploader/i).first();
        this.modelFilesUploaderTitle = page.getByText(/Model File\/s/i).first();
        this.createModelButton = page.getByRole('button', { name: /Create Model/i }).first();
    }

    async closeWelcomePopupIfVisible() {
        await this.welcomePopup.waitFor({ state: 'visible', timeout: 8000 }).catch(() => null);

        const popupVisible = await isVisible(this.welcomePopup, 1000);

        if (popupVisible) {
            if (await isVisible(this.dontShowAgainCheckbox, 1000)) {
                await this.dontShowAgainCheckbox.click({ force: true }).catch(() => {});
            }

            const closeCandidates = [
                this.page.locator('#welcome-cards-banner button.absolute').first(),
                this.welcomePopupBanner.locator('button.absolute').first(),
                this.welcomePopupBanner.locator('button').last(),
                this.page.getByRole('button', { name: /close/i }).first(),
            ];

            for (const closeButton of closeCandidates) {
                if (await isVisible(closeButton, 1000)) {
                    await closeButton.click({ force: true }).catch(() => {});
                    if (await isVisible(this.welcomePopup, 1000)) {
                        await closeButton.evaluate((node) => {
                            (node as HTMLButtonElement).click();
                        }).catch(() => {});
                    }
                    if (!(await isVisible(this.welcomePopup, 1500))) {
                        return;
                    }
                }
            }

            await this.page.keyboard.press('Escape').catch(() => {});
            if (!(await isVisible(this.welcomePopup, 1500))) {
                return;
            }

            throw new Error('Welcome popup is visible but could not be closed.');
        }
    }

    async startModelCreation() {

        await this.closeWelcomePopupIfVisible();
        await expect(this.welcomePopup).not.toBeVisible({ timeout: 10000 });

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

        await this.chatInput.fill('I want to create a model');
        await this.chatInput.press('Enter');

        await expect(this.modelNamePrompt).toBeVisible({ timeout: 60000 });
    }

    async waitForModelNamePrompt(timeout = 60000) {
        await expect(this.modelNamePrompt).toBeVisible({ timeout });
    }

    async provideModelName() {

        await expect(this.modelNamePrompt).toBeVisible({ timeout: 60000 });

        const modelName = `Automation Model creation ${new Date().toISOString()}`;
        await this.chatInput.fill(modelName);
        await this.chatInput.press('Enter');

        await expect(this.descriptionPrompt).toBeVisible({ timeout: 60000 });
    }

    async provideDescription() {

        await this.chatInput.fill('this is just a description');
        await this.chatInput.press('Enter');

        await this.thumbnailUploaderTitle.waitFor({ timeout: 60000 });
    }

    async uploadModelFile(filePath: string) {
        const widget = this.modelFilesUploaderTitle.locator('..').first();
        const uploadButton = widget.getByRole('button', { name: 'Select file to upload' }).first()
            .or(this.page.getByRole('button', { name: 'Select file to upload' }).last());
        const confirmButton = widget.getByRole('button', { name: /^Confirm$/i }).first()
            .or(this.page.getByRole('button', { name: /^Confirm$/i }).last());

        await expect(this.modelFilesUploaderTitle).toBeVisible({ timeout: 60000 });
        await this.uploadViaFileChooser(uploadButton, filePath);
        await expect(confirmButton).toBeEnabled({ timeout: 30000 });
        await confirmButton.click();
        await expect(this.createModelButton).toBeVisible({ timeout: 60000 });
    }

    async uploadThumbnail(filePath: string) {
        const widget = this.thumbnailUploaderTitle.locator('..').first();
        const uploadButton = widget.getByRole('button', { name: 'Select file to upload' }).first()
            .or(this.page.getByRole('button', { name: 'Select file to upload' }).first());
        const confirmButton = widget.getByRole('button', { name: /^Confirm$/i }).first()
            .or(this.page.getByRole('button', { name: /^Confirm$/i }).first());

        await expect(this.thumbnailUploaderTitle).toBeVisible({ timeout: 60000 });
        await this.uploadViaFileChooser(uploadButton, filePath);
        await expect(confirmButton).toBeEnabled({ timeout: 30000 });
        await confirmButton.click();
    }

    async uploadAdditionalModel(filePath: string) {
        const widget = this.modelFilesUploaderTitle.locator('..').first();
        const addMoreFilesButton = widget.getByRole('button', { name: /Add more files/i }).first();
        const confirmButton = widget.getByRole('button', { name: /^Confirm$/i }).first();

        if (!(await isVisible(addMoreFilesButton, 5000))) {
            return;
        }

        await this.uploadViaFileChooser(addMoreFilesButton, filePath);
        await expect(confirmButton).toBeEnabled({ timeout: 30000 });
        await confirmButton.click();
    }

    async createModel() {

        await this.page.getByRole('button', { name: /Create Model/i }).click();
    }

    async publishModel() {

        await this.page.getByRole('button', { name: 'Maximize widget' }).click();

        await this.page.getByRole('button', { name: 'Publish' }).click();

        const priceInput = this.page.getByRole('dialog').getByRole('textbox');

        await priceInput.fill('1');

        await this.page.getByTitle('Confirm').click();
    }

    private async uploadViaFileChooser(uploadButton: Locator, filePath: string) {
        await expect(uploadButton).toBeVisible({ timeout: 30000 });
        const [fileChooser] = await Promise.all([
            this.page.waitForEvent('filechooser'),
            uploadButton.click({ force: true }),
        ]);
        await fileChooser.setFiles(filePath);
    }
}
