// ColumnChart/index.tsx
// This is the main entry point for the ColumnChart component in the RN chart library.
// It composes the full column chart, handling layout, axis configuration, empty state, and rendering grouped/stacked columns.
// The component is highly configurable and uses subcomponents for axes, content, and frame.

import React, { useMemo, useState, useCallback, useEffect } from 'react';
import type { ViewStyle, LayoutChangeEvent } from 'react-native';
import ChartFrame from '../shared/ChartFrame';
import type {
  DataValue,
  HeaderConfig,
  Series,
  StyleConfig,
  XAxisConfig,
  YAxisConfig,
} from '../types';
import ColumnChartContent from './ColumnChartContent';
import { createNiceScale } from '../shared/niceNumbers';
import { StyledColumnChartWrapper } from './StyledColumnChart';
import useCustomColor from '../shared/hooks/useCustomColor';

/**
 * Props for the ColumnChart component.
 *
 * @property data - The data to be displayed in the chart. Each item represents a series.
 * @property yAxisConfig - Optional configuration for the Y axis.
 * @property xAxisConfig - Optional configuration for the X axis.
 * @property style - Additional style for the root View. Follows the same convention as other components (e.g., Button).
 * @property testID - Testing id of the component. Passed to the root View and used as a suffix for inner elements.
 * @property headerConfig - Optional header configuration (title, actions).
 * @property emptyText - Optional text to display when data is empty.
 * @property onBarPress - Called when a bar (column segment) is pressed. Receives info about the bar.
 */
export interface ColumnChartProps {
  /**
   * The data to be displayed in the chart. Each item represents a series.
   */
  data: Array<Series<Array<DataValue>>>;
  /**
   * Optional configuration for the Y axis.
   * Note: minValue is omitted and always set to zero internally by the component.
   */
  yAxisConfig?: Omit<YAxisConfig, 'minValue'>;
  /**
   * Optional configuration for the X axis.
   */
  xAxisConfig?: XAxisConfig;
  /**
   * Additional style for the root View.
   */
  style?: ViewStyle;
  /**
   * Testing id of the component.
   */
  testID?: string;
  /**
   * Header configuration for the chart.
   */
  headerConfig?: HeaderConfig;
  /**
   * Text to display when the chart has no data (empty state).
   */
  emptyText?: string;
  /**
   * Called when a bar (column segment) is pressed. Receives info about the bar.
   */
  onBarPress?: (info: {
    value: number | undefined;
    xLabel: string;
    seriesLabel: string;
    seriesIndex: number;
    xIndex: number;
  }) => void;
  /**
   * * styleConfig use to custom the style of the chart.
   * * styleConfig must be an object:
   *    * color?: use to custom the legend colors.
   */
  styleConfig?: StyleConfig;
}

/**
 * ColumnChart component for rendering grouped/stacked column charts.
 * Handles layout, axis configuration, empty state, and data validation.
 * Uses ChartFrame for layout and axes, and ColumnChartContent for rendering columns.
 *
 * @param data - Array of series, each with a label and array of values.
 * @param yAxisConfig - Optional Y axis configuration.
 * @param xAxisConfig - Optional X axis configuration.
 * @param style - Optional style for the chart container.
 * @param testID - Optional test ID for testing.
 * @param headerConfig - Optional header configuration (title, actions).
 * @param emptyText - Optional text to display when data is empty.
 * @param onBarPress - Called when a bar (column segment) is pressed. Receives info about the bar.
 *
 * Example usage:
 * <ColumnChart
 *   data={data}
 *   xAxisConfig={xAxisConfig}
 *   yAxisConfig={yAxisConfig}
 *   emptyText="No data available"
 * />
 */
const ColumnChart = ({
  data,
  yAxisConfig = {},
  xAxisConfig = {},
  style,
  testID,
  headerConfig,
  emptyText,
  onBarPress,
  styleConfig,
}: ColumnChartProps) => {
  const colorScale = useCustomColor({
    data,
    seriesConfig: styleConfig?.series,
  });

  const xLabels = useMemo(() => {
    return xAxisConfig.labels && xAxisConfig.labels.length > 0
      ? xAxisConfig.labels
      : data[0]?.data.map((_, index) => index.toString()) || [];
  }, [data[0]?.data.length, xAxisConfig.labels?.length]);

  useEffect(() => {
    // Validation: xLabels length must match each series' data length, only if labels are explicitly provided
    if (xAxisConfig.labels && xAxisConfig.labels.length > 0) {
      data.forEach((series) => {
        if (series.data.length !== xLabels.length) {
          throw new Error(
            `xAxisConfig.labels length (${xLabels.length}) does not match data length (${series.data.length}) in series label: ${series.label}`
          );
        }
      });
    }
  }, [data, xAxisConfig]);

  useEffect(() => {
    // Assert that the chart does not support negative values
    data.forEach((series) => {
      series.data.forEach((value, idx) => {
        if (typeof value === 'number' && value < 0) {
          throw new Error(
            `Negative values are not supported in ColumnChart. Found value ${value} in series "${series.label}" at index ${idx}.`
          );
        }
      });
    });
  }, [data]);

  const [dimensions, setDimensions] = useState<{
    width: number;
    height: number;
  } | null>(null);

  const onLayout = useCallback((event: LayoutChangeEvent) => {
    const { width, height } = event.nativeEvent.layout;
    if (width > 0 && height > 0) {
      setDimensions({ width, height });
    }
  }, []);

  // Calculate stacked maxY
  const yMax = useMemo(
    () =>
      Math.max(
        ...xLabels.map((_, xIdx) =>
          data.reduce((sum, series) => sum + (series.data[xIdx] ?? 0), 0)
        )
      ),
    [data, xLabels]
  );

  const niceValues = useMemo(() => {
    const maxDataValue = yMax;
    const minDataValue = 0;
    return createNiceScale(minDataValue, maxDataValue);
  }, [data]);

  const yAxisStep = yAxisConfig?.step ?? niceValues.tickSpacing;
  const yAxisInterval = yAxisConfig?.tick?.interval ?? yAxisStep;

  const calculatedYAxisConfig = useMemo(() => {
    return {
      ...yAxisConfig,
      maxValue: yAxisConfig?.maxValue ?? niceValues.niceMax,
      minValue: 0,
      step: yAxisConfig?.step ?? niceValues.tickSpacing,
      tick: {
        interval: yAxisInterval,
      },
    };
  }, [yAxisConfig, niceValues, yAxisInterval]);

  const calculatedXAxisConfig = useMemo(() => {
    return {
      ...xAxisConfig,
      labels: xLabels,
    };
  }, [xAxisConfig, xLabels]);

  return (
    <StyledColumnChartWrapper style={style} testID={testID} onLayout={onLayout}>
      {dimensions && (
        <ChartFrame
          isEmpty={data.length === 0}
          xAxisConfig={calculatedXAxisConfig}
          yAxisConfig={calculatedYAxisConfig}
          headerConfig={headerConfig}
          width={dimensions.width}
          height={dimensions.height}
          hideXAxisGrid
          emptyText={emptyText}
          renderContent={({ coordinates }) => (
            <ColumnChartContent
              coordinates={coordinates}
              data={data}
              xAxisConfig={calculatedXAxisConfig}
              yAxisConfig={calculatedYAxisConfig}
              onBarPress={onBarPress}
              colorScale={colorScale}
            />
          )}
        />
      )}
    </StyledColumnChartWrapper>
  );
};

export default ColumnChart;
