import React, { useCallback, useEffect, useMemo, useState } from "react";

import { Stack } from "@mui/material";
import { styled, useTheme } from "@mui/material/styles";
import { ChartContainer, ChartsLegend } from "@mui/x-charts";
import { HighlightItemData } from "@mui/x-charts/context/HighlightedProvider";
import { useDrawingArea } from "@mui/x-charts/hooks";
import { PieChart } from "@mui/x-charts/PieChart";

import WidgetCard from "../../../components/WidgetCard";
import { useI18n } from "../../../contexts/I18nContext";
import { ContribComponent } from "../../../types";
import { PaymentMethodItem } from "../../../types/dashboard";
import {
  getHumanReadablePaymentMethod,
  HumanReadablePaymentMethod,
} from "../utils/getHumanReadablePaymentMethod";

/**
 * Calculate the percentage of a payment method.
 */
function calculatePercentage(count: number, total: number): number {
  return Math.round((count / total) * 100);
}

interface PaymentMethod extends HumanReadablePaymentMethod, PaymentMethodItem {
  label: string;
  value: number;
}

const StyledTitle = styled("text")(({ theme }) => ({
  fill: theme.palette.text.primary,
  textAnchor: "middle",
  dominantBaseline: "central",
  fontWeight: 700,
  fontSize: theme.typography.h4.fontSize,
}));

const StyledSubtitle = styled("text")(({ theme }) => ({
  fill: theme.palette.text.secondary,
  textAnchor: "middle",
  dominantBaseline: "central",
  fontSize: theme.typography.htmlFontSize,
}));

function PieCenterLabel({ title, subtitle }: { title: string; subtitle: string }) {
  const { width, height, left, top } = useDrawingArea();
  return (
    <>
      <StyledTitle x={left + width / 2} y={top - 10 + height / 2}>
        {title}
      </StyledTitle>
      <StyledSubtitle x={left + width / 2} y={top + 25 + height / 2}>
        {subtitle}
      </StyledSubtitle>
    </>
  );
}

const PaymentMethodWidget: ContribComponent<PaymentMethodItem[]> = ({ data, sx }) => {
  const { t } = useI18n();
  const theme = useTheme();

  const total = useMemo(() => data.reduce((acc, curr) => acc + curr.count, 0), [data]);

  const dataSeries = useMemo(() => {
    // Accumulate counts per payment method name using a Map for efficient lookups
    const paymentMethodMap = data.reduce((map, item) => {
      const { name, color } = getHumanReadablePaymentMethod(item.method, {
        disambiguateCards: false,
        theme,
      });

      if (map.has(name)) {
        const existingItem = map.get(name)!;
        existingItem.count += item.count;
        existingItem.value += item.count;
      } else {
        map.set(name, { ...item, color, name, value: item.count, label: name });
      }
      return map;
    }, new Map<string, PaymentMethod>());

    // Convert Map values to an array and sort by count in descending order
    const sortedItems = Array.from(paymentMethodMap.values()).sort((a, b) => b.count - a.count);

    // Take the top 5 items and accumulate the rest into an 'Other' category
    const topItems = sortedItems.slice(0, 5);
    const otherItems = sortedItems.slice(5);

    if (otherItems.length > 0) {
      const otherTotals = otherItems.reduce(
        (totals, item) => {
          totals.count += item.count;
          totals.value += item.value;
          return totals;
        },
        { count: 0, value: 0 },
      );

      topItems.push({
        method: "other",
        name: "Other",
        count: otherTotals.count,
        value: otherTotals.value,
        color: theme.palette.grey[300],
        label: "Other",
      });
    }

    // Map the items to include percentage labels
    return topItems.map((item) => {
      const percentage = calculatePercentage(item.count, total);
      return { ...item, label: `${item.name} ${percentage}%` };
    });
  }, [data, total, theme]);

  const [highlighted, setHighlighted] = useState(dataSeries?.[0]);

  // Update highlight when data changes.
  useEffect(() => {
    setHighlighted(dataSeries?.[0]);
  }, [data, total, highlighted]);

  const handleHighlightChange = useCallback(
    (highlight: HighlightItemData | null) => {
      if (typeof highlight?.dataIndex === "number") {
        setHighlighted(dataSeries[highlight.dataIndex]);
      } else {
        setHighlighted(dataSeries?.[0]);
      }
    },
    [data, total, highlighted],
  );

  const highlightedPercentage = useMemo(() => {
    if (highlighted == null) return null;

    const percentage: number = calculatePercentage(highlighted.count, total);
    if (Number.isNaN(percentage) || !Number.isFinite(percentage)) return null;

    return percentage;
  }, [data, total, highlighted]);

  return (
    <WidgetCard
      gridColumn={{ md: "span 6", sm: "span 1" }}
      gridRow="span 2"
      sx={sx}
      title={t("Payment Methods")}
    >
      <Stack alignItems="center" direction={{ xs: "column", sm: "row" }} justifyContent="center">
        <PieChart
          height={275}
          margin={{ top: 0, right: 0, bottom: 0, left: 0 }}
          series={[
            {
              data: dataSeries,
              innerRadius: 75,
              highlightScope: { fade: "global", highlight: "item" },
            },
          ]}
          slotProps={{
            legend: {
              hidden: true,
            },
          }}
          sx={{ py: { xs: 1, sm: 3 } }}
          width={275}
          onHighlightChange={handleHighlightChange}
        >
          {highlighted != null && (
            <PieCenterLabel subtitle={highlighted.name} title={`${highlightedPercentage}%`} />
          )}
        </PieChart>
        <ChartContainer
          dataset={dataSeries}
          height={8 + dataSeries.length * 32} // NOTE: Approx. "line height"
          margin={{ top: 0, right: 0, bottom: 0, left: 0 }}
          series={dataSeries.map(({ label, color }) => ({ type: "bar", label, color }))}
          width={200}
        >
          <ChartsLegend direction="column" position={{ vertical: "middle", horizontal: "left" }} />
        </ChartContainer>
      </Stack>
    </WidgetCard>
  );
};

export default PaymentMethodWidget;
