import { jsx, jsxs } from 'react/jsx-runtime';
import React__default, { useState, useMemo, useCallback } from 'react';
import { StarIcon } from '@heroicons/react/24/solid';
import { StarIcon as StarIcon$1 } from '@heroicons/react/24/outline';
import { cn } from '../../../utils/cn.js';
import { ratingTheme } from './Rating.theme.js';

/**
 * Rating component for displaying and collecting star-based ratings.
 *
 * Supports controlled and uncontrolled modes, half-star increments,
 * read-only display, keyboard navigation, and custom icons.
 *
 * @component
 * @example
 * ```tsx
 * <Rating defaultValue={3} />
 * <Rating value={3.5} allowHalf onChange={setRating} />
 * <Rating value={4} readOnly />
 * <Rating defaultValue={4} size="lg" color="primary" />
 * ```
 */
const Rating = React__default.memo(React__default.forwardRef(({ value: controlledValue, defaultValue = 0, max = 5, size = "md", color = "warning", readOnly = false, disabled = false, allowHalf = false, onChange, iconFilled, iconEmpty, className, theme: customTheme = {}, ...props }, ref) => {
    var _a, _b, _c;
    // Determine controlled vs uncontrolled mode
    const isControlled = controlledValue !== undefined;
    const [internalValue, setInternalValue] = useState(defaultValue);
    const currentValue = isControlled ? controlledValue : internalValue;
    // Hover state for preview
    const [hoverValue, setHoverValue] = useState(null);
    // Merge default theme with custom theme overrides
    const theme = useMemo(() => ({
        ...ratingTheme,
        ...customTheme,
        starSizes: {
            ...ratingTheme.starSizes,
            ...customTheme.starSizes,
        },
        gapSizes: {
            ...ratingTheme.gapSizes,
            ...customTheme.gapSizes,
        },
        filledColors: {
            ...ratingTheme.filledColors,
            ...customTheme.filledColors,
        },
        states: {
            ...ratingTheme.states,
            ...customTheme.states,
        },
    }), [customTheme]);
    const isInteractive = !readOnly && !disabled;
    // Set rating value
    const handleSetValue = useCallback((newValue) => {
        if (!isInteractive)
            return;
        // Clamp the value between 0 and max
        const clampedValue = Math.min(Math.max(0, newValue), max);
        if (!isControlled) {
            setInternalValue(clampedValue);
        }
        onChange === null || onChange === void 0 ? void 0 : onChange(clampedValue);
    }, [isInteractive, isControlled, max, onChange]);
    // Handle click on a star
    const handleStarClick = useCallback((starIndex, event) => {
        if (!isInteractive)
            return;
        if (allowHalf) {
            const rect = event.currentTarget.getBoundingClientRect();
            const clickX = event.clientX - rect.left;
            const isLeftHalf = clickX < rect.width / 2;
            const newValue = isLeftHalf ? starIndex - 0.5 : starIndex;
            handleSetValue(newValue);
        }
        else {
            handleSetValue(starIndex);
        }
    }, [isInteractive, allowHalf, handleSetValue]);
    // Handle mouse move over a star (hover preview)
    const handleStarMouseMove = useCallback((starIndex, event) => {
        if (!isInteractive)
            return;
        if (allowHalf) {
            const rect = event.currentTarget.getBoundingClientRect();
            const mouseX = event.clientX - rect.left;
            const isLeftHalf = mouseX < rect.width / 2;
            setHoverValue(isLeftHalf ? starIndex - 0.5 : starIndex);
        }
        else {
            setHoverValue(starIndex);
        }
    }, [isInteractive, allowHalf]);
    // Clear hover state
    const handleMouseLeave = useCallback(() => {
        if (!isInteractive)
            return;
        setHoverValue(null);
    }, [isInteractive]);
    // Keyboard navigation
    const handleKeyDown = useCallback((event) => {
        if (!isInteractive)
            return;
        const step = allowHalf ? 0.5 : 1;
        let newValue = currentValue;
        switch (event.key) {
            case "ArrowRight":
            case "ArrowUp":
                event.preventDefault();
                newValue = Math.min(currentValue + step, max);
                break;
            case "ArrowLeft":
            case "ArrowDown":
                event.preventDefault();
                newValue = Math.max(currentValue - step, 0);
                break;
            case "Home":
                event.preventDefault();
                newValue = 0;
                break;
            case "End":
                event.preventDefault();
                newValue = max;
                break;
            default:
                return;
        }
        handleSetValue(newValue);
    }, [isInteractive, allowHalf, currentValue, max, handleSetValue]);
    // Determine the display value (hover preview takes precedence)
    const displayValue = hoverValue !== null ? hoverValue : currentValue;
    // Determine fill state for a given star index (1-based)
    const getStarFillState = (starIndex) => {
        if (displayValue >= starIndex)
            return "full";
        if (displayValue >= starIndex - 0.5)
            return "half";
        return "empty";
    };
    // Render a single star
    const renderStar = (starIndex) => {
        var _a, _b;
        const fillState = getStarFillState(starIndex);
        const starSizeClass = ((_a = theme.starSizes) === null || _a === void 0 ? void 0 : _a[size]) || "";
        const filledColorClass = ((_b = theme.filledColors) === null || _b === void 0 ? void 0 : _b[color]) || "";
        const emptyColorClass = theme.emptyColor || "";
        // Determine which icon to render based on fill state
        const renderStarIcon = () => {
            if (fillState === "full") {
                return iconFilled ? (jsx("span", { className: cn(starSizeClass, filledColorClass), "aria-hidden": "true", children: iconFilled })) : (jsx(StarIcon, { className: cn(starSizeClass, filledColorClass), "aria-hidden": "true" }));
            }
            if (fillState === "half") {
                // Half star: clip a filled star to the left half, overlay on empty star
                return (jsxs("span", { className: cn("relative inline-flex", starSizeClass), "aria-hidden": "true", children: [iconEmpty ? (jsx("span", { className: cn("absolute inset-0", emptyColorClass), children: iconEmpty })) : (jsx(StarIcon$1, { className: cn("absolute inset-0", starSizeClass, emptyColorClass) })), jsx("span", { className: "absolute inset-0 overflow-hidden", style: { width: "50%" }, children: iconFilled ? (jsx("span", { className: cn(filledColorClass), children: iconFilled })) : (jsx(StarIcon, { className: cn(starSizeClass, filledColorClass) })) })] }));
            }
            // Empty star
            return iconEmpty ? (jsx("span", { className: cn(starSizeClass, emptyColorClass), "aria-hidden": "true", children: iconEmpty })) : (jsx(StarIcon$1, { className: cn(starSizeClass, emptyColorClass), "aria-hidden": "true" }));
        };
        if (!isInteractive) {
            // Read-only or disabled: render as a span
            return (jsx("span", { className: "inline-flex items-center justify-center", children: renderStarIcon() }, starIndex));
        }
        // Interactive: render as a button
        return (jsx("button", { type: "button", className: cn(theme.starStyle, "hover:scale-110 active:scale-95"), onClick: (e) => handleStarClick(starIndex, e), onMouseMove: (e) => handleStarMouseMove(starIndex, e), "aria-label": `Rate ${starIndex} out of ${max}`, tabIndex: -1, children: renderStarIcon() }, starIndex));
    };
    // Build array of star indices (1-based)
    const stars = useMemo(() => Array.from({ length: max }, (_, i) => i + 1), [max]);
    return (jsx("div", { ref: ref, role: isInteractive ? "slider" : "img", "aria-label": `Rating: ${currentValue} out of ${max} stars`, "aria-valuenow": isInteractive ? currentValue : undefined, "aria-valuemin": isInteractive ? 0 : undefined, "aria-valuemax": isInteractive ? max : undefined, "aria-readonly": readOnly || undefined, "aria-disabled": disabled || undefined, tabIndex: isInteractive ? 0 : undefined, onKeyDown: isInteractive ? handleKeyDown : undefined, onMouseLeave: handleMouseLeave, "data-rating-value": currentValue, "data-state": disabled ? "disabled" : readOnly ? "readonly" : "interactive", ...props, className: cn(theme.baseStyle, (_a = theme.gapSizes) === null || _a === void 0 ? void 0 : _a[size], disabled && ((_b = theme.states) === null || _b === void 0 ? void 0 : _b.disabled), readOnly && ((_c = theme.states) === null || _c === void 0 ? void 0 : _c.readOnly), className), children: stars.map(renderStar) }));
}));
Rating.displayName = "Rating";

export { Rating };
