/**
 * WordPress dependencies
 */
import { _x, sprintf } from '@safe-wordpress/i18n';

/**
 * External dependencies
 */
import { numberFormat } from '@nab/i18n';
import { Bar } from 'react-chartjs-2';
import ChartDataLabels from 'chartjs-plugin-datalabels';
import {
	Chart as ChartJS,
	BarElement,
	CategoryScale,
	Legend,
	LinearScale,
	Title,
	Tooltip,
} from 'chart.js';
import { COLORS, isNumber } from '@nab/utils';

ChartJS.register(
	BarElement,
	CategoryScale,
	ChartDataLabels,
	Legend,
	LinearScale,
	Title,
	Tooltip
);

import type { ChartOptions } from 'chart.js';
import type { AlternativeTrackingData, GoalId } from '@nab/types';

/**
 * Internal dependencies
 */
import { generateNLabels, isTooltipContext } from './helpers';

export type ConversionImprovementChartProps = {
	readonly alternatives?: ReadonlyArray< AlternativeTrackingData >;
	readonly goal: GoalId;
	readonly unique: boolean;
};

export const ConversionImprovementChart = (
	props: ConversionImprovementChartProps
): JSX.Element => {
	const { alternatives = [], goal, unique } = props;

	const data = getData( alternatives, goal, unique );

	const NORMAL = COLORS.wpAdminThemeColorRgb;
	const RED = COLORS.nabBackgroundRed;
	const colors = data.map( ( value ): string =>
		0 > value ? RED : NORMAL
	);

	const chartData = {
		datasets: [
			{
				data,
				backgroundColor: colors,
				maxBarThickness: 60,
			},
		],
		labels: generateNLabels( data.length ),
	};
	const options = getOptions();

	// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment
	return <Bar data={ chartData as any } options={ options as any } />;
};

// =======
// HELPERS
// =======

const getOptions = (): ChartOptions => {
	return {
		indexAxis: 'y',
		layout: {
			padding: {
				left: 0,
				right: 0,
				top: 0,
				bottom: 0,
			},
		},
		scales: {
			x: {
				ticks: {
					callback: ( value ) =>
						isNumber( value ) ? `${ numberFormat( value ) }%` : '',
					maxRotation: 0,
				},
			},
		},
		responsive: true,
		maintainAspectRatio: false,
		plugins: {
			title: {
				display: true,
				color: COLORS.nabText,
				text: _x( 'Improvement', 'text', 'nelio-ab-testing' ),
				font: {
					size: 14,
					style: 'normal',
				},
			},
			tooltip: {
				callbacks: {
					title: ( [ context ] ) =>
						sprintf(
							/* translators: %s: Letter, such as A, B, or C. */
							_x( 'Variant %s', 'text', 'nelio-ab-testing' ),
							context?.label ?? ''
						),
					label: ( context ) => {
						if ( ! isTooltipContext< number >( context ) ) {
							return '';
						}

						const value = context.raw || 0;
						if ( 0 > value ) {
							return sprintf(
								/* translators: %s: Percentage number. */
								_x(
									'%s%% worse than the control version',
									'text',
									'nelio-ab-testing'
								),
								numberFormat( Math.abs( value ) )
							);
						}
						return sprintf(
							/* translators: %s: Percentage number. */
							_x(
								'%s%% better than the control version',
								'text',
								'nelio-ab-testing'
							),
							numberFormat( value )
						);
					},
				},
			},
			legend: {
				display: false,
			},
			datalabels: {
				align: ( context ) => {
					const value =
						context.dataset.data[ context?.dataIndex ?? '' ];
					const num = isNumber( value ) ? value : 0;
					return num < 0 ? 'end' : 'start';
				},
				anchor: ( context ) => {
					const value =
						context.dataset.data[ context?.dataIndex ?? '' ];
					const num = isNumber( value ) ? value : 0;
					return num < 0 ? 'start' : 'end';
				},
				color: COLORS.nabTextInverted,
				font: {
					family: '-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif',
					weight: 'bold',
				},
				formatter: ( value: number ) => {
					if ( ! value ) {
						return '';
					}
					return value < 0
						? `${ numberFormat( value ) }%`
						: `+${ numberFormat( value ) }%`;
				},
			},
		},
	};
};

const getData = (
	alternatives: ReadonlyArray< AlternativeTrackingData >,
	goal: GoalId,
	unique: boolean
) =>
	alternatives.map( ( alternative ) =>
		unique
			? alternative.uniqueImprovementFactors[ goal ] || 0
			: alternative.improvementFactors[ goal ] || 0
	);
