// Tremor Raw LineChart [v0.3.1]
/* eslint-disable @typescript-eslint/no-explicit-any */

"use client";

import React from "react";
import { RiArrowLeftSLine, RiArrowRightSLine } from "@remixicon/react";
import {
  CartesianGrid,
  Dot,
  Label,
  Line,
  Legend as RechartsLegend,
  LineChart as RechartsLineChart,
  ResponsiveContainer,
  Tooltip,
  XAxis,
  YAxis,
} from "recharts";
import { AxisDomain } from "recharts/types/util/types";

import { useOnWindowResize } from "../Utils/UseWindowOnResize";
import {
  AvailableChartColors,
  AvailableChartColorsKeys,
  constructCategoryColors,
  getColorClassName,
} from "../Utils/ChartColors";
import { cx } from "../Utils/Cx";
import { getYAxisDomain } from "../Utils/GetYAxisDomain";
import { hasOnlyOneValueForKey } from "../Utils/HasOnlyOneValueForKey";
import ChartCurve from "../../Types/ChartCurve";

//#region Legend

interface LegendItemProps {
  name: string;
  color: AvailableChartColorsKeys;
  onClick?: (name: string, color: AvailableChartColorsKeys) => void;
  activeLegend?: string;
}

const LegendItem: ({
  name,
  color,
  onClick,
  activeLegend,
}: LegendItemProps) => React.JSX.Element = ({
  name,
  color,
  onClick,
  activeLegend,
}: LegendItemProps) => {
  const hasOnValueChange: boolean = Boolean(onClick);
  return (
    <li
      className={cx(
        // base
        "group inline-flex flex-nowrap items-center gap-1.5 whitespace-nowrap rounded px-2 py-1 transition",
        hasOnValueChange
          ? "cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-800"
          : "cursor-default",
      )}
      onClick={(e: React.MouseEvent<HTMLLIElement, MouseEvent>) => {
        e.stopPropagation();
        onClick?.(name, color);
      }}
    >
      <span
        className={cx(
          "h-[3px] w-3.5 shrink-0 rounded-full",
          getColorClassName(color, "bg"),
          activeLegend && activeLegend !== name ? "opacity-40" : "opacity-100",
        )}
        aria-hidden={true}
      />
      <p
        className={cx(
          // base
          "truncate whitespace-nowrap text-xs",
          // text color
          "text-gray-700 dark:text-gray-300",
          hasOnValueChange &&
            "group-hover:text-gray-900 dark:group-hover:text-gray-50",
          activeLegend && activeLegend !== name ? "opacity-40" : "opacity-100",
        )}
      >
        {name}
      </p>
    </li>
  );
};

interface ScrollButtonProps {
  icon: React.ElementType;
  onClick?: () => void;
  disabled?: boolean;
}

const ScrollButton: ({
  icon,
  onClick,
  disabled,
}: ScrollButtonProps) => React.JSX.Element = ({
  icon,
  onClick,
  disabled,
}: ScrollButtonProps) => {
  const Icon: React.ElementType<any, keyof React.JSX.IntrinsicElements> = icon;
  const [isPressed, setIsPressed] = React.useState(false);
  const intervalRef: React.MutableRefObject<NodeJS.Timeout | null> =
    React.useRef<NodeJS.Timeout | null>(null);

  React.useEffect(() => {
    if (isPressed) {
      intervalRef.current = setInterval(() => {
        onClick?.();
      }, 300);
    } else {
      clearInterval(intervalRef.current as NodeJS.Timeout);
    }
    return () => {
      return clearInterval(intervalRef.current as NodeJS.Timeout);
    };
  }, [isPressed, onClick]);

  React.useEffect(() => {
    if (disabled) {
      clearInterval(intervalRef.current as NodeJS.Timeout);
      setIsPressed(false);
    }
  }, [disabled]);

  return (
    <button
      type="button"
      className={cx(
        // base
        "group inline-flex size-5 items-center truncate rounded transition",
        disabled
          ? "cursor-not-allowed text-gray-400 dark:text-gray-600"
          : "cursor-pointer text-gray-700 hover:bg-gray-100 hover:text-gray-900 dark:text-gray-300 dark:hover:bg-gray-800 dark:hover:text-gray-50",
      )}
      disabled={disabled}
      onClick={(e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
        e.stopPropagation();
        onClick?.();
      }}
      onMouseDown={(e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
        e.stopPropagation();
        setIsPressed(true);
      }}
      onMouseUp={(e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
        e.stopPropagation();
        setIsPressed(false);
      }}
    >
      <Icon className="size-full" aria-hidden="true" />
    </button>
  );
};

interface LegendProps extends React.OlHTMLAttributes<HTMLOListElement> {
  categories: string[];
  colors?: AvailableChartColorsKeys[];
  onClickLegendItem?: (category: string, color: string) => void;
  activeLegend?: string;
  enableLegendSlider?: boolean;
}

type HasScrollProps = {
  left: boolean;
  right: boolean;
};

const Legend: React.ForwardRefExoticComponent<
  LegendProps & React.RefAttributes<HTMLOListElement>
> = React.forwardRef<HTMLOListElement, LegendProps>(
  (props: LegendProps, ref: React.ForwardedRef<HTMLOListElement>) => {
    const {
      categories,
      colors = AvailableChartColors,
      className,
      onClickLegendItem,
      activeLegend,
      enableLegendSlider = false,
      ...other
    } = props;

    const scrollableRef: React.RefObject<HTMLInputElement> =
      React.useRef<HTMLInputElement>(null);
    const scrollButtonsRef: React.RefObject<HTMLDivElement> =
      React.useRef<HTMLDivElement>(null);
    const [hasScroll, setHasScroll] = React.useState<HasScrollProps | null>(
      null,
    );
    const [isKeyDowned, setIsKeyDowned] = React.useState<string | null>(null);
    const intervalRef: React.MutableRefObject<NodeJS.Timeout | null> =
      React.useRef<NodeJS.Timeout | null>(null);

    const checkScroll: () => void = React.useCallback(() => {
      const scrollable: HTMLInputElement | null = scrollableRef?.current;
      if (!scrollable) {
        return;
      }

      const hasLeftScroll: boolean = scrollable.scrollLeft > 0;
      const hasRightScroll: boolean =
        scrollable.scrollWidth - scrollable.clientWidth > scrollable.scrollLeft;

      setHasScroll({ left: hasLeftScroll, right: hasRightScroll });
    }, [setHasScroll]);

    const scrollToTest: (direction: "left" | "right") => void =
      React.useCallback(
        (direction: "left" | "right") => {
          const element: HTMLInputElement | null = scrollableRef?.current;
          const scrollButtons: HTMLDivElement | null =
            scrollButtonsRef?.current;
          const scrollButtonsWith: number = scrollButtons?.clientWidth ?? 0;
          const width: number = element?.clientWidth ?? 0;

          if (element && enableLegendSlider) {
            element.scrollTo({
              left:
                direction === "left"
                  ? element.scrollLeft - width + scrollButtonsWith
                  : element.scrollLeft + width - scrollButtonsWith,
              behavior: "smooth",
            });
            setTimeout(() => {
              checkScroll();
            }, 400);
          }
        },
        [enableLegendSlider, checkScroll],
      );

    React.useEffect(() => {
      const keyDownHandler: (key: string) => void = (key: string) => {
        if (key === "ArrowLeft") {
          scrollToTest("left");
        } else if (key === "ArrowRight") {
          scrollToTest("right");
        }
      };
      if (isKeyDowned) {
        keyDownHandler(isKeyDowned);
        intervalRef.current = setInterval(() => {
          keyDownHandler(isKeyDowned);
        }, 300);
      } else {
        clearInterval(intervalRef.current!);
      }
      return () => {
        return clearInterval(intervalRef.current as NodeJS.Timeout);
      };
    }, [isKeyDowned, scrollToTest]);

    const keyDown: (e: KeyboardEvent) => void = (e: KeyboardEvent) => {
      e.stopPropagation();
      if (e.key === "ArrowLeft" || e.key === "ArrowRight") {
        e.preventDefault();
        setIsKeyDowned(e.key);
      }
    };
    const keyUp: (e: KeyboardEvent) => void = (e: KeyboardEvent) => {
      e.stopPropagation();
      setIsKeyDowned(null);
    };

    React.useEffect(() => {
      const scrollable: HTMLInputElement | null = scrollableRef?.current;
      if (enableLegendSlider) {
        checkScroll();
        scrollable?.addEventListener("keydown", keyDown);
        scrollable?.addEventListener("keyup", keyUp);
      }

      return () => {
        scrollable?.removeEventListener("keydown", keyDown);
        scrollable?.removeEventListener("keyup", keyUp);
      };
    }, [checkScroll, enableLegendSlider]);

    return (
      <ol
        ref={ref}
        className={cx("relative overflow-hidden", className)}
        {...other}
      >
        <div
          ref={scrollableRef}
          tabIndex={0}
          className={cx(
            "flex h-full",
            enableLegendSlider
              ? hasScroll?.right || hasScroll?.left
                ? "snap-mandatory items-center overflow-auto pl-4 pr-12 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
                : ""
              : "flex-wrap",
          )}
        >
          {categories.map((category: string, index: number) => {
            return (
              <LegendItem
                key={`item-${index}`}
                name={category}
                color={colors[index] as AvailableChartColorsKeys}
                onClick={onClickLegendItem as any}
                activeLegend={activeLegend!}
              />
            );
          })}
        </div>
        {enableLegendSlider && (hasScroll?.right || hasScroll?.left) ? (
          <>
            <div
              ref={scrollButtonsRef}
              className={cx(
                // base
                "absolute bottom-0 right-0 top-0 flex h-full items-center justify-center pr-1",
                // background color
                "bg-white dark:bg-gray-950",
              )}
            >
              <ScrollButton
                icon={RiArrowLeftSLine}
                onClick={() => {
                  setIsKeyDowned(null);
                  scrollToTest("left");
                }}
                disabled={!hasScroll?.left}
              />
              <ScrollButton
                icon={RiArrowRightSLine}
                onClick={() => {
                  setIsKeyDowned(null);
                  scrollToTest("right");
                }}
                disabled={!hasScroll?.right}
              />
            </div>
          </>
        ) : null}
      </ol>
    );
  },
);

Legend.displayName = "Legend";

const ChartLegend: (
  { payload }: any,
  categoryColors: Map<string, AvailableChartColorsKeys>,
  setLegendHeight: React.Dispatch<React.SetStateAction<number>>,
  activeLegend: string | undefined,
  onClick?: (category: string, color: string) => void,
  enableLegendSlider?: boolean,
  legendPosition?: "left" | "center" | "right",
  yAxisWidth?: number,
) => React.JSX.Element = (
  { payload }: any,
  categoryColors: Map<string, AvailableChartColorsKeys>,
  setLegendHeight: React.Dispatch<React.SetStateAction<number>>,
  activeLegend: string | undefined,
  onClick?: (category: string, color: string) => void,
  enableLegendSlider?: boolean,
  legendPosition?: "left" | "center" | "right",
  yAxisWidth?: number,
): React.JSX.Element => {
  const legendRef: React.RefObject<HTMLDivElement> =
    React.useRef<HTMLDivElement>(null);

  useOnWindowResize(() => {
    const calculateHeight: (height: number | undefined) => number = (
      height: number | undefined,
    ) => {
      return height ? Number(height) + 15 : 60;
    };
    setLegendHeight(calculateHeight(legendRef.current?.clientHeight));
  });

  const legendPayload: Array<PayloadItem> = payload.filter(
    (item: PayloadItem) => {
      return item.type !== "none";
    },
  );

  const paddingLeft: number =
    legendPosition === "left" && yAxisWidth ? yAxisWidth - 8 : 0;

  return (
    <div
      ref={legendRef}
      style={{ paddingLeft: paddingLeft }}
      className={cx(
        "flex items-center",
        { "justify-center": legendPosition === "center" },
        { "justify-start": legendPosition === "left" },
        { "justify-end": legendPosition === "right" },
      )}
    >
      <Legend
        categories={legendPayload.map((entry: PayloadItem) => {
          return entry.value as any;
        })}
        colors={legendPayload.map((entry: PayloadItem) => {
          return categoryColors.get(entry.value! as any)!;
        })}
        onClickLegendItem={onClick!}
        activeLegend={activeLegend!}
        enableLegendSlider={enableLegendSlider!}
      />
    </div>
  );
};

//#region Tooltip

type TooltipProps = Pick<ChartTooltipProps, "active" | "payload" | "label">;

export type PayloadItem = {
  // eslint-disable-next-line react/no-unused-prop-types
  category: string;
  // eslint-disable-next-line react/no-unused-prop-types
  value: number;
  // eslint-disable-next-line react/no-unused-prop-types
  index: string;
  // eslint-disable-next-line react/no-unused-prop-types
  color: AvailableChartColorsKeys;
  // eslint-disable-next-line react/no-unused-prop-types
  type?: string;
  // eslint-disable-next-line react/no-unused-prop-types
  payload: any;
};

interface ChartTooltipProps {
  active: boolean | undefined;
  payload: PayloadItem[];
  label: string;
  valueFormatter: (value: number) => string;
}

const ChartTooltip: ({
  active,
  payload,
  label,
  valueFormatter,
}: ChartTooltipProps) => React.JSX.Element | null = ({
  active,
  payload,
  label,
  valueFormatter,
}: ChartTooltipProps): React.JSX.Element | null => {
  if (active && payload && payload.length) {
    const legendPayload: PayloadItem[] = payload.filter((item: PayloadItem) => {
      return item.type !== "none";
    });
    return (
      <div
        className={cx(
          // base
          "rounded-md border text-sm shadow-md",
          // border color
          "border-gray-200 dark:border-gray-800",
          // background color
          "bg-white dark:bg-gray-950",
        )}
      >
        <div className={cx("border-b border-inherit px-4 py-2")}>
          <p
            className={cx(
              // base
              "font-medium",
              // text color
              "text-gray-900 dark:text-gray-50",
            )}
          >
            {label}
          </p>
        </div>
        <div className={cx("space-y-1 px-4 py-2")}>
          {legendPayload.map(
            ({ value, category, color }: PayloadItem, index: number) => {
              return (
                <div
                  key={`id-${index}`}
                  className="flex items-center justify-between space-x-8"
                >
                  <div className="flex items-center space-x-2">
                    <span
                      aria-hidden="true"
                      className={cx(
                        "h-[3px] w-3.5 shrink-0 rounded-full",
                        getColorClassName(color, "bg"),
                      )}
                    />
                    <p
                      className={cx(
                        // base
                        "whitespace-nowrap text-right",
                        // text color
                        "text-gray-700 dark:text-gray-300",
                      )}
                    >
                      {category}
                    </p>
                  </div>
                  <p
                    className={cx(
                      // base
                      "whitespace-nowrap text-right font-medium tabular-nums",
                      // text color
                      "text-gray-900 dark:text-gray-50",
                    )}
                  >
                    {valueFormatter(value)}
                  </p>
                </div>
              );
            },
          )}
        </div>
      </div>
    );
  }
  return null;
};

//#region LineChart

interface ActiveDot {
  index?: number;
  dataKey?: string;
}

type BaseEventProps = {
  eventType: "dot" | "category";
  categoryClicked: string;
  [key: string]: number | string;
};

type LineChartEventProps = BaseEventProps | null | undefined;

interface LineChartProps extends React.HTMLAttributes<HTMLDivElement> {
  data: Record<string, any>[];
  index: string;
  categories: string[];
  colors?: AvailableChartColorsKeys[];
  valueFormatter?: (value: number) => string;
  startEndOnly?: boolean;
  showXAxis?: boolean;
  showYAxis?: boolean;
  showGridLines?: boolean;
  yAxisWidth?: number;
  intervalType?: "preserveStartEnd" | "equidistantPreserveStart";
  showTooltip?: boolean;
  showLegend?: boolean;
  autoMinValue?: boolean;
  minValue?: number;
  maxValue?: number;
  allowDecimals?: boolean;
  onValueChange?: (value: LineChartEventProps) => void;
  enableLegendSlider?: boolean;
  tickGap?: number;
  connectNulls?: boolean;
  xAxisLabel?: string;
  yAxisLabel?: string;
  curve?: ChartCurve;
  legendPosition?: "left" | "center" | "right";
  tooltipCallback?: (tooltipCallbackContent: TooltipProps) => void;
  customTooltip?: React.ComponentType<TooltipProps>;
  syncid?: string | undefined;
}

const LineChart: React.ForwardRefExoticComponent<
  LineChartProps & React.RefAttributes<HTMLDivElement>
> = React.forwardRef<HTMLDivElement, LineChartProps>(
  (props: LineChartProps, ref: React.ForwardedRef<HTMLDivElement>) => {
    const {
      data = [],
      categories = [],
      index,
      colors = AvailableChartColors,
      valueFormatter = (value: number) => {
        return value.toString();
      },
      startEndOnly = false,
      showXAxis = true,
      showYAxis = true,
      showGridLines = true,
      yAxisWidth = 56,
      intervalType = "equidistantPreserveStart",
      showTooltip = true,
      showLegend = true,
      autoMinValue = false,
      minValue,
      maxValue,
      allowDecimals = true,
      connectNulls = false,
      className,
      onValueChange,
      enableLegendSlider = false,
      tickGap = 5,
      xAxisLabel,
      yAxisLabel,
      legendPosition = "right",
      tooltipCallback,
      customTooltip,
      ...other
    } = props;
    const CustomTooltip: React.ComponentType<TooltipProps> | undefined =
      customTooltip;
    const paddingValue: 0 | 20 =
      (!showXAxis && !showYAxis) || (startEndOnly && !showYAxis) ? 0 : 20;
    const [legendHeight, setLegendHeight] = React.useState(60);
    const [activeDot, setActiveDot] = React.useState<ActiveDot | undefined>(
      undefined,
    );
    const [activeLegend, setActiveLegend] = React.useState<string | undefined>(
      undefined,
    );
    const categoryColors: Map<string, string | number> =
      constructCategoryColors(categories, colors);

    const yAxisDomain: (number | "auto")[] = getYAxisDomain(
      autoMinValue,
      minValue,
      maxValue,
    );
    const hasOnValueChange: boolean = Boolean(onValueChange);
    const prevActiveRef: React.MutableRefObject<boolean | undefined> =
      React.useRef<boolean | undefined>(undefined);
    const prevLabelRef: React.MutableRefObject<string | undefined> =
      React.useRef<string | undefined>(undefined);

    function onDotClick(itemData: any, event: React.MouseEvent): void {
      event.stopPropagation();

      if (!hasOnValueChange) {
        return;
      }
      if (
        (itemData.index === activeDot?.index &&
          itemData.dataKey === activeDot?.dataKey) ||
        (hasOnlyOneValueForKey(data, itemData.dataKey) &&
          activeLegend &&
          activeLegend === itemData.dataKey)
      ) {
        setActiveLegend(undefined);
        setActiveDot(undefined);
        onValueChange?.(null);
      } else {
        setActiveLegend(itemData.dataKey);
        setActiveDot({
          index: itemData.index,
          dataKey: itemData.dataKey,
        });
        onValueChange?.({
          eventType: "dot",
          categoryClicked: itemData.dataKey,
          ...itemData.payload,
        });
      }
    }

    function onCategoryClick(dataKey: string): void {
      if (!hasOnValueChange) {
        return;
      }
      if (
        (dataKey === activeLegend && !activeDot) ||
        (hasOnlyOneValueForKey(data, dataKey) &&
          activeDot &&
          activeDot.dataKey === dataKey)
      ) {
        setActiveLegend(undefined);
        onValueChange?.(null);
      } else {
        setActiveLegend(dataKey);
        onValueChange?.({
          eventType: "category",
          categoryClicked: dataKey,
        });
      }
      setActiveDot(undefined);
    }

    return (
      <div ref={ref} className={cx("h-80 w-full", className)} {...other}>
        <ResponsiveContainer>
          <RechartsLineChart
            data={data}
            syncId={props.syncid?.toString() || ""}
            onClick={
              hasOnValueChange && (activeLegend || activeDot)
                ? () => {
                    setActiveDot(undefined);
                    setActiveLegend(undefined);
                    onValueChange?.(null);
                  }
                : () => {} // do nothing
            }
            margin={{
              bottom: (xAxisLabel ? 30 : undefined) as unknown as number,
              left: (yAxisLabel ? 20 : undefined) as unknown as number,
              right: (yAxisLabel ? 5 : undefined) as unknown as number,
              top: 5,
            }}
          >
            {showGridLines ? (
              <CartesianGrid
                className={cx("stroke-gray-200 stroke-1 dark:stroke-gray-800")}
                horizontal={true}
                vertical={false}
              />
            ) : null}
            <XAxis
              padding={{ left: paddingValue, right: paddingValue }}
              hide={!showXAxis}
              dataKey={index}
              interval={startEndOnly ? "preserveStartEnd" : intervalType}
              tick={{ transform: "translate(0, 6)" }}
              ticks={
                startEndOnly
                  ? ([
                      (data[0] as any)[index],
                      (data[data.length - 1] as any)[index],
                    ] as any)
                  : undefined
              }
              fill=""
              stroke=""
              className={cx(
                // base
                "text-xs",
                // text fill
                "fill-gray-500 dark:fill-gray-500",
              )}
              tickLine={false}
              axisLine={false}
              minTickGap={tickGap}
            >
              {xAxisLabel && (
                <Label
                  position="insideBottom"
                  offset={-20}
                  className="fill-gray-800 text-sm font-medium dark:fill-gray-200"
                >
                  {xAxisLabel}
                </Label>
              )}
            </XAxis>
            <YAxis
              width={yAxisWidth}
              hide={!showYAxis}
              axisLine={false}
              tickLine={false}
              type="number"
              domain={yAxisDomain as AxisDomain}
              tick={{ transform: "translate(-3, 0)" }}
              fill=""
              stroke=""
              className={cx(
                // base
                "text-xs",
                // text fill
                "fill-gray-500 dark:fill-gray-500",
              )}
              tickFormatter={valueFormatter}
              allowDecimals={allowDecimals}
            >
              {yAxisLabel && (
                <Label
                  position="insideLeft"
                  style={{ textAnchor: "middle" }}
                  angle={-90}
                  offset={-15}
                  className="fill-gray-800 text-sm font-medium dark:fill-gray-200"
                >
                  {yAxisLabel}
                </Label>
              )}
            </YAxis>
            <Tooltip
              wrapperStyle={{ outline: "none" }}
              isAnimationActive={true}
              animationDuration={100}
              cursor={{ stroke: "#d1d5db", strokeWidth: 1 }}
              offset={20}
              position={{ y: 0 }}
              content={({ active, payload, label }: any) => {
                const cleanPayload: TooltipProps["payload"] = payload
                  ? payload.map((item: any) => {
                      return {
                        category: item.dataKey,
                        value: item.value,
                        index: item.payload[index],
                        color: categoryColors.get(
                          item.dataKey,
                        ) as AvailableChartColorsKeys,
                        type: item.type,
                        payload: item.payload,
                      };
                    })
                  : [];

                if (
                  tooltipCallback &&
                  (active !== prevActiveRef.current ||
                    label !== prevLabelRef.current)
                ) {
                  tooltipCallback({ active, payload: cleanPayload, label });
                  prevActiveRef.current = active;
                  prevLabelRef.current = label;
                }

                return showTooltip && active ? (
                  CustomTooltip ? (
                    <CustomTooltip
                      active={active}
                      payload={cleanPayload}
                      label={label}
                    />
                  ) : (
                    <ChartTooltip
                      active={active}
                      payload={cleanPayload}
                      label={label}
                      valueFormatter={valueFormatter}
                    />
                  )
                ) : null;
              }}
            />

            {showLegend ? (
              <RechartsLegend
                verticalAlign="top"
                height={legendHeight}
                content={({ payload }: any) => {
                  return ChartLegend(
                    { payload },
                    categoryColors,
                    setLegendHeight,
                    activeLegend,
                    hasOnValueChange
                      ? (clickedLegendItem: string) => {
                          return onCategoryClick(clickedLegendItem);
                        }
                      : undefined,
                    enableLegendSlider,
                    legendPosition,
                    yAxisWidth,
                  );
                }}
              />
            ) : null}
            {categories.map((category: string) => {
              return (
                <Line
                  className={cx(
                    getColorClassName(
                      categoryColors.get(category) as AvailableChartColorsKeys,
                      "stroke",
                    ),
                  )}
                  strokeOpacity={
                    activeDot || (activeLegend && activeLegend !== category)
                      ? 0.3
                      : 1
                  }
                  activeDot={(props: any) => {
                    const {
                      cx: cxCoord,
                      cy: cyCoord,
                      stroke,
                      strokeLinecap,
                      strokeLinejoin,
                      strokeWidth,
                      dataKey,
                    } = props;
                    return (
                      <Dot
                        className={cx(
                          "stroke-white dark:stroke-gray-950",
                          onValueChange ? "cursor-pointer" : "",
                          getColorClassName(
                            categoryColors.get(
                              dataKey,
                            ) as AvailableChartColorsKeys,
                            "fill",
                          ),
                        )}
                        cx={cxCoord}
                        cy={cyCoord}
                        r={5}
                        fill=""
                        stroke={stroke}
                        strokeLinecap={strokeLinecap}
                        strokeLinejoin={strokeLinejoin}
                        strokeWidth={strokeWidth}
                        onClick={(_: any, event: any) => {
                          return onDotClick(props, event);
                        }}
                      />
                    );
                  }}
                  dot={(props: any) => {
                    const {
                      stroke,
                      strokeLinecap,
                      strokeLinejoin,
                      strokeWidth,
                      cx: cxCoord,
                      cy: cyCoord,
                      dataKey,
                      index,
                    } = props;

                    if (
                      (hasOnlyOneValueForKey(data, category) &&
                        !(
                          activeDot ||
                          (activeLegend && activeLegend !== category)
                        )) ||
                      (activeDot?.index === index &&
                        activeDot?.dataKey === category)
                    ) {
                      return (
                        <Dot
                          key={index}
                          cx={cxCoord}
                          cy={cyCoord}
                          r={5}
                          stroke={stroke}
                          fill=""
                          strokeLinecap={strokeLinecap}
                          strokeLinejoin={strokeLinejoin}
                          strokeWidth={strokeWidth}
                          className={cx(
                            "stroke-white dark:stroke-gray-950",
                            onValueChange ? "cursor-pointer" : "",
                            getColorClassName(
                              categoryColors.get(
                                dataKey,
                              ) as AvailableChartColorsKeys,
                              "fill",
                            ),
                          )}
                        />
                      );
                    }
                    return <React.Fragment key={index}></React.Fragment>;
                  }}
                  key={category}
                  name={category}
                  type="linear"
                  dataKey={category}
                  stroke=""
                  strokeWidth={2}
                  strokeLinejoin="round"
                  strokeLinecap="round"
                  isAnimationActive={false}
                  connectNulls={connectNulls}
                />
              );
            })}
            {/* hidden lines to increase clickable target area */}
            {onValueChange
              ? categories.map((category: string) => {
                  return (
                    <Line
                      className={cx("cursor-pointer")}
                      strokeOpacity={0}
                      key={category}
                      name={category}
                      type={props.curve || ChartCurve.LINEAR}
                      dataKey={category}
                      stroke="transparent"
                      fill="transparent"
                      legendType="none"
                      tooltipType="none"
                      strokeWidth={12}
                      connectNulls={connectNulls}
                      onClick={(props: any, event: any) => {
                        event.stopPropagation();
                        const { name } = props;
                        onCategoryClick(name);
                      }}
                    />
                  );
                })
              : null}
          </RechartsLineChart>
        </ResponsiveContainer>
      </div>
    );
  },
);

LineChart.displayName = "LineChart";

export { LineChart, type LineChartEventProps, type TooltipProps };
