import { memo, useMemo } from 'react';

import { scaleLinear } from '@visx/scale';
import { Point } from '@visx/point';
import { Line } from '@visx/shape';
import { Group } from '@visx/group';

import {
  ChartNestedData,
  itemsSelector,
  labelSelector,
  valueSelector,
  groupSelector,
} from '../../utils/data';
import { call } from '../../utils';

import { COLORS } from '../../config';

import { TooltipBinder } from '../../hooks/useTooltip';
import { TextLabelSettings } from '../../types';
import { RadarChartTooltipData } from './RadarChartTooltip';

function groupByItemLabel(data: ChartNestedData[]) {
  const groups: Record<string, { label: string; value: number }[]> = {};

  data.forEach((group) => {
    itemsSelector(group).forEach((item) => {
      const datum = {
        label: groupSelector(group),
        value: valueSelector(item),
      };
      const label = labelSelector(item);
      if (groups[label]) {
        groups[label].push(datum);
      } else {
        groups[label] = [datum];
      }
    });
  });

  return groups;
}

function polarToCartesian(distance: number, angle: number) {
  const x = distance * Math.cos(angle - Math.PI / 2);
  const y = distance * Math.sin(angle - Math.PI / 2);
  return [x, y];
}

type RadarChartLabelProps = React.SVGProps<SVGTextElement> & {
  angle: number;
  radius: number;
};

const RadarChartLabel = ({
  angle,
  radius,
  children,
  ...props
}: RadarChartLabelProps) => {
  const [x, y] = polarToCartesian(radius, angle);

  const alignment = Math.abs(y) === radius ? 'middle' : x > 0 ? 'start' : 'end';

  return (
    <text
      x={x}
      y={y}
      dominantBaseline="middle"
      textAnchor={alignment}
      {...props}
    >
      {children}
    </text>
  );
};

export type RadarChartSVGProps = TextLabelSettings & {
  data: ChartNestedData[];
  maxValue?: number;
  numTicks: number;
  width: number;
  height: number;
  padding?: number;
  colors: string[];
  fillOpacity: number;
  strokeWidth: number;
  textLabelOffset: number;
  showGrid?: boolean;
  gridColor: string;
  vectorsColor: string;
  vectorsWidth: number;
  bindTooltip?: TooltipBinder<RadarChartTooltipData[]>;
};

export const defaultProps = {
  padding: 40,
  numTicks: 5,
  colors: COLORS,
  fillOpacity: 0.5,
  strokeWidth: 2,
  textLabel: true,
  textLabelSize: 12,
  textLabelOffset: 12,
  // textLabelColor,
  // onClick,
  showGrid: true,
  gridColor: 'hsla(0,0%,0%,.2)',
  vectorsColor: 'hsla(0,0%,0%,.2)',
  vectorsWidth: 1,
};

const ZERO = new Point({ x: 0, y: 0 });

function RadarChartSVG({
  width,
  height,
  padding = 0,
  data,
  maxValue,
  numTicks,
  colors,
  fillOpacity,
  strokeWidth,
  textLabel,
  textLabelSize,
  textLabelColor,
  textLabelOffset,
  showGrid,
  gridColor,
  vectorsColor,
  vectorsWidth,
  bindTooltip,
}: RadarChartSVGProps & typeof defaultProps) {
  const groups = useMemo(() => groupByItemLabel(data), [data]);
  const keys = Object.keys(groups);

  const radius = Math.min(width - padding, height - padding) / 2;

  const values = data.map(itemsSelector).flat().map(valueSelector);

  const domainMax = maxValue !== undefined ? maxValue : Math.max(...values);

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

  const levels = rScale.ticks(numTicks);

  // const grid = [...new Array(keys.length + 1)].map((_, i) => {
  //   const radians = (Math.PI * 2 * i) / keys.length;
  //   return radians;
  // });
  const vectors = keys.map((key, i) => {
    const radians = (Math.PI * 2 * i) / keys.length;
    const [x, y] = polarToCartesian(radius, radians);
    return { x, y, angle: radians };
  });

  const shapes = data.map((item) => {
    const scaledValues = itemsSelector(item).map(valueSelector).map(rScale);
    const polygonPoints = scaledValues.map((value, i) => {
      const radians = (Math.PI * 2 * i) / keys.length;
      return polarToCartesian(value, radians);
    });
    const pointsString = polygonPoints.map((p) => p.join(',')).join(' ');

    return pointsString;
  });

  return (
    <svg width={width} height={height}>
      <Group top={height / 2} left={width / 2}>
        {showGrid && (
          <g>
            {levels.map((_, i, arr) => (
              <circle
                cx={0}
                cy={0}
                r={((i + 1) * radius) / arr.length}
                stroke={gridColor}
                strokeWidth={1}
                // strokeOpacity={0.2}
                fill="none"
                // fillOpacity={0.1}
                key={i}
              />
            ))}
            {/* {levels.map((_, i, arr) => (
                      <LineRadial
                        data={grid}
                        angle={(d) => d}
                        radius={((i + 1) * radius) / arr.length}
                        fill="none"
                        stroke={gridColor}
                        strokeWidth={1}
                        strokeLinecap="round"
                        key={i}
                      />
                    ))} */}
          </g>
        )}
        <g>
          {vectors.map((vector, i) => {
            const key = keys[i];

            const tooltipData = groups[key]
              .map((item, i) => ({ ...item, color: colors[i] }))
              .sort((a, b) => a.value - b.value);
            const tooltipProps = call(bindTooltip, tooltipData);

            return (
              <g key={key}>
                <Line
                  from={ZERO}
                  to={vector}
                  stroke={vectorsColor}
                  strokeWidth={vectorsWidth}
                />
                {textLabel && (
                  <RadarChartLabel
                    angle={vector.angle}
                    radius={radius + textLabelOffset}
                    fill={textLabelColor}
                    fontSize={textLabelSize}
                    {...tooltipProps}
                  >
                    {key}
                  </RadarChartLabel>
                )}
              </g>
            );
          })}
        </g>
        <g>
          {shapes.map((shape, i) => {
            const color = colors[i];
            return (
              <polygon
                points={shape}
                fill={color}
                fillOpacity={fillOpacity}
                stroke={color}
                strokeWidth={strokeWidth}
                key={i}
              />
            );
          })}
        </g>
      </Group>
    </svg>
  );
}

RadarChartSVG.defaultProps = defaultProps;

export default memo(RadarChartSVG);
