import React, { useMemo, useState } from "react";
import {
  Chart as ChartJS,
  CategoryScale,
  LinearScale,
  BarController,
  BarElement,
  Tooltip,
  type ChartOptions,
  type ChartData,
} from "chart.js";
import { Chart } from "react-chartjs-2";
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,
  FALLBACK_PRIMARY,
  formatTooltipDate,
  formatMonthLabel,
  formatCount,
  hexToRgba,
  ChartLegendItem,
  ChartPeriodButton,
} from "./chart-shared";

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

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

export interface ContactHistoryDataPoint {
  /** ISO date string e.g. "2024-01-01" */
  date: string;
  calls: number;
  emails: number;
  meetings: number;
  notes?: number;
}

export type ContactHistoryPeriod = 3 | 6 | 12;

export interface BackofficeContactHistoryChartProps {
  chartData?: ContactHistoryDataPoint[] | null;
  title?: string;
  /** Show or hide the chart legend */
  showLegend?: boolean;
  /** Legend placement relative to the chart */
  legendPosition?: "top" | "bottom";
  /** Show or hide X axis labels */
  showXAxis?: boolean;
  /** Show or hide Y axis labels */
  showYAxis?: boolean;
  /** Default period selector value */
  defaultPeriod?: ContactHistoryPeriod;
  /** Chart canvas height in pixels */
  height?: number;
  /** Card max-width in pixels or CSS string */
  width?: number | string;
  className?: string;
  /** Show skeleton loading state instead of the chart */
  isLoading?: boolean;
}

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

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

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

function ChartLegend({
  callColor,
  emailColor,
  meetingColor,
  noteColor,
  showNotes,
}: {
  callColor: string;
  emailColor: string;
  meetingColor: string;
  noteColor: string;
  showNotes: boolean;
}) {
  return (
    <div className="flex flex-wrap gap-x-3 gap-y-1.5 pb-2">
      <ChartLegendItem label="Calls" color={callColor} />
      <ChartLegendItem label="Emails" color={emailColor} />
      <ChartLegendItem label="Meetings" color={meetingColor} />
      {showNotes && <ChartLegendItem label="Notes" color={noteColor} />}
    </div>
  );
}

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

export function BackofficeContactHistoryChart({
  chartData,
  title = "Client History",
  showLegend = true,
  legendPosition = "top",
  showXAxis = true,
  showYAxis = false,
  defaultPeriod = 6,
  height = 200,
  width = "100%",
  className,
  isLoading = false,
}: BackofficeContactHistoryChartProps) {
  const [period, setPeriod] = useState<ContactHistoryPeriod>(defaultPeriod);

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

  // Derive 4 shades from the brand primary
  const callColor = brandPrimary;
  const emailColor = hexToRgba(brandPrimary, 0.65);
  const meetingColor = hexToRgba(brandPrimary, 0.4);
  const noteColor = hexToRgba(brandPrimary, 0.22);

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

  const showNotes = useMemo(
    () => !!sliced?.some((p) => (p.notes ?? 0) > 0),
    [sliced],
  );

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

  const data = useMemo<ChartData<"bar">>(() => {
    if (!sliced) return { labels: [], datasets: [] };
    return {
      labels,
      datasets: [
        {
          label: "Calls",
          data: sliced.map((p) => p.calls),
          backgroundColor: callColor,
          borderWidth: 0,
          stack: "contacts",
        },
        {
          label: "Emails",
          data: sliced.map((p) => p.emails),
          backgroundColor: emailColor,
          borderWidth: 0,
          stack: "contacts",
        },
        {
          label: "Meetings",
          data: sliced.map((p) => p.meetings),
          backgroundColor: meetingColor,
          borderWidth: 0,
          stack: "contacts",
        },
        ...(showNotes
          ? [
              {
                label: "Notes",
                data: sliced.map((p) => p.notes ?? 0),
                backgroundColor: noteColor,
                borderWidth: 0,
                stack: "contacts",
              },
            ]
          : []),
      ],
    };
  }, [
    sliced,
    labels,
    callColor,
    emailColor,
    meetingColor,
    noteColor,
    showNotes,
  ]);

  const options = useMemo<ChartOptions<"bar">>(
    () => ({
      responsive: true,
      maintainAspectRatio: false,
      animation: { duration: 600, easing: "easeOutQuart" },
      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: {
          stacked: true,
          display: showXAxis,
          grid: { display: false },
          border: { display: false },
          ticks: {
            maxRotation: 0,
            minRotation: 0,
            color: FALLBACK_TICK,
            font: { size: 10 },
          },
        },
        y: {
          stacked: true,
          display: showYAxis,
          grid: { display: false },
          border: { display: false },
          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 gap-0.5 sm:gap-1">
            {CONTACT_PERIODS.map((p) => (
              <ChartPeriodButton
                key={p}
                period={p}
                active={period === p}
                onClick={() => setPeriod(p)}
              />
            ))}
          </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" && (
              <ChartLegend
                callColor={callColor}
                emailColor={emailColor}
                meetingColor={meetingColor}
                noteColor={noteColor}
                showNotes={showNotes}
              />
            )}
            <div style={{ height, width: "100%", position: "relative" }}>
              <Chart
                key={brandPrimary}
                type="bar"
                data={data}
                options={options}
                aria-label={title}
              />
            </div>
            {showLegend && legendPosition === "bottom" && (
              <ChartLegend
                callColor={callColor}
                emailColor={emailColor}
                meetingColor={meetingColor}
                noteColor={noteColor}
                showNotes={showNotes}
              />
            )}
          </div>
        )}
      </CardContent>
    </Card>
  );
}
