import { jsx, jsxs } from 'react/jsx-runtime';
import { useId, useRef, useState, useCallback, useMemo } from 'react';
import { AnimatePresence, motion } from 'motion/react';
import { cn } from '../../../utils/cn.js';
import { funnelChartTheme } from './FunnelChart.theme.js';
import { labelVariants, badgeVariants, summaryVariants, segmentVariants, verticalSegmentVariants } from './FunnelChart.animations.js';
import { ChartTooltip } from '../shared/ChartTooltip/ChartTooltip.js';
import { Badge } from '../../core/Badge/Badge.js';
import { useTooltipPosition } from '../hooks/useTooltipPosition.js';

/**
 * FunnelChart Component
 *
 * Displays conversion funnel data with trapezoid shapes showing progressive dropoff.
 * Features animations, keyboard navigation, and accessibility support.
 */
const FunnelChart = ({ data, height = 400, width = 600, showPercentages = true, showValues = true, showLabels = true, showDropoff = false, showConversionRate = true, showSummary = true, colors, theme: themeOverrides, className, orientation = "horizontal", curved = true, useGradient = true, fillOpacity = 0.8, onStageClick, formatValue, labelPosition = "top", summaryPosition = "top-right", animate = true, onStageFocus, ...props }) => {
    const theme = { ...funnelChartTheme, ...(themeOverrides || {}) };
    const chartColors = colors || theme.colors || funnelChartTheme.colors;
    const chartId = useId();
    const containerRef = useRef(null);
    // Use the shared tooltip positioning hook
    const { tooltipData, tooltipRef, handleMouseEnter: tooltipMouseEnter, handleMouseMove: tooltipMouseMove, handleMouseLeave: tooltipMouseLeave, } = useTooltipPosition({
        containerRef,
    });
    // Hover state for dimming non-hovered segments
    const [hoveredIndex, setHoveredIndex] = useState(null);
    // Keyboard focus state
    const [focusedIndex, setFocusedIndex] = useState(null);
    // Format value helper
    const formatDisplayValue = useCallback((value) => {
        if (formatValue) {
            return formatValue(value);
        }
        return value.toLocaleString();
    }, [formatValue]);
    const maxValue = useMemo(() => {
        if (data.length === 0)
            return 0;
        return Math.max(...data.map((d) => d.value));
    }, [data]);
    const stages = useMemo(() => {
        var _a;
        if (data.length === 0 || maxValue === 0) {
            return [];
        }
        const baseColor = ((_a = data[0]) === null || _a === void 0 ? void 0 : _a.color) || chartColors[0];
        return data.map((item, index) => {
            const percentage = (item.value / maxValue) * 100;
            const prevValue = index > 0 ? data[index - 1].value : 0;
            const dropoff = index > 0 && prevValue > 0
                ? ((prevValue - item.value) / prevValue) * 100
                : 0;
            const conversionRate = index > 0 && prevValue > 0 ? (item.value / prevValue) * 100 : 100;
            const usersLost = index > 0 ? prevValue - item.value : 0;
            const opacityRange = 0.8;
            const minOpacity = 0.4;
            const stageOpacity = minOpacity + opacityRange * (index / Math.max(1, data.length - 1));
            return {
                ...item,
                percentage,
                dropoff,
                conversionRate,
                usersLost,
                color: baseColor,
                opacity: stageOpacity,
                gradientId: `funnel-gradient-${chartId}-${index}`,
            };
        });
    }, [data, maxValue, chartColors, chartId]);
    const containerHeight = typeof height === "number" ? `${height}px` : height;
    const containerWidth = typeof width === "number" ? `${width}px` : width;
    // Return empty state if no valid data
    if (stages.length === 0) {
        return (jsx("div", { className: cn(theme.baseStyle, className), style: { width: containerWidth, height: containerHeight }, role: "img", "aria-label": "Empty funnel chart - no data available", ...props, children: jsx("div", { className: "flex items-center justify-center h-full text-[var(--color-text-muted)] text-base", children: "No data available" }) }));
    }
    const viewBoxWidth = 150;
    const viewBoxHeight = 100;
    const stageHeight = viewBoxHeight / stages.length;
    const stageWidth = viewBoxWidth / stages.length;
    const verticalPadding = 5;
    const availableThickness = viewBoxHeight - verticalPadding * 2;
    const minThickness = 3;
    const getThickness = (percentage) => {
        if (!percentage)
            return minThickness;
        return Math.max(minThickness, (percentage / 100) * availableThickness);
    };
    const generateHorizontalFunnelPath = (index) => {
        const stage = stages[index];
        const nextStage = stages[index + 1] || stage;
        const xStart = index * stageWidth;
        const xEnd = xStart + stageWidth;
        const startThickness = getThickness(stage.percentage);
        const endThickness = getThickness(nextStage.percentage);
        const centerY = viewBoxHeight / 2;
        const startTop = centerY - startThickness / 2;
        const startBottom = centerY + startThickness / 2;
        const endTop = centerY - endThickness / 2;
        const endBottom = centerY + endThickness / 2;
        if (!curved) {
            return `
        M ${xStart} ${startTop}
        L ${xEnd} ${endTop}
        L ${xEnd} ${endBottom}
        L ${xStart} ${startBottom}
        Z
      `;
        }
        const dx = xEnd - xStart;
        const controlDistance = dx * 0.025;
        const topCp1x = xStart + controlDistance;
        const topCp1y = startTop;
        const topCp2x = xEnd - controlDistance;
        const topCp2y = endTop;
        const bottomCp1x = xEnd - controlDistance;
        const bottomCp1y = endBottom;
        const bottomCp2x = xStart + controlDistance;
        const bottomCp2y = startBottom;
        return `
      M ${xStart} ${startTop}
      C ${topCp1x} ${topCp1y}, ${topCp2x} ${topCp2y}, ${xEnd} ${endTop}
      L ${xEnd} ${endBottom}
      C ${bottomCp1x} ${bottomCp1y}, ${bottomCp2x} ${bottomCp2y}, ${xStart} ${startBottom}
      Z
    `;
    };
    const generateVerticalFunnelPath = (index) => {
        const stage = stages[index];
        const nextStage = stages[index + 1] || stage;
        const y = index * stageHeight;
        const topWidth = stage.percentage;
        const bottomWidth = nextStage.percentage;
        const leftOffset = (100 - topWidth) / 2;
        const rightOffset = 100 - leftOffset;
        const nextLeftOffset = (100 - bottomWidth) / 2;
        const nextRightOffset = 100 - nextLeftOffset;
        return `
      M ${leftOffset} ${y}
      L ${rightOffset} ${y}
      L ${nextRightOffset} ${y + stageHeight}
      L ${nextLeftOffset} ${y + stageHeight}
      Z
    `;
    };
    // Tooltip handlers
    const handleMouseEnter = (e, stage, index) => {
        // Build series data for the tooltip
        const seriesItems = [
            {
                key: "value",
                label: "Value",
                value: formatDisplayValue(stage.value),
                color: stage.color,
            },
            {
                key: "percentage",
                label: "Of Total",
                value: `${stage.percentage.toFixed(1)}%`,
            },
        ];
        // Add conversion and lost info for non-first stages
        if (index > 0) {
            seriesItems.push({
                key: "conversion",
                label: "Conversion",
                value: `${stage.conversionRate.toFixed(1)}%`,
                color: stage.conversionRate >= 50
                    ? "var(--color-success)"
                    : "var(--color-warning)",
            });
            seriesItems.push({
                key: "lost",
                label: "Lost",
                value: formatDisplayValue(stage.usersLost),
                color: "var(--color-danger)",
            });
        }
        const tooltipContent = {
            category: stage.name,
            series: seriesItems,
        };
        tooltipMouseEnter(e, tooltipContent);
        setHoveredIndex(index);
    };
    const handleMouseMove = (e) => {
        tooltipMouseMove(e);
    };
    const handleMouseLeave = () => {
        tooltipMouseLeave();
        setHoveredIndex(null);
    };
    const handleClick = (stage, index) => {
        if (onStageClick) {
            onStageClick(stage, index);
        }
    };
    // Keyboard navigation handlers
    const handleKeyDown = (e, stage, index) => {
        var _a, _b, _c, _d;
        const isHorizontal = orientation === "horizontal";
        const nextKey = isHorizontal ? "ArrowRight" : "ArrowDown";
        const prevKey = isHorizontal ? "ArrowLeft" : "ArrowUp";
        switch (e.key) {
            case nextKey:
                e.preventDefault();
                if (index < stages.length - 1) {
                    setFocusedIndex(index + 1);
                    const nextButton = (_a = containerRef.current) === null || _a === void 0 ? void 0 : _a.querySelector(`[data-stage-index="${index + 1}"]`);
                    nextButton === null || nextButton === void 0 ? void 0 : nextButton.focus();
                }
                break;
            case prevKey:
                e.preventDefault();
                if (index > 0) {
                    setFocusedIndex(index - 1);
                    const prevButton = (_b = containerRef.current) === null || _b === void 0 ? void 0 : _b.querySelector(`[data-stage-index="${index - 1}"]`);
                    prevButton === null || prevButton === void 0 ? void 0 : prevButton.focus();
                }
                break;
            case "Enter":
            case " ":
                e.preventDefault();
                handleClick(stage, index);
                break;
            case "Home":
                e.preventDefault();
                setFocusedIndex(0);
                const firstButton = (_c = containerRef.current) === null || _c === void 0 ? void 0 : _c.querySelector('[data-stage-index="0"]');
                firstButton === null || firstButton === void 0 ? void 0 : firstButton.focus();
                break;
            case "End":
                e.preventDefault();
                const lastIndex = stages.length - 1;
                setFocusedIndex(lastIndex);
                const lastButton = (_d = containerRef.current) === null || _d === void 0 ? void 0 : _d.querySelector(`[data-stage-index="${lastIndex}"]`);
                lastButton === null || lastButton === void 0 ? void 0 : lastButton.focus();
                break;
        }
    };
    const handleFocus = (stage, index) => {
        setFocusedIndex(index);
        setHoveredIndex(index);
        onStageFocus === null || onStageFocus === void 0 ? void 0 : onStageFocus(stage, index);
    };
    const handleBlur = () => {
        setFocusedIndex(null);
        setHoveredIndex(null);
    };
    // Get summary position classes
    const getSummaryPositionClasses = (position) => {
        switch (position) {
            case "top-right":
                return "top-2 right-2";
            case "top-left":
                return "top-2 left-2";
            case "bottom-right":
                return "bottom-2 right-2";
            case "bottom-left":
                return "bottom-2 left-2";
            default:
                return "top-2 right-2";
        }
    };
    // Get label vertical position
    const getLabelTopPosition = (position) => {
        switch (position) {
            case "top":
                return "0";
            case "bottom":
                return "auto";
            case "inside":
                return "50%";
            default:
                return "0";
        }
    };
    const getLabelBottomPosition = (position) => {
        switch (position) {
            case "bottom":
                return "0";
            default:
                return "auto";
        }
    };
    // Calculate overall conversion
    const overallConversion = stages.length > 1
        ? ((stages[stages.length - 1].value / stages[0].value) * 100).toFixed(1)
        : "100";
    // Animation props based on animate flag
    const getAnimationProps = (index) => {
        if (!animate) {
            return {};
        }
        return {
            variants: orientation === "horizontal"
                ? segmentVariants
                : verticalSegmentVariants,
            initial: "hidden",
            animate: "visible",
            exit: "exit",
            custom: index,
        };
    };
    return (jsxs("div", { ref: containerRef, className: cn(theme.baseStyle, "relative", className), style: { width: containerWidth, height: containerHeight }, role: "img", "aria-label": `Conversion funnel chart with ${stages.length} stages. Overall conversion: ${overallConversion}%`, ...props, children: [jsxs("svg", { viewBox: `0 0 ${viewBoxWidth} ${viewBoxHeight}`, width: "100%", height: "100%", preserveAspectRatio: "none", "aria-hidden": "true", children: [useGradient && (jsx("defs", { children: stages.map((stage) => {
                            const centerOpacity = stage.opacity || 0.5;
                            const edgeOpacity = Math.max(0.2, centerOpacity - 0.3);
                            return (jsxs("linearGradient", { id: stage.gradientId, x1: "0%", y1: "0%", x2: "0%", y2: "100%", children: [jsx("stop", { offset: "0%", stopColor: stage.color, stopOpacity: edgeOpacity }), jsx("stop", { offset: "50%", stopColor: stage.color, stopOpacity: centerOpacity }), jsx("stop", { offset: "100%", stopColor: stage.color, stopOpacity: edgeOpacity })] }, stage.gradientId));
                        }) })), jsx(AnimatePresence, { children: stages.map((stage, index) => {
                            const path = orientation === "horizontal"
                                ? generateHorizontalFunnelPath(index)
                                : generateVerticalFunnelPath(index);
                            const fill = useGradient
                                ? `url(#${stage.gradientId})`
                                : stage.color;
                            const computedFillOpacity = useGradient ? 1 : fillOpacity;
                            // Dim non-hovered segments when one is hovered
                            const isDimmed = hoveredIndex !== null && hoveredIndex !== index;
                            const segmentOpacity = isDimmed ? 0.4 : computedFillOpacity;
                            const transformOrigin = orientation === "horizontal"
                                ? `${((index * stageWidth + stageWidth / 2) / viewBoxWidth) * 100}% 50%`
                                : `50% ${((index * stageHeight + stageHeight / 2) / viewBoxHeight) *
                                    100}%`;
                            return (jsx(motion.path, { d: path, fill: fill, fillOpacity: segmentOpacity, stroke: "rgba(255, 255, 255, 0.8)", strokeWidth: "0.5", className: "cursor-pointer outline-none", style: { transformOrigin }, ...getAnimationProps(index), whileHover: animate ? { scale: 1.02 } : undefined, transition: { duration: 0.2 } }, stage.name));
                        }) })] }), stages.map((stage, index) => {
                const widthPercent = (stageWidth / viewBoxWidth) * 100;
                const heightPercent = (stageHeight / viewBoxHeight) * 100;
                return (jsx("button", { "data-stage-index": index, className: "absolute bg-transparent border-0 cursor-pointer focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-primary)] focus-visible:ring-offset-2 rounded", style: {
                        left: orientation === "horizontal"
                            ? `${((index * stageWidth) / viewBoxWidth) * 100}%`
                            : "10%",
                        top: orientation === "vertical"
                            ? `${((index * stageHeight) / viewBoxHeight) * 100}%`
                            : "20%",
                        width: orientation === "horizontal" ? `${widthPercent}%` : "80%",
                        height: orientation === "horizontal" ? "60%" : `${heightPercent}%`,
                    }, onMouseEnter: (e) => handleMouseEnter(e, stage, index), onMouseMove: handleMouseMove, onMouseLeave: handleMouseLeave, onClick: () => handleClick(stage, index), onKeyDown: (e) => handleKeyDown(e, stage, index), onFocus: () => handleFocus(stage, index), onBlur: handleBlur, "aria-label": `${stage.name}: ${formatDisplayValue(stage.value)} (${stage.percentage.toFixed(0)}% of total)${index > 0
                        ? `. Conversion from previous: ${stage.conversionRate.toFixed(1)}%`
                        : ""}`, tabIndex: index === 0 ? 0 : -1 }, `interactive-${stage.name}`));
            }), orientation === "horizontal" &&
                showLabels &&
                labelPosition !== "tooltip-only" &&
                stages.map((stage, index) => {
                    const leftPercent = ((index * stageWidth + stageWidth / 2) / viewBoxWidth) * 100;
                    return (jsxs(motion.div, { className: "absolute pointer-events-none", style: {
                            left: `${leftPercent}%`,
                            top: getLabelTopPosition(labelPosition),
                            bottom: getLabelBottomPosition(labelPosition),
                            transform: labelPosition === "inside"
                                ? "translate(-50%, -50%)"
                                : "translateX(-50%)",
                        }, ...(animate
                            ? {
                                variants: labelVariants,
                                initial: "hidden",
                                animate: "visible",
                                custom: index,
                            }
                            : {}), children: [jsx("div", { className: cn("text-base font-semibold whitespace-nowrap", labelPosition === "inside"
                                    ? "text-white drop-shadow-md"
                                    : "text-[var(--color-text-primary)]"), children: stage.name }), (showValues || showPercentages) && (jsxs("div", { className: cn("text-sm whitespace-nowrap", labelPosition === "inside"
                                    ? "text-white/90 drop-shadow-md"
                                    : "text-[var(--color-text-secondary)]"), children: [showValues && formatDisplayValue(stage.value), showValues && showPercentages && " • ", showPercentages && `${stage.percentage.toFixed(0)}%`] }))] }, `label-${stage.name}`));
                }), orientation === "vertical" &&
                showLabels &&
                labelPosition !== "tooltip-only" &&
                stages.map((stage, index) => {
                    const topPercent = ((index * stageHeight + stageHeight / 2) / viewBoxHeight) * 100;
                    return (jsxs(motion.div, { className: "absolute left-1/2 pointer-events-none", style: {
                            top: `${topPercent}%`,
                            transform: "translate(-50%, -50%)",
                        }, ...(animate
                            ? {
                                variants: labelVariants,
                                initial: "hidden",
                                animate: "visible",
                                custom: index,
                            }
                            : {}), children: [jsx("div", { className: "text-base font-semibold text-white drop-shadow-md whitespace-nowrap text-center", children: stage.name }), (showValues || showPercentages) && (jsxs("div", { className: "text-sm text-white/90 drop-shadow-md whitespace-nowrap text-center", children: [showValues && formatDisplayValue(stage.value), showValues && showPercentages && " • ", showPercentages && `${stage.percentage.toFixed(0)}%`] }))] }, `label-${stage.name}`));
                }), orientation === "horizontal" &&
                showDropoff &&
                stages.map((stage, index) => {
                    if (index === 0)
                        return null;
                    const leftPercent = ((index * stageWidth) / viewBoxWidth) * 100;
                    return (jsx(motion.div, { className: "absolute pointer-events-none", style: {
                            left: `${leftPercent}%`,
                            bottom: "4px",
                            transform: "translateX(-50%)",
                        }, ...(animate
                            ? {
                                variants: badgeVariants,
                                initial: "hidden",
                                animate: "visible",
                                custom: index,
                            }
                            : {}), children: jsxs("span", { className: "text-sm text-[var(--color-danger)] font-medium", children: ["-", stage.dropoff.toFixed(1), "%"] }) }, `dropoff-${stage.name}`));
                }), orientation === "vertical" &&
                showDropoff &&
                stages.map((stage, index) => {
                    if (index === 0)
                        return null;
                    const topPercent = ((index * stageHeight) / viewBoxHeight) * 100;
                    return (jsx(motion.div, { className: "absolute right-2 pointer-events-none", style: {
                            top: `${topPercent}%`,
                            transform: "translateY(-50%)",
                        }, ...(animate
                            ? {
                                variants: badgeVariants,
                                initial: "hidden",
                                animate: "visible",
                                custom: index,
                            }
                            : {}), children: jsxs("span", { className: "text-sm text-[var(--color-danger)] font-medium", children: ["-", stage.dropoff.toFixed(1), "%"] }) }, `dropoff-${stage.name}`));
                }), orientation === "horizontal" &&
                showConversionRate &&
                stages.map((stage, index) => {
                    if (index === 0)
                        return null;
                    const leftPercent = ((index * stageWidth) / viewBoxWidth) * 100;
                    const isGoodConversion = stage.conversionRate >= 50;
                    return (jsx(motion.div, { className: "absolute pointer-events-none", style: {
                            left: `${leftPercent}%`,
                            top: "50%",
                            transform: "translate(-50%, -50%)",
                        }, ...(animate
                            ? {
                                variants: badgeVariants,
                                initial: "hidden",
                                animate: "visible",
                                custom: index,
                            }
                            : {}), children: jsxs(Badge, { color: isGoodConversion ? "success" : "warning", variant: "soft", size: "sm", pill: true, children: ["\u2193 ", stage.conversionRate.toFixed(1), "%"] }) }, `conversion-${stage.name}`));
                }), orientation === "vertical" &&
                showConversionRate &&
                stages.map((stage, index) => {
                    if (index === 0)
                        return null;
                    const topPercent = ((index * stageHeight) / viewBoxHeight) * 100;
                    const isGoodConversion = stage.conversionRate >= 50;
                    return (jsx(motion.div, { className: "absolute left-2 pointer-events-none", style: {
                            top: `${topPercent}%`,
                            transform: "translateY(-50%)",
                        }, ...(animate
                            ? {
                                variants: badgeVariants,
                                initial: "hidden",
                                animate: "visible",
                                custom: index,
                            }
                            : {}), children: jsxs(Badge, { color: isGoodConversion ? "success" : "warning", variant: "soft", size: "sm", pill: true, children: [stage.conversionRate.toFixed(0), "%"] }) }, `conversion-${stage.name}`));
                }), showSummary && summaryPosition !== "none" && stages.length > 1 && (jsxs(motion.div, { className: cn("absolute bg-[var(--color-background)] rounded-lg shadow-sm border border-[var(--color-border)] px-3 py-2 pointer-events-none", getSummaryPositionClasses(summaryPosition)), ...(animate
                    ? {
                        variants: summaryVariants,
                        initial: "hidden",
                        animate: "visible",
                    }
                    : {}), children: [jsx("div", { className: "text-sm text-[var(--color-text-secondary)] mb-1", children: "Overall Conversion" }), jsxs("div", { className: "text-xl font-bold text-[var(--color-text-primary)]", children: [overallConversion, "%"] }), jsxs("div", { className: "text-sm text-[var(--color-text-muted)] mt-1", children: [formatDisplayValue(stages[0].value), " \u2192", " ", formatDisplayValue(stages[stages.length - 1].value)] })] })), jsx(ChartTooltip, { active: !!tooltipData, position: tooltipData ? { x: tooltipData.x, y: tooltipData.y } : { x: 0, y: 0 }, tooltipRef: tooltipRef, content: tooltipData === null || tooltipData === void 0 ? void 0 : tooltipData.content, chartType: "area" })] }));
};
FunnelChart.displayName = "FunnelChart";

export { FunnelChart };
