import {Page, Response, expect} from '@playwright/test';
import {v1 as uuidv1} from 'uuid';
import {
    DialogCreateWorkbookEntryQa,
    EntryDialogQA,
} from '../../../src/shared/constants/qa/components';
import {
    DatasetPanelQA,
    DatasetActionQA,
    DatasetSourcesTableQa,
    DatasetSourcesLeftPanelQA,
} from '../../../src/shared/constants/qa/datasets';
import {
    CollectionFiltersQa,
    DialogCollectionStructureQa,
    SharedEntriesBaseQa,
    SharedEntriesPermissionsDialogQa,
    ValueOf,
    DATASET_TAB,
} from '../../../src/shared';

import {deleteEntity, slct} from '../../utils';
import {BasePage, BasePageProps} from '../BasePage';
import DialogParameter from '../common/DialogParameter';
import Revisions from '../common/Revisions';

import DatasetTabSection from './DatasetTabSection';
import DatasetConnectionSection, {SetConnectionProps} from './DatasetConnectionSection';
import DatasetFieldsTable from './DatasetFieldsTable';
import {VALIDATE_DATASET_URL} from './constants';
import {NavigationMinimalPopup} from '../workbook/NavigationMinimalPopup';

export interface DatasetPageProps extends BasePageProps {}

export const waitForBiValidateDatasetResponses = (page: Page, timeout: number): Promise<void> => {
    return new Promise((resolve: any) => {
        const timerId = setTimeout(() => {
            page.off('response', onResponse);
            resolve();
        }, timeout);

        async function onResponse(response: Response) {
            if (!response.url().match('validateDataset')) {
                return;
            }

            const request = await response.request();
            const requestData = JSON.parse(request.postData() || '');

            // When the page loads, the validation request with empty updates initially goes away
            // We are only interested in the one that leaves after the avatar is deleted
            if (!requestData.data.updates.length) {
                return;
            }

            clearTimeout(timerId);

            if (response.status() !== 200) {
                throw new Error(
                    'After deleting the avatar, the dataset validation returned an error',
                );
            }

            resolve();
        }

        page.on('response', onResponse);
    });
};

class DatasetPage extends BasePage {
    datasetTabSection: DatasetTabSection;
    datasetConnectionSection: DatasetConnectionSection;
    datasetFieldsTable: DatasetFieldsTable;
    workbookNavigationMinimal: NavigationMinimalPopup;
    dialogParameter: DialogParameter;
    revisions: Revisions;

    constructor({page}: DatasetPageProps) {
        super({page});
        this.workbookNavigationMinimal = new NavigationMinimalPopup(page);
        this.datasetConnectionSection = new DatasetConnectionSection(page);
        this.datasetFieldsTable = new DatasetFieldsTable(page);
        this.datasetTabSection = new DatasetTabSection(page);
        this.dialogParameter = new DialogParameter(page);
        this.revisions = new Revisions(page);
    }

    async addAvatarByDragAndDrop(sourceTitle?: string) {
        const selector = sourceTitle
            ? `${slct(DatasetSourcesTableQa.Source)} span >> text=${sourceTitle}`
            : slct(DatasetSourcesTableQa.Source);

        const source = await this.page.$(selector);

        if (!source) {
            throw new Error("Couldn't find the table");
        }

        const targetSelector = slct('ds-relations-map');
        const target = await this.page.$(targetSelector);

        if (!target) {
            throw new Error("Couldn't find an area to drag");
        }

        await this.page.dragAndDrop(selector, targetSelector);
    }

    async openTab(tab: ValueOf<typeof DATASET_TAB>) {
        await this.page.click(`.dataset-panel input[value=${tab}]`);
    }

    async createDatasetInWorkbookOrCollection({
        name = uuidv1(),
        collectionId,
    }: {name?: string; collectionId?: string} = {}) {
        const dsCreateBtn = this.page.locator(slct(DatasetActionQA.CreateButton));
        await dsCreateBtn.click();

        const textInput = this.page
            .locator(slct(DialogCreateWorkbookEntryQa.Input))
            .locator('input');
        // clear input
        await textInput.press('Meta+A');
        await textInput.press('Backspace');
        // type dataset name
        await textInput.fill(name);
        const dialogApplyButton = await this.page.waitForSelector(
            slct(DialogCreateWorkbookEntryQa.ApplyButton),
        );
        // create connection
        await dialogApplyButton.click();
        try {
            if (collectionId) {
                await this.page.waitForURL(() => {
                    return this.page.url().endsWith(collectionId);
                });
            } else {
                await this.page.waitForURL(() => {
                    return this.page.url().includes(name);
                });
            }
            return name;
        } catch {
            throw new Error("Dataset wasn't created");
        }
    }

    async createDatasetInFolder({name = uuidv1()}: {name?: string} = {}) {
        // open creation dialog
        await this.page.locator(slct(DatasetActionQA.CreateButton)).click();
        // type dataset name
        await this.page.locator(slct(EntryDialogQA.PathSelect)).locator('input').fill(name);
        const dialogApplyButton = await this.page.waitForSelector(slct(EntryDialogQA.Apply));
        // create dataset
        await dialogApplyButton.click();
        try {
            await this.page.waitForURL(() => this.page.url().includes(name));
        } catch {
            throw new Error("Dataset wasn't created");
        }
    }

    async deleteEntry() {
        await deleteEntity(this.page);
    }

    async getCurrentTabName() {
        const input = await this.page.waitForSelector(
            `${slct(DatasetPanelQA.TabRadio)} input[aria-checked="true"]`,
        );

        return await input.inputValue();
    }

    async setConnectionDelegation({
        delegation = true,
    }: {
        delegation?: boolean;
    } = {}) {
        if (delegation) {
            const delegateBtn = await this.page.waitForSelector(
                slct(SharedEntriesPermissionsDialogQa.DelegateBtn),
            );
            await delegateBtn.click();
        } else {
            const delegateBtn = await this.page.waitForSelector(
                slct(SharedEntriesPermissionsDialogQa.NotDelegateBtn),
            );
            await delegateBtn.click();
        }

        const delegationApplyBtn = this.page.locator(
            slct(SharedEntriesPermissionsDialogQa.ApplyBtn),
        );
        await delegationApplyBtn.click();
    }

    async saveSharedDataset({name, collectionId}: {name?: string; collectionId: string}) {
        await this.page.waitForSelector(slct(DatasetSourcesTableQa.Source));

        await this.addAvatarByDragAndDrop();

        const dsName = await this.createDatasetInWorkbookOrCollection({
            collectionId,
            name,
        });

        return dsName;
    }

    async setConnectionInWorkbookDataset({method, connectionName}: SetConnectionProps) {
        await this.datasetConnectionSection.openConnectionSelectViaMethod(method);
        await this.workbookNavigationMinimal.fillInput(connectionName);
        await this.workbookNavigationMinimal.selectListItem({innerText: connectionName});
    }

    async setSharedConnection({connectionName, method}: SetConnectionProps) {
        await this.datasetConnectionSection.openConnectionSelectViaMethod(method);
        await this.page.waitForSelector(slct(CollectionFiltersQa.SearchInput));
        const search = this.page.locator(slct(CollectionFiltersQa.SearchInput)).locator('input');
        await search.press('Meta+A');
        await search.press('Backspace');
        await search.fill(connectionName);
        await this.waitForSuccessfulResponse('/getCollectionBreadcrumbs');

        await this.page.waitForSelector(slct(DialogCollectionStructureQa.ListItem));

        const sharedConn = this.page
            .locator(slct(DialogCollectionStructureQa.ListItem))
            .filter({hasText: connectionName});
        await sharedConn.click();
    }

    async checkIsReadonlyState() {
        await this.page.waitForSelector(slct(SharedEntriesBaseQa.OpenOriginalBtn));
    }

    async scrollSourcesList({scrollHeight = 99999}: {scrollHeight?: number} = {}) {
        const sourcesList = await this.page.waitForSelector(
            slct(DatasetSourcesLeftPanelQA.SourcesList),
        );
        await this.page.waitForSelector(slct(DatasetSourcesTableQa.Source));
        await sourcesList.hover();
        await this.page.mouse.wheel(0, scrollHeight);
    }

    async changeDbName({namePattern}: {namePattern?: string} = {}) {
        const select = await this.page.waitForSelector(
            slct(DatasetSourcesLeftPanelQA.SelectSourcesDbName),
        );
        await this.page.waitForFunction(
            async (element) => {
                return !(element as HTMLSelectElement).disabled;
            },
            select,
            {polling: 500},
        );

        const disabled = await select.isDisabled();
        expect(disabled).toBe(false);
        await select.click();
        await this.page.waitForSelector(slct('select-popup'));
        const popup = this.page.locator(slct('select-popup'));
        const option = popup.locator('[role="option"]', {hasText: namePattern});
        await option.click();
    }

    async renameFirstField({value}: {value?: string} = {}) {
        const fieldInput = this.datasetFieldsTable.getFieldNameInput();
        const originalValue = await fieldInput.inputValue();
        const newValue = value || `${originalValue}_modified`;

        await fieldInput.fill(newValue);
        const validatePromise = this.waitForSuccessfulResponse(VALIDATE_DATASET_URL);
        await this.page.keyboard.press('Enter');
        await validatePromise;

        return {newValue, originalValue};
    }

    async saveUpdatedDataset() {
        const getEntrySuccessfulPromise = this.waitForSuccessfulResponse(
            '/gateway/root/us/getEntryMeta',
        );
        const saveBtn = await this.page.locator(slct(DatasetActionQA.CreateButton));
        await expect(saveBtn).toBeEnabled();
        await saveBtn.click();
        await getEntrySuccessfulPromise;
    }
}

export default DatasetPage;
