import React, { useCallback } from "react";
import {
  Bar,
  BarChart,
  CartesianGrid,
  ReferenceArea,
  ReferenceLine,
  XAxis,
  YAxis,
  type XAxisProps,
  type YAxisProps,
} from "recharts";
import { ChartTooltip, ChartTooltipContent } from "~/components/primitives/charts/Chart";
import { useChartContext } from "./ChartContext";
import { ChartBarInvalid, ChartBarLoading, ChartBarNoData } from "./ChartLoading";
import { useHasNoData } from "./ChartRoot";
import { useYAxisWidth } from "./useYAxisWidth";
import { ZoomTooltip, useZoomHandlers } from "./ChartZoom";

//TODO: fix the first and last bars in a stack not having rounded corners

type ReferenceLineProps = {
  value: number;
  label: string;
};

// ============================================================================
// COMPOUND COMPONENT API
// ============================================================================

export type ChartBarRendererProps = {
  /** Stack ID for stacked bar charts */
  stackId?: string;
  /** Custom X-axis props to merge with defaults */
  xAxisProps?: Partial<XAxisProps>;
  /** Custom Y-axis props to merge with defaults */
  yAxisProps?: Partial<YAxisProps>;
  /** Reference line (horizontal) */
  referenceLine?: ReferenceLineProps;
  /** Custom tooltip label formatter */
  tooltipLabelFormatter?: (label: string, payload: any[]) => string;
  /** Optional formatter for numeric tooltip values (e.g. bytes, duration) */
  tooltipValueFormatter?: (value: number) => string;
  /** Corner radius for the outermost bars in each stack (defaults to 2). Pass 0 for square corners. */
  barRadius?: number;
  /** Width injected by ResponsiveContainer */
  width?: number;
  /** Height injected by ResponsiveContainer */
  height?: number;
};

/**
 * Bar chart renderer for the compound component system.
 * Must be used within a Chart.Root.
 *
 * @example
 * ```tsx
 * <Chart.Root config={config} data={data} dataKey="day">
 *   <Chart.Bar stackId="a" />
 *   <Chart.Legend />
 * </Chart.Root>
 * ```
 */
export function ChartBarRenderer({
  stackId,
  xAxisProps: xAxisPropsProp,
  yAxisProps: yAxisPropsProp,
  referenceLine,
  tooltipLabelFormatter,
  tooltipValueFormatter,
  barRadius = 2,
  width,
  height,
}: ChartBarRendererProps) {
  const {
    config,
    data,
    dataKey,
    dataKeys,
    visibleSeries,
    state,
    highlight,
    setActivePayload,
    zoom,
    showLegend,
  } = useChartContext();
  const hasNoData = useHasNoData();
  const zoomHandlers = useZoomHandlers();
  const enableZoom = zoom !== null;
  const computedYAxisWidth = useYAxisWidth(data, visibleSeries, yAxisPropsProp?.tickFormatter);

  const handleBarClick = useCallback(
    (barData: any, e: React.MouseEvent) => {
      if (!enableZoom || !zoom) return;
      e.stopPropagation();

      if (!zoom.isSelecting) {
        zoom.toggleInspectionLine(barData[dataKey]);
      }
    },
    [enableZoom, zoom, dataKey]
  );

  // Handle mouse leave to also reset highlight
  const handleMouseLeave = useCallback(() => {
    zoomHandlers.onMouseLeave?.();
    highlight.reset();
  }, [zoomHandlers, highlight]);

  // Render loading/error states
  if (state === "loading") {
    return <ChartBarLoading />;
  } else if (state === "noData" || hasNoData) {
    return <ChartBarNoData />;
  } else if (state === "invalid") {
    return <ChartBarInvalid />;
  }

  // Get the x-axis ticks based on tooltip state
  // Only hide middle ticks when zoom is enabled (to make room for zoom instructions)
  const xAxisTicks =
    enableZoom && highlight.tooltipActive && data.length > 2
      ? [data[0]?.[dataKey], data[data.length - 1]?.[dataKey]]
      : undefined;

  return (
    <BarChart
      data={data}
      width={width}
      height={height}
      barCategoryGap={1}
      onMouseDown={zoomHandlers.onMouseDown}
      onMouseMove={(e: any) => {
        zoomHandlers.onMouseMove?.(e);
        if (e?.activePayload?.length) {
          setActivePayload(e.activePayload, e.activeTooltipIndex);
          highlight.setTooltipActive(true);
        } else {
          highlight.setTooltipActive(false);
        }
      }}
      onMouseUp={zoomHandlers.onMouseUp}
      onClick={zoomHandlers.onClick}
      onMouseLeave={handleMouseLeave}
    >
      <CartesianGrid vertical={false} stroke="#272A2E" />
      <XAxis
        dataKey={dataKey}
        tickLine={false}
        tickMargin={10}
        axisLine={false}
        ticks={xAxisTicks}
        interval="preserveStartEnd"
        tick={{
          fill: "#878C99",
          fontSize: 11,
          style: { fontVariantNumeric: "tabular-nums" },
        }}
        {...xAxisPropsProp}
      />
      <YAxis
        axisLine={false}
        tickLine={false}
        tickMargin={8}
        width={computedYAxisWidth}
        tick={{
          fill: "#878C99",
          fontSize: 11,
          style: { fontVariantNumeric: "tabular-nums" },
        }}
        domain={["auto", (dataMax: number) => dataMax * 1.15]}
        {...yAxisPropsProp}
      />
      {/* When legend is shown below the chart, render tooltip with cursor only (no content popup).
          Otherwise render the full tooltip with zoom instructions. */}
      <ChartTooltip
        cursor={{ fill: "rgba(255, 255, 255, 0.06)" }}
        content={
          showLegend ? (
            () => null
          ) : tooltipLabelFormatter ? (
            <ChartTooltipContent valueFormatter={tooltipValueFormatter} />
          ) : (
            <ZoomTooltip
              isSelecting={zoom?.isSelecting}
              refAreaLeft={zoom?.refAreaLeft}
              refAreaRight={zoom?.refAreaRight}
              invalidSelection={zoom?.invalidSelection}
            />
          )
        }
        labelFormatter={tooltipLabelFormatter}
        allowEscapeViewBox={{ x: false, y: true }}
        animationDuration={0}
      />

      {/* Zoom selection area - rendered before bars to appear behind them */}
      {enableZoom && zoom?.refAreaLeft !== null && zoom?.refAreaRight !== null && (
        <ReferenceArea
          x1={zoom.refAreaLeft}
          x2={zoom.refAreaRight}
          strokeOpacity={0.4}
          fill="#3B82F6"
          fillOpacity={0.3}
        />
      )}

      {visibleSeries.map((key, index, array) => {
        const dimmed =
          !zoom?.isSelecting && highlight.activeBarKey !== null && highlight.activeBarKey !== key;

        return (
          <Bar
            key={key}
            dataKey={key}
            stackId={stackId}
            fill={config[key]?.color}
            radius={
              [
                index === array.length - 1 ? barRadius : 0,
                index === array.length - 1 ? barRadius : 0,
                index === 0 ? barRadius : 0,
                index === 0 ? barRadius : 0,
              ] as [number, number, number, number]
            }
            activeBar={false}
            fillOpacity={dimmed ? 0.2 : 1}
            onClick={(data, index, e) => handleBarClick(data, e)}
            onMouseEnter={(entry, index) => {
              if (entry.tooltipPayload?.[0]) {
                const { dataKey: hoveredKey } = entry.tooltipPayload[0];
                highlight.setHoveredBar(hoveredKey, index);
              }
            }}
            onMouseLeave={highlight.reset}
            isAnimationActive={false}
          />
        );
      })}

      {/* Horizontal reference line */}
      {referenceLine && (
        <ReferenceLine
          y={referenceLine.value}
          label={{
            position: "top",
            value: referenceLine.label,
            fill: "#878C99",
            fontSize: 11,
          }}
          isFront={true}
          stroke="#3B3E45"
          strokeDasharray="4 4"
          className="pointer-events-none"
        />
      )}

      {/* Zoom inspection line - rendered after bars to appear on top */}
      {enableZoom && zoom?.inspectionLine && (
        <ReferenceLine
          x={zoom.inspectionLine}
          stroke="#D7D9DD"
          strokeWidth={2}
          isFront={true}
          onClick={(e: any) => {
            e?.stopPropagation?.();
            zoom.clearInspectionLine();
          }}
        />
      )}

      {/* Note: Legend is now rendered by ChartRoot outside the chart container */}
    </BarChart>
  );
}
