import { jsxs, jsx } from 'react/jsx-runtime';
import { useState, useEffect, useRef, useMemo } from 'react';
import { useReducedMotion, motion } from 'motion/react';
import { cn } from '../../../utils/cn.js';
import { useTooltipPosition } from '../hooks/useTooltipPosition.js';
import { useChartAccessibility } from '../hooks/useChartAccessibility.js';
import { useChartValidation } from '../hooks/useChartValidation.js';
import { ChartErrorBoundary } from '../shared/ChartErrorBoundary/ChartErrorBoundary.js';
import { XAxis } from '../shared/ChartAxis/XAxis.js';
import { YAxis } from '../shared/ChartAxis/YAxis.js';
import { HorizontalGrid } from '../shared/ChartGrid/HorizontalGrid.js';
import { ChartTooltip } from '../shared/ChartTooltip/ChartTooltip.js';
import { BAR_CHART_DEFAULTS } from './BarChart.types.js';
import { ExclamationTriangleIcon } from '@heroicons/react/20/solid';

// Pure helper functions moved to module level to avoid recreation per render
const createRoundedTopBarPath = (x, y, width, height, radius) => {
    const r = Math.min(radius, width / 2, height / 2);
    return `
    M ${x},${y + height}
    L ${x},${y + r}
    Q ${x},${y} ${x + r},${y}
    L ${x + width - r},${y}
    Q ${x + width},${y} ${x + width},${y + r}
    L ${x + width},${y + height}
    Z
  `.trim();
};
const createRoundedBottomBarPath = (x, y, width, height, radius) => {
    const r = Math.min(radius, width / 2, height / 2);
    return `
    M ${x},${y}
    L ${x + width},${y}
    L ${x + width},${y + height - r}
    Q ${x + width},${y + height} ${x + width - r},${y + height}
    L ${x + r},${y + height}
    Q ${x},${y + height} ${x},${y + height - r}
    Z
  `.trim();
};
const calculateAverageForKey = (sanitizedData, key, safeMathDivide) => {
    const nonNullValues = sanitizedData
        .map((item) => item[key])
        .filter((value) => typeof value === "number" && isFinite(value));
    if (nonNullValues.length === 0)
        return 0;
    const sum = nonNullValues.reduce((acc, val) => acc + val, 0);
    return safeMathDivide(sum, nonNullValues.length);
};
const BarChart = ({ data, config, 
// Standardized display props with defaults
showGrid = BAR_CHART_DEFAULTS.showGrid, showXAxis = BAR_CHART_DEFAULTS.showXAxis, showYAxis = BAR_CHART_DEFAULTS.showYAxis, minValue: propMinValue, maxValue: propMaxValue, variant = BAR_CHART_DEFAULTS.variant, theme, 
// Standardized visual props with defaults (renamed from bar-specific names)
barRadius, radius, // @deprecated Use barRadius instead
gap = BAR_CHART_DEFAULTS.gap, categoryGap = BAR_CHART_DEFAULTS.categoryGap, 
// Bar-specific props
orientation = BAR_CHART_DEFAULTS.orientation, 
// Animation props
showAnimation = BAR_CHART_DEFAULTS.showAnimation, 
// Placeholder bar props
showPlaceholderBars = BAR_CHART_DEFAULTS.showPlaceholderBars, placeholderData, 
// Color customization
colors, 
// Base props
className, title, description, enableKeyboardNavigation = BAR_CHART_DEFAULTS.enableKeyboardNavigation, showValidationWarnings = process.env.NODE_ENV === "development", darkMode = false, children, ...props }) => {
    var _a, _b;
    // Resolve barRadius with backward compatibility
    const resolvedBarRadius = (_a = barRadius !== null && barRadius !== void 0 ? barRadius : radius) !== null && _a !== void 0 ? _a : BAR_CHART_DEFAULTS.barRadius;
    // Development warning for deprecated radius prop
    if (process.env.NODE_ENV !== "production" && radius !== undefined) {
        console.warn("[BarChart]: The `radius` prop is deprecated and will be removed in a future version. " +
            "Use `barRadius` instead.");
    }
    // Animation state
    const shouldReduceMotion = useReducedMotion();
    const [hasAnimated, setHasAnimated] = useState(false);
    useEffect(() => {
        if (!shouldReduceMotion && showAnimation) {
            setHasAnimated(true);
        }
        else {
            setHasAnimated(true); // Show immediately if animations disabled
        }
    }, [shouldReduceMotion, showAnimation]);
    // Validate data and config with error handling
    const { validation, sanitizedData, isValid, hasWarnings, safeScale, safeMath, } = useChartValidation(data, config, {
        autoSanitize: true,
        logWarnings: true,
    });
    // Early return for invalid data with error message
    if (!isValid) {
        return (jsxs("div", { className: cn("chart-container flex flex-col items-center justify-center p-6 border border-border rounded-lg text-2xl", className), role: "alert", "aria-live": "polite", ...props, children: [jsxs("div", { className: "flex flex-col items-center gap-4 mb-1", children: [jsx(ExclamationTriangleIcon, { className: "size-6 text-[var(--color-danger)]" }), jsx("div", { className: "text-base font-semibold text-[var(--color-text-secondary)]", children: "Invalid Chart Data" })] }), jsx("p", { className: "text-base text-[var(--color-text-secondary)]/70 mb-2", children: ((_b = validation.errors[0]) === null || _b === void 0 ? void 0 : _b.message) ||
                        "Unable to render chart due to data validation errors" }), process.env.NODE_ENV === "development" &&
                    validation.errors.length > 1 && (jsxs("details", { className: "text-sm text-warning-500", children: [jsxs("summary", { className: "cursor-pointer", children: ["Show all errors (", validation.errors.length, ")"] }), jsx("ul", { className: "mt-2 text-left", children: validation.errors.map((error, index) => (jsxs("li", { children: ["\u2022 ", error.message] }, index))) })] }))] }));
    }
    // Early return for empty data
    if (!sanitizedData || sanitizedData.length === 0) {
        return (jsx("div", { className: cn("chart-container flex items-center justify-center text-[var(--color-text-muted)]", className), role: "img", "aria-label": "Empty bar chart", ...props, children: jsx("span", { children: "No data available" }) }));
    }
    // Simple color functions based on direct CSS variables (following guidelines)
    const getSeriesFillColor = (index) => {
        const defaultColors = [
            "var(--color-primary)",
            "var(--color-warning)",
            "var(--color-success)",
            "var(--color-danger)",
        ];
        // Use custom colors if provided, otherwise fall back to defaults
        const palette = colors && colors.length > 0 ? colors : defaultColors;
        return palette[index % palette.length];
    };
    const containerRef = useRef(null);
    const [dimensions, setDimensions] = useState({ width: 600, height: 400 });
    const [hoveredCategory, setHoveredCategory] = useState(null);
    // Detect if device is mobile/touch
    const isMobile = typeof window !== "undefined" &&
        ("ontouchstart" in window || navigator.maxTouchPoints > 0);
    // Use the new tooltip positioning hook
    const { tooltipData, tooltipRef, handleMouseEnter, handleMouseMove, handleMouseLeave, } = useTooltipPosition({
        containerRef,
        isMobile,
    });
    // Use accessibility hook
    const { chartAccessibilityProps, descriptionProps, focusedElementIndex, isKeyboardMode, handleMouseInteraction, } = useChartAccessibility({
        chartType: "bar",
        data: sanitizedData,
        config,
        title,
        description,
        enableKeyboardNavigation,
    });
    // Measure container dimensions
    useEffect(() => {
        const updateDimensions = () => {
            if (containerRef.current) {
                const { width, height } = containerRef.current.getBoundingClientRect();
                setDimensions({
                    width: width || 600,
                    height: height || 400,
                });
            }
        };
        updateDimensions();
        const resizeObserver = new ResizeObserver(updateDimensions);
        if (containerRef.current) {
            resizeObserver.observe(containerRef.current);
        }
        return () => {
            resizeObserver.disconnect();
        };
    }, []);
    // Calculate dimensions with safe math
    const margin = useMemo(() => ({
        top: 15,
        right: showYAxis ? 20 : 0,
        bottom: showXAxis ? 20 : 0,
        left: showYAxis ? 60 : 0,
    }), [showYAxis, showXAxis]);
    const innerWidth = Math.max(0, dimensions.width - margin.left - margin.right);
    const innerHeight = Math.max(0, dimensions.height - margin.top - margin.bottom);
    // Get all data keys from config
    const dataKeys = useMemo(() => Object.keys(config || {}), [config]);
    // Animation timing constant
    const BAR_STAGGER = 0.05;
    // Calculate safe scale range
    const scaleRange = safeScale.calculateRange({
        minValue: propMinValue,
        maxValue: propMaxValue,
        includeZero: true,
    });
    const { min: minValue, max: maxValue, hasValidData } = scaleRange;
    // If no valid data, show empty state
    if (!hasValidData) {
        return (jsx("div", { className: cn("chart-container flex items-center justify-center text-neutral-500", className), role: "img", "aria-label": "Empty bar chart - no valid data", ...props, children: jsx("span", { children: "No valid numeric data available" }) }));
    }
    // Safe bar width calculations
    const totalCategoryGap = Math.max(0, (sanitizedData.length - 1) * categoryGap);
    const availableWidth = Math.max(0, innerWidth - totalCategoryGap);
    const barWidth = safeMath.divide(availableWidth, sanitizedData.length, 0);
    const totalBarGap = Math.max(0, (dataKeys.length - 1) * gap);
    const availableBarWidth = Math.max(0, barWidth - totalBarGap);
    const individualBarWidth = safeMath.divide(availableBarWidth, dataKeys.length, 0);
    // Handle keyboard focus tooltips
    useEffect(() => {
        var _a, _b;
        if (isKeyboardMode &&
            focusedElementIndex >= 0 &&
            focusedElementIndex < sanitizedData.length) {
            const item = sanitizedData[focusedElementIndex];
            if (dataKeys.length > 0) {
                const key = dataKeys[0]; // Show first series for keyboard navigation
                const value = item[key];
                if (typeof value === "number" && isFinite(value)) {
                    // Calculate position for focused bar with safe math using zero baseline
                    const x = margin.left + focusedElementIndex * (barWidth + categoryGap);
                    const range = maxValue - minValue;
                    const scaledValue = range > 0 ? ((value - minValue) / range) * innerHeight : 0;
                    const scaledZero = range > 0 ? ((0 - minValue) / range) * innerHeight : 0;
                    const zeroY = margin.top + innerHeight - scaledZero;
                    const barHeight = Math.abs(scaledValue - scaledZero);
                    const isNeg = value < 0;
                    const y = isNeg ? zeroY : zeroY - barHeight;
                    // Get the fill color for the first series (index 0)
                    const fillColor = getSeriesFillColor(0);
                    const content = {
                        category: item.name || `Category ${focusedElementIndex + 1}`,
                        series: [
                            {
                                key,
                                label: ((_a = config[key]) === null || _a === void 0 ? void 0 : _a.label) || key,
                                value: value,
                                color: ((_b = config[key]) === null || _b === void 0 ? void 0 : _b.color) || fillColor,
                            },
                        ],
                    };
                    const svgCoordinates = {
                        x: x + barWidth / 2,
                        y: y,
                        dimensions,
                    };
                    // Simulate mouse enter for keyboard focus
                    const syntheticEvent = new MouseEvent("mouseenter", {
                        clientX: x + barWidth / 2,
                        clientY: y,
                    });
                    handleMouseEnter(syntheticEvent, content, svgCoordinates);
                }
            }
        }
        else if (isKeyboardMode && focusedElementIndex === -1) {
            // Only clear tooltip when explicitly exiting keyboard mode (Escape key)
            handleMouseLeave();
        }
        // Don't clear tooltips when switching from keyboard to mouse mode
    }, [
        isKeyboardMode,
        focusedElementIndex,
        sanitizedData,
        config,
        dimensions,
        margin,
        innerWidth,
        innerHeight,
        categoryGap,
        minValue,
        maxValue,
        handleMouseEnter,
        handleMouseLeave,
        dataKeys,
        barWidth,
    ]);
    // Memoize placeholder bar heights to prevent flickering on re-renders
    const placeholderHeightMap = useMemo(() => {
        const map = new Map();
        if (showPlaceholderBars) {
            sanitizedData.forEach((item, index) => {
                dataKeys.forEach((key) => {
                    const rawValue = item[key];
                    const isNull = rawValue === null ||
                        rawValue === undefined ||
                        (typeof rawValue === "number" && !isFinite(rawValue));
                    if (isNull) {
                        const mapKey = `${index}-${key}`;
                        // Generate stable random factor between 0.4 and 0.8 (40% to 80% of max)
                        const randomFactor = Math.random() * 0.4 + 0.4;
                        map.set(mapKey, randomFactor);
                    }
                });
            });
        }
        return map;
    }, [sanitizedData.length, dataKeys.join(","), showPlaceholderBars]);
    // Helper function to render individual bar path
    const renderBarPath = (item, index, key, keyIndex, barX, barY, barHeight, shouldAnimate = true, isNegative = false) => {
        var _a;
        const rawValue = item[key];
        const isNull = rawValue === null ||
            rawValue === undefined ||
            (typeof rawValue === "number" && !isFinite(rawValue));
        // Get fill color for this data series using direct CSS variables
        const fillColor = getSeriesFillColor(keyIndex);
        const isHovered = hoveredCategory === index;
        const isAnyBarHovered = hoveredCategory !== null;
        const shouldReduceOpacity = isAnyBarHovered && !isHovered;
        const isFocused = isKeyboardMode && focusedElementIndex === index;
        // Handle null bars - either skip or show placeholder
        if (isNull) {
            if (!showPlaceholderBars) {
                return null; // Original behavior: don't render
            }
            // Show placeholder bar
            let placeholderHeight = 0;
            let placeholderY = barY;
            // Try to get value from placeholderData (previous period)
            if (placeholderData && placeholderData[index]) {
                const placeholderValue = placeholderData[index][key];
                if (typeof placeholderValue === "number" &&
                    isFinite(placeholderValue)) {
                    // Use previous period value
                    const range = maxValue - minValue;
                    placeholderHeight =
                        range > 0
                            ? safeMath.scale(placeholderValue, minValue, maxValue, 0, innerHeight)
                            : 0;
                    placeholderY = margin.top + innerHeight - placeholderHeight;
                }
            }
            // If no placeholderData or invalid, use stable random height from memoized map
            if (placeholderHeight === 0) {
                const mapKey = `${index}-${key}`;
                const randomFactor = placeholderHeightMap.get(mapKey) || 0.6; // fallback to 60%
                placeholderHeight = innerHeight * randomFactor;
                placeholderY = margin.top + innerHeight - placeholderHeight;
            }
            const pathD = createRoundedTopBarPath(barX, placeholderY, individualBarWidth, placeholderHeight, resolvedBarRadius);
            const delay = (index * dataKeys.length + keyIndex) * BAR_STAGGER;
            return (jsx(motion.path, { d: pathD, initial: shouldAnimate && showAnimation && !shouldReduceMotion
                    ? { scaleY: 0, opacity: 0 }
                    : { scaleY: 1, opacity: 1 }, animate: {
                    scaleY: 1,
                    opacity: 1,
                }, transition: {
                    type: "spring",
                    stiffness: 100,
                    damping: 20,
                    delay,
                }, className: "transition-opacity duration-200", style: {
                    originY: 1,
                    transformOrigin: "bottom",
                    fill: "url(#diagonalHatch)",
                    opacity: 0.75,
                }, "aria-label": `${item.name || `Category ${index + 1}`} - ${((_a = config[key]) === null || _a === void 0 ? void 0 : _a.label) || key}: No data` }, keyIndex));
        }
        // Skip rendering if bar dimensions are invalid
        if (barHeight <= 0 || individualBarWidth <= 0) {
            return null;
        }
        const pathD = isNegative
            ? createRoundedBottomBarPath(barX, barY, individualBarWidth, barHeight, resolvedBarRadius)
            : createRoundedTopBarPath(barX, barY, individualBarWidth, barHeight, resolvedBarRadius);
        const delay = (index * dataKeys.length + keyIndex) * BAR_STAGGER;
        return (jsx(motion.path, { d: pathD, initial: shouldAnimate && showAnimation && !shouldReduceMotion
                ? { scaleY: 0, opacity: 0 }
                : { scaleY: 1, opacity: 1 }, animate: {
                scaleY: 1,
                opacity: 1,
            }, transition: {
                type: "spring",
                stiffness: 100,
                damping: 20,
                delay,
            }, className: cn("transition-colors duration-200", !isNull && "cursor-pointer", isFocused && "stroke-2"), style: {
                originY: isNegative ? 0 : 1,
                transformOrigin: isNegative ? "top" : "bottom",
                fill: isNull
                    ? "var(--color-background-tertiary)"
                    : shouldReduceOpacity
                        ? "var(--color-background-tertiary)"
                        : fillColor,
                opacity: !isNull && shouldReduceOpacity ? 0.9 : 1,
                stroke: isFocused ? "var(--color-primary)" : "none",
            }, onMouseEnter: !isNull
                ? (e) => {
                    setHoveredCategory(index);
                    // Create content with ALL series for this category
                    const series = dataKeys
                        .map((dataKey, idx) => {
                        var _a, _b;
                        const seriesValue = item[dataKey];
                        const isSeriesNull = seriesValue === null ||
                            seriesValue === undefined ||
                            (typeof seriesValue === "number" &&
                                !isFinite(seriesValue));
                        return {
                            key: dataKey,
                            label: ((_a = config[dataKey]) === null || _a === void 0 ? void 0 : _a.label) || dataKey,
                            value: isSeriesNull ? 0 : seriesValue,
                            color: ((_b = config[dataKey]) === null || _b === void 0 ? void 0 : _b.color) || getSeriesFillColor(idx),
                        };
                    })
                        .filter((series) => {
                        // Only include non-null and non-zero values in tooltip
                        const rawVal = item[series.key];
                        return (rawVal != null &&
                            typeof rawVal === "number" &&
                            isFinite(rawVal) &&
                            rawVal !== 0);
                    });
                    const content = {
                        category: item.name || `Category ${index + 1}`,
                        series,
                    };
                    const svgCoordinates = {
                        x: barX + individualBarWidth / 2,
                        y: barY,
                        dimensions,
                    };
                    handleMouseEnter(e, content, svgCoordinates);
                }
                : undefined, onMouseMove: !isNull
                ? (e) => {
                    if (hoveredCategory === index) {
                        const svgCoordinates = {
                            x: barX + individualBarWidth / 2,
                            y: barY,
                            dimensions,
                        };
                        handleMouseMove(e, svgCoordinates);
                    }
                }
                : undefined, onMouseLeave: !isNull
                ? () => {
                    setHoveredCategory(null);
                    handleMouseLeave();
                }
                : undefined }, keyIndex));
    };
    return (jsx(ChartErrorBoundary, { className: className, showErrorDetails: process.env.NODE_ENV === "development", children: jsxs("div", { ref: containerRef, className: cn("chart-container w-full h-full", darkMode && "dark", className), ...chartAccessibilityProps, onMouseEnter: handleMouseInteraction, onMouseMove: handleMouseInteraction, ...props, children: [descriptionProps && jsx("div", { ...descriptionProps }), showValidationWarnings && hasWarnings && (jsxs("div", { className: "absolute top-2 left-2 right-2 z-10 p-2 bg-[var(--color-warning-50)] border border-[var(--color-warning-200)] rounded text-sm text-[var(--color-warning)] shadow-sm", children: [jsx("strong", { children: "Chart Warnings:" }), jsx("ul", { className: "mt-1", children: validation.warnings
                                .filter((warning) => !warning.message.includes("has no valid numeric values"))
                                .map((warning, index) => (jsxs("li", { children: ["\u2022 ", warning.message] }, index))) }), validation.warnings.some((warning) => warning.message.includes("has no valid numeric values")) && (jsx("div", { className: "mt-2 pt-2 border-t border-[var(--color-warning-200)]", children: jsx("span", { className: "text-[var(--color-warning)]", children: "\u2139\uFE0F Null/undefined values are handled by using averages from other data points" }) }))] })), jsxs("svg", { width: "100%", height: "100%", viewBox: `0 0 ${dimensions.width} ${dimensions.height}`, children: [jsx("defs", { children: jsxs("pattern", { id: "diagonalHatch", patternUnits: "userSpaceOnUse", width: "8", height: "8", children: [jsx("rect", { width: "8", height: "8", fill: "var(--color-neutral-50)" }), jsx("path", { d: "M-2,2 l4,-4\n                   M0,8 l8,-8\n                   M6,10 l4,-4", style: { stroke: "black", strokeWidth: 4, opacity: 0.1 } })] }) }), jsx(HorizontalGrid, { show: showGrid, x: margin.left, y: margin.top, width: innerWidth, height: innerHeight, lineCount: 5 }), jsx(YAxis, { show: showYAxis, min: minValue, max: maxValue, x: margin.left, y: margin.top, height: innerHeight, showGrid: false, gridWidth: innerWidth, tickFormatter: (value) => {
                                if (Math.abs(value) >= 1000)
                                    return `${(value / 1000).toFixed(0)}k`;
                                return value.toString();
                            } }), minValue < 0 && maxValue > 0 && (jsx("line", { x1: margin.left, y1: margin.top + innerHeight - ((0 - minValue) / (maxValue - minValue)) * innerHeight, x2: margin.left + innerWidth, y2: margin.top + innerHeight - ((0 - minValue) / (maxValue - minValue)) * innerHeight, className: "stroke-[var(--color-text-secondary)]", strokeWidth: 1, opacity: 0.6 })), jsx("g", { className: "chart-bars", children: (() => {
                                // Calculate the zero baseline position (where value=0 maps to in pixel space)
                                const range = maxValue - minValue;
                                const scaledZero = range > 0
                                    ? safeMath.scale(0, minValue, maxValue, 0, innerHeight)
                                    : 0;
                                const zeroY = margin.top + innerHeight - scaledZero;
                                return sanitizedData.map((item, index) => {
                                    const x = margin.left + index * (barWidth + categoryGap);
                                    return (jsx("g", { children: dataKeys.map((key, keyIndex) => {
                                            const rawValue = item[key];
                                            const value = typeof rawValue === "number" && isFinite(rawValue)
                                                ? rawValue
                                                : 0;
                                            const isNull = rawValue === null ||
                                                rawValue === undefined ||
                                                (typeof rawValue === "number" && !isFinite(rawValue));
                                            // For null values, use the average of non-null values
                                            const actualValue = isNull
                                                ? calculateAverageForKey(sanitizedData, key, safeMath.divide)
                                                : value;
                                            const scaledValue = range > 0
                                                ? safeMath.scale(actualValue, minValue, maxValue, 0, innerHeight)
                                                : 0;
                                            const barHeight = Math.abs(scaledValue - scaledZero);
                                            const isNeg = actualValue < 0;
                                            const barX = x + keyIndex * (individualBarWidth + gap);
                                            // Positive: bar extends upward from zero baseline
                                            // Negative: bar extends downward from zero baseline
                                            const barY = isNeg ? zeroY : zeroY - barHeight;
                                            return renderBarPath(item, index, key, keyIndex, barX, barY, barHeight, true, isNeg);
                                        }) }, index));
                                });
                            })() }), jsx(XAxis, { show: showXAxis, data: sanitizedData, x: margin.left, y: margin.top + innerHeight, width: innerWidth, categoryWidth: barWidth, categoryGap: categoryGap })] }), jsx(ChartTooltip, { tooltipRef: tooltipRef, content: tooltipData === null || tooltipData === void 0 ? void 0 : tooltipData.content, active: !!tooltipData, position: tooltipData
                        ? { x: tooltipData.x, y: tooltipData.y }
                        : { x: 0, y: 0 } }), children] }) }));
};

export { BarChart };
