import type {ChartKitProps, ChartKitType} from '@gravity-ui/chartkit';
import cloneDeep from 'lodash/cloneDeep';
import get from 'lodash/get';
import type {ChartKitHolidays} from 'ui/store/toolkit/chartkit/types';
import {isEnabledFeature} from 'ui/utils/isEnabledFeature';

import {Feature, isMarkupItem} from '../../../../../shared';
import {DL} from '../../../../constants/common';
import type {GraphWidget, LoadedWidgetData} from '../../types';
import type {ChartKitAdapterProps} from '../types';

import {
    applyGoToEvents,
    applyRunActivityEvent,
    applySetActionParamsEvents,
    applyTreemapLabelFormatter,
    fixPieTotals,
} from './apply-hc-handlers';
import {getGravityChartsChartKitData} from './gravity-charts/chartkit-adapter';
import {extractHcTypeFromData, getNormalizedClickActions} from './utils';

export const extractWidgetType = (data?: LoadedWidgetData) => {
    return data && 'type' in data && data.type;
};

export const getChartkitType = (data?: LoadedWidgetData): ChartKitType | undefined => {
    const type = extractWidgetType(data);

    if (!type) {
        return undefined;
    }

    let chartkitType: ChartKitType | undefined;

    switch (type) {
        // for some reason, `yagr` charts inside DL have historically been called `timeseries`
        case 'timeseries': {
            chartkitType = 'yagr';

            break;
        }
        // historical background:
        // - initially, 'metric' was created, but it came out complicated and unsuitable for Wizard
        // - then, simple 'metric2' was created specifically for Wizard
        // - 'metric2' go to opensource with more common name for such charts in the BI community - 'indicator'
        case 'metric2': {
            if (isMarkupItem(get(data?.data, 'value'))) {
                chartkitType = 'markup';
            } else {
                chartkitType = 'indicator';
            }

            break;
        }

        case 'metric': {
            chartkitType = 'metric';

            break;
        }

        case 'markup': {
            chartkitType = 'markup';

            break;
        }

        case 'map': {
            chartkitType = 'highchartsmap';

            break;
        }

        case 'ymap': {
            chartkitType = 'yandexmap';

            break;
        }

        case 'graph': {
            chartkitType = 'highcharts';

            break;
        }

        case 'd3': {
            chartkitType = 'gravity-charts';

            break;
        }

        case 'table': {
            const isWizardOrQl = get(data, 'isNewWizard') || get(data, 'isQL');

            if (isWizardOrQl || isEnabledFeature(Feature.NewTableWidgetForCE)) {
                chartkitType = 'table';
            }

            break;
        }

        // @ts-ignore
        case 'blank-chart':
        case 'advanced-chart': {
            chartkitType = 'advanced-chart';

            break;
        }
    }

    return chartkitType;
};

export const getOpensourceChartKitData = <T extends ChartKitType>({
    type,
    loadedData,
    chartkitHolidays,
    onChange,
    runActivity,
    splitTooltip,
    lang,
}: {
    type: T;
    loadedData: ChartKitAdapterProps['loadedData'];
    chartkitHolidays?: ChartKitHolidays;
    onChange?: ChartKitAdapterProps['onChange'];
    runActivity?: ChartKitAdapterProps['runActivity'];
    splitTooltip?: boolean;
    lang?: string;
}) => {
    switch (type) {
        case 'indicator': {
            const data = {...(loadedData as ChartKitProps<'indicator'>['data'])};
            // CHARTS-5786
            // The value is taken here https://github.com/datalens-tech/ui/blob/217dca46745b184e25b3d9c3ac3f564397e06b61/src/shared/constants/colors/default.ts#L6
            data.defaultColor = '#4da2f1';

            return data;
        }
        case 'metric': {
            const data = {...(loadedData as ChartKitProps<'metric'>['data'])};

            return data;
        }
        case 'markup': {
            const data = {...(loadedData as ChartKitProps<'markup'>['data'])};

            return data;
        }
        case 'highchartsmap': {
            const data = {...(loadedData as ChartKitProps<'highchartsmap'>['data'])};

            return data;
        }
        case 'yandexmap': {
            const data = {...(loadedData as ChartKitProps<'yandexmap'>['data'])};

            return data;
        }
        case 'highcharts': {
            if (!DL.CHARTKIT_SETTINGS.highcharts?.enabled) {
                throw Error(
                    'This chart requires Highcharts to be enabled. See the documentation for more details.\n',
                );
            }

            const data = cloneDeep(loadedData) as GraphWidget;

            if (!data.config.tooltip) {
                data.config.tooltip = {};
            }

            // check data.config.tooltip because TS does not recognize that the field is initialized
            if (data.config.tooltip && typeof data.config.tooltip.pin === 'undefined') {
                data.config.tooltip.pin = {altKey: true};
            }

            const clickActions = getNormalizedClickActions(data);

            if (clickActions.length) {
                clickActions.forEach((action) => {
                    const handlers = Array.isArray(action.handler)
                        ? action.handler
                        : [action.handler];
                    handlers.forEach((handler) => {
                        switch (handler.type) {
                            case 'setActionParams': {
                                applySetActionParamsEvents({
                                    action: {
                                        type: handler.type,
                                        scope: action.scope,
                                    },
                                    data,
                                    onChange,
                                });
                                break;
                            }
                            case 'goTo': {
                                applyGoToEvents({data, target: handler.target});
                                break;
                            }
                            case 'runActivity': {
                                if (runActivity) {
                                    applyRunActivityEvent({data, runActivity});
                                }

                                break;
                            }
                        }
                    });
                });
            }

            const chartType = extractHcTypeFromData(data);
            switch (chartType) {
                case 'pie': {
                    fixPieTotals({data});
                    break;
                }
                case 'treemap': {
                    applyTreemapLabelFormatter({data});
                    break;
                }
            }

            return data;
        }
        case 'gravity-charts': {
            const data = cloneDeep(loadedData) as GraphWidget;
            return getGravityChartsChartKitData({
                chartkitHolidays,
                loadedData: data,
                onChange,
                runActivity,
                splitTooltip,
                lang,
            });
        }
        case 'table': {
            return {...loadedData, runActivity};
        }
        default: {
            return loadedData as ChartKitProps<T>['data'];
        }
    }
};

export const getAdditionalProps = <T extends ChartKitType>({
    type,
    loadedData,
    splitTooltip,
}: {
    type: T;
    loadedData?: LoadedWidgetData;
    splitTooltip?: boolean;
}): Partial<ChartKitProps<ChartKitType>> | undefined => {
    switch (type) {
        case 'highcharts': {
            return {
                hoistConfigError: false,
            } as Partial<ChartKitProps<ChartKitType>>;
        }
        case 'gravity-charts': {
            const withoutLineLimit =
                loadedData?.config &&
                'withoutLineLimit' in loadedData.config &&
                loadedData.config.withoutLineLimit;
            const seriesCountLimit =
                loadedData?.config &&
                'linesLimit' in loadedData.config &&
                loadedData.config.linesLimit;

            return {
                tooltip: {splitted: splitTooltip},
                validation: {
                    seriesCountLimit: withoutLineLimit ? undefined : seriesCountLimit,
                },
            } as Partial<ChartKitProps<ChartKitType>>;
        }
        default: {
            return undefined;
        }
    }
};
