import { memo } from 'react';
import { scaleLinear, scaleBand } from '@visx/scale';
import { Group } from '@visx/group';
import { Area, LinePath } from '@visx/shape';

import { useBounds } from '../../hooks/useBounds';
import { useRefreshKey } from '../../hooks/useRefreshKey';

import { Axes, AxesSettings } from '../../common/Axes';
import { Grid } from '../../common/Grid';
import { MouseMoveHandler } from '../../common/MouseMoveHandler';
import { TooltipSetter } from '../../hooks/useTooltip';
import { valueSelector, labelSelector, ChartData } from '../../utils/data';

import { curveFactory, CURVES, DEFAULT_AREA_COLOR } from '../../config';

export interface AreaChartSVGProps extends AxesSettings {
  data: ChartData[];
  width: number;
  height: number;
  maxValue?: number;
  curve?: keyof typeof CURVES;
  graphColor?: string;
  strokeWidth?: number;
  fillOpacity?: number;
  background?: string;
  showGrid?: boolean;
  gridColor?: string;

  onHover?: TooltipSetter;
}

const defaultProps = {
  curve: 'linear',
  graphColor: DEFAULT_AREA_COLOR,
  strokeWidth: 1,
  fillOpacity: 1,
  showGrid: true,
  showLeftAxis: true,
  showBottomAxis: true,
};

function AreaChartSVG({
  data,
  width,
  height,
  curve,
  maxValue,
  graphColor,
  strokeWidth,
  fillOpacity,
  background,
  showGrid,
  gridColor,
  textColor,
  axisColor,
  showLeftAxis,
  axisLeftAngle,
  labelLeft,
  showRightAxis,
  axisRightAngle,
  labelRight,
  showBottomAxis,
  axisBottomAngle,
  labelBottom,
  axisLeftProps,
  axisRightProps,
  axisBottomProps,
  onHover,
}: AreaChartSVGProps & typeof defaultProps) {
  //TODO temp fix for axes bounds recalculation
  const refreshKey = useRefreshKey([data]);

  const [bounds, updateBounds] = useBounds(width, height, {
    top: 16,
    left: 0,
    right: 0,
    bottom: 0,
  });

  const [chartWidth, chartHeight, margin] = bounds;

  const domainMax =
    maxValue !== undefined ? maxValue : Math.max(...data.map(valueSelector));

  // scales
  const xScale = scaleLinear({
    range: [0, chartWidth],
    domain: [0, data.length - 1],
  });

  const xScaleBand = scaleBand({
    range: [0, chartWidth],
    domain: data.map(labelSelector),
    paddingInner: 1,
    paddingOuter: 0,
  });

  const yScale = scaleLinear({
    range: [chartHeight, 0],
    domain: [0, domainMax],
    nice: true,
  });

  return (
    <svg width={width} height={height}>
      {background && (
        <rect
          x={margin.left}
          y={margin.top}
          width={chartWidth}
          height={chartHeight}
          fill={background}
        />
      )}
      {showGrid && (
        <Grid
          width={chartWidth}
          height={height}
          top={margin.top}
          left={margin.left}
          yScale={yScale}
          color={gridColor}
        />
      )}
      <Group top={margin.top} left={margin.left}>
        {fillOpacity ? (
          <>
            <defs>
              <linearGradient id="linear" x1="0%" y1="0%" x2="0%" y2="100%">
                <stop offset="0%" stopColor={graphColor} stopOpacity={1} />
                <stop offset="100%" stopColor={graphColor} stopOpacity={0} />
              </linearGradient>
            </defs>
            <Area
              // className
              data={data}
              // defined
              x={(d, i) => xScale(i)}
              y0={(d) => yScale.range()[0]}
              y1={(d) => yScale(valueSelector(d))}
              fill="url(#linear)"
              opacity={fillOpacity}
              curve={curveFactory(curve)}
            />
          </>
        ) : null}
        {strokeWidth ? (
          <LinePath
            // className
            data={data}
            x={(d, i) => xScale(i)}
            y={(d) => yScale(valueSelector(d))}
            stroke={graphColor}
            strokeWidth={strokeWidth}
            curve={curveFactory(curve)}
          />
        ) : null}
      </Group>
      {onHover && (
        <MouseMoveHandler
          width={chartWidth}
          height={chartHeight}
          top={margin.top}
          left={margin.left}
          data={data}
          xScale={xScale}
          yScale={(d) => yScale(valueSelector(d))}
          onHover={onHover!}
        />
      )}
      <Axes
        width={chartWidth}
        height={chartHeight}
        margin={margin}
        xScale={xScaleBand}
        yScale={yScale}
        showLeftAxis={showLeftAxis}
        axisLeftAngle={axisLeftAngle}
        labelLeft={labelLeft}
        showRightAxis={showRightAxis}
        axisRightAngle={axisRightAngle}
        labelRight={labelRight}
        showBottomAxis={showBottomAxis}
        axisBottomAngle={axisBottomAngle}
        labelBottom={labelBottom}
        textColor={textColor}
        axisColor={axisColor}
        onMount={updateBounds}
        axisLeftProps={axisLeftProps}
        axisRightProps={axisRightProps}
        axisBottomProps={axisBottomProps}
        key={refreshKey}
      />
    </svg>
  );
}

AreaChartSVG.defaultProps = defaultProps;

export default memo(AreaChartSVG);
