import { scaleLinear, ScaleTime, scaleTime } from 'd3-scale'
import * as d3Shape from 'd3-shape'
import { Interval } from 'date-fns'
import _ from 'lodash'
import React from 'react'
import { Line, Path } from 'react-native-svg'

import { colors } from '../../Colors'
import { useComponentLayout } from '../../hooks/useComponentLayout'
import {
  scaleToTicks,
  YAxis,
  YAxisLabelPadding,
  YAxisWidth,
} from './ornaments/Axis'
import { Grid, gridPadding } from './ornaments/Grid'
import { Figure } from './useFigure'

/**
 * Vertical line that indicates the currently selected date, if any
 */
const SelectedDateIndicatorLine: React.FC<{
  selectedDate: Date | null
  scaleX: ScaleTime<number, number>
  height: number
}> = ({ selectedDate, scaleX, height }) => {
  return selectedDate != null ? (
    <Line
      x={scaleX(selectedDate)}
      y1={0}
      y2={height}
      stroke={colors.white}
      strokeWidth={2}
    />
  ) : null
}

export const StackedAreaFigure: React.FC<{
  interval: Interval
  selectedDate: Date | null
  data: { date: string; sources: Record<string, number | null> }[]
  sourceOrder: string[]
  sourceColors: Record<string, string>
}> = ({ data, sourceOrder, sourceColors, interval, selectedDate }) => {
  const [onLayout, layout] = useComponentLayout({
    width: 0,
    height: 0,
    x: 0,
    y: 0,
  })

  const padding = {
    left: 0,
    right: YAxisWidth,
    top: YAxisLabelPadding(true),
    bottom: gridPadding(true),
  }

  if (!layout) {
    return null
  }

  const contentWidth = layout.width - padding.left - padding.right
  const contentHeight = layout.height - padding.top - padding.bottom

  const maxSum = Math.max(...data.map((x) => _.sum(Object.values(x.sources))))

  const scaleX = scaleTime()
    .domain([interval.start, interval.end])
    .range([0, contentWidth])

  const scaleY = scaleLinear().domain([0, maxSum]).range([contentHeight, 0])

  const getArea = d3Shape
    .area<
      d3Shape.SeriesPoint<{
        date: string
        sources: Record<string, number | null>
      }>
    >()
    .x0((d) => scaleX(new Date(d.data.date)) ?? 0)
    .y0((d) => scaleY(d[0]) ?? 0)
    .y1((d) => scaleY(d[1]) ?? 0)
    .defined((d) => d.data != null)

  const sources = _.uniq(data.flatMap((d) => Object.keys(d.sources))).sort(
    (a, b) => sourceOrder.indexOf(a) - sourceOrder.indexOf(b)
  )
  const getStack = d3Shape
    .stack<{ date: string; sources: Record<string, number | null> }, string>()
    .keys(sources)
    .value((d, source) => d.sources[source] ?? 0)

  const ticks = scaleToTicks(scaleY, {})

  return (
    <Figure onLayout={onLayout} size={layout} padding={padding}>
      {getStack(data).map((x, i) => {
        const key = x.key
        return <Path d={getArea(x) ?? ''} fill={sourceColors[key]} key={i} />
      })}
      <YAxis
        translateX={layout.width - YAxisWidth}
        ticks={ticks}
        color={colors.offWhite}
        side="right"
      />
      <Grid
        translateX={0}
        ticks={ticks}
        size={{ ...layout, width: layout.width - YAxisWidth }}
      />
      <SelectedDateIndicatorLine
        selectedDate={selectedDate}
        scaleX={scaleX}
        height={layout.height}
      />
    </Figure>
  )
}
