import React from "react";
import { View, ViewStyle } from "react-native";
import { G, Rect, Svg, Text } from "react-native-svg";

import AbstractChart, {
  AbstractChartConfig,
  AbstractChartProps,
} from "@yuyongmao/react-native-chart-kit/dist/AbstractChart";
import { ChartData } from "@yuyongmao/react-native-chart-kit/dist/HelperTypes";

const barWidth = 16;

export interface BarChartProps extends AbstractChartProps {
  data: ChartData;
  width: number;
  height: number;
  fromZero?: boolean;
  withInnerLines?: boolean;
  yAxisLabel: string;
  yAxisSuffix: string;
  chartConfig: AbstractChartConfig;
  style?: Partial<ViewStyle>;
  horizontalLabelRotation?: number;
  verticalLabelRotation?: number;
  /**
   * Show vertical labels - default: True.
   */
  withVerticalLabels?: boolean;
  /**
   * Show horizontal labels - default: True.
   */
  withHorizontalLabels?: boolean;
  /**
   * The number of horizontal lines
   */
  segments?: number;
  showBarTops?: boolean;
  showValuesOnTopOfBars?: boolean;
}

// eslint-disable-next-line @typescript-eslint/ban-types
class BarChart extends AbstractChart<BarChartProps, {}> {
  getBarPercentage = () => {
    const { barPercentage = 1 } = this.props.chartConfig;
    return barPercentage;
  };

  renderBars = ({
    data,
    width = 0,
    height = 0,
    paddingTop = 0,
    paddingRight = 0,
    barRadius = 0,
  }: Pick<
    Omit<AbstractChartConfig, "data">,
    "width" | "height" | "paddingRight" | "paddingTop" | "barRadius"
  > & {
    data: number[];
  }) => {
    const baseHeight = this.calcBaseHeight(data, height);

    return data.map((x, i) => {
      const barHeight = this.calcHeight(x, data, height);
      const bw = barWidth * this.getBarPercentage();
      return (
        <Rect
          key={Math.random()}
          x={paddingRight + (i * (width - paddingRight)) / data.length + bw / 2}
          y={
            ((barHeight > 0 ? baseHeight - barHeight : baseHeight) / 4) * 3 +
            paddingTop
          }
          rx={barRadius}
          width={bw}
          height={(Math.abs(barHeight) / 4) * 3}
          fill={this.props.chartConfig.color && this.props.chartConfig.color(0)}
        />
      );
    });
  };

  renderBarTops = ({
    data,
    width = 0,
    height = 0,
    paddingTop = 0,
    paddingRight = 0,
  }: Pick<
    Omit<AbstractChartConfig, "data">,
    "width" | "height" | "paddingRight" | "paddingTop"
  > & {
    data: number[];
  }) => {
    const baseHeight = this.calcBaseHeight(data, height);

    return data.map((x, i) => {
      const barHeight = this.calcHeight(x, data, height);
      const bw = barWidth * this.getBarPercentage();
      return (
        <Rect
          key={Math.random()}
          x={paddingRight + (i * (width - paddingRight)) / data.length + bw / 2}
          y={((baseHeight - barHeight) / 4) * 3 + paddingTop}
          width={bw}
          height={2}
          fill={
            this.props.chartConfig.color && this.props.chartConfig.color(0.6)
          }
        />
      );
    });
  };

  renderValuesOnTopOfBars = ({
    data,
    width = 0,
    height = 0,
    paddingTop = 0,
    paddingRight = 0,
  }: Pick<
    Omit<AbstractChartConfig, "data">,
    "width" | "height" | "paddingRight" | "paddingTop"
  > & {
    data: number[];
  }) => {
    const baseHeight = this.calcBaseHeight(data, height);

    return data.map((x, i) => {
      const barHeight = this.calcHeight(x, data, height);
      const bw = barWidth * this.getBarPercentage();
      return (
        <Text
          key={Math.random()}
          x={paddingRight + (i * (width - paddingRight)) / data.length + bw / 1}
          y={((baseHeight - barHeight) / 4) * 3 + paddingTop - 1}
          fill={
            this.props.chartConfig.color && this.props.chartConfig.color(0.6)
          }
          fontSize="12"
          textAnchor="middle"
        >
          {data[i]}
        </Text>
      );
    });
  };

  render() {
    const {
      width,
      height,
      data,
      style = {},
      withHorizontalLabels = true,
      withVerticalLabels = true,
      verticalLabelRotation = 0,
      horizontalLabelRotation = 0,
      withInnerLines = true,
      showBarTops = true,
      showValuesOnTopOfBars = false,
      segments = 4,
    } = this.props;

    const { borderRadius = 0, paddingTop = 16, paddingRight = 64 } = style;

    const config = {
      width,
      height,
      verticalLabelRotation,
      horizontalLabelRotation,
      barRadius:
        (this.props.chartConfig && this.props.chartConfig.barRadius) || 0,
      decimalPlaces:
        (this.props.chartConfig && this.props.chartConfig.decimalPlaces) || 2,
      formatYLabel:
        (this.props.chartConfig && this.props.chartConfig.formatYLabel) ||
        function (label) {
          return label;
        },
      formatXLabel:
        (this.props.chartConfig && this.props.chartConfig.formatXLabel) ||
        function (label) {
          return label;
        },
    };

    return (
      <View style={style}>
        <Svg height={height} width={width}>
          {this.renderDefs({
            ...config,
            ...this.props.chartConfig,
          })}
          <Rect
            width="100%"
            height={height}
            rx={borderRadius}
            ry={borderRadius}
            fill="url(#backgroundGradient)"
          />
          <G>
            {withInnerLines
              ? this.renderHorizontalLines({
                  ...config,
                  count: segments,
                  paddingTop,
                  paddingRight,
                })
              : null}
          </G>
          <G>
            {withHorizontalLabels
              ? this.renderHorizontalLabels({
                  ...config,
                  count: segments,
                  data: data.datasets[0].data,
                  paddingTop: paddingTop as number,
                  paddingRight: paddingRight as number,
                })
              : null}
          </G>
          <G>
            {withVerticalLabels
              ? this.renderVerticalLabels({
                  ...config,
                  labels: data.labels,
                  paddingRight: paddingRight as number,
                  paddingTop: paddingTop as number,
                  horizontalOffset: barWidth * this.getBarPercentage(),
                })
              : null}
          </G>
          <G>
            {this.renderBars({
              ...config,
              data: data.datasets[0].data,
              paddingTop: paddingTop as number,
              paddingRight: paddingRight as number,
            })}
          </G>
          <G>
            {showValuesOnTopOfBars &&
              this.renderValuesOnTopOfBars({
                ...config,
                data: data.datasets[0].data,
                paddingTop: paddingTop as number,
                paddingRight: paddingRight as number,
              })}
          </G>
          <G>
            {showBarTops &&
              this.renderBarTops({
                ...config,
                data: data.datasets[0].data,
                paddingTop: paddingTop as number,
                paddingRight: paddingRight as number,
              })}
          </G>
        </Svg>
      </View>
    );
  }
}

export { BarChart };