"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.toast = void 0;
exports.useToast = useToast;
exports.ToastProvider = ToastProvider;
exports.AndMore = AndMore;
exports.Toast = Toast;
const tslib_1 = require("tslib");
const jsx_runtime_1 = require("react/jsx-runtime");
const portal_1 = require("@rn-primitives/portal");
const lucide_react_native_1 = require("lucide-react-native");
const react_1 = require("react");
const react_native_1 = require("react-native");
const react_native_gesture_handler_1 = require("react-native-gesture-handler");
const react_native_reanimated_1 = tslib_1.__importStar(require("react-native-reanimated"));
const react_native_safe_area_context_1 = require("react-native-safe-area-context");
const react_native_svg_1 = require("react-native-svg");
const theme_1 = require("../../lib/theme/theme");
const button_1 = require("./button");
const text_1 = require("./text");
class ToastManager {
    constructor() {
        this.listeners = new Set();
        this.toasts = [];
        this.toastHeights = new Map();
        this.hoverListeners = new Set();
        this.isHovered = false;
    }
    show(config) {
        const toast = {
            id: Math.random().toString(36).slice(2, 12),
            open: true,
            title: config.title,
            description: config.description,
            duration: config.duration ?? 3,
            actionLabel: config.actionLabel,
            onAction: config.onAction,
            onClose: config.onClose,
            variant: config.variant ?? "default",
            render: config.render,
            position: config.position ?? "auto",
            iconLeft: config.iconLeft,
            iconRight: config.iconRight,
            onToastPress: config.onToastPress,
        };
        this.toasts = [...this.toasts, toast];
        this.notifyListeners();
    }
    getToasts() {
        return this.toasts;
    }
    hide(id) {
        const toast = this.toasts.find((t) => t.id === id);
        if (toast?.onClose) {
            toast.onClose();
        }
        this.toasts = this.toasts.map((toast) => toast.id === id ? { ...toast, open: false } : toast);
        this.notifyListeners();
        setTimeout(() => {
            this.toasts = this.toasts.filter((toast) => toast.id !== id);
            this.notifyListeners();
        }, 500);
    }
    subscribe(listener) {
        this.listeners.add(listener);
        return () => {
            this.listeners.delete(listener);
        };
    }
    subscribeHover(listener) {
        this.hoverListeners.add(listener);
        return () => {
            this.hoverListeners.delete(listener);
        };
    }
    setHovered(hovered) {
        this.isHovered = hovered;
        this.notifyHoverListeners();
    }
    updateToastHeight(id, height) {
        this.toastHeights.set(id, height);
    }
    getToastHeight(id) {
        return this.toastHeights.get(id) || 100; // Default fallback height
    }
    notifyListeners() {
        this.listeners.forEach((listener) => listener(this.toasts));
    }
    notifyHoverListeners() {
        this.hoverListeners.forEach((listener) => listener(this.isHovered));
    }
}
const toastManager = new ToastManager();
exports.toast = {
    show: (title, description, options) => {
        toastManager.show({
            title,
            description,
            ...options,
        });
    },
    hide: (id) => {
        if (id) {
            toastManager.hide(id);
        }
        else {
            const toasts = toastManager.getToasts();
            if (toasts.length > 0) {
                toastManager.hide(toasts[toasts.length - 1].id);
            }
        }
    },
    showManual: (render, options) => {
        toastManager.show({
            render,
            ...options,
        });
    },
};
function useToast() {
    const [toasts, setToasts] = (0, react_1.useState)([]);
    (0, react_1.useEffect)(() => {
        return toastManager.subscribe(setToasts);
    }, []);
    return {
        toasts,
        ...exports.toast,
    };
}
function ToastProvider() {
    const [toasts, setToasts] = (0, react_1.useState)([]);
    (0, react_1.useEffect)(() => {
        return toastManager.subscribe(setToasts);
    }, []);
    const toastsByPosition = toasts.reduce((acc, toast) => {
        const { position } = toast;
        if (!acc[position]) {
            acc[position] = [];
        }
        acc[position].push(toast);
        return acc;
    }, {});
    return ((0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: Object.entries(toastsByPosition).map(([position, toasts]) => ((0, jsx_runtime_1.jsx)(ToastContainer, { position: position, toasts: toasts }, position))) }));
}
function ToastContainer({ position = "auto", toasts, }) {
    const insets = (0, react_native_safe_area_context_1.useSafeAreaInsets)();
    const { theme } = (0, theme_1.useTheme)();
    const isWeb = react_native_1.Platform.OS === "web";
    const { width } = (0, react_native_1.useWindowDimensions)();
    const isDesktop = isWeb && width >= 768;
    const isTop = position.includes("top");
    const visibleToasts = toasts.slice(-4);
    const prevToastIds = (0, react_1.useRef)(visibleToasts.map((t) => t.id));
    const allKnownToastIds = (0, react_1.useRef)(new Set(toasts.map((t) => t.id)));
    const [isHovered, setIsHovered] = (0, react_1.useState)(false);
    (0, react_1.useEffect)(() => {
        return toastManager.subscribeHover(setIsHovered);
    }, []);
    (0, react_1.useEffect)(() => {
        const currentIds = visibleToasts.map((t) => t.id);
        const hasNewToast = currentIds.some((id) => !allKnownToastIds.current.has(id));
        if (hasNewToast && isHovered) {
            // Brand new toast arrived while expanded - collapse momentarily
            toastManager.setHovered(false);
            setTimeout(() => {
                toastManager.setHovered(true);
            }, 700);
        }
        else if (isHovered) {
            toastManager.setHovered(true);
            setTimeout(() => {
                toastManager.setHovered(true);
            }, 700);
        }
        // Update known toast IDs
        toasts.forEach((t) => allKnownToastIds.current.add(t.id));
        prevToastIds.current = currentIds;
    }, [visibleToasts, isHovered, toasts]);
    const setHovered = (value) => toastManager.setHovered(value);
    const pan = react_native_gesture_handler_1.Gesture.Pan()
        .onUpdate((event) => {
        const velocity = isTop ? -event.velocityY : event.velocityY;
        if (velocity > 500) {
            (0, react_native_reanimated_1.runOnJS)(setHovered)(true);
        }
        else if (velocity < -500) {
            (0, react_native_reanimated_1.runOnJS)(setHovered)(false);
        }
    })
        .onEnd((event) => {
        const translationY = isTop ? -event.translationY : event.translationY;
        if (translationY > 50) {
            (0, react_native_reanimated_1.runOnJS)(setHovered)(true);
        }
        else if (translationY < -50) {
            (0, react_native_reanimated_1.runOnJS)(setHovered)(false);
        }
    });
    const gesture = react_native_1.Platform.OS === "web"
        ? react_native_gesture_handler_1.Gesture.Hover()
            .onStart(() => {
            (0, react_native_reanimated_1.runOnJS)(setHovered)(true);
        })
            .onEnd(() => {
            (0, react_native_reanimated_1.runOnJS)(setHovered)(false);
        })
        : pan;
    const getPositionStyle = () => {
        const resolvedPosition = position === "auto"
            ? isDesktop
                ? "bottom-right"
                : "top-center"
            : position;
        const styles = {
            position: "absolute",
            zIndex: 1000,
            paddingHorizontal: isDesktop ? 0 : theme.spacing[4],
        };
        // Set width/maxWidth
        if (isDesktop) {
            styles.width = 400;
        }
        else {
            styles.width = "100%";
            styles.maxWidth = 400;
        }
        if (resolvedPosition.includes("top")) {
            styles.top = insets.top + theme.spacing[4];
        }
        if (resolvedPosition.includes("bottom")) {
            styles.bottom = insets.bottom + theme.spacing[4];
        }
        if (resolvedPosition.includes("left")) {
            styles.left = insets.left + theme.spacing[4];
            styles.alignItems = "flex-start";
        }
        if (resolvedPosition.includes("right")) {
            styles.right = insets.right + theme.spacing[4];
            styles.alignItems = "flex-end";
        }
        if (resolvedPosition.includes("center")) {
            styles.alignItems = "center";
            // Center the container itself when it has maxWidth
            if (!isDesktop) {
                styles.left = "50%";
                styles.transform = [{ translateX: "-50%" }];
            }
            else {
                styles.left = 0;
                styles.right = 0;
            }
        }
        return styles;
    };
    return ((0, jsx_runtime_1.jsx)(portal_1.Portal, { name: "toasties", children: (0, jsx_runtime_1.jsx)(react_native_gesture_handler_1.GestureDetector, { gesture: gesture, children: (0, jsx_runtime_1.jsx)(react_native_1.View, { style: getPositionStyle(), children: visibleToasts.reverse().map((toastState, index) => ((0, jsx_runtime_1.jsx)(Toast, { ...toastState, onOpenChange: (open) => {
                        if (!open)
                            toastManager.hide(toastState.id);
                    }, index: index, isLatest: index === 0, position: position === "auto"
                        ? isDesktop
                            ? "bottom-right"
                            : "top-center"
                        : position }, toastState.id))) }) }) }));
}
function AndMore({ more }) {
    const { theme } = (0, theme_1.useTheme)();
    return ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: {
            padding: theme.spacing[2],
            paddingHorizontal: theme.spacing[4],
            backgroundColor: theme.colors.muted,
            borderRadius: theme.borderRadius.xl,
            marginTop: theme.spacing[2],
            alignSelf: "center",
        }, children: (0, jsx_runtime_1.jsxs)(text_1.Text, { size: "sm", style: { color: theme.colors.mutedForeground }, children: ["and ", more, " more notification", more === 1 ? "" : "s"] }) }));
}
const AnimatedCircle = react_native_reanimated_1.default.createAnimatedComponent(react_native_svg_1.Circle);
function CloseButton({ onPress, isLatest, duration, animatedCircleProps, theme, RADIUS, CIRCUMFERENCE, }) {
    return ((0, jsx_runtime_1.jsxs)(react_native_1.Pressable, { onPress: onPress, style: {
            alignItems: "center",
            justifyContent: "center",
        }, children: [(0, jsx_runtime_1.jsxs)(react_native_svg_1.Svg, { width: RADIUS * 2, height: RADIUS * 2, viewBox: `0 0 ${RADIUS * 2 + 2} ${RADIUS * 2 + 2}`, children: [(0, jsx_runtime_1.jsx)(AnimatedCircle, { stroke: theme.colors.border, fill: "transparent", strokeWidth: "2", r: RADIUS, cx: RADIUS + 1, cy: RADIUS + 1 }), isLatest && duration > 0 && ((0, jsx_runtime_1.jsx)(AnimatedCircle, { animatedProps: animatedCircleProps, stroke: theme.colors.primary, fill: "transparent", strokeWidth: "2", strokeDasharray: CIRCUMFERENCE, r: RADIUS, cx: RADIUS + 1, cy: RADIUS + 1, rotation: "-90", originX: RADIUS + 1, originY: RADIUS + 1, strokeLinecap: "round" }))] }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: { position: "absolute" }, children: (0, jsx_runtime_1.jsx)(lucide_react_native_1.X, { color: theme.colors.foreground, size: 12 }) })] }));
}
function Toast({ open = false, onOpenChange = () => { }, title = "", description, actionLabel = "Action", onAction, duration = 60, index = 0, isLatest = true, variant = "default", render, position = "auto", id, iconLeft: IconLeft, iconRight: IconRight, onToastPress, }) {
    const [isHovered, setIsHovered] = (0, react_1.useState)(false);
    const progress = (0, react_native_reanimated_1.useSharedValue)(1);
    const remainingTime = (0, react_native_reanimated_1.useSharedValue)(duration * 1000);
    const wasOpen = (0, react_1.useRef)(open);
    const [measuredHeight, setMeasuredHeight] = (0, react_1.useState)(100);
    const { theme } = (0, theme_1.useTheme)();
    const isWeb = react_native_1.Platform.OS === "web";
    const { width } = (0, react_native_1.useWindowDimensions)();
    const isDesktop = isWeb && width >= 768;
    const { top, bottom } = (0, react_native_safe_area_context_1.useSafeAreaInsets)();
    const isTop = position.includes("top");
    const RADIUS = 12;
    const CIRCUMFERENCE = 2 * Math.PI * RADIUS;
    const dismissTranslateY = (0, react_native_reanimated_1.useSharedValue)(0);
    const dismissTranslateX = (0, react_native_reanimated_1.useSharedValue)(0);
    // Auto-select iconLeft based on variant if not provided
    const defaultIconLeft = !IconLeft && !onAction
        ? variant === "success"
            ? lucide_react_native_1.CheckCircle
            : variant === "error"
                ? lucide_react_native_1.XCircle
                : variant === "info"
                    ? lucide_react_native_1.Info
                    : null
        : null;
    const FinalIconLeft = IconLeft || defaultIconLeft;
    const FinalIconRight = IconRight;
    const animatedCircleProps = (0, react_native_reanimated_1.useAnimatedProps)(() => {
        return {
            strokeDashoffset: CIRCUMFERENCE * (1 - progress.value),
        };
    });
    const opacity = (0, react_native_reanimated_1.useSharedValue)(0);
    const translateY = (0, react_native_reanimated_1.useSharedValue)(isTop ? -100 : 100);
    const scale = (0, react_native_reanimated_1.useSharedValue)(1 - index * 0.05);
    (0, react_1.useEffect)(() => {
        return toastManager.subscribeHover(setIsHovered);
    }, []);
    (0, react_1.useEffect)(() => {
        if (open && !wasOpen.current) {
            // Toast just opened
            progress.value = 1;
            remainingTime.value = duration * 1000;
        }
        wasOpen.current = open;
        if (!open) {
            // Close animation
            opacity.value = (0, react_native_reanimated_1.withTiming)(0, { duration: 250 });
            translateY.value = (0, react_native_reanimated_1.withTiming)(isTop ? -100 : 100, { duration: 250 });
            return;
        }
        if (isHovered) {
            // Stack vertically with proper spacing when hovered
            // Calculate cumulative height of all toasts before this one
            let cumulativeHeight = 0;
            const allToasts = toastManager.getToasts().slice(-4).reverse(); // Get visible toasts in same order as rendered
            for (let i = 0; i < index; i++) {
                if (allToasts[i]) {
                    cumulativeHeight += toastManager.getToastHeight(allToasts[i].id) + 8; // height + gap
                }
            }
            translateY.value = (0, react_native_reanimated_1.withTiming)(isTop ? cumulativeHeight : -cumulativeHeight, {
                duration: 750,
                easing: react_native_reanimated_1.Easing.out(react_native_reanimated_1.Easing.exp),
            });
            scale.value = (0, react_native_reanimated_1.withTiming)(1, {
                duration: 750,
                easing: react_native_reanimated_1.Easing.out(react_native_reanimated_1.Easing.exp),
            });
            opacity.value = (0, react_native_reanimated_1.withTiming)(1, {
                duration: 750,
                easing: react_native_reanimated_1.Easing.out(react_native_reanimated_1.Easing.exp),
            });
        }
        else {
            // Compact stacked view when not hovered
            translateY.value = (0, react_native_reanimated_1.withTiming)((isTop ? index : -index) * 15, {
                duration: 750,
                easing: react_native_reanimated_1.Easing.out(react_native_reanimated_1.Easing.exp),
            });
            scale.value = (0, react_native_reanimated_1.withTiming)(1 - index * 0.1, {
                duration: 750,
                easing: react_native_reanimated_1.Easing.out(react_native_reanimated_1.Easing.exp),
            });
            opacity.value = (0, react_native_reanimated_1.withTiming)(isLatest ? 1 : 0.95, {
                duration: 750,
                easing: react_native_reanimated_1.Easing.out(react_native_reanimated_1.Easing.exp),
            });
        }
    }, [open, isHovered, index, isLatest, measuredHeight, duration, id]);
    (0, react_1.useEffect)(() => {
        if (open && isLatest && duration > 0) {
            if (isHovered) {
                (0, react_native_reanimated_1.cancelAnimation)(progress);
                remainingTime.value = progress.value * duration * 1000;
            }
            else {
                progress.value = (0, react_native_reanimated_1.withTiming)(0, {
                    duration: remainingTime.value,
                    easing: react_native_reanimated_1.Easing.linear,
                }, (finished) => {
                    if (finished) {
                        (0, react_native_reanimated_1.runOnJS)(onOpenChange)(false);
                    }
                });
            }
        }
        else {
            (0, react_native_reanimated_1.cancelAnimation)(progress);
        }
        return () => {
            (0, react_native_reanimated_1.cancelAnimation)(progress);
        };
    }, [open, isLatest, isHovered, duration]);
    const dismissGestureVertical = react_native_gesture_handler_1.Gesture.Pan()
        .enabled(isLatest && !isHovered)
        .activeOffsetY(isTop ? [-10, 999999] : [-999999, 10])
        .onUpdate((event) => {
        const direction = isTop ? -1 : 1;
        const movement = event.translationY * direction;
        if (movement > 0) {
            dismissTranslateY.value = event.translationY;
            dismissTranslateX.value = 0;
        }
    })
        .onEnd((event) => {
        const direction = isTop ? -1 : 1;
        const movement = event.translationY * direction;
        const velocity = event.velocityY * direction;
        if (movement > 50 || velocity > 500) {
            // Dismiss - slide out in expansion direction
            dismissTranslateY.value = (0, react_native_reanimated_1.withTiming)(isTop ? -200 : 200, { duration: 250 }, (finished) => {
                if (finished) {
                    (0, react_native_reanimated_1.runOnJS)(onOpenChange)(false);
                }
            });
            opacity.value = (0, react_native_reanimated_1.withTiming)(0, { duration: 250 });
        }
        else {
            // Spring back
            dismissTranslateY.value = (0, react_native_reanimated_1.withTiming)(0, { duration: 200 });
        }
    });
    const dismissGestureHorizontal = react_native_gesture_handler_1.Gesture.Pan()
        .enabled(isLatest || isHovered)
        .activeOffsetX([-10, 10])
        .onUpdate((event) => {
        if (event.translationX < 0) {
            dismissTranslateX.value = event.translationX;
            dismissTranslateY.value = 0;
        }
    })
        .onEnd((event) => {
        // Check for horizontal swipe left
        if (event.translationX < -100 || event.velocityX < -500) {
            dismissTranslateX.value = (0, react_native_reanimated_1.withTiming)(-400, { duration: 250 }, (finished) => {
                if (finished) {
                    (0, react_native_reanimated_1.runOnJS)(onOpenChange)(false);
                }
            });
            opacity.value = (0, react_native_reanimated_1.withTiming)(0, { duration: 250 });
        }
        else {
            // Spring back
            dismissTranslateX.value = (0, react_native_reanimated_1.withTiming)(0, { duration: 200 });
        }
    });
    const dismissGesture = react_native_gesture_handler_1.Gesture.Race(dismissGestureVertical, dismissGestureHorizontal);
    const animatedStyle = (0, react_native_reanimated_1.useAnimatedStyle)(() => {
        return {
            opacity: opacity.value,
            transform: [
                // +22 is to get it just below the header
                {
                    translateY: translateY.value +
                        dismissTranslateY.value +
                        (isTop ? top / 2 : -bottom),
                },
                { translateX: dismissTranslateX.value },
                { scale: scale.value },
            ],
            zIndex: 1000 - index,
        };
    });
    const variantStyles = {
        default: {
            backgroundColor: theme.colors.secondary,
            borderColor: theme.colors.border,
        },
        success: {
            backgroundColor: theme.colors.success,
            borderColor: theme.colors.success,
        },
        error: {
            backgroundColor: theme.colors.destructive,
            borderColor: theme.colors.destructive,
        },
        info: {
            backgroundColor: theme.colors.info,
            borderColor: theme.colors.info,
        },
    };
    const buttonTypeMap = {
        default: "primary",
        success: "success",
        error: "primary",
        info: "secondary",
    };
    return ((0, jsx_runtime_1.jsx)(react_native_reanimated_1.default.View, { onLayout: (l) => {
            const height = l.nativeEvent.layout.height;
            setMeasuredHeight(height);
            if (id) {
                toastManager.updateToastHeight(id, height);
            }
        }, style: [
            isTop ? styles.containerTop : styles.containerBottom,
            animatedStyle,
        ], children: (0, jsx_runtime_1.jsx)(react_native_gesture_handler_1.GestureDetector, { gesture: dismissGesture, children: (0, jsx_runtime_1.jsx)(react_native_1.Pressable, { onPress: onToastPress, disabled: !onToastPress, style: [
                    styles.toast,
                    {
                        borderRadius: theme.borderRadius.xl,
                        flexDirection: "column",
                        justifyContent: "space-between",
                        alignItems: "center",
                        padding: theme.spacing[4],
                        width: "100%",
                    },
                    variantStyles[variant],
                ], children: render ? (render({ close: () => onOpenChange(false), action: onAction })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)(react_native_1.View, { style: {
                                flexDirection: "row",
                                alignItems: "flex-start",
                                justifyContent: "space-between",
                                width: "100%",
                                gap: theme.spacing[4],
                            }, children: [(0, jsx_runtime_1.jsxs)(react_native_1.View, { style: {
                                        flexDirection: "row",
                                        flex: 1,
                                        gap: theme.spacing[3],
                                    }, children: [FinalIconLeft && ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: { paddingTop: 2 }, children: (0, jsx_runtime_1.jsx)(FinalIconLeft, { color: theme.colors.foreground }) })), (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: { flex: 1 }, children: [(0, jsx_runtime_1.jsx)(text_1.Text, { size: "lg", children: title }), description ? (0, jsx_runtime_1.jsx)(text_1.Text, { children: description }) : null] })] }), FinalIconRight && !onAction ? ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: {
                                        gap: theme.spacing[2],
                                        height: "100%",
                                        justifyContent: "space-between",
                                    }, children: [(0, jsx_runtime_1.jsx)(react_native_1.Pressable, { onPress: onToastPress, children: (0, jsx_runtime_1.jsx)(FinalIconRight, { color: theme.colors.foreground }) }), (0, jsx_runtime_1.jsx)(CloseButton, { onPress: () => onOpenChange(false), isLatest: isLatest, duration: duration, animatedCircleProps: animatedCircleProps, theme: theme, RADIUS: RADIUS, CIRCUMFERENCE: CIRCUMFERENCE })] })) : !onAction ? ((0, jsx_runtime_1.jsx)(CloseButton, { onPress: () => onOpenChange(false), isLatest: isLatest, duration: duration, animatedCircleProps: animatedCircleProps, theme: theme, RADIUS: RADIUS, CIRCUMFERENCE: CIRCUMFERENCE })) : null] }), onAction && ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: {
                                gap: theme.spacing[1],
                                flexDirection: "row",
                                justifyContent: "flex-end",
                                width: "100%",
                            }, children: [(0, jsx_runtime_1.jsx)(button_1.Button, { width: "min", variant: buttonTypeMap[variant], onPress: onAction, children: (0, jsx_runtime_1.jsx)(text_1.Text, { style: { color: theme.colors.foreground }, children: actionLabel }) }), (0, jsx_runtime_1.jsx)(button_1.Button, { variant: "secondary", width: "min", onPress: () => onOpenChange(false), children: (0, jsx_runtime_1.jsx)(text_1.Text, { style: { color: theme.colors.foreground }, children: "Close" }) })] }))] })) }) }) }));
}
const styles = react_native_1.StyleSheet.create({
    providerContainer: {
        position: "absolute",
        zIndex: 1000,
    },
    containerBottom: {
        position: "absolute",
        bottom: 0,
        left: 0,
        right: 0,
        alignItems: "center",
    },
    containerTop: {
        position: "absolute",
        top: 0,
        left: 0,
        right: 0,
        alignItems: "center",
    },
    toast: {
        opacity: 0.99,
        borderWidth: 1,
        gap: 8,
    },
    button: {
        borderWidth: 1,
        borderRadius: 8,
        alignItems: "center",
        justifyContent: "center",
        backgroundColor: "transparent",
    },
});
//# sourceMappingURL=toast.js.map