import React from 'react'
import { GestureResponderEvent, StyleProp, View, ViewStyle } from 'react-native'
import { Line } from 'react-native-svg'
import { colorsV2 } from '../Colors'
import { useComponentLayout } from '../hooks/useComponentLayout'
import { TextStylesV2 } from '../Styles'
import { DataStatus } from '../types/types'
import { notEmpty, readableAxisLabels } from '../util'
import { BarPlot, BarStyle, barStyleFn } from './figures/BarPlot'
import { FigureGrid } from './figures/FigureGrid'
import { StripedPattern } from './figures/StripedPattern'
import { Figure, useFigure } from './figures/useFigure'
import { GraphYAxisScale } from './GraphYAxisScale'

function barWidthAuto(totalWidth: number, gap: number, numBars: number) {
  const gaps = gap * numBars - 1
  const bars = totalWidth - gaps
  return bars / numBars
}

export function BarChart<T>({
  data,
  valueExtractor,
  statusExtractor,
  barStyle,
  barWidth,
  onPressBar,
  selectedBarIndex,
  grid,
  tickLabels,
  rangeMin,
  rangeMax,
  disablePointerEvents,
  style,
}: {
  data: T[]
  valueExtractor: (v: T) => number
  statusExtractor?: (item: T) => DataStatus
  barStyle?: BarStyle | barStyleFn<T>
  barWidth: number | 'auto'
  onPressBar?: (event: GestureResponderEvent, v: T, i: number) => void
  selectedBarIndex?: number
  grid?: boolean
  tickLabels?: 'left' | 'right'
  rangeMin?: number
  rangeMax?: number
  disablePointerEvents?: boolean
  style?: StyleProp<ViewStyle>
}): React.ReactElement {
  const allNonNullData = data.map(valueExtractor).filter(notEmpty)

  // Range of actual values in the dataset
  const dataDomain = {
    min: Math.min(...allNonNullData),
    max: Math.max(...allNonNullData),
  }
  const axisProperties = readableAxisLabels(dataDomain.max)

  // Range of values displayed
  const minValue = rangeMin != null ? rangeMin : dataDomain.min
  const maxValue = rangeMax != null ? rangeMax : axisProperties.maxLabelValue

  const [onLayout, layout] = useComponentLayout({
    width: 0,
    height: 0,
    x: 0,
    y: 0,
  })

  const numBars = data.length
  const gap = 2
  const defaultBarWidth = 4
  const width =
    barWidth === 'auto'
      ? layout != null
        ? barWidthAuto(layout.width, gap, numBars)
        : defaultBarWidth
      : barWidth ?? defaultBarWidth

  const plotPadding = {
    left: width / 2,
    right: width / 2,
    top: 0,
    bottom: 0,
  }

  const figureProps =
    layout != null
      ? useFigure({
          size: layout,
          dataLength: data.length,
          plotPadding,
          dataSets: [{ domain: [minValue, maxValue] }],
        })
      : null

  return (
    <View
      style={[
        {
          flexDirection: 'row',
        },
        style,
      ]}
      pointerEvents={disablePointerEvents === true ? 'none' : 'auto'}
    >
      {tickLabels === 'left' && (
        <GraphYAxisScale
          data={[minValue, maxValue]}
          dividers={axisProperties.dividers}
          style={{ marginEnd: 4 }}
          textStyle={{
            ...TextStylesV2.tinyBold,
            ...{ color: colorsV2.offWhite },
          }}
        />
      )}
      {layout != null && figureProps != null ? (
        <Figure
          onLayout={onLayout}
          size={layout}
          patterns={<StripedPattern />}
          padding={figureProps.padding}
        >
          {grid != null && axisProperties != null ? (
            <FigureGrid
              size={figureProps.contentSize}
              dividersY={axisProperties.dividers + 2}
            />
          ) : null}
          <BarPlot
            data={data}
            size={figureProps.contentSize}
            scaleX={figureProps.scaleX}
            scaleY={figureProps.scaleY[0]}
            valueExtractor={valueExtractor}
            statusExtractor={statusExtractor}
            barStyle={barStyle}
            barWidth={width}
            onPressBar={onPressBar}
          />
          {selectedBarIndex != null ? (
            <Line
              x={figureProps.scaleX(selectedBarIndex)}
              y1={0}
              y2={figureProps.contentSize.width}
              stroke={colorsV2.white}
              strokeWidth={2}
            />
          ) : null}
        </Figure>
      ) : null}
      {tickLabels === 'right' && (
        <GraphYAxisScale
          data={[minValue, maxValue]}
          dividers={axisProperties.dividers}
          style={{ marginStart: 4 }}
          textStyle={{
            ...TextStylesV2.tinyBold,
            ...{ color: colorsV2.offWhite },
          }}
        />
      )}
    </View>
  )
}
