import { memo } from 'react';
import { AnyD3Scale, scaleBand, scaleLinear } from '@visx/scale';
import { Group } from '@visx/group';
import { AnyScaleBand } from '@visx/shape/lib/types';

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

import { Axes, AxesSettings } from '../../common/Axes';
import { Grid } from '../../common/Grid';
import { BarElement } from '../../common/BarElement';
import { BarSpacing } from '../bar/types';

import { ParetoGraph } from './ParetoGraph';
import { valueSelector, labelSelector, ChartData } from '../../utils/data';
import { call, renderProp } from '../../utils';
import { format } from '../../utils/format';
import { DEFAULT_AREA_COLOR, DEFAULT_COLOR } from '../../config';

import { TextLabelSettings, BackgroundSettings } from '../../types';
import { TooltipBinder } from '../../hooks/useTooltip';

function verticalBar(
  xScale: AnyScaleBand,
  yScale: AnyD3Scale,
  value: number,
  label: string,
  chartHeight: number
) {
  const width = xScale.bandwidth();
  const height = Math.max(0, chartHeight - yScale(value));
  const x = xScale(label) || 0;
  const y = chartHeight - height;
  return { x, y, width, height };
}

const TEXT_LABEL_OFFSET = 24;

export type ParetoChartSVGProps = AxesSettings &
  BackgroundSettings &
  TextLabelSettings &
  BarSpacing & {
    data: ChartData[];
    width: number;
    height: number;
    maxValue?: number;
    horizontal?: boolean;
    valueFormat?: string;

    barLabel?: string;
    lineLabel?: string;
    barColor?: string;
    lineColor?: string;

    showParetoValue?: boolean;
    paretoValueFormat?: string;
    // paretoTooltipText?: string;

    onBarClick?: (
      data: ChartData,
      ev: React.MouseEvent<SVGGElement, MouseEvent>
    ) => void;
    bindTooltip?: TooltipBinder<ChartData>;
  };

export const defaultProps = {
  barColor: DEFAULT_COLOR,
  spacingInner: 0.1,
  spacingOuter: 0,
  showGrid: true,

  showLeftAxis: false,
  showRightAxis: false,
  showBottomAxis: true,

  // paretoTooltipText: 'Percent of total',

  lineColor: DEFAULT_AREA_COLOR,
  textLabel: true,
  textLabelSize: 12,
};

function ParetoChartSVG({
  data,
  width,
  height,
  maxValue,
  barColor,
  spacing,
  spacingInner,
  spacingOuter,
  background,
  showGrid,
  gridColor,
  textColor,
  axisColor,
  showLeftAxis,
  axisLeftAngle,
  labelLeft,
  showRightAxis,
  axisRightAngle,
  labelRight,
  showBottomAxis,
  axisBottomAngle,
  labelBottom,
  axisLeftProps,
  axisRightProps,
  axisBottomProps,
  showParetoValue,
  paretoValueFormat,
  // paretoTooltipText,
  lineColor,
  valueFormat,
  textLabel,
  textLabelSize,
  textLabelColor,
  onBarClick,
  bindTooltip,
}: ParetoChartSVGProps & typeof defaultProps) {
  //TODO temp fix for axes bounds recalculation
  const refreshKey = useRefreshKey([data]);

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

  const [chartWidth, chartHeight, margin] = bounds;

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

  const values = data.map(valueSelector);
  const total = values.reduce((prev, next) => +prev + +next, 0);

  let yPercent = 0;
  let pareto = data.map((item) => {
    yPercent += (item.value / total) * 100;
    return { ...item, value: yPercent };
  });

  const xScale = scaleBand({
    range: [0, chartWidth],
    domain: domain,
    padding: spacing,
    paddingInner: spacingInner,
    paddingOuter: spacingOuter,
  });

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

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

  return (
    <svg width={width} height={height}>
      {background && (
        <rect
          x={margin.left}
          y={margin.top}
          width={chartWidth}
          height={chartHeight}
          fill={background}
        />
      )}
      {showGrid && (
        <Grid
          top={margin.top}
          left={margin.left}
          width={chartWidth}
          height={chartHeight}
          color={gridColor}
          yScale={yScale}
        />
      )}
      <Group top={margin.top} left={margin.left}>
        {data.map((datum, i) => {
          const domain = labelSelector(datum);
          const value = valueSelector(datum);

          const bar = verticalBar(xScale, yScale, value, domain, chartHeight);

          const fill = renderProp(barColor, datum, i);

          const prev = i === 0 ? 0 : valueSelector(pareto[i - 1]);
          const curr = valueSelector(pareto[i]) - prev;
          const formattedValue = valueFormat
            ? format(valueFormat, {
                ...datum,
                prev: Math.round(prev),
                curr: Math.round(curr),
                total: Math.round(prev + curr),
              })
            : String(value);

          const handleClick: React.MouseEventHandler<SVGGElement> = (ev) => {
            onBarClick!(datum, ev);
          };
          const tooltipProps = call(bindTooltip, datum, formattedValue);

          return (
            <BarElement
              x={bar.x}
              y={bar.y}
              width={bar.width}
              height={bar.height}
              fill={fill}
              textLabelColor={textLabelColor}
              textLabelSize={textLabelSize}
              value={textLabel ? formattedValue : undefined}
              onClick={onBarClick && handleClick}
              {...tooltipProps}
              key={datum.id || i}
            />
          );
        })}
      </Group>
      <ParetoGraph
        // width={chartWidth}
        top={margin.top}
        left={margin.left}
        data={pareto}
        xScale={xScale}
        yScale={paretoYScale}
        lineColor={lineColor}
        valueFormat={paretoValueFormat}
        textLabel={showParetoValue}
        textLabelColor={textLabelColor}
        textLabelSize={textLabelSize}
        // tooltipText={paretoTooltipText}
        // bindTooltip={bindTooltip}
      />
      <Axes
        width={chartWidth}
        height={chartHeight}
        margin={margin}
        xScale={xScale}
        yScale={yScale}
        showLeftAxis={showLeftAxis}
        axisLeftAngle={axisLeftAngle}
        labelLeft={labelLeft}
        showRightAxis={showRightAxis}
        axisRightAngle={axisRightAngle}
        labelRight={labelRight}
        showBottomAxis={showBottomAxis}
        axisBottomAngle={axisBottomAngle}
        labelBottom={labelBottom}
        textColor={textColor}
        axisColor={axisColor}
        horizontal={false}
        onMount={updateBounds}
        axisLeftProps={axisLeftProps}
        axisRightProps={axisRightProps}
        axisBottomProps={axisBottomProps}
        key={refreshKey}
      />
    </svg>
  );
}

ParetoChartSVG.defaultProps = defaultProps;

export default memo(ParetoChartSVG);
