'use client';

import * as React from 'react';
import * as RechartsPrimitive from 'recharts';
import type {
  Payload as TooltipPayloadItem,
  ValueType as TooltipValueType,
  NameType as TooltipNameType,
} from 'recharts/types/component/DefaultTooltipContent';
import type { Payload as LegendPayloadItem } from 'recharts/types/component/DefaultLegendContent';

import { cn } from '../../shared/utils';
import { Alert, AlertDescription, AlertTitle } from '../alert';
import { Button } from '../button';
import {
  Card,
  CardAction,
  CardContent,
  CardDescription,
  CardFooter,
  CardHeader,
  CardTitle,
} from '../card';
import { Empty, EmptyAction, EmptyDescription, EmptyIcon, EmptyTitle } from '../empty';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../select';
import { Skeleton } from '../skeleton';
import { BarChart3, RefreshCw, WifiOff } from 'lucide-react';

// Format: { THEME_NAME: CSS_SELECTOR }
const THEMES = { light: '', dark: '.dark' } as const;

export type ChartConfig = {
  [k in string]: {
    label?: React.ReactNode;
    icon?: React.ComponentType;
  } & (
    | { color?: string; theme?: never }
    | { color?: never; theme: Record<keyof typeof THEMES, string> }
  );
};

type ChartContextProps = {
  config: ChartConfig;
};

export type DashboardChartDatum = Record<string, string | number | null | undefined>;

export type DashboardChartSeries = {
  key: string;
  label?: React.ReactNode;
  type?: 'bar' | 'line' | 'area';
  stackId?: string;
  yAxisId?: string;
};

export type DashboardChartColors = string[] | Record<string, string>;

export type ChartPeriod = {
  value: string;
  label: string;
};

export type ChartBarSize = 'sm' | 'md' | 'lg' | 'xl' | number;

/** Curve interpolation type for line and area charts */
export type ChartCurveType =
  | 'monotone'
  | 'linear'
  | 'step'
  | 'stepBefore'
  | 'stepAfter'
  | 'natural'
  | 'basis';

type ChartValueFormatter = (value: number | string) => string;

export type ChartErrorState = boolean | string | Error | React.ReactNode;

export type ChartStateProps = {
  isLoading?: boolean;
  error?: ChartErrorState;
  onRetry?: () => void;
  retryLabel?: React.ReactNode;
  emptyTitle?: React.ReactNode;
  emptyDescription?: React.ReactNode;
  errorTitle?: React.ReactNode;
  errorDescription?: React.ReactNode;
  loadingLabel?: React.ReactNode;
  stateClassName?: string;
};

const defaultPeriods: ChartPeriod[] = [
  { value: '7d', label: '7 days' },
  { value: '30d', label: '30 days' },
  { value: '90d', label: '90 days' },
];

const defaultChartColors = [
  'var(--chart-1)',
  'var(--chart-2)',
  'var(--chart-3)',
  'var(--chart-4)',
  'var(--chart-5)',
  'var(--chart-6)',
  'var(--chart-7)',
  'var(--chart-8)',
];

const chartBarSizes: Record<Exclude<ChartBarSize, number>, number> = {
  sm: 8,
  md: 14,
  lg: 22,
  xl: 32,
};

const ChartContext = React.createContext<ChartContextProps | null>(null);

export function useChart() {
  const context = React.useContext(ChartContext);

  if (!context) {
    throw new Error('useChart must be used within a <ChartContainer />');
  }

  return context;
}

/**
 * Root container for Recharts-based charts with theme-aware color injection.
 *
 * @description
 * Wraps Recharts' `ResponsiveContainer` and injects CSS custom properties
 * (`--color-*`) from a `ChartConfig` object, enabling full dark-mode
 * support without hard-coded hex values in chart elements.
 *
 * @ai-rules
 * 1. NEVER pass hex colors directly to Recharts elements (e.g., `fill="#4F46E5"`).
 *    Always use `fill="var(--color-keyName)"` referencing the injected CSS variables.
 * 2. This wrapper is REQUIRED to use `ChartTooltipContent` and `ChartLegendContent`.
 * 3. Set height via `className="h-[300px]"` on `ChartContainer`, not on Recharts components.
 * 4. Do NOT add another `<ResponsiveContainer>` — it is already included inside.
 */
function ChartContainer({
  id,
  className,
  children,
  config,
  ...props
}: React.ComponentProps<'div'> & {
  config: ChartConfig;
  children: React.ComponentProps<typeof RechartsPrimitive.ResponsiveContainer>['children'];
}) {
  const uniqueId = React.useId();
  const chartId = `chart-${id || uniqueId.replace(/:/g, '')}`;

  return (
    <ChartContext.Provider value={{ config }}>
      <div
        data-slot="chart"
        data-chart={chartId}
        className={cn(
          "[&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border relative h-[300px] min-h-[200px] w-full min-w-0 overflow-hidden text-xs [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-hidden [&_.recharts-sector]:outline-hidden [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-surface]:outline-hidden",
          className
        )}
        {...props}
      >
        <ChartStyle id={chartId} config={config} />
        <RechartsPrimitive.ResponsiveContainer width="100%" height="100%">
          {children}
        </RechartsPrimitive.ResponsiveContainer>
      </div>
    </ChartContext.Provider>
  );
}

const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
  const colorConfig = Object.entries(config).filter(([, config]) => config.theme || config.color);

  if (!colorConfig.length) {
    return null;
  }

  return (
    <style
      dangerouslySetInnerHTML={{
        __html: Object.entries(THEMES)
          .map(
            ([theme, prefix]) => `
${prefix} [data-chart=${id}] {
${colorConfig
  .map(([key, itemConfig]) => {
    const color = itemConfig.theme?.[theme as keyof typeof itemConfig.theme] || itemConfig.color;
    return color ? `  --color-${key}: ${color};` : null;
  })
  .join('\n')}
}
`
          )
          .join('\n'),
      }}
    />
  );
};

const ChartTooltip = RechartsPrimitive.Tooltip;

function ChartTooltipContent({
  active,
  payload,
  className,
  indicator = 'dot',
  hideLabel = false,
  hideIndicator = false,
  label,
  labelFormatter,
  labelClassName,
  formatter,
  color,
  nameKey,
  labelKey,
}: React.ComponentProps<'div'> & {
    active?: boolean;
    payload?: TooltipPayloadItem<TooltipValueType, TooltipNameType>[];
    label?: React.ReactNode;
    labelFormatter?: (label: React.ReactNode, payload: TooltipPayloadItem<TooltipValueType, TooltipNameType>[]) => React.ReactNode;
    labelClassName?: string;
    hideLabel?: boolean;
    hideIndicator?: boolean;
    indicator?: 'line' | 'dot' | 'dashed';
    nameKey?: string;
    labelKey?: string;
    color?: string;
    formatter?: RechartsPrimitive.DefaultTooltipContentProps<TooltipValueType, TooltipNameType>['formatter'];
  }) {
  const { config } = useChart();

  const tooltipLabel = React.useMemo(() => {
    if (hideLabel || !payload?.length) {
      return null;
    }

    const [item] = payload;
    const key = `${labelKey || item?.dataKey || item?.name || 'value'}`;
    const itemConfig = getPayloadConfigFromPayload(config, item, key);
    const value =
      !labelKey && typeof label === 'string'
        ? config[label as keyof typeof config]?.label || label
        : itemConfig?.label;

    if (labelFormatter) {
      return (
        <div className={cn('font-medium', labelClassName)}>{labelFormatter(value, payload)}</div>
      );
    }

    if (!value) {
      return null;
    }

    return <div className={cn('font-medium', labelClassName)}>{value}</div>;
  }, [label, labelFormatter, payload, hideLabel, labelClassName, config, labelKey]);

  if (!active || !payload?.length) {
    return null;
  }

  const nestLabel = payload.length === 1 && indicator !== 'dot';

  return (
    <div
      className={cn(
        'border-border/50 bg-background/95 backdrop-blur-sm grid min-w-[8rem] items-start gap-1.5 rounded-xl border px-3 py-2 text-xs shadow-xl',
        className
      )}
    >
      {!nestLabel ? tooltipLabel : null}
      <div className="grid gap-1.5">
        {payload.map((item: TooltipPayloadItem<TooltipValueType, TooltipNameType>, index: number) => {
          const key = `${nameKey || item.name || item.dataKey || 'value'}`;
          const itemConfig = getPayloadConfigFromPayload(config, item, key);
          const indicatorColor = color || item.payload?.fill || item.color;

          return (
            <div
              key={`${item.dataKey ?? index}`}
              className={cn(
                '[&>svg]:text-muted-foreground flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5',
                indicator === 'dot' && 'items-center'
              )}
            >
              {formatter && item?.value !== undefined && item.name ? (
                formatter(item.value, item.name, item, index, item.payload)
              ) : (
                <>
                  {itemConfig?.icon ? (
                    <itemConfig.icon />
                  ) : (
                    !hideIndicator && (
                      <div
                        className={cn(
                          'shrink-0 rounded-[2px] border-(--color-border) bg-(--color-bg)',
                          {
                            'h-2.5 w-2.5': indicator === 'dot',
                            'w-1': indicator === 'line',
                            'w-0 border-[1.5px] border-dashed bg-transparent':
                              indicator === 'dashed',
                            'my-0.5': nestLabel && indicator === 'dashed',
                          }
                        )}
                        style={
                          {
                            '--color-bg': indicatorColor,
                            '--color-border': indicatorColor,
                          } as React.CSSProperties
                        }
                      />
                    )
                  )}
                  <div
                    className={cn(
                      'flex flex-1 justify-between leading-none',
                      nestLabel ? 'items-end' : 'items-center'
                    )}
                  >
                    <div className="grid gap-1.5">
                      {nestLabel ? tooltipLabel : null}
                      <span className="text-muted-foreground">
                        {itemConfig?.label || item.name}
                      </span>
                    </div>
                    {item.value && (
                      <span className="text-foreground font-mono font-semibold tabular-nums">
                        {item.value.toLocaleString()}
                      </span>
                    )}
                  </div>
                </>
              )}
            </div>
          );
        })}
      </div>
    </div>
  );
}

const ChartLegend = RechartsPrimitive.Legend;

function ChartLegendContent({
  className,
  hideIcon = false,
  payload,
  verticalAlign = 'bottom',
  nameKey,
}: React.ComponentProps<'div'> & {
    payload?: LegendPayloadItem[];
    verticalAlign?: 'top' | 'middle' | 'bottom';
    hideIcon?: boolean;
    nameKey?: string;
  }) {
  const { config } = useChart();

  if (!payload?.length) {
    return null;
  }

  return (
    <div
      className={cn(
        'flex items-center justify-center gap-4',
        verticalAlign === 'top' ? 'pb-3' : 'pt-3',
        className
      )}
    >
      {payload.map(item => {
        const key = `${nameKey ? item.value : item.dataKey || item.value || 'value'}`;
        const itemConfig = getPayloadConfigFromPayload(config, item, key);

        return (
          <div
            key={item.value}
            className={cn(
              '[&>svg]:text-muted-foreground flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3'
            )}
          >
            {itemConfig?.icon && !hideIcon ? (
              <itemConfig.icon />
            ) : (
              <div
                className="h-2 w-2 shrink-0 rounded-full bg-(--color-bg)"
                style={
                  {
                    '--color-bg': item.color || `var(--color-${key})`,
                  } as React.CSSProperties
                }
              />
            )}
            <span className="text-muted-foreground text-xs">{itemConfig?.label || item.value}</span>
          </div>
        );
      })}
    </div>
  );
}

// Helper to extract item config from a payload.
function getPayloadConfigFromPayload(config: ChartConfig, payload: unknown, key: string) {
  if (typeof payload !== 'object' || payload === null) {
    return undefined;
  }

  const payloadPayload =
    'payload' in payload && typeof payload.payload === 'object' && payload.payload !== null
      ? payload.payload
      : undefined;

  let configLabelKey: string = key;

  if (key in payload && typeof payload[key as keyof typeof payload] === 'string') {
    configLabelKey = payload[key as keyof typeof payload] as string;
  } else if (
    payloadPayload &&
    key in payloadPayload &&
    typeof payloadPayload[key as keyof typeof payloadPayload] === 'string'
  ) {
    configLabelKey = payloadPayload[key as keyof typeof payloadPayload] as string;
  }

  return configLabelKey in config ? config[configLabelKey] : config[key as keyof typeof config];
}

function getChartSeries(
  config: ChartConfig,
  series?: DashboardChartSeries[]
): DashboardChartSeries[] {
  if (series?.length) {
    return series;
  }

  return Object.entries(config).map(([key, item]) => ({
    key,
    label: item.label,
  }));
}

function getSeriesColor(key: string, index: number, colors?: DashboardChartColors) {
  if (Array.isArray(colors)) {
    return colors[index] || defaultChartColors[index % defaultChartColors.length];
  }

  return colors?.[key] || defaultChartColors[index % defaultChartColors.length];
}

function getChartConfigWithColors(
  config: ChartConfig,
  keys: string[],
  colors?: DashboardChartColors
): ChartConfig {
  return keys.reduce<ChartConfig>((nextConfig, key, index) => {
    const item = config[key];

    if (item?.theme && !colors) {
      nextConfig[key] = item;
      return nextConfig;
    }

    nextConfig[key] = {
      label: item?.label || key,
      icon: item?.icon,
      color: colors
        ? getSeriesColor(key, index, colors)
        : item?.color || getSeriesColor(key, index),
    };

    return nextConfig;
  }, {});
}

/** Build a ChartConfig from a list of keys (no pre-existing config needed). */
function buildChartConfig(keys: string[], colors?: DashboardChartColors): ChartConfig {
  return keys.reduce<ChartConfig>((cfg, key, index) => {
    cfg[key] = {
      label: key,
      color: getSeriesColor(key, index, colors),
    };
    return cfg;
  }, {});
}

function formatTick(value: number | string) {
  if (typeof value !== 'number') {
    return value;
  }

  return Intl.NumberFormat('en', {
    notation: 'compact',
    maximumFractionDigits: 1,
  }).format(value);
}

function defaultFilterData(data: DashboardChartDatum[], period: string) {
  const match = period.match(/^(\d+)/);

  if (!match) {
    return data;
  }

  const limit = Number(match[1]);
  return data.slice(Math.max(data.length - limit, 0));
}

function getErrorDescription(error: ChartErrorState) {
  if (typeof error === 'string') {
    return error;
  }

  if (error instanceof Error) {
    return error.message;
  }

  return undefined;
}

function getBarSize(barSize: ChartBarSize = 'md') {
  return typeof barSize === 'number' ? barSize : chartBarSizes[barSize];
}

function hasChartData(data: DashboardChartDatum[], series: DashboardChartSeries[]) {
  if (!data.length || !series.length) {
    return false;
  }

  return data.some(item =>
    series.some(serie => {
      const value = item[serie.key];
      return value !== null && value !== undefined && value !== '';
    })
  );
}

function hasPieData(data: DashboardChartDatum[], nameKey: string, valueKey: string) {
  return data.some(item => {
    const name = item[nameKey];
    const value = item[valueKey];
    return (
      name !== null &&
      name !== undefined &&
      name !== '' &&
      value !== null &&
      value !== undefined &&
      value !== ''
    );
  });
}

function ChartState({
  type,
  className,
  error,
  onRetry,
  retryLabel = 'Try again',
  emptyTitle = 'No data available',
  emptyDescription = 'There is no data available for this chart yet.',
  errorTitle = 'Connection error',
  errorDescription,
  loadingLabel = 'Loading chart data',
}: ChartStateProps & {
  type: 'empty' | 'error' | 'loading';
  className?: string;
}) {
  if (type === 'loading') {
    return (
      <div
        className={cn(
          'flex min-h-[240px] flex-col justify-end gap-3 rounded-[var(--radius-card)] border border-border p-6',
          className
        )}
        aria-label={typeof loadingLabel === 'string' ? loadingLabel : undefined}
      >
        <Skeleton className="h-8 w-2/5" />
        <Skeleton className="h-14 w-3/5" />
        <Skeleton className="h-24 w-4/5" />
        <Skeleton className="h-36 w-full" />
      </div>
    );
  }

  if (type === 'error') {
    return (
      <div
        className={cn(
          'flex min-h-[240px] items-center justify-center rounded-[var(--radius-card)] border border-border p-6',
          className
        )}
      >
        <Alert variant="destructive" className="max-w-xl">
          <AlertTitle>{errorTitle}</AlertTitle>
          <AlertDescription>
            {errorDescription ||
              getErrorDescription(error) ||
              'Unable to load chart data. Check your connection and try again.'}
          </AlertDescription>
          {onRetry ? (
            <div className="mt-3">
              <Button size="sm" variant="outline" onClick={onRetry}>
                <RefreshCw className="size-4" />
                {retryLabel}
              </Button>
            </div>
          ) : null}
        </Alert>
      </div>
    );
  }

  return (
    <Empty className={cn('min-h-[240px]', className)}>
      <EmptyIcon>
        <BarChart3 className="size-10 text-muted-foreground" />
      </EmptyIcon>
      <EmptyTitle>{emptyTitle}</EmptyTitle>
      <EmptyDescription>{emptyDescription}</EmptyDescription>
      {onRetry ? (
        <EmptyAction>
          <Button size="sm" variant="outline" onClick={onRetry}>
            <WifiOff className="size-4" />
            {retryLabel}
          </Button>
        </EmptyAction>
      ) : null}
    </Empty>
  );
}

function getChartState(state: ChartStateProps, hasData: boolean, className?: string) {
  if (state.isLoading) {
    return <ChartState {...state} type="loading" className={className} />;
  }

  if (state.error) {
    return <ChartState {...state} type="error" className={className} />;
  }

  if (!hasData) {
    return <ChartState {...state} type="empty" className={className} />;
  }

  return null;
}

export interface ChartCardProps extends Omit<React.ComponentProps<typeof Card>, 'title'> {
  title: React.ReactNode;
  description?: React.ReactNode;
  action?: React.ReactNode;
  footer?: React.ReactNode;
  contentClassName?: string;
}

function ChartCard({
  title,
  description,
  action,
  footer,
  children,
  className,
  contentClassName,
  ...props
}: ChartCardProps) {
  return (
    <Card className={cn('w-full min-w-0 overflow-hidden', className)} {...props}>
      <CardHeader>
        <CardTitle>{title}</CardTitle>
        {description ? <CardDescription>{description}</CardDescription> : null}
        {action ? <CardAction>{action}</CardAction> : null}
      </CardHeader>
      <CardContent className={contentClassName}>{children}</CardContent>
      {footer ? <CardFooter>{footer}</CardFooter> : null}
    </Card>
  );
}

// ─── Gradient defs helper ────────────────────────────────────────────────────

/**
 * Renders SVG `<defs>` with linear gradients for each series key.
 * Used internally by area charts when `gradientFill` is enabled.
 */
function AreaGradientDefs({
  seriesKeys,
  opacity = 0.3,
}: {
  seriesKeys: string[];
  opacity?: number;
}) {
  return (
    <defs>
      {seriesKeys.map(key => (
        <linearGradient key={key} id={`gradient-${key}`} x1="0" y1="0" x2="0" y2="1">
          <stop offset="5%" stopColor={`var(--color-${key})`} stopOpacity={opacity} />
          <stop offset="95%" stopColor={`var(--color-${key})`} stopOpacity={0} />
        </linearGradient>
      ))}
    </defs>
  );
}

// ─── DashboardBarChart ────────────────────────────────────────────────────────

export interface DashboardBarChartProps
  extends Omit<React.ComponentProps<typeof ChartContainer>, 'children'>, ChartStateProps {
  data: DashboardChartDatum[];
  indexKey?: string;
  series?: DashboardChartSeries[];
  colors?: DashboardChartColors;
  barSize?: ChartBarSize;
  stacked?: boolean;
  showGrid?: boolean;
  showLegend?: boolean;
  valueFormatter?: ChartValueFormatter;
}

function DashboardBarChart({
  data,
  config,
  indexKey = 'name',
  series,
  colors,
  barSize = 'md',
  stacked = false,
  showGrid = true,
  showLegend = true,
  valueFormatter = formatTick,
  isLoading,
  error,
  onRetry,
  retryLabel,
  emptyTitle,
  emptyDescription,
  errorTitle,
  errorDescription,
  loadingLabel,
  stateClassName,
  className,
  ...props
}: DashboardBarChartProps) {
  const chartSeries = getChartSeries(config, series);
  const chartConfig = getChartConfigWithColors(
    config,
    chartSeries.map(item => item.key),
    colors
  );
  const chartState = getChartState(
    {
      isLoading,
      error,
      onRetry,
      retryLabel,
      emptyTitle,
      emptyDescription,
      errorTitle,
      errorDescription,
      loadingLabel,
    },
    hasChartData(data, chartSeries),
    cn('h-[320px] w-full', stateClassName)
  );

  const barElements = React.useMemo(() => {
    const topOfStack = new Set<string>();
    if (stacked) {
      const lastByStack = new Map<string, string>();
      chartSeries.forEach(s => lastByStack.set(s.stackId || 'total', s.key));
      lastByStack.forEach(key => topOfStack.add(key));
    }
    return chartSeries.map(item => {
      const isTop = !stacked || topOfStack.has(item.key);
      return (
        <RechartsPrimitive.Bar
          key={item.key}
          dataKey={item.key}
          fill={`var(--color-${item.key})`}
          radius={isTop ? [4, 4, 0, 0] : [0, 0, 0, 0]}
          barSize={getBarSize(barSize)}
          stackId={stacked ? item.stackId || 'total' : item.stackId}
          isAnimationActive
          animationDuration={600}
          animationEasing="ease-out"
        />
      );
    });
  }, [stacked, chartSeries]);

  if (chartState) {
    return chartState;
  }

  return (
    <ChartContainer config={chartConfig} className={cn('h-[320px] w-full', className)} {...props}>
      <RechartsPrimitive.BarChart data={data} accessibilityLayer barGap={4}>
        {showGrid ? (
          <RechartsPrimitive.CartesianGrid
            vertical={false}
            strokeDasharray="3 3"
            stroke="var(--border)"
            strokeOpacity={0.5}
          />
        ) : null}
        <RechartsPrimitive.XAxis
          dataKey={indexKey}
          tickLine={false}
          axisLine={false}
          tickMargin={8}
        />
        <RechartsPrimitive.YAxis
          tickLine={false}
          axisLine={false}
          tickMargin={8}
          tickFormatter={valueFormatter}
        />
        <ChartTooltip
          cursor={{ fill: 'var(--muted)', opacity: 0.4, radius: 4 }}
          content={<ChartTooltipContent />}
        />
        {showLegend ? <ChartLegend content={<ChartLegendContent />} /> : null}
        {barElements}
      </RechartsPrimitive.BarChart>
    </ChartContainer>
  );
}

// ─── DashboardLineChart ───────────────────────────────────────────────────────

export interface DashboardLineChartProps
  extends Omit<React.ComponentProps<typeof ChartContainer>, 'children'>, ChartStateProps {
  data: DashboardChartDatum[];
  indexKey?: string;
  series?: DashboardChartSeries[];
  colors?: DashboardChartColors;
  showDots?: boolean;
  showGrid?: boolean;
  showLegend?: boolean;
  curveType?: ChartCurveType;
  strokeWidth?: number;
  valueFormatter?: ChartValueFormatter;
}

function DashboardLineChart({
  data,
  config,
  indexKey = 'name',
  series,
  colors,
  showDots = false,
  showGrid = true,
  showLegend = true,
  curveType = 'monotone',
  strokeWidth = 2,
  valueFormatter = formatTick,
  isLoading,
  error,
  onRetry,
  retryLabel,
  emptyTitle,
  emptyDescription,
  errorTitle,
  errorDescription,
  loadingLabel,
  stateClassName,
  className,
  ...props
}: DashboardLineChartProps) {
  const chartSeries = getChartSeries(config, series);
  const chartConfig = getChartConfigWithColors(
    config,
    chartSeries.map(item => item.key),
    colors
  );
  const chartState = getChartState(
    {
      isLoading,
      error,
      onRetry,
      retryLabel,
      emptyTitle,
      emptyDescription,
      errorTitle,
      errorDescription,
      loadingLabel,
    },
    hasChartData(data, chartSeries),
    cn('h-[320px] w-full', stateClassName)
  );

  if (chartState) {
    return chartState;
  }

  return (
    <ChartContainer config={chartConfig} className={cn('h-[320px] w-full', className)} {...props}>
      <RechartsPrimitive.LineChart data={data} accessibilityLayer>
        {showGrid ? (
          <RechartsPrimitive.CartesianGrid
            vertical={false}
            strokeDasharray="3 3"
            stroke="var(--border)"
            strokeOpacity={0.5}
          />
        ) : null}
        <RechartsPrimitive.XAxis
          dataKey={indexKey}
          tickLine={false}
          axisLine={false}
          tickMargin={8}
        />
        <RechartsPrimitive.YAxis
          tickLine={false}
          axisLine={false}
          tickMargin={8}
          tickFormatter={valueFormatter}
        />
        <ChartTooltip content={<ChartTooltipContent indicator="line" />} />
        {showLegend ? <ChartLegend content={<ChartLegendContent />} /> : null}
        {chartSeries.map(item => (
          <RechartsPrimitive.Line
            key={item.key}
            type={curveType}
            dataKey={item.key}
            stroke={`var(--color-${item.key})`}
            strokeWidth={strokeWidth}
            dot={showDots ? { r: 3, fill: `var(--color-${item.key})`, strokeWidth: 0 } : false}
            activeDot={{ r: 5, strokeWidth: 0 }}
            yAxisId={item.yAxisId}
            isAnimationActive
            animationDuration={600}
            animationEasing="ease-out"
          />
        ))}
      </RechartsPrimitive.LineChart>
    </ChartContainer>
  );
}

// ─── HorizontalBarChart ───────────────────────────────────────────────────────

export interface HorizontalBarChartProps
  extends Omit<React.ComponentProps<typeof ChartContainer>, 'children'>, ChartStateProps {
  data: DashboardChartDatum[];
  indexKey?: string;
  series?: DashboardChartSeries[];
  colors?: DashboardChartColors;
  barSize?: ChartBarSize;
  stacked?: boolean;
  categoryWidth?: number;
  showGrid?: boolean;
  showLegend?: boolean;
  valueFormatter?: ChartValueFormatter;
}

function HorizontalBarChart({
  data,
  config,
  indexKey = 'name',
  series,
  colors,
  barSize = 'md',
  stacked = false,
  categoryWidth = 96,
  showGrid = true,
  showLegend = true,
  valueFormatter = formatTick,
  isLoading,
  error,
  onRetry,
  retryLabel,
  emptyTitle,
  emptyDescription,
  errorTitle,
  errorDescription,
  loadingLabel,
  stateClassName,
  className,
  ...props
}: HorizontalBarChartProps) {
  const chartSeries = getChartSeries(config, series);
  const chartConfig = getChartConfigWithColors(
    config,
    chartSeries.map(item => item.key),
    colors
  );
  const chartState = getChartState(
    {
      isLoading,
      error,
      onRetry,
      retryLabel,
      emptyTitle,
      emptyDescription,
      errorTitle,
      errorDescription,
      loadingLabel,
    },
    hasChartData(data, chartSeries),
    cn('h-[320px] w-full', stateClassName)
  );

  if (chartState) {
    return chartState;
  }

  return (
    <ChartContainer config={chartConfig} className={cn('h-[320px] w-full', className)} {...props}>
      <RechartsPrimitive.BarChart
        data={data}
        layout="vertical"
        accessibilityLayer
        margin={{ left: 8, right: 16 }}
      >
        {showGrid ? (
          <RechartsPrimitive.CartesianGrid
            horizontal={false}
            strokeDasharray="3 3"
            stroke="var(--border)"
            strokeOpacity={0.5}
          />
        ) : null}
        <RechartsPrimitive.XAxis
          type="number"
          tickLine={false}
          axisLine={false}
          tickMargin={8}
          tickFormatter={valueFormatter}
        />
        <RechartsPrimitive.YAxis
          dataKey={indexKey}
          type="category"
          tickLine={false}
          axisLine={false}
          tickMargin={8}
          width={categoryWidth}
        />
        <ChartTooltip content={<ChartTooltipContent />} />
        {showLegend ? <ChartLegend content={<ChartLegendContent />} /> : null}
        {chartSeries.map(item => (
          <RechartsPrimitive.Bar
            key={item.key}
            dataKey={item.key}
            fill={`var(--color-${item.key})`}
            radius={[0, 4, 4, 0]}
            barSize={getBarSize(barSize)}
            stackId={stacked ? item.stackId || 'total' : item.stackId}
            isAnimationActive
            animationDuration={600}
            animationEasing="ease-out"
          />
        ))}
      </RechartsPrimitive.BarChart>
    </ChartContainer>
  );
}

// ─── InteractiveTimeSeriesChart ───────────────────────────────────────────────

export interface InteractiveTimeSeriesChartProps
  extends Omit<React.ComponentProps<typeof ChartContainer>, 'children'>, ChartStateProps {
  data: DashboardChartDatum[];
  indexKey?: string;
  series?: DashboardChartSeries[];
  colors?: DashboardChartColors;
  periods?: ChartPeriod[];
  defaultPeriod?: string;
  defaultSeries?: string;
  filterData?: (data: DashboardChartDatum[], period: string) => DashboardChartDatum[];
  showGrid?: boolean;
  showLegend?: boolean;
  showDots?: boolean;
  gradientFill?: boolean;
  curveType?: ChartCurveType;
  strokeWidth?: number;
  valueFormatter?: ChartValueFormatter;
}

function InteractiveTimeSeriesChart({
  data,
  config,
  indexKey = 'date',
  series,
  colors,
  periods = defaultPeriods,
  defaultPeriod = periods[1]?.value || periods[0]?.value || '30d',
  defaultSeries,
  filterData = defaultFilterData,
  showGrid = true,
  showLegend = false,
  showDots = false,
  gradientFill = true,
  curveType = 'monotone',
  strokeWidth = 2,
  valueFormatter = formatTick,
  isLoading,
  error,
  onRetry,
  retryLabel,
  emptyTitle,
  emptyDescription,
  errorTitle,
  errorDescription,
  loadingLabel,
  stateClassName,
  className,
  ...props
}: InteractiveTimeSeriesChartProps) {
  const chartSeries = getChartSeries(config, series);
  const chartConfig = getChartConfigWithColors(
    config,
    chartSeries.map(item => item.key),
    colors
  );
  const [period, setPeriod] = React.useState(defaultPeriod);
  const [activeSeries, setActiveSeries] = React.useState(
    defaultSeries || chartSeries[0]?.key || ''
  );
  const filteredData = React.useMemo(() => filterData(data, period), [data, filterData, period]);
  const visibleSeries =
    chartSeries.length > 1 ? chartSeries.filter(item => item.key === activeSeries) : chartSeries;
  const chartState = getChartState(
    {
      isLoading,
      error,
      onRetry,
      retryLabel,
      emptyTitle,
      emptyDescription,
      errorTitle,
      errorDescription,
      loadingLabel,
    },
    hasChartData(filteredData, visibleSeries),
    cn('h-[320px] w-full', stateClassName)
  );

  return (
    <div className="w-full space-y-4">
      <div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
        {chartSeries.length > 1 ? (
          <div
            className="inline-flex h-10 items-center justify-center rounded-lg bg-muted p-1 text-muted-foreground"
            role="group"
            aria-label="Selecionar metrica do grafico"
          >
            {chartSeries.map(item => {
              const label = item.label || config[item.key]?.label || item.key;

              return (
                <Button
                  key={item.key}
                  type="button"
                  variant="ghost"
                  size="sm"
                  aria-pressed={activeSeries === item.key}
                  onClick={() => setActiveSeries(item.key)}
                  className={cn(
                    'h-8 px-3 text-sm font-medium transition-all',
                    activeSeries === item.key
                      ? 'bg-background text-foreground shadow-sm'
                      : 'text-muted-foreground hover:text-foreground'
                  )}
                >
                  {label}
                </Button>
              );
            })}
          </div>
        ) : (
          <div />
        )}
        <Select value={period} onValueChange={setPeriod}>
          <SelectTrigger
            className="w-full sm:w-[160px]"
            size="sm"
            aria-label="Selecionar periodo do grafico"
          >
            <SelectValue placeholder="Period" />
          </SelectTrigger>
          <SelectContent>
            {periods.map(item => (
              <SelectItem key={item.value} value={item.value}>
                {item.label}
              </SelectItem>
            ))}
          </SelectContent>
        </Select>
      </div>
      {chartState || (
        <ChartContainer
          config={chartConfig}
          className={cn('h-[320px] w-full', className)}
          {...props}
        >
          <RechartsPrimitive.AreaChart data={filteredData} accessibilityLayer>
            {gradientFill && (
              <AreaGradientDefs seriesKeys={visibleSeries.map(s => s.key)} opacity={0.4} />
            )}
            {showGrid ? (
              <RechartsPrimitive.CartesianGrid
                vertical={false}
                strokeDasharray="3 3"
                stroke="var(--border)"
                strokeOpacity={0.5}
              />
            ) : null}
            <RechartsPrimitive.XAxis
              dataKey={indexKey}
              tickLine={false}
              axisLine={false}
              tickMargin={8}
            />
            <RechartsPrimitive.YAxis
              tickLine={false}
              axisLine={false}
              tickMargin={8}
              tickFormatter={valueFormatter}
            />
            <ChartTooltip content={<ChartTooltipContent indicator="line" />} />
            {showLegend ? <ChartLegend content={<ChartLegendContent />} /> : null}
            {visibleSeries.map(item => (
              <RechartsPrimitive.Area
                key={item.key}
                type={curveType}
                dataKey={item.key}
                stroke={`var(--color-${item.key})`}
                fill={gradientFill ? `url(#gradient-${item.key})` : `var(--color-${item.key})`}
                fillOpacity={gradientFill ? 1 : 0.16}
                strokeWidth={strokeWidth}
                dot={showDots ? { r: 3, fill: `var(--color-${item.key})`, strokeWidth: 0 } : false}
                activeDot={{ r: 5, strokeWidth: 0 }}
                isAnimationActive
                animationDuration={600}
                animationEasing="ease-out"
              />
            ))}
          </RechartsPrimitive.AreaChart>
        </ChartContainer>
      )}
    </div>
  );
}

// ─── ComboMetricChart ─────────────────────────────────────────────────────────

export interface ComboMetricChartProps
  extends Omit<React.ComponentProps<typeof ChartContainer>, 'children'>, ChartStateProps {
  data: DashboardChartDatum[];
  indexKey?: string;
  series?: DashboardChartSeries[];
  colors?: DashboardChartColors;
  barSize?: ChartBarSize;
  showGrid?: boolean;
  showLegend?: boolean;
  gradientFill?: boolean;
  curveType?: ChartCurveType;
  valueFormatter?: ChartValueFormatter;
}

function ComboMetricChart({
  data,
  config,
  indexKey = 'name',
  series,
  colors,
  barSize = 'md',
  showGrid = true,
  showLegend = true,
  gradientFill = false,
  curveType = 'monotone',
  valueFormatter = formatTick,
  isLoading,
  error,
  onRetry,
  retryLabel,
  emptyTitle,
  emptyDescription,
  errorTitle,
  errorDescription,
  loadingLabel,
  stateClassName,
  className,
  ...props
}: ComboMetricChartProps) {
  const chartSeries = getChartSeries(config, series);
  const chartConfig = getChartConfigWithColors(
    config,
    chartSeries.map(item => item.key),
    colors
  );
  const chartState = getChartState(
    {
      isLoading,
      error,
      onRetry,
      retryLabel,
      emptyTitle,
      emptyDescription,
      errorTitle,
      errorDescription,
      loadingLabel,
    },
    hasChartData(data, chartSeries),
    cn('h-[340px] w-full', stateClassName)
  );

  if (chartState) {
    return chartState;
  }

  const areaSeries = gradientFill ? chartSeries.filter(s => s.type === 'area').map(s => s.key) : [];

  return (
    <ChartContainer config={chartConfig} className={cn('h-[340px] w-full', className)} {...props}>
      <RechartsPrimitive.ComposedChart data={data} accessibilityLayer>
        {gradientFill && areaSeries.length > 0 && (
          <AreaGradientDefs seriesKeys={areaSeries} opacity={0.35} />
        )}
        {showGrid ? (
          <RechartsPrimitive.CartesianGrid
            vertical={false}
            strokeDasharray="3 3"
            stroke="var(--border)"
            strokeOpacity={0.5}
          />
        ) : null}
        <RechartsPrimitive.XAxis
          dataKey={indexKey}
          tickLine={false}
          axisLine={false}
          tickMargin={8}
        />
        <RechartsPrimitive.YAxis
          tickLine={false}
          axisLine={false}
          tickMargin={8}
          tickFormatter={valueFormatter}
        />
        <ChartTooltip content={<ChartTooltipContent />} />
        {showLegend ? <ChartLegend content={<ChartLegendContent />} /> : null}
        {chartSeries.map(item =>
          item.type === 'line' ? (
            <RechartsPrimitive.Line
              key={item.key}
              type={curveType}
              dataKey={item.key}
              stroke={`var(--color-${item.key})`}
              strokeWidth={2}
              dot={false}
              activeDot={{ r: 5, strokeWidth: 0 }}
              yAxisId={item.yAxisId}
              isAnimationActive
              animationDuration={600}
              animationEasing="ease-out"
            />
          ) : item.type === 'area' ? (
            <RechartsPrimitive.Area
              key={item.key}
              type={curveType}
              dataKey={item.key}
              stroke={`var(--color-${item.key})`}
              fill={gradientFill ? `url(#gradient-${item.key})` : `var(--color-${item.key})`}
              fillOpacity={gradientFill ? 1 : 0.12}
              strokeWidth={2}
              dot={false}
              activeDot={{ r: 5, strokeWidth: 0 }}
              yAxisId={item.yAxisId}
              isAnimationActive
              animationDuration={600}
              animationEasing="ease-out"
            />
          ) : (
            <RechartsPrimitive.Bar
              key={item.key}
              dataKey={item.key}
              fill={`var(--color-${item.key})`}
              radius={[4, 4, 0, 0]}
              barSize={getBarSize(barSize)}
              yAxisId={item.yAxisId}
              isAnimationActive
              animationDuration={600}
              animationEasing="ease-out"
            />
          )
        )}
      </RechartsPrimitive.ComposedChart>
    </ChartContainer>
  );
}

// ─── DonutBreakdownChart ──────────────────────────────────────────────────────

export interface DonutBreakdownChartProps
  extends Omit<React.ComponentProps<typeof ChartContainer>, 'children'>, ChartStateProps {
  data: DashboardChartDatum[];
  nameKey?: string;
  valueKey?: string;
  colors?: DashboardChartColors;
  centerLabel?: React.ReactNode;
  centerValue?: React.ReactNode;
  showLegend?: boolean;
  /** Inner radius as percentage string (e.g. "58%") or number */
  innerRadius?: string | number;
  /** Outer radius as percentage string (e.g. "82%") or number */
  outerRadius?: string | number;
}

function DonutBreakdownChart({
  data,
  config,
  nameKey = 'name',
  valueKey = 'value',
  colors,
  centerLabel,
  centerValue,
  showLegend = true,
  innerRadius = '58%',
  outerRadius = '82%',
  isLoading,
  error,
  onRetry,
  retryLabel,
  emptyTitle,
  emptyDescription,
  errorTitle,
  errorDescription,
  loadingLabel,
  stateClassName,
  className,
  ...props
}: DonutBreakdownChartProps) {
  const [activeIndex, setActiveIndex] = React.useState(0);
  const chartKeys = data.map((entry, index) => String(entry[nameKey] || `segment-${index}`));
  const chartConfig = getChartConfigWithColors(config, chartKeys, colors);
  const chartState = getChartState(
    {
      isLoading,
      error,
      onRetry,
      retryLabel,
      emptyTitle,
      emptyDescription,
      errorTitle,
      errorDescription,
      loadingLabel,
    },
    hasPieData(data, nameKey, valueKey),
    cn('h-[320px] w-full', stateClassName)
  );

  if (chartState) {
    return chartState;
  }

  return (
    <ChartContainer config={chartConfig} className={cn('h-[320px] w-full', className)} {...props}>
      <RechartsPrimitive.PieChart accessibilityLayer>
        <ChartTooltip
          cursor={false}
          content={<ChartTooltipContent hideLabel nameKey={nameKey} />}
        />
        {showLegend ? (
          <ChartLegend content={<ChartLegendContent nameKey={nameKey} />} verticalAlign="bottom" />
        ) : null}
        <RechartsPrimitive.Pie
          data={data}
          dataKey={valueKey}
          nameKey={nameKey}
          innerRadius={innerRadius}
          outerRadius={outerRadius}
          paddingAngle={3}
          onMouseEnter={(_, index) => setActiveIndex(index)}
          isAnimationActive
          animationDuration={600}
          animationEasing="ease-out"
        >
          {data.map((entry, index) => {
            const key = String(entry[nameKey] || `segment-${index}`);

            return (
              <RechartsPrimitive.Cell
                key={key}
                fill={`var(--color-${key})`}
                opacity={index === activeIndex ? 1 : 0.7}
                stroke="transparent"
              />
            );
          })}
          {centerValue || centerLabel ? (
            <RechartsPrimitive.Label
              position="center"
              content={({ viewBox }) => {
                if (!viewBox || !('cx' in viewBox) || !('cy' in viewBox)) {
                  return null;
                }

                return (
                  <text x={viewBox.cx} y={viewBox.cy} textAnchor="middle" dominantBaseline="middle">
                    {centerValue ? (
                      <tspan
                        x={viewBox.cx}
                        y={viewBox.cy}
                        className="fill-foreground text-2xl font-semibold"
                      >
                        {centerValue}
                      </tspan>
                    ) : null}
                    {centerLabel ? (
                      <tspan
                        x={viewBox.cx}
                        y={Number(viewBox.cy) + 22}
                        className="fill-muted-foreground text-xs"
                      >
                        {centerLabel}
                      </tspan>
                    ) : null}
                  </text>
                );
              }}
            />
          ) : null}
        </RechartsPrimitive.Pie>
      </RechartsPrimitive.PieChart>
    </ChartContainer>
  );
}

// ─── SparklineChart ───────────────────────────────────────────────────────────

export interface SparklineChartProps {
  /** Data array — each item must have the `dataKey` field */
  data: DashboardChartDatum[];
  /** Key to plot on the Y axis */
  dataKey: string;
  /** Color for the line/area — defaults to `var(--chart-1)` */
  color?: string;
  /** Show filled area under the line */
  filled?: boolean;
  /** Show the last data point as a dot */
  showEndDot?: boolean;
  /** Curve interpolation type */
  curveType?: ChartCurveType;
  /** Stroke width */
  strokeWidth?: number;
  className?: string;
}

/**
 * Minimal inline sparkline chart — no axes, no grid, no tooltip.
 * Ideal for embedding inside stat cards or table cells.
 *
 * @ai-rules
 * - Use `filled` for area-style sparklines.
 * - Keep `className` height small (e.g. `h-12` or `h-16`).
 * - Do NOT wrap in `ChartCard` — it is designed to be inline.
 */
function SparklineChart({
  data,
  dataKey,
  color = 'var(--chart-1)',
  filled = true,
  showEndDot = true,
  curveType = 'monotone',
  strokeWidth = 2,
  className,
}: SparklineChartProps) {
  const sparkId = React.useId().replace(/:/g, '');
  const lastPoint = data[data.length - 1];
  const lastValue = lastPoint ? lastPoint[dataKey] : undefined;

  return (
    <div className={cn('relative h-12 w-full min-w-0', className)}>
      <RechartsPrimitive.ResponsiveContainer width="100%" height="100%">
        <RechartsPrimitive.AreaChart
          data={data}
          margin={{ top: 4, right: showEndDot ? 8 : 0, bottom: 0, left: 0 }}
        >
          {filled && (
            <defs>
              <linearGradient id={`spark-gradient-${sparkId}`} x1="0" y1="0" x2="0" y2="1">
                <stop offset="5%" stopColor={color} stopOpacity={0.3} />
                <stop offset="95%" stopColor={color} stopOpacity={0} />
              </linearGradient>
            </defs>
          )}
          <RechartsPrimitive.Area
            type={curveType}
            dataKey={dataKey}
            stroke={color}
            strokeWidth={strokeWidth}
            fill={filled ? `url(#spark-gradient-${sparkId})` : 'none'}
            fillOpacity={1}
            dot={false}
            activeDot={false}
            isAnimationActive
            animationDuration={600}
            animationEasing="ease-out"
          />
        </RechartsPrimitive.AreaChart>
      </RechartsPrimitive.ResponsiveContainer>
      {showEndDot && lastValue !== undefined && lastValue !== null && (
        <div
          className="pointer-events-none absolute right-0 top-1/2 h-2.5 w-2.5 -translate-y-1/2 rounded-full ring-2 ring-background"
          style={{ backgroundColor: color }}
        />
      )}
    </div>
  );
}

// ─── RadarChart ───────────────────────────────────────────────────────────────

export interface RadarMetricChartProps extends ChartStateProps {
  /** Data array — each item is one axis point on the radar */
  data: DashboardChartDatum[];
  /** Key in each datum used as the axis label */
  labelKey: string;
  /**
   * Series to render. Each entry maps to one `<Radar>` element.
   * Use a single entry for a simple radar, multiple for comparison.
   */
  series: DashboardChartSeries[];
  /** Override colors per series key or as an ordered array */
  colors?: DashboardChartColors;
  /** Fill the radar polygon (default: true) */
  filled?: boolean;
  /** Fill opacity when `filled` is true (default: 0.25) */
  fillOpacity?: number;
  /** Show dots on each axis point (default: false) */
  showDots?: boolean;
  /** Show the polar grid lines (default: true) */
  showGrid?: boolean;
  /** Show the legend (default: true when multiple series) */
  showLegend?: boolean;
  /** Format axis tick values */
  valueFormatter?: ChartValueFormatter;
  className?: string;
}

function RadarMetricChart({
  data,
  labelKey,
  series,
  colors,
  filled = true,
  fillOpacity = 0.25,
  showDots = false,
  showGrid = true,
  showLegend,
  valueFormatter,
  isLoading,
  error,
  onRetry,
  retryLabel,
  emptyTitle,
  emptyDescription,
  errorTitle,
  errorDescription,
  loadingLabel,
  stateClassName,
  className,
}: RadarMetricChartProps) {
  const chartConfig = React.useMemo(
    () =>
      buildChartConfig(
        series.map(s => s.key),
        colors
      ),
    [series, colors]
  );

  const hasData = data.length > 0 && series.length > 0;
  const chartState = getChartState(
    {
      isLoading,
      error,
      emptyTitle,
      emptyDescription,
      errorTitle,
      errorDescription,
      loadingLabel,
      stateClassName,
      onRetry,
      retryLabel,
    },
    hasData
  );

  const displayLegend = showLegend ?? series.length > 1;

  return (
    <ChartContainer config={chartConfig} className={cn('h-[300px] w-full', className)}>
      {chartState ?? (
        <RechartsPrimitive.RadarChart data={data} accessibilityLayer>
          {showGrid && <RechartsPrimitive.PolarGrid stroke="var(--border)" strokeOpacity={0.6} />}
          <RechartsPrimitive.PolarAngleAxis
            dataKey={labelKey}
            tick={{ fill: 'var(--muted-foreground)', fontSize: 12 }}
          />
          <RechartsPrimitive.PolarRadiusAxis
            tick={false}
            axisLine={false}
            tickFormatter={valueFormatter}
          />
          <ChartTooltip
            content={
              <ChartTooltipContent
                formatter={valueFormatter ? (v: TooltipValueType) => valueFormatter(v as number) : undefined}
              />
            }
          />
          {displayLegend && <ChartLegend content={<ChartLegendContent />} />}
          {series.map(s => (
            <RechartsPrimitive.Radar
              key={s.key}
              name={(s.label as string) ?? s.key}
              dataKey={s.key}
              stroke={`var(--color-${s.key})`}
              fill={filled ? `var(--color-${s.key})` : 'none'}
              fillOpacity={filled ? fillOpacity : 0}
              dot={showDots ? { r: 3, fill: `var(--color-${s.key})` } : false}
              isAnimationActive
              animationDuration={600}
              animationEasing="ease-out"
            />
          ))}
        </RechartsPrimitive.RadarChart>
      )}
    </ChartContainer>
  );
}

// ─── PieMetricChart ───────────────────────────────────────────────────────────

export interface PieMetricChartProps extends ChartStateProps {
  /** Data array — each item is one slice */
  data: DashboardChartDatum[];
  /** Key in each datum used as the slice name/label */
  nameKey: string;
  /** Key in each datum used as the slice value */
  valueKey: string;
  /** Override colors as an ordered array or per-name map */
  colors?: DashboardChartColors;
  /** Outer radius of the pie (default: "80%") */
  outerRadius?: number | string;
  /** Inner radius — set > 0 to make a donut (default: 0) */
  innerRadius?: number | string;
  /** Show percentage labels inside/outside each slice (default: false) */
  showLabels?: boolean;
  /** Show the legend (default: true) */
  showLegend?: boolean;
  /**
   * Index of the slice to "explode" (offset outward).
   * Pass -1 or undefined to disable.
   */
  explodeIndex?: number;
  /** Offset distance for the exploded slice in px (default: 12) */
  explodeOffset?: number;
  /** Format tooltip values */
  valueFormatter?: ChartValueFormatter;
  className?: string;
}

function PieMetricChart({
  data,
  nameKey,
  valueKey,
  colors,
  outerRadius = '80%',
  innerRadius = 0,
  showLabels = false,
  showLegend = true,
  explodeIndex,
  explodeOffset = 12,
  valueFormatter,
  isLoading,
  error,
  onRetry,
  retryLabel,
  emptyTitle,
  emptyDescription,
  errorTitle,
  errorDescription,
  loadingLabel,
  stateClassName,
  className,
}: PieMetricChartProps) {
  // Build config from unique name values
  const names = React.useMemo(() => data.map(d => String(d[nameKey] ?? '')), [data, nameKey]);

  const chartConfig = React.useMemo(() => buildChartConfig(names, colors), [names, colors]);

  const hasData = hasPieData(data, nameKey, valueKey);
  const chartState = getChartState(
    {
      isLoading,
      error,
      emptyTitle,
      emptyDescription,
      errorTitle,
      errorDescription,
      loadingLabel,
      stateClassName,
      onRetry,
      retryLabel,
    },
    hasData
  );

  return (
    <ChartContainer config={chartConfig} className={cn('h-[300px] w-full', className)}>
      {chartState ?? (
        <RechartsPrimitive.PieChart accessibilityLayer>
          <ChartTooltip
            content={
              <ChartTooltipContent
                nameKey={nameKey}
                formatter={valueFormatter ? (v: TooltipValueType) => valueFormatter(v as number) : undefined}
              />
            }
          />
          {showLegend && <ChartLegend content={<ChartLegendContent nameKey={nameKey} />} />}
          <RechartsPrimitive.Pie
            data={data}
            dataKey={valueKey}
            nameKey={nameKey}
            outerRadius={outerRadius}
            innerRadius={innerRadius}
            paddingAngle={2}
            label={
              showLabels
                ? ({ cx, cy, midAngle, innerRadius: ir, outerRadius: or, percent }) => {
                    if (midAngle == null || percent == null) return null;
                    const RADIAN = Math.PI / 180;
                    const radius = Number(ir) + (Number(or) - Number(ir)) * 1.35;
                    const x = Number(cx) + radius * Math.cos(-midAngle * RADIAN);
                    const y = Number(cy) + radius * Math.sin(-midAngle * RADIAN);
                    return percent > 0.04 ? (
                      <text
                        x={x}
                        y={y}
                        fill="var(--foreground)"
                        textAnchor={x > Number(cx) ? 'start' : 'end'}
                        dominantBaseline="central"
                        fontSize={12}
                        fontWeight={500}
                      >
                        {`${(percent * 100).toFixed(0)}%`}
                      </text>
                    ) : null;
                  }
                : false
            }
            labelLine={false}
            isAnimationActive
            animationDuration={600}
            animationEasing="ease-out"
          >
            {data.map((entry, index) => {
              const name = String(entry[nameKey] ?? '');
              const isExploded = index === explodeIndex;
              return (
                <RechartsPrimitive.Cell
                  key={`cell-${index}`}
                  fill={chartConfig[name]?.color ?? `var(--chart-${(index % 8) + 1})`}
                  stroke="var(--background)"
                  strokeWidth={2}
                  style={
                    isExploded
                      ? {
                          filter: `drop-shadow(0 4px 8px color-mix(in srgb, ${chartConfig[name]?.color ?? 'var(--chart-1)'} 40%, transparent))`,
                        }
                      : undefined
                  }
                />
              );
            })}
          </RechartsPrimitive.Pie>
        </RechartsPrimitive.PieChart>
      )}
    </ChartContainer>
  );
}

// ─── RadialBarMetricChart ─────────────────────────────────────────────────────

export interface RadialBarMetricChartProps extends ChartStateProps {
  /**
   * Data array. Each item should have a `name` field (for labels) and
   * the `dataKey` field (for values, 0–100 for percentage-based display).
   */
  data: DashboardChartDatum[];
  /** Key in each datum used as the bar value (default: "value") */
  dataKey?: string;
  /** Key in each datum used as the bar label (default: "name") */
  nameKey?: string;
  /** Override colors as an ordered array or per-name map */
  colors?: DashboardChartColors;
  /** Inner radius of the radial bar (default: "30%") */
  innerRadius?: number | string;
  /** Outer radius of the radial bar (default: "100%") */
  outerRadius?: number | string;
  /** Start angle in degrees (default: 90 — top) */
  startAngle?: number;
  /** End angle in degrees (default: -270 — full circle) */
  endAngle?: number;
  /** Show background track behind each bar (default: true) */
  showBackground?: boolean;
  /** Show the legend (default: true) */
  showLegend?: boolean;
  /** Format tooltip values */
  valueFormatter?: ChartValueFormatter;
  className?: string;
}

function RadialBarMetricChart({
  data,
  dataKey = 'value',
  nameKey = 'name',
  colors,
  innerRadius = '30%',
  outerRadius = '100%',
  startAngle = 90,
  endAngle = -270,
  showBackground = true,
  showLegend = true,
  valueFormatter,
  isLoading,
  error,
  onRetry,
  retryLabel,
  emptyTitle,
  emptyDescription,
  errorTitle,
  errorDescription,
  loadingLabel,
  stateClassName,
  className,
}: RadialBarMetricChartProps) {
  const names = React.useMemo(() => data.map(d => String(d[nameKey] ?? '')), [data, nameKey]);

  const chartConfig = React.useMemo(() => buildChartConfig(names, colors), [names, colors]);

  const hasData = data.length > 0;
  const chartState = getChartState(
    {
      isLoading,
      error,
      emptyTitle,
      emptyDescription,
      errorTitle,
      errorDescription,
      loadingLabel,
      stateClassName,
      onRetry,
      retryLabel,
    },
    hasData
  );

  // Inject per-item fill color
  const coloredData = React.useMemo(
    () =>
      data.map((item, index) => {
        const name = String(item[nameKey] ?? '');
        return {
          ...item,
          fill: chartConfig[name]?.color ?? `var(--chart-${(index % 8) + 1})`,
        };
      }),
    [data, nameKey, chartConfig]
  );

  return (
    <div className={cn('flex w-full flex-col gap-3', className)}>
      <ChartContainer config={chartConfig} className="h-[260px] w-full">
        {chartState ?? (
          <RechartsPrimitive.RadialBarChart
            data={coloredData}
            innerRadius={innerRadius}
            outerRadius={outerRadius}
            startAngle={startAngle}
            endAngle={endAngle}
            accessibilityLayer
          >
            <ChartTooltip
              cursor={false}
              content={
                <ChartTooltipContent
                  nameKey={nameKey}
                  formatter={valueFormatter ? (v: TooltipValueType) => valueFormatter(v as number) : undefined}
                />
              }
            />
            <RechartsPrimitive.RadialBar
              dataKey={dataKey}
              background={showBackground ? { fill: 'var(--muted)' } : false}
              cornerRadius={6}
              isAnimationActive
              animationDuration={700}
              animationEasing="ease-out"
            />
          </RechartsPrimitive.RadialBarChart>
        )}
      </ChartContainer>
      {showLegend && !chartState && (
        <div className="flex flex-wrap justify-center gap-x-4 gap-y-1.5 px-2">
          {coloredData.map((item, index) => {
            const d = item as DashboardChartDatum & { fill: string };
            const name = String(d[nameKey] ?? '');
            const fillColor = d.fill ?? `var(--chart-${(index % 8) + 1})`;
            const val = d[dataKey];
            return (
              <div key={name} className="flex items-center gap-1.5">
                <span
                  className="inline-block h-2.5 w-2.5 shrink-0 rounded-full"
                  style={{ backgroundColor: fillColor }}
                />
                <span className="text-xs text-muted-foreground">
                  {name}
                  {val !== undefined && val !== null && (
                    <span className="ml-1 font-medium text-foreground">
                      {valueFormatter ? valueFormatter(val as number) : String(val)}
                    </span>
                  )}
                </span>
              </div>
            );
          })}
        </div>
      )}
    </div>
  );
}

// ─── GaugeChart ───────────────────────────────────────────────────────────────

export interface GaugeChartThreshold {
  /** Upper bound of this zone (0–100) */
  value: number;
  /** Color for this zone */
  color: string;
  /** Optional label for this zone */
  label?: string;
}

export interface GaugeChartProps {
  /** Current value (must be within [min, max]) */
  value: number;
  /** Minimum value (default: 0) */
  min?: number;
  /** Maximum value (default: 100) */
  max?: number;
  /**
   * Color zones. Each threshold defines the upper bound of a zone.
   * Zones are evaluated in order; the first zone whose `value >= current %`
   * determines the active color.
   * If omitted, uses `--chart-1`.
   */
  thresholds?: GaugeChartThreshold[];
  /** Label shown below the value (e.g. "CPU Usage") */
  label?: React.ReactNode;
  /** Format the center value text (default: shows percentage) */
  valueFormatter?: (value: number, percent: number) => string;
  /** Show the needle indicator (default: true) */
  showNeedle?: boolean;
  className?: string;
}

function GaugeChart({
  value,
  min = 0,
  max = 100,
  thresholds,
  label,
  valueFormatter,
  showNeedle = true,
  className,
}: GaugeChartProps) {
  const percent = Math.min(1, Math.max(0, (value - min) / (max - min)));
  const percentInt = Math.round(percent * 100);

  // Determine active color from thresholds
  const activeColor = React.useMemo(() => {
    if (!thresholds || thresholds.length === 0) return 'var(--chart-1)';
    for (const t of thresholds) {
      if (percentInt <= t.value) return t.color;
    }
    return thresholds[thresholds.length - 1].color;
  }, [thresholds, percentInt]);

  const displayText = valueFormatter ? valueFormatter(value, percentInt) : `${percentInt}%`;

  // SVG gauge constants — viewBox is 200×110, arc center at (100, 100)
  const cx = 100;
  const cy = 100;
  const R = 80; // outer radius
  const r = 54; // inner radius (track width = 26)

  // Helper: polar → cartesian (angle in degrees, 0° = right, CCW)
  const polar = (angleDeg: number, radius: number) => {
    const rad = (angleDeg * Math.PI) / 180;
    return {
      x: cx + radius * Math.cos(rad),
      y: cy - radius * Math.sin(rad),
    };
  };

  // Semicircle: from 180° (left) to 0° (right)
  // Track background arc path
  const trackStart = polar(180, R);
  const trackEnd = polar(0, R);
  const trackStartI = polar(180, r);
  const trackEndI = polar(0, r);
  const trackPath = [
    `M ${trackStart.x} ${trackStart.y}`,
    `A ${R} ${R} 0 0 1 ${trackEnd.x} ${trackEnd.y}`,
    `L ${trackEndI.x} ${trackEndI.y}`,
    `A ${r} ${r} 0 0 0 ${trackStartI.x} ${trackStartI.y}`,
    'Z',
  ].join(' ');

  // Value arc: from 180° (left) clockwise to valueAngle
  // The gauge is a semicircle (max 180°), so largeArc is always 0.
  // largeArc=1 would sweep the reflex arc (>180°) which creates the filled-blob bug.
  const valueAngle = 180 - percent * 180;
  const valueEnd = polar(valueAngle, R);
  const valueEndI = polar(valueAngle, r);
  const valuePath =
    percent <= 0
      ? ''
      : percent >= 1
        ? trackPath // full arc = same as track
        : [
            `M ${trackStart.x} ${trackStart.y}`,
            `A ${R} ${R} 0 0 1 ${valueEnd.x} ${valueEnd.y}`,
            `L ${valueEndI.x} ${valueEndI.y}`,
            `A ${r} ${r} 0 0 0 ${trackStartI.x} ${trackStartI.y}`,
            'Z',
          ].join(' ');

  // Needle: rotates from -90° (left, 0%) to +90° (right, 100%)
  // In our coordinate system: 180° at 0%, 0° at 100%
  const needleAngle = 180 - percent * 180;
  const needleTip = polar(needleAngle, r - 6);
  const needleBase1 = polar(needleAngle + 90, 5);
  const needleBase2 = polar(needleAngle - 90, 5);

  return (
    <div className={cn('flex flex-col items-center gap-2', className)}>
      <div className="relative w-full max-w-[260px]">
        <svg viewBox="0 0 200 130" className="w-full" aria-label={`Gauge: ${displayText}`}>
          {/* Background track */}
          <path d={trackPath} fill="var(--muted)" />

          {/* Value arc */}
          {valuePath && <path d={valuePath} fill={activeColor} />}

          {/* Needle */}
          {showNeedle && (
            <g>
              <polygon
                points={`${needleTip.x},${needleTip.y} ${needleBase1.x},${needleBase1.y} ${needleBase2.x},${needleBase2.y}`}
                fill="var(--foreground)"
                opacity={0.85}
              />
              <circle cx={cx} cy={cy} r={5} fill="var(--foreground)" />
            </g>
          )}

          {/* Value text — placed below the arc baseline (cy=100) to avoid needle overlap */}
          <text
            x={cx}
            y={cy + 18}
            textAnchor="middle"
            dominantBaseline="middle"
            fontSize={22}
            fontWeight={700}
            fill="currentColor"
            className="fill-foreground"
          >
            {displayText}
          </text>

          {/* Label below value */}
          {label && (
            <text
              x={cx}
              y={cy + 36}
              textAnchor="middle"
              dominantBaseline="middle"
              fontSize={10}
              fill="currentColor"
              className="fill-muted-foreground"
            >
              {label}
            </text>
          )}
        </svg>
      </div>

      {/* Threshold legend */}
      {thresholds && thresholds.length > 0 && (
        <div className="flex flex-wrap justify-center gap-x-3 gap-y-1">
          {thresholds.map((t, i) => (
            <div key={i} className="flex items-center gap-1.5">
              <span
                className="inline-block h-2 w-2 shrink-0 rounded-full"
                style={{ backgroundColor: t.color }}
              />
              <span className="text-xs text-muted-foreground">{t.label}</span>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

// ─── Exports ──────────────────────────────────────────────────────────────────

export {
  ChartContainer,
  ChartTooltip,
  ChartTooltipContent,
  ChartLegend,
  ChartLegendContent,
  ChartStyle,
  ChartCard,
  DashboardBarChart,
  DashboardLineChart,
  HorizontalBarChart,
  InteractiveTimeSeriesChart,
  ComboMetricChart,
  DonutBreakdownChart,
  SparklineChart,
  RadarMetricChart,
  PieMetricChart,
  RadialBarMetricChart,
  GaugeChart,
};
