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

import {
    ControlQA,
    DialogControlDateQa,
    DialogControlParamsQa,
    DialogGroupControlQa,
    DialogQLParameterQA,
    NavigationInputQA,
    TabMenuQA,
} from '../../../../src/shared/constants';
import DialogControl from '../../common/DialogControl';
import {clickGSelectOption, fillDatePicker, getControlByTitle, slct} from '../../../utils';

import {BasePageProps} from '../../BasePage';

import {
    DashTabItemControlSourceType,
    DialogControlQa,
    TitlePlacement,
    TitlePlacements,
} from '../../../../src/shared';
import type {ImpactType} from '../../../../src/shared/types/dash';

import {
    DashboardAddWidgetQa,
    DashboardDialogControl,
    DashkitQa,
} from '../../../../src/shared/constants/qa/dash';
import {DatasetParams, ListItemByParams} from '../../types';
import {IMPACT_TYPE_QA_BY_VALUE} from './constants';

export enum SelectorElementType {
    List = 'list',
    Input = 'input',
    Checkbox = 'checkbox',
    Date = 'date',
}

export type SelectorSettings = {
    sourceType?: DashTabItemControlSourceType;
    fieldName?: string;
    dataset?: DatasetParams;
    datasetField?: ListItemByParams;
    elementType?: SelectorElementType;
    appearance?: {
        title?: string;
        titlePlacement?: TitlePlacement;
        innerTitle?: string;
        innerTitleEnabled?: boolean;
    };
    items?: string[];
    defaultValue?: string;
    required?: boolean;
    impactType?: ImpactType;
    impactTabsIds?: string[];
    impactTabsIndexes?: number[];
};

type GroupSelectorOptions = {
    buttonApply?: boolean;
    updateControlOnChange?: boolean;
};

export interface DashboardPageProps extends BasePageProps {}

class ControlActions {
    page: Page;

    dialogControl: DialogControl;

    constructor(page: Page) {
        this.page = page;
        this.dialogControl = new DialogControl(page);
    }

    async waitForDialog() {
        await this.dialogControl.waitForVisible();
    }

    async applyControlSettings() {
        await this.dialogControl.applyControlSettings();
    }

    async clickAddExternalSelector() {
        await this.page.click(slct(DashboardAddWidgetQa.AddControl));
    }

    async clickAddSelector() {
        await this.page.click(slct(DashboardAddWidgetQa.AddGroupControl));
    }

    async addDateRangeSelector({
        title,
        fieldName,
        range,
    }: {
        title: string;
        fieldName: string;
        range: string[];
    }) {
        // adding a selector
        await this.clickAddSelector();

        await this.editSelectorBySettings({
            appearance: {title},
            fieldName,
        });

        await this.dialogControl.elementType.click();
        await this.dialogControl.datasetFieldSelector.selectListItemByQa(
            slct(DialogControlQa.typeControlCalendar),
        );

        await this.page.click(slct(DialogControlQa.dateRangeCheckbox));
        await this.page.click(slct(DialogControlQa.dateTimeCheckbox));

        // click on the button for setting possible values
        await this.page.click(slct(ControlQA.acceptableDialogButton));

        await this.page.locator(`${slct(DialogControlDateQa.defaultSelectValue)} label`).click();

        await fillDatePicker({
            page: this.page,
            selector: `${slct(DialogQLParameterQA.DatepickerStart)} input`,
            value: range[0],
        });

        await fillDatePicker({
            page: this.page,
            selector: `${slct(DialogQLParameterQA.DatepickerEnd)} input`,
            value: range[1],
        });

        // saving the added possible values
        await this.page.click(slct(DialogControlParamsQa.buttonApply));

        // adding a selector to the dashboard
        await this.applyControlSettings();
    }

    async addSelectorToGroup(params: SelectorSettings) {
        await this.page.locator(slct(TabMenuQA.Add)).click();

        await this.editSelectorBySettings(params);
    }

    async addSelector({items = ['Richmond', 'Springfield'], ...params}: SelectorSettings) {
        // adding a selector
        await this.clickAddSelector();

        await this.editSelectorBySettings({...params, items});

        // adding a selector to the dashboard
        await this.page.click(slct(ControlQA.dialogControlApplyBtn));
    }

    async addSelectorsGroup(selectorsParams: SelectorSettings[], options?: GroupSelectorOptions) {
        // adding a selector
        await this.clickAddSelector();

        await this.editSelectorBySettings(selectorsParams[0]);

        for (let i = 1; i < selectorsParams.length; i++) {
            await this.addSelectorToGroup(selectorsParams[i]);
        }

        if (options && options.buttonApply) {
            await this.page.locator(slct(DialogGroupControlQa.groupSettingsTab)).click();
            await this.page.locator(slct(DialogGroupControlQa.applyButtonSwitch)).click();

            await this.page
                .locator(`${slct(DialogGroupControlQa.updateControlOnChangeCheckbox)} input`)
                .setChecked(Boolean(options?.updateControlOnChange));
        }

        // adding a selector to the dashboard
        await this.page.click(slct(ControlQA.dialogControlApplyBtn));
    }

    async fillSelectSelectorItems({items, defaultValue}: SelectorSettings) {
        if (!items) {
            return;
        }

        // click on the button for setting possible values
        await this.page.click(slct(ControlQA.acceptableDialogButton));

        // waiting for the dialog for setting possible values to appear
        await this.page.waitForSelector(slct(ControlQA.controlSelectAcceptable));

        for (let i = 0; i < items.length; i++) {
            // specify the value
            await this.page.fill(`${slct(ControlQA.controlSelectAcceptableInput)} input`, items[i]);
            // adding
            await this.page.click(slct(ControlQA.controlSelectAcceptableButton));
        }

        // saving the added possible values
        await this.page.click(slct(DialogControlParamsQa.buttonApply));

        // specify the default value if there is
        if (defaultValue !== undefined) {
            await clickGSelectOption({
                page: this.page,
                key: ControlQA.selectDefaultAcceptable,
                optionText: defaultValue,
            });

            // check that the number of available values has not changed
            await this.page.waitForSelector(slct(ControlQA.acceptableDialogButton));
            await this.page.waitForSelector(
                slct(`${DashboardDialogControl.AcceptableValues}${items.length}`),
            );
        }
    }

    async addDatasetByLink(link: string) {
        await this.page.locator(slct(NavigationInputQA.Link)).click();
        await this.page.locator(`${slct(NavigationInputQA.Input)} input`).fill(link);
        await this.page.locator(slct(NavigationInputQA.Apply)).click();

        const openDatasetButton = this.page.locator(slct(NavigationInputQA.Open));

        await expect(openDatasetButton).toBeVisible();
    }

    async selectElementType(elementType: SelectorElementType) {
        let elementTypeQa = DialogControlQa.typeControlSelect;
        switch (elementType) {
            case 'input':
                elementTypeQa = DialogControlQa.typeControlInput;
                break;
            case 'checkbox':
                elementTypeQa = DialogControlQa.typeControlCheckbox;
                break;
            case 'date':
                elementTypeQa = DialogControlQa.typeControlCalendar;
                break;
        }

        await this.dialogControl.datasetFieldSelector.selectListItem({qa: slct(elementTypeQa)});
    }

    // eslint-disable-next-line complexity
    async editSelectorBySettings({
        sourceType = DashTabItemControlSourceType.Manual,
        ...setting
    }: SelectorSettings = {}) {
        await this.dialogControl.waitForVisible();

        if (sourceType) {
            await this.dialogControl.sourceTypeSelect.click();
            await this.dialogControl.sourceTypeSelect.selectListItemByQa(slct(sourceType));
        }

        if (sourceType === DashTabItemControlSourceType.Manual) {
            if (setting.fieldName) {
                await this.dialogControl.fieldName.fill(setting.fieldName);
            }

            // TODO: remove condition with innerText after removing addSelectorWithDefaultSettings
            if (!setting.elementType || setting.elementType === 'list') {
                await this.fillSelectSelectorItems({
                    items: setting.items,
                    defaultValue: setting.defaultValue,
                });
            }
        } else {
            if (setting.dataset?.link) {
                await this.addDatasetByLink(setting.dataset?.link);
            } else if (setting.dataset?.innerText || typeof setting.dataset?.idx === 'number') {
                await this.dialogControl.selectDatasetButton.click();
                await this.dialogControl.selectDatasetButton.navigationMinimal.selectListItem(
                    setting.dataset,
                );
            }

            if (setting.datasetField?.innerText || typeof setting.datasetField?.idx === 'number') {
                await this.dialogControl.datasetFieldSelector.click();
                await this.dialogControl.datasetFieldSelector.selectListItem(setting.datasetField);
            }
        }

        if (setting.elementType) {
            await this.dialogControl.elementType.click();
            await this.selectElementType(setting.elementType);
        }

        if (setting.appearance?.titlePlacement) {
            await this.dialogControl.appearanceTitle.radioGroup.selectByName(
                setting.appearance.titlePlacement,
            );
        }

        if (setting.appearance?.title) {
            await this.dialogControl.appearanceTitle.textInput.fill(setting.appearance.title);
        }

        if (typeof setting.appearance?.innerTitleEnabled === 'boolean') {
            await this.dialogControl.appearanceInnerTitle.checkbox.toggle(
                setting.appearance.innerTitleEnabled,
            );
        }

        if (setting.appearance?.innerTitle) {
            await this.dialogControl.appearanceInnerTitle.textInput.fill(
                setting.appearance.innerTitle,
            );
        }

        if (typeof setting.required === 'boolean') {
            await this.dialogControl.requiredCheckbox.toggle(setting.required);
        }

        if (typeof setting.defaultValue === 'string' && setting.elementType === 'input') {
            await this.page
                .locator(`${slct(DialogControlQa.valueInput)} input`)
                .fill(setting.defaultValue);
        }

        // Handle impact type settings
        if (setting.impactType) {
            await this.dialogControl.impactTypeSelector.click();
            await this.dialogControl.impactTypeSelector.selectListItemByQa(
                slct(IMPACT_TYPE_QA_BY_VALUE[setting.impactType]),
            );

            // If impact type is "selectedTabs", select the specified tabs
            if (
                setting.impactType === 'selectedTabs' &&
                (setting.impactTabsIds || setting.impactTabsIndexes)
            ) {
                await expect(this.dialogControl.impactTabsIdsSelector.getLocator()).toBeVisible();
                await this.dialogControl.impactTabsIdsSelector.click();

                if (setting.impactTabsIds) {
                    // Select each tab from the list
                    for (const tabId of setting.impactTabsIds) {
                        await this.dialogControl.impactTabsIdsSelector.selectListItemByName(tabId);
                    }
                }

                if (setting.impactTabsIndexes) {
                    // Select each tab from the list
                    for (const tabIndex of setting.impactTabsIndexes) {
                        await this.dialogControl.impactTabsIdsSelector.selectListItemByIdx(
                            tabIndex,
                        );
                    }
                }

                // Close the multi-select popup
                await this.page.keyboard.press('Escape');
            }
        }
    }

    /** @deprecated instead use addSelector */
    async addSelectorWithDefaultSettings(setting: SelectorSettings = {}) {
        const defaultSettings: SelectorSettings = {
            sourceType: DashTabItemControlSourceType.Dataset,
            appearance: {titlePlacement: TitlePlacements.Left},
            dataset: {idx: 0},
            datasetField: {idx: 0},
        };
        await this.clickAddSelector();

        await this.editSelectorBySettings({...defaultSettings, ...setting});

        await this.page.click(slct(ControlQA.dialogControlApplyBtn));
    }

    async getControlByTitle(controlTitle: string) {
        return getControlByTitle(this.page, controlTitle);
    }

    async getDashControlLinksIconElem(counter?: number) {
        return this.page
            .locator(slct(DashkitQa.GRID_ITEM))
            .filter({
                has: this.page
                    .locator(slct(ControlQA.groupChartkitControl))
                    .or(this.page.locator(slct(ControlQA.chartkitControl))),
            })
            .nth(counter || 0)
            .locator(slct(ControlQA.controlLinks));
    }

    async openSelectPopupByTitle(controlTitle: string) {
        const control = await this.getControlByTitle(controlTitle);

        await control.click();

        const selectPopup = this.page.locator(slct(ControlQA.controlSelectItems)).first();
        await selectPopup.waitFor({state: 'visible'});
    }

    async getSelectItemsLocatorByTitle(controlTitle: string) {
        await this.openSelectPopupByTitle(controlTitle);

        return this.page.locator(
            `${slct(ControlQA.controlSelectItems)} [data-value]:not([data-value=""])`,
        );
    }

    async selectControlValueByTitle(controlTitle: string, value: string) {
        await this.openSelectPopupByTitle(controlTitle);

        await this.page.locator(`[data-value="${value}"]`).click();
    }
}

export default ControlActions;
