import React, { useMemo } from "react";
import {
  Chart as ChartJS,
  CategoryScale,
  LinearScale,
  BarController,
  BarElement,
  Tooltip,
  type ChartOptions,
  type ChartData,
} from "chart.js";
import ChartDataLabels from "chartjs-plugin-datalabels";
import { Chart } from "react-chartjs-2";
import { useThemeVars } from "@/lib/theme-provider";
import { Card, CardContent, CardHeader, CardTitle } from "./card";
import { Empty, EmptyDescription } from "./empty";
import { Spinner } from "./spinner";
import { cn } from "@/lib/utils";
import { formatCurrency } from "@/lib/format-currency";
import {
  FALLBACK_TICK,
  FALLBACK_PRIMARY,
  FALLBACK_SECONDARY,
} from "./chart-shared";

ChartJS.register(
  CategoryScale,
  LinearScale,
  BarController,
  BarElement,
  Tooltip,
);

// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------


// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------

export interface TransactionsIncomeExpenseBarChartProps {
  /** Total income amount (positive number) */
  totalIncome: number | null;
  /** Total expense amount (negative or positive — absolute value is used) */
  totalExpense: number | null;
  title?: string;
  /** Chart canvas height in pixels */
  height?: number;
  /** Width of the card */
  width?: number | string;
  className?: string;
  /** Show skeleton loading state instead of the chart */
  isLoading?: boolean;
}

// ---------------------------------------------------------------------------
// Component
// ---------------------------------------------------------------------------

export function TransactionsIncomeExpenseBarChart({
  totalIncome,
  totalExpense,
  title = "Transactions — Income vs Expense",
  height = 120,
  width = "100%",
  className,
  isLoading = false,
}: TransactionsIncomeExpenseBarChartProps) {
  const themeVars = useThemeVars();
  const brandPrimary =
    (themeVars["--theme-primary"] as string | undefined) || FALLBACK_PRIMARY;
  const brandSecondary =
    (themeVars["--theme-secondary"] as string | undefined) ||
    FALLBACK_SECONDARY;
  const fontFamily: string =
    (themeVars["--font-sans"] as string | undefined) || "Figtree, sans-serif";

  const hasData = totalIncome != null && totalExpense != null;
  const incomeVal = totalIncome ?? 0;
  const expenseVal = Math.abs(totalExpense ?? 0);
  const maxVal = Math.max(incomeVal, expenseVal);

  const chartData = useMemo<ChartData<"bar">>(() => {
    if (!hasData) return { labels: [], datasets: [] };
    return {
      labels: ["Incoming", "Outgoing"],
      datasets: [
        {
          barThickness: 40,
          backgroundColor: [brandPrimary, brandSecondary],
          hoverBackgroundColor: [brandPrimary, brandSecondary],
          borderWidth: 0,
          borderRadius: 0,
          borderSkipped: false,
          data: [incomeVal, expenseVal],
          // chartjs-plugin-datalabels config — typed via plugin module augmentation
          datalabels: {
            labels: {
              value: {
                anchor: "end",
                align: "end",
                offset: 10,
                clamp: false,
                font: { weight: "bold", size: 14 },
                color: FALLBACK_SECONDARY,
                textAlign: "left",
                // Returns array for multi-line: dollar value on line 1, blank on line 2
                formatter: (v: number) => [formatCurrency(v, { decimals: 2 }), ""],
              },
              name: {
                anchor: "end",
                align: "end",
                offset: 10,
                clamp: false,
                font: { size: 12 },
                color: FALLBACK_TICK,
                textAlign: "left",
                // Returns array for multi-line: blank on line 1, bar label on line 2
                // eslint-disable-next-line @typescript-eslint/no-explicit-any
                formatter: (_: number, ctx: any) => [
                  "",
                  String(ctx.chart.data.labels?.[ctx.dataIndex] ?? ""),
                ],
              },
            },
          } as unknown as never,
        },
      ],
    };
  }, [hasData, incomeVal, expenseVal, brandPrimary, brandSecondary]);

  const options = useMemo<ChartOptions<"bar">>(
    () => ({
      indexAxis: "y",
      responsive: true,
      maintainAspectRatio: false,
      animation: { duration: 800, easing: "easeOutQuart" },
      layout: {
        // Right padding reserves space for the datalabels rendered outside the bar area
        padding: { right: 180, left: 0, top: 10, bottom: 10 },
      },
      plugins: {
        legend: { display: false },
        tooltip: { enabled: false },
      },
      scales: {
        y: {
          display: true,
          grid: { display: false },
          border: { display: false },
          ticks: { display: false },
        },
        x: {
          display: true,
          suggestedMax: maxVal * 1.3,
          grid: { display: false },
          border: { display: false },
          ticks: { display: false },
        },
      },
    }),
    [maxVal],
  );

  return (
    <Card
      className={cn("w-full py-4 sm:py-6 gap-2", className)}
      style={{ maxWidth: width, fontFamily }}
    >
      <CardHeader className="px-3 sm:px-6">
        <CardTitle className="text-xs font-semibold uppercase tracking-wide">
          {title}
        </CardTitle>
      </CardHeader>

      <CardContent className="px-3 sm:px-6">
        {isLoading ? (
          <div
            className="flex items-center justify-center text-muted-foreground"
            style={{ height, width: "100%" }}
          >
            <Spinner size="lg" />
          </div>
        ) : !hasData ? (
          <Empty className="flex-none p-4" style={{ height }}>
            <EmptyDescription>No data available</EmptyDescription>
          </Empty>
        ) : (
          <div style={{ height, width: "100%", position: "relative" }}>
            <Chart
              key={`${brandPrimary}__${brandSecondary}`}
              type="bar"
              data={chartData}
              options={options}
              plugins={[ChartDataLabels]}
              aria-label={title}
            />
          </div>
        )}
      </CardContent>
    </Card>
  );
}
