'use client';

import { enrichEventWithDetails, ThemingParameters, useIsRTL, useSyncRef } from '@ui5/webcomponents-react-base';
import { forwardRef, useCallback, useRef } from 'react';
import type { LineProps, YAxisProps } from 'recharts';
import {
  Brush,
  CartesianGrid,
  Legend,
  Line,
  LineChart as LineChartLib,
  ReferenceLine,
  Tooltip,
  XAxis,
  YAxis
} from 'recharts';
import { useChartMargin } from '../../hooks/useChartMargin.js';
import { useLabelFormatter } from '../../hooks/useLabelFormatter.js';
import { useLegendItemClick } from '../../hooks/useLegendItemClick.js';
import { useLongestYAxisLabel } from '../../hooks/useLongestYAxisLabel.js';
import { useObserveXAxisHeights } from '../../hooks/useObserveXAxisHeights.js';
import { usePrepareDimensionsAndMeasures } from '../../hooks/usePrepareDimensionsAndMeasures.js';
import { useTooltipFormatter } from '../../hooks/useTooltipFormatter.js';
import type { IChartBaseProps } from '../../interfaces/IChartBaseProps.js';
import type { IChartDimension } from '../../interfaces/IChartDimension.js';
import type { IChartMeasure } from '../../interfaces/IChartMeasure.js';
import { ChartContainer } from '../../internal/ChartContainer.js';
import { ChartDataLabel } from '../../internal/ChartDataLabel.js';
import { defaultFormatter } from '../../internal/defaults.js';
import {
  brushProps,
  tickLineConfig,
  tooltipContentStyle,
  tooltipFillOpacity,
  xAxisPadding
} from '../../internal/staticProps.js';
import { resolvePrimaryAndSecondaryMeasures } from '../../internal/Utils.js';
import { XAxisTicks } from '../../internal/XAxisTicks.js';
import { YAxisTicks } from '../../internal/YAxisTicks.js';
import { LineChartPlaceholder } from './Placeholder.js';

interface MeasureConfig extends IChartMeasure {
  /**
   * Line Width
   * @default 1
   */
  width?: number;
  /**
   * Line Opacity
   * @default 1
   */
  opacity?: number;

  /**
   * Flag whether the line dot should be displayed or not.
   */
  showDot?: boolean;
  /**
   * This prop allows passing all [Line Properties](https://recharts.org/en-US/api/Line) of the Recharts library.
   *
   * __Note:__ It is possible to overwrite internal implementations. Please use with caution!
   */
  lineConfig?: LineProps;
}

interface DimensionConfig extends IChartDimension {
  interval?: YAxisProps['interval'];
}

export interface LineChartProps extends IChartBaseProps {
  /**
   * An array of config objects. Each object will define one dimension of the chart.
   *
   * **Required Properties**
   * - `accessor`: string containing the path to the dataset key the dimension should display. Supports object structures by using <code>'parent.child'</code>.
   *   Can also be a getter.
   *
   * **Optional Properties**
   * - `formatter`: function will be called for each data label and allows you to format it according to your needs
   * - `interval`: number that controls how many ticks are rendered on the x axis
   *
   */
  dimensions: DimensionConfig[];
  /**
   * An array of config objects. Each object is defining one line in the chart.
   *
   * **Required properties**
   * - `accessor`: string containing the path to the dataset key this line should display. Supports object structures by using <code>'parent.child'</code>.
   *   Can also be a getter.
   *
   * **Optional properties**
   *
   * - `label`: Label to display in legends or tooltips. Falls back to the <code>accessor</code> if not present.
   * - `color`: any valid CSS Color or CSS Variable. Defaults to the `sapChart_Ordinal` colors
   * - `formatter`: function will be called for each data label and allows you to format it according to your needs
   * - `hideDataLabel`: flag whether the data labels should be hidden in the chart for this line.
   * - `DataLabel`: a custom component to be used for the data label
   * - `width`: line width, defaults to `1`
   * - `opacity`: line opacity, defaults to `1`
   * - `showDot`: Flag whether the line dot should be displayed or not.
   * - `lineConfig`: This prop allows passing all [Line Properties](https://recharts.org/en-US/api/Line) of the Recharts library.
   *
   */
  measures: MeasureConfig[];
}

const dimensionDefaults = {
  formatter: defaultFormatter
};

const measureDefaults = {
  formatter: defaultFormatter,
  width: 1,
  opacity: 1
};

/**
 * A `LineChart` is a type of chart used to show information that changes over time - it connects multiple dots.
 */
const LineChart = forwardRef<HTMLDivElement, LineChartProps>((props, ref) => {
  const {
    dataset,
    loading,
    loadingDelay,
    noLegend,
    noAnimation,
    tooltipConfig,
    onDataPointClick,
    onLegendClick,
    onClick,
    style,
    className,
    slot,
    syncId,
    ChartPlaceholder,
    children,
    ...rest
  } = props;

  const chartConfig: LineChartProps['chartConfig'] = {
    yAxisVisible: false,
    xAxisVisible: true,
    gridStroke: ThemingParameters.sapList_BorderColor,
    gridHorizontal: true,
    gridVertical: false,
    legendPosition: 'bottom',
    legendHorizontalAlign: 'left',
    zoomingTool: false,
    resizeDebounce: 250,
    yAxisTicksVisible: true,
    yAxisConfig: {},
    xAxisConfig: {},
    secondYAxisConfig: {},
    ...props.chartConfig
  };

  const { dimensions, measures } = usePrepareDimensionsAndMeasures(
    props.dimensions,
    props.measures,
    dimensionDefaults,
    measureDefaults
  );

  const tooltipValueFormatter = useTooltipFormatter(measures);

  const primaryDimension = dimensions[0];
  const { primaryMeasure, secondaryMeasure } = resolvePrimaryAndSecondaryMeasures(
    measures,
    chartConfig?.secondYAxis?.dataKey
  );

  const labelFormatter = useLabelFormatter(primaryDimension);

  const [componentRef, chartRef] = useSyncRef<any>(ref);

  const dataKeys = measures.map(({ accessor }) => accessor);
  const colorSecondY = chartConfig.secondYAxis
    ? dataKeys.findIndex((key) => key === chartConfig.secondYAxis?.dataKey)
    : 0;

  const onItemLegendClick = useLegendItemClick(onLegendClick);
  const preventOnClickCall = useRef(0);
  const onDataPointClickInternal = useCallback(
    (payload, eventOrIndex) => {
      if (eventOrIndex.dataKey && typeof onDataPointClick === 'function') {
        preventOnClickCall.current = 2;
        onDataPointClick(
          enrichEventWithDetails({} as any, {
            value: eventOrIndex.value,
            dataKey: eventOrIndex.dataKey,
            dataIndex: eventOrIndex.index,
            payload: eventOrIndex.payload
          })
        );
      } else if (typeof onClick === 'function' && preventOnClickCall.current === 0) {
        onClick(
          enrichEventWithDetails(eventOrIndex, {
            payload: payload?.activePayload?.[0]?.payload,
            activePayloads: payload?.activePayload
          })
        );
      }
      if (preventOnClickCall.current > 0) {
        preventOnClickCall.current -= 1;
      }
    },
    [onDataPointClick, preventOnClickCall.current]
  );

  const isBigDataSet = dataset?.length > 30;
  const primaryDimensionAccessor = primaryDimension?.accessor;

  const [yAxisWidth, legendPosition] = useLongestYAxisLabel(dataset, measures, chartConfig.legendPosition);
  const marginChart = useChartMargin(chartConfig.margin, chartConfig.zoomingTool);
  const xAxisHeights = useObserveXAxisHeights(chartRef, props.dimensions.length);
  const { chartConfig: _0, dimensions: _1, measures: _2, ...propsWithoutOmitted } = rest;
  const isRTL = useIsRTL(chartRef);
  const referenceLine = chartConfig.referenceLine;

  return (
    <ChartContainer
      dataset={dataset}
      loading={loading}
      loadingDelay={loadingDelay}
      Placeholder={ChartPlaceholder ?? LineChartPlaceholder}
      ref={componentRef}
      style={style}
      className={className}
      slot={slot}
      resizeDebounce={chartConfig.resizeDebounce}
      {...propsWithoutOmitted}
    >
      {/* eslint-disable-next-line @typescript-eslint/ban-ts-comment */}
      {/*// @ts-ignore:: todo not yet compatible with React19*/}
      <LineChartLib
        syncId={syncId}
        margin={marginChart}
        data={dataset}
        onClick={onDataPointClickInternal}
        accessibilityLayer={chartConfig.accessibilityLayer}
        className={typeof onDataPointClick === 'function' ? 'has-click-handler' : undefined}
      >
        <CartesianGrid
          vertical={chartConfig.gridVertical}
          horizontal={chartConfig.gridHorizontal}
          stroke={chartConfig.gridStroke}
        />
        {dimensions.map((dimension, index) => {
          return (
            <XAxis
              key={dimension.reactKey}
              dataKey={dimension.accessor}
              xAxisId={index}
              interval={dimension?.interval ?? (isBigDataSet ? 'preserveStart' : 0)}
              tick={<XAxisTicks config={dimension} />}
              tickLine={index < 1}
              axisLine={index < 1}
              height={chartConfig.xAxisVisible ? xAxisHeights[index] : 0}
              padding={xAxisPadding}
              allowDuplicatedCategory={index === 0}
              reversed={isRTL}
              {...chartConfig.xAxisConfig}
            />
          );
        })}
        <YAxis
          orientation={isRTL === true ? 'right' : 'left'}
          axisLine={chartConfig.yAxisVisible}
          tickLine={tickLineConfig}
          yAxisId="left"
          tickFormatter={primaryMeasure?.formatter}
          interval={0}
          tick={chartConfig.yAxisTicksVisible && <YAxisTicks config={primaryMeasure} />}
          width={yAxisWidth}
          {...chartConfig.yAxisConfig}
        />
        {chartConfig.secondYAxis?.dataKey && (
          <YAxis
            dataKey={chartConfig.secondYAxis.dataKey}
            axisLine={{
              stroke: chartConfig.secondYAxis.color ?? `var(--sapChart_OrderedColor_${(colorSecondY % 12) + 1})`
            }}
            tick={
              <YAxisTicks
                config={secondaryMeasure}
                secondYAxisConfig={{
                  color: chartConfig.secondYAxis.color ?? `var(--sapChart_OrderedColor_${(colorSecondY % 12) + 1})`
                }}
              />
            }
            tickLine={{
              stroke: chartConfig.secondYAxis.color ?? `var(--sapChart_OrderedColor_${(colorSecondY % 12) + 1})`
            }}
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore
            label={{ value: chartConfig.secondYAxis.name, offset: 2, angle: +90, position: 'center' }}
            orientation={isRTL === true ? 'left' : 'right'}
            yAxisId="right"
            interval={0}
            {...chartConfig.secondYAxisConfig}
          />
        )}
        {measures.map((element, index) => {
          return (
            <Line
              dot={element.showDot ?? !isBigDataSet}
              yAxisId={chartConfig.secondYAxis?.dataKey === element.accessor ? 'right' : 'left'}
              key={element.reactKey}
              name={element.label ?? element.accessor}
              strokeOpacity={element.opacity}
              // eslint-disable-next-line @typescript-eslint/ban-ts-comment
              // @ts-ignore
              label={isBigDataSet ? false : <ChartDataLabel config={element} chartType="line" position="top" />}
              type="monotone"
              dataKey={element.accessor}
              stroke={element.color ?? `var(--sapChart_OrderedColor_${(index % 12) + 1})`}
              strokeWidth={element.width}
              activeDot={{ onClick: onDataPointClickInternal }}
              isAnimationActive={!noAnimation}
              {...element.lineConfig}
            />
          );
        })}
        {!noLegend && (
          // eslint-disable-next-line @typescript-eslint/ban-ts-comment
          // @ts-ignore
          <Legend
            verticalAlign={chartConfig.legendPosition}
            align={chartConfig.legendHorizontalAlign}
            onClick={onItemLegendClick}
            wrapperStyle={legendPosition}
            {...chartConfig.legendConfig}
          />
        )}
        {referenceLine && (
          <ReferenceLine
            {...referenceLine}
            stroke={referenceLine?.color ?? referenceLine?.stroke}
            y={referenceLine?.value ?? referenceLine?.y}
            yAxisId={referenceLine?.yAxisId ?? 'left'}
            label={referenceLine?.label}
          />
        )}
        {/*ToDo: remove conditional rendering once `active` is working again (https://github.com/recharts/recharts/issues/2703)*/}
        {tooltipConfig?.active !== false && (
          <Tooltip
            cursor={tooltipFillOpacity}
            formatter={tooltipValueFormatter}
            contentStyle={tooltipContentStyle}
            labelFormatter={labelFormatter}
            {...tooltipConfig}
          />
        )}
        {!!chartConfig.zoomingTool && (
          <Brush
            dataKey={primaryDimensionAccessor}
            tickFormatter={primaryDimension?.formatter}
            {...brushProps}
            {...(typeof chartConfig.zoomingTool === 'object' ? chartConfig.zoomingTool : {})}
          />
        )}
        {children}
      </LineChartLib>
    </ChartContainer>
  );
});

LineChart.displayName = 'LineChart';

export { LineChart };