import { localPoint } from "@visx/event";
import { Group } from "@visx/group";
import { Point } from "@visx/point";
import Pie, { ProvidedProps } from "@visx/shape/lib/shapes/Pie";
import { Text } from "@visx/text";
import { withTooltip } from "@visx/tooltip";
import React from "react";

import { TooltipWrapper } from "./TooltipWrapper";

import styles from "./PieChart.module.css";

interface PieChartProps {
  data: PieChartData[];
  /** Width of the entire graph, including labels, in pixels. */
  width: number;
  /** Width of the outer ring of labels, in pixels. Label text may be cut off if specified value is too small. */
  labelWidth: number;
  /** Distance between pie slices, in pixels. */
  padRadius?: number;
  /** Distance of gap in center of pie graph, in pixels. */
  innerRadius?: number;
  /** Font size of labels outside the pie, in pixels. */
  labelTextSize?: number;
  /** X-axis offset of the label text, in pixels. */
  labelTextXOffset?: number;
  /** Y-axis offset of the label text, in pixels. */
  labelTextYOffset?: number;
  /** If set, the minimum width of this graph */
  minWidth?: number;
  /** Accessor function to get value to display as label text from datum. */
  getLabelDisplayValueFromDatum?: (datum: PieChartData) => string;
  className?: string;
}

interface PieChartData {
  category: string;
  value: number;
}

export const PieChart = withTooltip<PieChartProps>(
  ({
    data,
    width,
    labelWidth,
    padRadius = width * 0.25,
    innerRadius = width * 0,
    labelTextSize = 40,
    labelTextXOffset = 0,
    labelTextYOffset = 0,
    minWidth,
    getLabelDisplayValueFromDatum = (datum: PieChartData) =>
      `${datum.category}`,
    className,
    tooltipOpen,
    tooltipLeft,
    tooltipTop,
    tooltipData,
    hideTooltip,
    showTooltip,
  }) => {
    if (minWidth) {
      width = width < minWidth ? minWidth : width;
    }

    const pieWidth = width * 0.5 - labelWidth;
    const cornerRadius = 0;
    const padAngle = 0;

    return (
      <div>
        <svg className={className} width={width} height={width}>
          <Group top={width * 0.5} left={width * 0.5}>
            <Pie
              data={data}
              pieValue={(d: PieChartData) => d.value}
              cornerRadius={cornerRadius}
              padAngle={padAngle}
              padRadius={padRadius}
              innerRadius={innerRadius}
              outerRadius={pieWidth}
            >
              {({ arcs, path }) => {
                return arcs.map((arc) => {
                  const pathArc = path(arc) as string;
                  return (
                    <Group
                      className={styles.group}
                      key={`arc-${arc.data.category}`}
                    >
                      <path
                        onMouseMove={(e) => {
                          const eventSvgCoords = localPoint(
                            // ownerSVGElement is given by visx docs but not recognized by typescript
                            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
                            // @ts-ignore
                            e.target.ownerSVGElement as Element,
                            e
                          ) as Point;
                          showTooltip({
                            tooltipData: `${arc.data.category}: ${arc.data.value}%`,
                            tooltipTop: eventSvgCoords.y,
                            tooltipLeft: eventSvgCoords.x,
                          });
                        }}
                        onMouseOut={hideTooltip}
                        className={styles.piePath}
                        d={pathArc}
                      />
                    </Group>
                  );
                });
              }}
            </Pie>
            <Pie
              data={data}
              pieValue={(d: PieChartData) => d.value}
              innerRadius={pieWidth}
              outerRadius={width * 0.5}
            >
              {(pie) => (
                <PieSliceLabel
                  {...pie}
                  labelTextSize={labelTextSize}
                  labelTextXOffset={labelTextXOffset}
                  labelTextYOffset={labelTextYOffset}
                  getLabelDisplayValueFromDatum={getLabelDisplayValueFromDatum}
                />
              )}
            </Pie>
          </Group>
        </svg>

        {tooltipOpen && (
          <TooltipWrapper
            top={tooltipTop}
            left={tooltipLeft}
            header={tooltipData as string}
          ></TooltipWrapper>
        )}
      </div>
    );
  }
);

type PieSliceLabelProps<PieChartData> = ProvidedProps<PieChartData> & {
  labelTextSize: number;
  labelTextXOffset: number;
  labelTextYOffset: number;
  getLabelDisplayValueFromDatum: (datum: PieChartData) => string;
};

export function PieSliceLabel({
  path,
  arcs,
  labelTextSize,
  labelTextXOffset,
  labelTextYOffset,
  getLabelDisplayValueFromDatum,
}: PieSliceLabelProps<PieChartData>) {
  return (
    <>
      {arcs.map((arc) => {
        const [centroidX, centroidY] = path.centroid(arc);
        const pathArc = path(arc) as string;

        return (
          <Group className={styles.group} key={`arc-${arc.data.category}`}>
            <path className={styles.labelPath} d={pathArc} />
            <Text
              className={styles.labelText}
              x={centroidX + labelTextXOffset}
              y={centroidY + labelTextYOffset}
              textAnchor="middle"
              fontSize={labelTextSize}
            >
              {`${getLabelDisplayValueFromDatum(arc.data)}`}
            </Text>
          </Group>
        );
      })}
    </>
  );
}
