import React, { useCallback, useMemo, useState } from 'react';
import type { LayoutChangeEvent } from 'react-native';
import Svg from 'react-native-svg';
import { useTheme } from '../../../theme';
import Box from '../../Box';
import type {
  AxisCoordinates,
  HeaderConfig,
  XAxisConfig,
  YAxisConfig,
} from '../types';
import ChartHeader from './ChartHeader';
import EmptyState from './EmptyState';
import XAxis from './XAxis';
import XAxisGrid from './XAxisGrid';
import YAxis from './YAxis';
import YAxisGrid from './YAxisGrid';

export type ChartFrameProps = {
  isEmpty?: boolean;
  xAxisConfig: XAxisConfig;
  yAxisConfig: YAxisConfig;
  headerConfig?: HeaderConfig;
  width: number;
  height: number;
  renderContent: (props: { coordinates: AxisCoordinates }) => React.ReactNode;
  /**
   * Text to display when the chart has no data (empty state).
   * If undefined, no empty label will appear even if the chart is empty.
   */
  emptyText?: string;
  /**
   * If true, hides the grid lines for the Y-axis.
   * Defaults to false.
   */
  hideYAxisGrid?: boolean;
  /**
   * If true, hides the grid lines for the X-axis.
   * Defaults to false.
   */
  hideXAxisGrid?: boolean;
};

const yStart = 0;

const ChartFrame = ({
  xAxisConfig,
  yAxisConfig,
  width,
  height,
  headerConfig,
  renderContent,
  isEmpty,
  emptyText,
  hideYAxisGrid = false,
  hideXAxisGrid = false,
}: ChartFrameProps) => {
  const theme = useTheme();
  const { title, actionsExtra } = headerConfig || {};
  const [headerHeight, setHeaderHeight] = useState<number>(0);
  const onHeaderLayout = useCallback((event: LayoutChangeEvent) => {
    setHeaderHeight(event.nativeEvent.layout.height);
  }, []);

  // Sizing and coordinates setup
  const {
    space: {
      yAxisGridTextMarginRight,
      xAxisGridTextMarginTop,
      headerMarginBottom,
    },
    sizes: {
      yAxisDefaultTextWidth,
      xAxisDefaultTextHeight,
      yAxisDefaultTextHeight,
    },
  } = theme.__hd__.chart;

  // Drawable height for the whole chart: content, axis, grid, etc.
  const chartContentHeight =
    height - headerHeight - (headerHeight > 0 ? headerMarginBottom : 0);

  // Start offsets for various chart components, after accounting for the charts axis text size
  const xStartOffset = yAxisDefaultTextWidth + yAxisGridTextMarginRight;
  const yStartOffset = xAxisDefaultTextHeight + xAxisGridTextMarginTop;

  const coordinates = useMemo(
    () => ({
      yStart,
      yEnd: yStart + chartContentHeight - yStartOffset,
      xStart: xStartOffset,
      xEnd: width,
    }),
    [chartContentHeight, yStartOffset, xStartOffset, width]
  );

  return (
    <Box style={{ width, height }}>
      {headerConfig && (
        <ChartHeader
          title={title}
          actionsExtra={actionsExtra}
          onLayout={onHeaderLayout}
        />
      )}

      <Box style={{ paddingTop: yAxisDefaultTextHeight }}>
        <Svg style={{ width, height: chartContentHeight, overflow: 'visible' }}>
          <XAxis xAxisConfig={xAxisConfig} coordinates={coordinates} />
          <YAxis yAxisConfig={yAxisConfig} coordinates={coordinates} />
          {!hideXAxisGrid && (
            <XAxisGrid coordinates={coordinates} xAxisConfig={xAxisConfig} />
          )}
          {!hideYAxisGrid && (
            <YAxisGrid coordinates={coordinates} yAxisConfig={yAxisConfig} />
          )}
          {/** Chart data component */}
          {/* If the chart is empty and emptyText is provided, show the empty state text.
              If emptyText is undefined, the empty label will not appear. */}
          {!isEmpty
            ? renderContent({ coordinates })
            : emptyText && (
                <EmptyState content={emptyText} coordinates={coordinates} />
              )}
        </Svg>
      </Box>
    </Box>
  );
};

export default ChartFrame;
