import React from 'react'
import { GestureResponderEvent, StyleProp, ViewStyle } from 'react-native'
import { Line } from 'react-native-svg'

import { colors } from '../../Colors'
import { useComponentLayout } from '../../hooks/useComponentLayout'
import { DataStatus } from '../../types/types'
import { notEmpty } from '../../util'
import {
  AxisConfig,
  scaleToTicks,
  YAxis,
  YAxisLabelPadding,
  YAxisWidth,
} from './ornaments/Axis'
import { Grid, gridPadding } from './ornaments/Grid'
import { StripedPattern } from './ornaments/StripedPattern'
import { BarPlot, BarStyle, barStyleFn } from './plots/BarPlot'
import { Figure, useFigure } from './useFigure'

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

export function BarFigure<T>({
  data,
  valueExtractor,
  statusExtractor,
  barStyle,
  barWidth,
  onPressBar,
  selectedBarIndex,
  grid,
  tickLabels,
  rangeMin,
  rangeMax,
  style,
  centered,
}: {
  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
  style?: StyleProp<ViewStyle>
  centered?: boolean
}): React.ReactElement | null {
  const allNonNullData = data.map(valueExtractor).filter(notEmpty)

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

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

  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 - (tickLabels ? YAxisWidth : 0),
            gap,
            numBars
          )
        : defaultBarWidth
      : barWidth ?? defaultBarWidth

  const plotPadding = {
    left: width / 2 + (tickLabels === 'left' || centered ? YAxisWidth : 0),
    right: width / 2 + (tickLabels === 'right' || centered ? YAxisWidth : 0),
    top: gridPadding(grid),
    bottom: YAxisLabelPadding(tickLabels != null),
  }

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

  if (layout == null || figureProps == null) {
    return null
  }

  const { contentSize, padding, scaleX, scaleY } = figureProps

  const lineAxisConfig: AxisConfig = {}

  return (
    <Figure
      onLayout={onLayout}
      size={layout}
      patterns={<StripedPattern />}
      padding={padding}
      style={style}
    >
      {tickLabels === 'left' && (
        <YAxis
          translateX={0}
          ticks={scaleToTicks(scaleY[0], lineAxisConfig)}
          color={colors.offWhite}
          side="left"
          config={lineAxisConfig}
        />
      )}
      {grid != null ? (
        <Grid
          translateX={tickLabels === 'left' || centered ? YAxisWidth : 0}
          size={{
            ...contentSize,
            width:
              contentSize.width -
              (centered
                ? YAxisWidth * 2
                : tickLabels !== undefined
                ? YAxisWidth
                : 0),
          }}
          ticks={scaleToTicks(scaleY[0], lineAxisConfig)}
        />
      ) : null}
      <BarPlot
        data={data}
        size={contentSize}
        scaleX={scaleX[0]}
        scaleY={scaleY[0]}
        valueExtractor={valueExtractor}
        statusExtractor={statusExtractor}
        barStyle={barStyle}
        barWidth={width}
        onPressBar={onPressBar}
      />
      {selectedBarIndex != null &&
      /* TODO: How come this can be -1? Added >= check. */
      selectedBarIndex >= 0 ? (
        <Line
          x={scaleX[0](selectedBarIndex)}
          y1={0}
          y2={contentSize.width}
          stroke={colors.white}
          strokeWidth={2}
        />
      ) : null}
      {tickLabels === 'right' && (
        <YAxis
          translateX={contentSize.width - YAxisWidth}
          ticks={scaleToTicks(scaleY[0], lineAxisConfig)}
          color={colors.offWhite}
          side="right"
          config={lineAxisConfig}
        />
      )}
    </Figure>
  )
}
