import React, { useMemo, useState } from "react";
import {
  Chart as ChartJS,
  CategoryScale,
  LinearScale,
  LineController,
  LineElement,
  PointElement,
  BarController,
  BarElement,
  Tooltip,
  type ChartOptions,
} from "chart.js";
import { Chart } from "react-chartjs-2";
import { BarChart2, LineChart } from "lucide-react";
import { useThemeVars } from "@/lib/theme-provider";
import { Card, CardContent, CardHeader, CardTitle, CardAction } from "./card";
import { Empty, EmptyDescription } from "./empty";
import { Skeleton } from "./skeleton";
import { cn } from "@/lib/utils";
import {
  FALLBACK_TICK,
  hexToRgba,
  formatTooltipDate,
  formatMonthLabel,
  formatCount,
  ChartLegendItem,
  ChartPeriodButton,
} from "./chart-shared";
import { ToggleGroup, ToggleGroupItem } from "./toggle-group";

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

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

export interface ConnectionSummaryDataPoint {
  /** ISO date string e.g. "2024-01-01" */
  date: string;
  /** Total client count */
  all: number;
  /** Clients with bank connected */
  bankConnected: number;
  /** Clients with property connected */
  propertyConnected: number;
  /** Clients with no connection */
  notConnected: number;
}

export type ConnectionSummaryPeriod = 3 | 6 | 12;
type ConnectionChartType = "line" | "bar";

export interface BackofficeConnectionsChartProps {
  chartData?: ConnectionSummaryDataPoint[] | null;
  title?: string;
  showLegend?: boolean;
  legendPosition?: "top" | "bottom";
  showXAxis?: boolean;
  showYAxis?: boolean;
  defaultPeriod?: ConnectionSummaryPeriod;
  height?: number;
  width?: number | string;
  className?: string;
  isLoading?: boolean;
}

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

const PERIODS: ConnectionSummaryPeriod[] = [3, 6, 12];
const SLICE_COUNT: Record<ConnectionSummaryPeriod, number> = {
  3: 3,
  6: 6,
  12: 12,
};

const ALL_COLOR = "#040D13";
const BANK_COLOR = "#33FF99";
const PROPERTY_COLOR = "#3B82F6";
const NONE_COLOR = "#9EAAB5";

// ---------------------------------------------------------------------------
// Legend
// ---------------------------------------------------------------------------

function ConnectionsLegend() {
  return (
    <div className="flex flex-wrap gap-x-3 gap-y-1.5 pb-2">
      <ChartLegendItem
        label="All Clients"
        color={ALL_COLOR}
        lineStyle="solid"
      />
      <ChartLegendItem
        label="Bank Connected"
        color={BANK_COLOR}
        lineStyle="solid"
      />
      <ChartLegendItem
        label="Property Connected"
        color={PROPERTY_COLOR}
        lineStyle="solid"
      />
      <ChartLegendItem
        label="Not Connected"
        color={NONE_COLOR}
        lineStyle="solid"
      />
    </div>
  );
}

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

export function BackofficeConnectionsChart({
  chartData,
  title = "Clients and Connections",
  showLegend = true,
  legendPosition = "top",
  showXAxis = true,
  showYAxis = false,
  defaultPeriod = 6,
  height = 200,
  width = "100%",
  className,
  isLoading = false,
}: BackofficeConnectionsChartProps) {
  const [period, setPeriod] = useState<ConnectionSummaryPeriod>(defaultPeriod);
  const [chartType, setChartType] = useState<ConnectionChartType>("line");

  const themeVars = useThemeVars();
  const fontFamily: string =
    (themeVars["--font-sans"] as string | undefined) || "Figtree, sans-serif";

  const sliced = useMemo<ConnectionSummaryDataPoint[] | null>(() => {
    if (!chartData?.length) return null;
    const count = Math.min(SLICE_COUNT[period], chartData.length);
    return chartData.slice(chartData.length - count);
  }, [chartData, period]);

  const labels = useMemo(
    () => sliced?.map((p) => formatMonthLabel(p.date)) ?? [],
    [sliced],
  );

  const isBar = chartType === "bar";

  const datasets = useMemo(() => {
    if (!sliced) return [];
    return [
      {
        type: chartType,
        label: "All Clients",
        data: sliced.map((p) => p.all),
        borderColor: ALL_COLOR,
        backgroundColor: isBar ? ALL_COLOR : hexToRgba(ALL_COLOR, 0.08),
        fill: !isBar,
        tension: 0.3,
        pointRadius: 0,
        borderWidth: 2,
      },
      {
        type: chartType,
        label: "Bank Connected",
        data: sliced.map((p) => p.bankConnected),
        borderColor: BANK_COLOR,
        backgroundColor: isBar ? BANK_COLOR : hexToRgba(BANK_COLOR, 0.08),
        fill: !isBar,
        tension: 0.3,
        pointRadius: 0,
        borderWidth: 2,
      },
      {
        type: chartType,
        label: "Property Connected",
        data: sliced.map((p) => p.propertyConnected),
        borderColor: PROPERTY_COLOR,
        backgroundColor: isBar
          ? PROPERTY_COLOR
          : hexToRgba(PROPERTY_COLOR, 0.08),
        fill: !isBar,
        tension: 0.3,
        pointRadius: 0,
        borderWidth: 2,
      },
      {
        type: chartType,
        label: "Not Connected",
        data: sliced.map((p) => p.notConnected),
        borderColor: NONE_COLOR,
        backgroundColor: isBar ? NONE_COLOR : hexToRgba(NONE_COLOR, 0.08),
        fill: !isBar,
        tension: 0.3,
        pointRadius: 0,
        borderWidth: 2,
      },
    ];
  }, [sliced, chartType, isBar]);

  const options = useMemo<ChartOptions<"bar">>(
    () => ({
      responsive: true,
      maintainAspectRatio: false,
      animation: { duration: 300 },
      layout: { padding: 0 },
      plugins: {
        legend: { display: false },
        tooltip: {
          mode: "index",
          intersect: false,
          padding: 12,
          cornerRadius: 0,
          titleFont: { size: 11, weight: 600 },
          bodyFont: { size: 12, weight: 500 },
          callbacks: {
            title: (tooltipItems) => {
              const idx = tooltipItems[0]?.dataIndex;
              if (idx != null && sliced?.[idx]?.date) {
                return formatTooltipDate(sliced[idx].date, "monthly");
              }
              return tooltipItems[0]?.label ?? "";
            },
            label: (ctx) => {
              const val = ctx.raw as number;
              if (val === 0) return;
              return ` ${ctx.dataset.label}: ${formatCount(val)}`;
            },
          },
        },
      },
      scales: {
        x: {
          display: showXAxis,
          grid: { display: false },
          border: { display: false },
          ticks: {
            maxRotation: 0,
            minRotation: 0,
            color: FALLBACK_TICK,
            font: { size: 10 },
          },
        },
        y: {
          display: showYAxis,
          grid: { display: false },
          border: { display: false },
          beginAtZero: true,
          ticks: {
            padding: 8,
            maxTicksLimit: 5,
            color: FALLBACK_TICK,
            font: { size: 10 },
            callback: (v) => formatCount(Number(v)),
          },
        },
      },
    }),
    [showXAxis, showYAxis, sliced],
  );

  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>
        <CardAction>
          <div className="flex items-center gap-2">
            <ToggleGroup
              type="single"
              variant="outline"
              size="sm"
              value={[chartType]}
              onValueChange={(v) =>
                v[0] && setChartType(v[0] as ConnectionChartType)
              }
            >
              <ToggleGroupItem value="line" aria-label="Line chart">
                <LineChart className="size-3.5" />
              </ToggleGroupItem>
              <ToggleGroupItem value="bar" aria-label="Bar chart">
                <BarChart2 className="size-3.5" />
              </ToggleGroupItem>
            </ToggleGroup>
            <div className="flex gap-0.5 sm:gap-1">
              {PERIODS.map((p) => (
                <ChartPeriodButton
                  key={p}
                  period={p}
                  active={period === p}
                  onClick={() => setPeriod(p)}
                />
              ))}
            </div>
          </div>
        </CardAction>
      </CardHeader>

      <CardContent className="px-3 sm:px-6">
        {isLoading ? (
          <Skeleton style={{ height, width: "100%" }} />
        ) : !sliced ? (
          <Empty className="flex-none p-4" style={{ height }}>
            <EmptyDescription>No data available</EmptyDescription>
          </Empty>
        ) : (
          <div className="flex flex-col gap-2">
            {showLegend && legendPosition === "top" && <ConnectionsLegend />}
            <div style={{ height, width: "100%", position: "relative" }}>
              {/* eslint-disable-next-line @typescript-eslint/no-explicit-any */}
              <Chart
                key={chartType}
                type={chartType as any}
                data={{ labels, datasets } as any}
                options={options}
                aria-label={title}
              />
            </div>
            {showLegend && legendPosition === "bottom" && <ConnectionsLegend />}
          </div>
        )}
      </CardContent>
    </Card>
  );
}
