import {Page, expect} from '@playwright/test';
import {dateTimeUtc} from '@gravity-ui/date-utils';

import {WizardVisualizationId} from '../../../page-objects/common/Visualization';
import {PlaceholderName} from '../../../page-objects/wizard/SectionVisualization';
import WizardPage from '../../../page-objects/wizard/WizardPage';
import {getXAxisValues, openTestPage, waitForCondition} from '../../../utils';
import {RobotChartsWizardUrls} from '../../../utils/constants';
import datalensTest from '../../../utils/playwright/globalTestDefinition';
import {COMMON_CHARTKIT_SELECTORS} from '../../../page-objects/constants/chartkit';
import {CommonUrls} from '../../../page-objects/constants/common-urls';

const checkXAxisValuesOrder = async (wizardPage: WizardPage, expectedValues: string[]) => {
    let xAxisValues: (string | null)[] = [];
    await wizardPage.chartkit.waitUntilLoaderExists();

    await waitForCondition(async () => {
        xAxisValues = await getXAxisValues(wizardPage.page);

        return xAxisValues.join(',') === expectedValues.join(',');
    }).catch(() => {
        throw new Error(
            `Expected order [${expectedValues.join(',')}], current order [${xAxisValues.join(
                ',',
            )}]`,
        );
    });
};

const formatAxisValues = (values: Array<number | string>, isDateTimeAxis: boolean) =>
    values
        .map((value) => (isDateTimeAxis ? dateTimeUtc({input: value}).format('DD.MM.YYYY') : value))
        .map(String);

const getApiRunXAxisValues = async (responseData: any): Promise<string[]> => {
    const chartData = responseData?.data;
    const highchartsConfig = JSON.parse(responseData.highchartsConfig ?? '{}');

    const isDateTime =
        chartData.xAxis?.type === 'datetime' || highchartsConfig?.xAxis?.type === 'datetime';

    const categories = chartData.categories ?? chartData.xAxis?.categories;
    const data = chartData.graphs?.[0] ?? chartData.series?.data?.[0];

    const apiValues = categories
        ? categories.map(String)
        : formatAxisValues(
              data.data.map(({x}: {x: number}) => x),
              isDateTime,
          );

    const isBarChart = data.type === 'bar-x' || highchartsConfig?.chart?.type === 'column';
    const isReversed = chartData.xAxis?.order === 'reverse' || highchartsConfig?.xAxis?.reversed;
    const needReverse = isReversed && !isBarChart;

    return needReverse ? [...apiValues].reverse() : apiValues;
};

const checkApiRunXAxisValuesOrderAfterAction = async (
    wizardPage: WizardPage,
    expectedValues: string[],
    action: () => Promise<void>,
) => {
    const apiRunResponse = wizardPage.page.waitForRequest(
        (request) => new URL(request.url()).pathname === CommonUrls.ApiRun,
    );

    await action();

    const responseData = await (await (await apiRunResponse).response())?.json();

    await wizardPage.chartkit.waitUntilLoaderExists();

    const xAxisValues = await getApiRunXAxisValues(responseData);

    expect(xAxisValues).toEqual(expectedValues);
};

const expectedNumericValues = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'];

const expectedValuesAfterNumericSort = [
    '12',
    '9',
    '11',
    '10',
    '3',
    '5',
    '8',
    '6',
    '7',
    '4',
    '2',
    '1',
];

const expectedValuesAfterChangeNumericSort = [
    '1',
    '2',
    '4',
    '7',
    '6',
    '8',
    '5',
    '3',
    '10',
    '11',
    '9',
    '12',
];

const expectedDateValues = [
    '01.12.2017',
    '02.12.2017',
    '03.12.2017',
    '04.12.2017',
    '05.12.2017',
    '06.12.2017',
    '07.12.2017',
];

const expectedDateValuesAfterChangeSort = [
    '07.12.2017',
    '04.12.2017',
    '02.12.2017',
    '06.12.2017',
    '03.12.2017',
    '05.12.2017',
    '01.12.2017',
];

const expectedDateValuesAfterSort = [
    '01.12.2017',
    '05.12.2017',
    '03.12.2017',
    '06.12.2017',
    '02.12.2017',
    '04.12.2017',
    '07.12.2017',
];

datalensTest.describe('Wizard - Sort', () => {
    datalensTest.beforeEach(async ({page}: {page: Page}) => {
        await openTestPage(page, RobotChartsWizardUrls.WizardForDatasetSampleCh);
    });

    datalensTest(
        'When you click on the sorting icon, the order changes along the X axis (ascending/descending)',
        async ({page}: {page: Page}) => {
            const wizardPage = new WizardPage({page});

            await wizardPage.setVisualization(WizardVisualizationId.Line);

            await wizardPage.sectionVisualization.addFieldByClick(PlaceholderName.X, 'Category');
            await wizardPage.sectionVisualization.addFieldByClick(PlaceholderName.Y, 'Profit');
            await wizardPage.sectionVisualization.addFieldByClick(PlaceholderName.Sort, 'Category');

            const expectedValues = ['Technology', 'Office Supplies', 'Furniture'];

            await checkXAxisValuesOrder(wizardPage, expectedValues);

            await wizardPage.sectionVisualization.clickOnSortIcon();

            const expectedValuesAfterSort = ['Furniture', 'Office Supplies', 'Technology'];

            await checkXAxisValuesOrder(wizardPage, expectedValuesAfterSort);
        },
    );

    datalensTest(
        'Sorting works if there is a field with the number type in section X',
        async ({page}: {page: Page}) => {
            const wizardPage = new WizardPage({page});

            await wizardPage.sectionVisualization.addFieldByClick(PlaceholderName.X, 'Month');

            await wizardPage.sectionVisualization.addFieldByClick(PlaceholderName.Y, 'Profit');

            await checkXAxisValuesOrder(wizardPage, expectedNumericValues);

            await wizardPage.sectionVisualization.addFieldByClick(PlaceholderName.Sort, 'Profit');

            await checkXAxisValuesOrder(wizardPage, expectedValuesAfterNumericSort);

            await wizardPage.sectionVisualization.clickOnSortIcon();

            await checkXAxisValuesOrder(wizardPage, expectedValuesAfterChangeNumericSort);
        },
    );

    datalensTest(
        'Sorting works if there is a field with the number type in section X and there is the same field in colors as in X',
        async ({page}: {page: Page}) => {
            const wizardPage = new WizardPage({page});

            await wizardPage.sectionVisualization.addFieldByClick(PlaceholderName.X, 'Month');

            await wizardPage.sectionVisualization.addFieldByClick(PlaceholderName.Y, 'Profit');

            await wizardPage.sectionVisualization.addFieldByClick(PlaceholderName.Colors, 'Month');

            await checkXAxisValuesOrder(wizardPage, expectedNumericValues);

            await wizardPage.sectionVisualization.addFieldByClick(PlaceholderName.Sort, 'Profit');

            await checkXAxisValuesOrder(wizardPage, expectedValuesAfterNumericSort);

            await wizardPage.sectionVisualization.clickOnSortIcon();

            await checkXAxisValuesOrder(wizardPage, expectedValuesAfterChangeNumericSort);
        },
    );

    datalensTest(
        'Sorting works if there is a field with the Date type in section X',
        async ({page}: {page: Page}) => {
            const wizardPage = new WizardPage({page});

            await wizardPage.sectionVisualization.addFieldByClick(PlaceholderName.X, 'DATE');

            await wizardPage.sectionVisualization.addFieldByClick(PlaceholderName.Y, 'Profit');

            // There are too many dates in the dataset.
            await wizardPage.sectionVisualization.addFieldByClick(PlaceholderName.Filters, 'DATE');

            await wizardPage.filterEditor.selectRangeDate(['01.12.2017', '07.12.2017']);

            await checkApiRunXAxisValuesOrderAfterAction(
                wizardPage,
                expectedDateValues,
                async () => {
                    await wizardPage.filterEditor.apply();
                },
            );

            await checkApiRunXAxisValuesOrderAfterAction(
                wizardPage,
                expectedDateValuesAfterSort,
                async () => {
                    await wizardPage.sectionVisualization.addFieldByClick(
                        PlaceholderName.Sort,
                        'Profit',
                    );
                },
            );

            await checkApiRunXAxisValuesOrderAfterAction(
                wizardPage,
                expectedDateValuesAfterChangeSort,
                async () => {
                    await wizardPage.sectionVisualization.clickOnSortIcon();
                },
            );
        },
    );

    datalensTest(
        'Sorting works if there is a field with the number type in section X, and the same field in the Sorting',
        async ({page}: {page: Page}) => {
            const wizardPage = new WizardPage({page});

            await wizardPage.setVisualization(WizardVisualizationId.Line);

            await wizardPage.sectionVisualization.addFieldByClick(PlaceholderName.X, 'Month');

            await wizardPage.sectionVisualization.addFieldByClick(PlaceholderName.Y, 'Profit');

            await checkApiRunXAxisValuesOrderAfterAction(
                wizardPage,
                [...expectedNumericValues].reverse(),
                async () => {
                    await wizardPage.sectionVisualization.addFieldByClick(
                        PlaceholderName.Sort,
                        'Month',
                    );
                },
            );

            await checkApiRunXAxisValuesOrderAfterAction(
                wizardPage,
                expectedNumericValues,
                async () => {
                    await wizardPage.sectionVisualization.clickOnSortIcon();
                },
            );

            await wizardPage.sectionVisualization.removeFieldByClick(PlaceholderName.Sort, 'Month');

            await wizardPage.setVisualization(WizardVisualizationId.Column);

            await checkApiRunXAxisValuesOrderAfterAction(
                wizardPage,
                [...expectedNumericValues].reverse(),
                async () => {
                    await wizardPage.sectionVisualization.addFieldByClick(
                        PlaceholderName.Sort,
                        'Month',
                    );
                },
            );

            await checkApiRunXAxisValuesOrderAfterAction(
                wizardPage,
                expectedNumericValues,
                async () => {
                    await wizardPage.sectionVisualization.clickOnSortIcon();
                },
            );
        },
    );

    datalensTest(
        'Sorting works if we sort by the same field that lies in the Color section when the number type lies there',
        async ({page}) => {
            const wizardPage = new WizardPage({page});

            await wizardPage.setVisualization(WizardVisualizationId.Area);

            await wizardPage.sectionVisualization.addFieldByClick(PlaceholderName.X, 'Category');

            await wizardPage.sectionVisualization.addFieldByClick(PlaceholderName.Y, 'Profit');

            const formula = `FLOAT(ROUND([Month]/123, 2))`;
            const name = 'FloatColor';

            await wizardPage.createNewFieldWithFormula(name, formula);

            const expectedValues = [
                '0.1',
                '0.09',
                '0.08',
                '0.07',
                '0.06',
                '0.05',
                '0.04',
                '0.03',
                '0.02',
                '0.01',
            ];

            await wizardPage.sectionVisualization.addFieldByClick(PlaceholderName.Colors, name);

            await wizardPage.sectionVisualization.addFieldByClick(PlaceholderName.Sort, name);

            let legendItemsTexts: string[] = [];

            await waitForCondition(async () => {
                const legendItems = await wizardPage.page.$$(
                    COMMON_CHARTKIT_SELECTORS.chartLegendItem,
                );

                if (!legendItems?.length) {
                    return false;
                }

                legendItemsTexts = (await Promise.all(
                    legendItems.map((item) => item.textContent()),
                )) as string[];

                return legendItemsTexts.join(',') === expectedValues.join(',');
            }).catch(() => {
                throw new Error(
                    `Expected order [${expectedValues.join(
                        ',',
                    )}], current order [${legendItemsTexts.join(',')}]`,
                );
            });
        },
    );

    datalensTest(
        'Sorting works if we sort by the same field that lies in the Color section when there is an Indicator',
        async ({page}) => {
            const wizardPage = new WizardPage({page});

            await wizardPage.setVisualization(WizardVisualizationId.Column);

            await wizardPage.sectionVisualization.addFieldByClick(PlaceholderName.X, 'Month');

            await wizardPage.sectionVisualization.addFieldByClick(PlaceholderName.Y, 'Sales');

            await wizardPage.sectionVisualization.addFieldByClick(PlaceholderName.Colors, 'Profit');

            await wizardPage.sectionVisualization.addFieldByClick(PlaceholderName.Sort, 'Profit');

            const expectedSortedCategories = [
                '12',
                '9',
                '11',
                '10',
                '3',
                '5',
                '8',
                '6',
                '7',
                '4',
                '2',
                '1',
            ];

            await checkXAxisValuesOrder(wizardPage, expectedSortedCategories);

            await wizardPage.sectionVisualization.clickOnSortIcon();

            await checkXAxisValuesOrder(wizardPage, [...expectedSortedCategories].reverse());
        },
    );
});
