import React from 'react';

import type {
    AreaSeries,
    BarXSeries,
    BarXSeriesData,
    BarYSeries,
    BarYSeriesData,
    ChartData,
    ChartTooltip,
    PieSeriesData,
    ScatterSeries,
    ScatterSeriesData,
    TreemapSeriesData,
} from '@gravity-ui/chartkit/gravity-charts';
import block from 'bem-cn-lite';
import get from 'lodash/get';
import {formatNumber} from 'shared/modules/format-units/index';
import {DL} from 'ui/constants';

import type {
    ExtendedChartData,
    PointCustomData,
    ScatterSeriesCustomData,
} from '../../../../../shared/types/chartkit';
import {TooltipWithComments} from '../components';

const b = block('dl-chart-tooltip-content');

type CustomScatterSeries = ScatterSeries<PointCustomData> & {custom: ScatterSeriesCustomData};
type TooltipRenderer = NonNullable<ChartTooltip['renderer']>;

const scatterTooltipRenderer = (
    widgetData: ChartData,
    data: Parameters<TooltipRenderer>[0],
    qa?: string,
) => {
    const seriesName = data.hovered[0].series.name;
    const series = widgetData.series.data.find(
        (s) => (s as CustomScatterSeries).name === seriesName,
    );
    const point = data.hovered[0].data as ScatterSeriesData<PointCustomData> | undefined;

    if (!series || !point) {
        return null;
    }

    const {pointTitle, xTitle, yTitle, colorTitle, shapeTitle, sizeTitle} =
        (series as CustomScatterSeries).custom || {};
    const shouldShowShape = shapeTitle && shapeTitle !== colorTitle;

    return (
        <div className={b({mobile: DL.IS_MOBILE})} data-qa={qa}>
            {pointTitle && (
                <div>
                    {pointTitle}:{' '}
                    <b dangerouslySetInnerHTML={{__html: String(point.custom?.name ?? '')}} />
                </div>
            )}
            <div>
                {xTitle}:{' '}
                <span dangerouslySetInnerHTML={{__html: String(point.custom?.xLabel ?? '')}} />
            </div>
            <div>
                {yTitle}:{' '}
                <span dangerouslySetInnerHTML={{__html: String(point.custom?.yLabel ?? '')}} />
            </div>
            {sizeTitle && (
                <div>
                    {sizeTitle}:{' '}
                    <span
                        dangerouslySetInnerHTML={{__html: String(point.custom?.sizeLabel ?? '')}}
                    />
                </div>
            )}
            {shouldShowShape && (
                <div>
                    {shapeTitle}:{' '}
                    <span dangerouslySetInnerHTML={{__html: String(point.custom?.sLabel ?? '')}} />
                </div>
            )}
            {colorTitle && (
                <div>
                    {colorTitle}:{' '}
                    <span dangerouslySetInnerHTML={{__html: String(point.custom?.cLabel ?? '')}} />
                </div>
            )}
        </div>
    );
};

function treemapTooltipRenderer(
    widgetData: ChartData,
    data: Parameters<TooltipRenderer>[0],
    qa?: string,
) {
    const series = widgetData.series.data[0];
    const point = data?.hovered[0]?.data as TreemapSeriesData | undefined;

    if (!series || !point) {
        return null;
    }

    const names = Array.isArray(point.name) ? point.name : [point.name];
    const label = get(point, 'label', '');

    return (
        <div className={b({mobile: DL.IS_MOBILE})} data-qa={qa}>
            {names.map((name, index) => (
                <div key={`${name}_${index}`} dangerouslySetInnerHTML={{__html: name}} />
            ))}
            <div>
                <b>{label}</b>
            </div>
        </div>
    );
}

function pieTooltipRenderer(
    _widgetData: ChartData,
    data: Parameters<TooltipRenderer>[0],
    qa?: string,
) {
    const hovered = data?.hovered[0];
    const point = hovered?.data as PieSeriesData | undefined;

    if (!point) {
        return null;
    }

    const color = get(hovered?.series, 'color');
    const value = get(point, 'formattedValue', point.value);
    let percentage = get(point, 'percentage');
    percentage = percentage ? formatNumber(percentage, {precision: 1, format: 'percent'}) : null;

    return (
        <div className={b({mobile: DL.IS_MOBILE})} data-qa={qa}>
            <div className={b('row')}>
                <div className={b('block')}>
                    <span className={b('color')} style={{backgroundColor: color}} />
                </div>
                <div
                    className={b('block')}
                    style={DL.IS_MOBILE ? {marginRight: 'auto'} : {}}
                    dangerouslySetInnerHTML={{__html: point.name}}
                />
                <div className={b('block')}>{percentage}</div>
                <div className={b('block')}>{value}</div>
            </div>
        </div>
    );
}

function customTooltipRenderer(widgetData: ChartData, data: Parameters<TooltipRenderer>) {
    const render = widgetData?.tooltip?.renderer;
    if (typeof render !== 'function') {
        return null;
    }

    const content = render(...data);
    return (
        <div
            className={b({mobile: DL.IS_MOBILE})}
            dangerouslySetInnerHTML={{__html: String(content ?? '')}}
        />
    );
}

function tooltipWithCommentsRenderer(args: {
    data: Parameters<TooltipRenderer>[0];
    qa?: string;
    rowRenderer?: ChartTooltip['rowRenderer'];
    valueFormat?: ChartTooltip['valueFormat'];
    totals?: ChartTooltip['totals'];
}) {
    const {data, qa, rowRenderer, valueFormat, totals} = args;
    const {hoveredPlotBands, hoveredPlotLines} = data;
    const plotBands = (hoveredPlotBands ?? []).filter((d) => get(d.custom, 'tooltip.enabled'));
    const plotLines = (hoveredPlotLines ?? []).filter((d) => get(d.custom, 'tooltip.enabled'));
    const hasPlotsBandsOrLInes = plotBands.length > 0 || plotLines.length > 0;

    if (!hasPlotsBandsOrLInes) {
        return null;
    }

    return (
        <React.Suspense fallback={null}>
            <TooltipWithComments
                data={data}
                plotBands={plotBands}
                plotLines={plotLines}
                qa={qa}
                rowRenderer={rowRenderer}
                valueFormat={valueFormat}
                totals={totals}
            />
        </React.Suspense>
    );
}

export const getTooltipRenderer = ({
    widgetData,
    qa,
    rowRenderer,
    valueFormat,
    totals,
}: {
    widgetData: ChartData;
    qa?: string;
    rowRenderer?: ChartTooltip['rowRenderer'];
    valueFormat?: ChartTooltip['valueFormat'];
    totals?: ChartTooltip['totals'];
}): TooltipRenderer | undefined => {
    if (widgetData?.tooltip?.renderer) {
        return (...args) => customTooltipRenderer(widgetData, args);
    }

    const seriesData = widgetData?.series?.data || [];
    const seriesTypes = Array.from(new Set(seriesData.map((s) => s.type)));

    if (seriesTypes.length === 1) {
        switch (seriesTypes[0]) {
            case 'scatter': {
                return (data: Parameters<TooltipRenderer>[0]) =>
                    scatterTooltipRenderer(widgetData, data, qa);
            }
            case 'treemap': {
                return (data: Parameters<TooltipRenderer>[0]) =>
                    treemapTooltipRenderer(widgetData, data, qa);
            }
            case 'pie': {
                return (data: Parameters<TooltipRenderer>[0]) =>
                    pieTooltipRenderer(widgetData, data, qa);
            }
        }
    }

    const commentsTypes = new Set(['area', 'bar-x', 'line']);

    if (seriesTypes.some((t) => commentsTypes.has(t))) {
        return (data: Parameters<TooltipRenderer>[0]) => {
            return tooltipWithCommentsRenderer({data, qa, rowRenderer, valueFormat, totals});
        };
    }

    return undefined;
};

const getStackedPercentRowRenderer = ({
    valueKey,
}: {
    valueKey: 'x' | 'y';
}): ChartTooltip['rowRenderer'] => {
    return function barXYWithPercentRowRenderer({
        name,
        value,
        formattedValue,
        hovered,
        className,
        color,
    }) {
        const total =
            hovered?.reduce(
                (acc, item) =>
                    acc + Number((item.data as BarXSeriesData | BarYSeriesData)[valueKey] ?? 0),
                0,
            ) ?? 0;
        const numericValue = Number(value);
        const ratio = total === 0 ? 0 : numericValue / total;
        const percentage = Number.isFinite(numericValue)
            ? formatNumber(ratio, {format: 'percent', precision: 1})
            : '';

        return (
            <tr className={className}>
                <td className={b('content-row-color-cell')}>
                    <div className={b('color')} style={{backgroundColor: color}} />
                </td>
                <td
                    className={b('content-row-label-cell')}
                    dangerouslySetInnerHTML={{__html: name}}
                />
                <td className={b('content-row-percent-cell')}>{percentage}</td>
                <td className={b('content-row-value-cell')}>{formattedValue}</td>
            </tr>
        );
    };
};

export const getTooltipRowRenderer = ({
    widgetData,
}: {
    widgetData: ChartData;
}): ChartTooltip['rowRenderer'] => {
    if (widgetData?.tooltip?.rowRenderer) {
        return widgetData.tooltip.rowRenderer;
    }

    const seriesData = widgetData?.series?.data || [];
    const seriesTypes = Array.from(new Set(seriesData.map((s) => s.type)));

    if (seriesTypes.length === 1) {
        switch (seriesTypes[0]) {
            case 'area': {
                if (seriesData.some((s) => (s as AreaSeries).stacking === 'percent')) {
                    return getStackedPercentRowRenderer({valueKey: 'y'});
                }

                break;
            }
            case 'bar-x': {
                if (seriesData.some((s) => (s as BarXSeries).stacking === 'percent')) {
                    return getStackedPercentRowRenderer({valueKey: 'y'});
                }

                break;
            }
            case 'bar-y': {
                if (seriesData.some((s) => (s as BarYSeries).stacking === 'percent')) {
                    return getStackedPercentRowRenderer({valueKey: 'x'});
                }

                break;
            }
            default: {
                return undefined;
            }
        }
    }

    return undefined;
};

export function getTooltipHeaderFormat({
    widgetData,
}: {
    widgetData: ExtendedChartData;
}): ChartTooltip['headerFormat'] {
    if (widgetData?.tooltip?.headerFormat) {
        return widgetData.tooltip.headerFormat;
    }

    const tooltipHeaderLabel = widgetData?.custom?.tooltip?.headerLabel;
    if (tooltipHeaderLabel) {
        return {
            type: 'custom',
            formatter: ({value}) => {
                return `${tooltipHeaderLabel}: ${value}`;
            },
        };
    }

    return undefined;
}
