import {Page} from '@playwright/test';
import {
    AddFieldQA,
    DialogConfirmQA,
    SectionDatasetQA,
    WizardVisualizationId,
} from '../../../src/shared';
import {getParentByQARole, slct, waitForCondition} from '../../utils';
import {waitForSuccessfulResponse} from '../BasePage';

export enum PlaceholderName {
    X = 'placeholder-x',
    Y = 'placeholder-y',
    Y2 = 'placeholder-y2',
    FlatTableColumns = 'placeholder-flat-table-columns',
    Geopoint = 'placeholder-geopoint',
    Geopoligon = 'placeholder-geopolygon',
    Measures = 'placeholder-measures',
    Colors = 'placeholder-colors',
    Labels = 'placeholder-labels',
    Tooltips = 'placeholder-tooltips',
    Filters = 'placeholder-filters',
    DashboardFilters = 'placeholder-dashboard-filters',
    DashboardParameters = 'placeholder-dashboard-parameters',
    LayerFilters = 'placeholder-layer-filters',
    Size = 'placeholder-size',
    Shapes = 'placeholder-shapes',
    Points = 'placeholder-points',
    Dimensions = 'placeholder-dimensions',
    PivotTableColumns = 'placeholder-pivot-table-columns',
    Rows = 'placeholder-rows',
    Polyline = 'placeholder-polyline',
    Grouping = 'placeholder-grouping',
    Heatmap = 'placeholder-heatmap',
    Sort = 'placeholder-sort',
    Segments = 'placeholder-segments',
}

export enum GeopointType {
    Geopoints = 'geopoint',
    Geopoligons = 'geopolygon',
    GeopointsHeatMap = 'heatmap',
    Polyline = 'polyline',
}

export default class SectionVisualization {
    private page: Page;
    private layersSelectSelector = slct('geolayers-select');
    private layerPopupItemSelector = '.geolayers-select__popup-item';
    private geotypeControlSelector = slct('geotype-select');

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

    getPlaceholderItems(placeholder: PlaceholderName) {
        return this.page.$$(`${slct(placeholder)} .data-qa-placeholder-item`);
    }

    async removeFieldByClick(placeholder: PlaceholderName, fieldTitle: string) {
        await this.page.hover(`${slct(placeholder)} ${slct(fieldTitle)}`);

        return this.page.click(`${slct(placeholder)} ${slct(fieldTitle)} .cross-icon`);
    }

    async removeFieldByDragAndDrop(placeholder: PlaceholderName, fieldName: string) {
        const source = `${slct(placeholder)} ${slct('dnd-container')} >> text=${fieldName}`;
        const target = `${slct(SectionDatasetQA.DatasetContainer)} ${slct('dnd-container')}`;

        await this.page.dragAndDrop(source, target);
    }

    async dragAndDropFieldBetweenPlaceholders({
        from,
        to,
        fieldName,
    }: {
        from: PlaceholderName;
        to: PlaceholderName;
        fieldName: string;
    }) {
        const dndContainerSelector = slct('dnd-container');
        const sourceLocator = `${slct(from)} ${dndContainerSelector} >> text=${fieldName}`;
        const targetLocator = `${slct(to)} ${dndContainerSelector}`;

        await this.page.dragAndDrop(sourceLocator, targetLocator);
    }

    async addFieldByDragAndDrop(placeholder: PlaceholderName, fieldName: string) {
        await this.page.waitForSelector(slct(SectionDatasetQA.ItemTitle));

        const source = slct(SectionDatasetQA.ItemTitle, fieldName);
        const target = `${slct(placeholder)} ${slct('dnd-container')}`;

        await this.page.dragAndDrop(source, target);
    }

    async replaceFieldByDragAndDrop(
        placeholder: PlaceholderName,
        oldField: string,
        newField: string,
    ) {
        const sourceSelector = slct(SectionDatasetQA.ItemTitle, newField);
        const targetSelector = `${slct(placeholder)} ${slct('dnd-container')} >> text=${oldField}`;
        await this.page.dragAndDrop(sourceSelector, targetSelector);
    }

    async addFieldByClick(
        placeholder: PlaceholderName,
        field: string,
        options?: {waitForApiRun?: boolean},
    ) {
        const promise: Promise<void> = options?.waitForApiRun
            ? waitForSuccessfulResponse('/api/run', this.page)
            : Promise.resolve();

        await this.page.hover(slct(placeholder));

        await this.page.click(`${slct(placeholder)} ${slct(AddFieldQA.AddFieldButton)}`, {
            force: true,
        });

        const selectList = this.page.locator(slct('select-list'));
        await selectList.getByText(new RegExp(`^${field}$`, 'i')).click();

        await promise;
    }

    async waitForPlaceholderFields(placeholder: PlaceholderName, fields: string[]) {
        let itemTexts: string[] = [];

        try {
            await waitForCondition(async () => {
                const items = await this.page.$$(`${slct(placeholder)} .data-qa-placeholder-item`);

                itemTexts = await Promise.all(items.map((item) => item.innerText()));

                return itemTexts.join().includes(fields.join());
            });
        } catch {
            throw new Error(`Expected: ${fields.join()}, got: ${itemTexts.join()}`);
        }
    }

    async addLayer() {
        await this.page.click(slct('add-layer'));
    }

    async toggleLayerList() {
        await this.page.click(this.layersSelectSelector);
    }

    async switchLayer(layerQa: string) {
        await this.toggleLayerList();

        await this.page.click(slct(layerQa));
    }

    async selectCombinedChartLayerVisualization(
        visualization:
            | WizardVisualizationId.Line
            | WizardVisualizationId.Column
            | WizardVisualizationId.Area,
    ) {
        await this.page.click(`.combined-chart-layer-type-switcher [value="${visualization}"]`);
    }

    async renameGeoLayer(oldGeoLayerQa: string, newName: string) {
        const oldLayerName = await this.page.waitForSelector(slct(oldGeoLayerQa));

        const oldLayerItem = (await getParentByQARole(oldLayerName, 'geolayer-select-item'))!;

        await oldLayerItem.hover();

        const oldLayerActions = (await oldLayerItem.$('[data-qa="geolayer-item-actions"]'))!;

        await oldLayerActions.click();

        await this.page.click(slct('geolayer-select-option-menu-rename'));

        await this.page.fill(`${slct('layer-name-input')} input`, newName);

        await this.page.click(slct('apply-layer-settings-button'));
    }

    async removeGeoLayer(name: string) {
        const oldLayerItem = this.page.locator(slct('geolayer-select-item'), {
            has: this.page.locator(slct(name)),
        });
        await oldLayerItem.hover();

        const oldLayerActions = oldLayerItem.locator(slct('geolayer-item-actions'));
        await oldLayerActions.click();

        await this.page.locator(slct('geolayer-select-option-menu-remove')).click();
        await this.page.locator(slct(DialogConfirmQA.ApplyButton)).click();
    }

    async setGeotype(type: GeopointType) {
        await this.page.click(this.geotypeControlSelector);
        await this.page.click(`[data-qa="${type}"]`);
    }

    async waitForLayers(layers: string[]) {
        const locatorAll = this.page
            .locator(this.layerPopupItemSelector)
            .locator('.geolayers-select__popup-item-label');

        await this.expectLayersSelectItemsCount(layers.length);

        await Promise.all(
            layers.map((layer, i) => expect(locatorAll.nth(i)).toHaveAttribute('data-qa', layer)),
        );
    }

    async getAddFieldItemsList(placeholderName: PlaceholderName) {
        let itemsList: string[] = [];

        await waitForCondition(async () => {
            const placeholderSelector = slct(placeholderName);
            await this.page.hover(placeholderSelector);

            await this.page.click(`${placeholderSelector} ${slct(AddFieldQA.AddFieldButton)}`);

            const itemNodes = await this.page.$$(`${slct('select-list')} .add-field__option-text`);

            itemsList = await Promise.all(itemNodes.map((node) => node.innerText()));

            return itemsList.length;
        });

        return itemsList;
    }

    async getPlaceholderAddItemTooltipValue(placeholderName: PlaceholderName) {
        const placeholderSelector = slct(placeholderName);
        await this.page.hover(placeholderSelector);

        await this.page.hover(`${placeholderSelector} .add-field`);
        return await this.page.locator('.g-popover [data-qa]').getAttribute('data-qa');
    }

    async getPlaceholderItemsInnerText(placeholderName: PlaceholderName) {
        const items = await this.getPlaceholderItems(placeholderName);

        const titles = await Promise.all(items.map((item) => item.$('.item-title span')));

        const titleTexts = await Promise.all(titles.map((title) => title?.innerText()));

        return titleTexts.filter(Boolean) as string[];
    }

    async clickOnSortIcon() {
        await this.page.click('.sort-icon');
    }

    async setLayerOpacity(opacityValue: string) {
        this.page.fill('.visualization-layers-control__range input', opacityValue);
    }

    async expectLayersSelectItemsCount(count: number) {
        await expect(this.page.locator(this.layerPopupItemSelector)).toHaveCount(count);
    }

    async expectLayersSelectItemsTexts(texts: string[]) {
        await expect(this.page.locator(this.layerPopupItemSelector)).toHaveText(texts);
    }
}

export const VISUALIZATION_PLACEHOLDERS = {
    line: [
        'placeholder-x',
        'placeholder-y',
        'placeholder-y2',
        'placeholder-colors',
        'placeholder-shapes',
        'placeholder-sort',
        'placeholder-labels',
        'placeholder-segments',
        'placeholder-filters',
    ],
    area: [
        'placeholder-x',
        'placeholder-y',
        'placeholder-colors',
        'placeholder-sort',
        'placeholder-labels',
        'placeholder-segments',
        'placeholder-filters',
    ],
    area100p: [
        'placeholder-x',
        'placeholder-y',
        'placeholder-colors',
        'placeholder-sort',
        'placeholder-labels',
        'placeholder-segments',
        'placeholder-filters',
    ],
    column: [
        'placeholder-x',
        'placeholder-y',
        'placeholder-colors',
        'placeholder-sort',
        'placeholder-labels',
        'placeholder-segments',
        'placeholder-filters',
    ],
    column100p: [
        'placeholder-x',
        'placeholder-y',
        'placeholder-colors',
        'placeholder-sort',
        'placeholder-labels',
        'placeholder-segments',
        'placeholder-filters',
    ],
    bar: [
        'placeholder-y',
        'placeholder-x',
        'placeholder-colors',
        'placeholder-sort',
        'placeholder-labels',
        'placeholder-filters',
    ],
    bar100p: [
        'placeholder-y',
        'placeholder-x',
        'placeholder-colors',
        'placeholder-sort',
        'placeholder-labels',
        'placeholder-filters',
    ],
    pie: [
        'placeholder-dimensions',
        'placeholder-colors',
        'placeholder-measures',
        'placeholder-sort',
        'placeholder-labels',
        'placeholder-filters',
    ],
    donut: [
        'placeholder-dimensions',
        'placeholder-colors',
        'placeholder-measures',
        'placeholder-sort',
        'placeholder-labels',
        'placeholder-filters',
    ],
    metric: ['placeholder-measures', 'placeholder-metric-colors', 'placeholder-filters'],
    scatter: [
        'placeholder-x',
        'placeholder-y',
        'placeholder-points',
        'placeholder-size',
        'placeholder-colors',
        'placeholder-shapes',
        'placeholder-sort',
        'placeholder-filters',
    ],
    treemap: [
        'placeholder-dimensions',
        'placeholder-measures',
        'placeholder-colors',
        'placeholder-filters',
    ],
    flatTable: [
        'placeholder-flat-table-columns',
        'placeholder-colors',
        'placeholder-sort',
        'placeholder-filters',
    ],
    pivotTable: [
        'placeholder-pivot-table-columns',
        'placeholder-rows',
        'placeholder-measures',
        'placeholder-colors',
        'placeholder-sort',
        'placeholder-filters',
    ],
    geopoint: [
        'placeholder-geopoint',
        'placeholder-size',
        'placeholder-colors',
        'placeholder-labels',
        'placeholder-tooltips',
        'placeholder-layer-filters',
        'placeholder-filters',
    ],
    geopolygon: [
        'placeholder-geopolygon',
        'placeholder-colors',
        'placeholder-tooltips',
        'placeholder-layer-filters',
        'placeholder-filters',
    ],
    heatmap: [
        'placeholder-heatmap',
        'placeholder-colors',
        'placeholder-layer-filters',
        'placeholder-filters',
    ],
    polyline: [
        'placeholder-polyline',
        'placeholder-measures',
        'placeholder-grouping',
        'placeholder-colors',
        'placeholder-sort',
        'placeholder-layer-filters',
        'placeholder-filters',
    ],
};
