"use client";
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useEffect, useState, useMemo, useRef, useCallback } from "react";
import { Button } from "../../components/ui/button";
import { Input } from "../../components/ui/input";
import { Dialog, DialogContent } from "../../components/ui/dialog";
import { StyledDialogTitle } from "../../components/ui/styled-dialog-title";
import DialogButtons from "./DialogButtons";
import { getAvailableIcons } from "../services/iconService";
import { useEditContext } from "../client/editContext";
import { Spinner } from "./Spinner";
import { Search, ImageOff, ImageIcon } from "lucide-react";
import { cn } from "../../lib/utils";
// Helper function to try alternative URL patterns for failed icons
function getAlternativeIconUrl(originalUrl) {
    // If it's a /-/icon/ pattern, try converting to iconcache pattern
    const iconMatch = originalUrl.match(/(.+?)\/-\/icon\/(.+)/);
    if (iconMatch && iconMatch[1] && iconMatch[2]) {
        const baseUrl = iconMatch[1]; // e.g., "http://localhost:5002"
        const iconPath = iconMatch[2]; // e.g., "BusinessV2/BusinessV2/32x32/adhesive_tape.png.aspx"
        // Remove .aspx extension if present
        const cleanPath = iconPath.replace(/\.aspx$/, "");
        // Try iconcache pattern
        return `${baseUrl}/temp/iconcache/${cleanPath}`;
    }
    return null;
}
// Lazy-loaded icon component with Intersection Observer
function LazyIcon({ icon, isSelected, onSelect, onDoubleClick, onLoadError, }) {
    const containerRef = useRef(null);
    const [isInView, setIsInView] = useState(false);
    const [imageError, setImageError] = useState(false);
    const [fallbackUrl, setFallbackUrl] = useState(null);
    useEffect(() => {
        const container = containerRef.current;
        if (!container)
            return;
        const observer = new IntersectionObserver((entries) => {
            entries.forEach((entry) => {
                if (entry.isIntersecting) {
                    setIsInView(true);
                    observer.disconnect();
                }
            });
        }, {
            rootMargin: "50px", // Start loading 50px before entering viewport
        });
        observer.observe(container);
        return () => {
            observer.disconnect();
        };
    }, []);
    const handleImageError = (e) => {
        // If we haven't tried a fallback yet, try alternative URL
        if (!fallbackUrl) {
            const alternativeUrl = getAlternativeIconUrl(icon.url);
            if (alternativeUrl) {
                setFallbackUrl(alternativeUrl);
                // Component will re-render and try the fallback URL
                return;
            }
        }
        // If fallback also failed or no fallback available, mark as error
        // Only remove from list if both original and fallback failed
        if (fallbackUrl || !getAlternativeIconUrl(icon.url)) {
            setImageError(true);
            // Notify parent to remove this icon from the list
            onLoadError?.(icon.path);
        }
    };
    const imageUrl = fallbackUrl || icon.url;
    const showPlaceholder = !isInView || imageError;
    return (_jsxs("button", { ref: containerRef, type: "button", "data-testid": "icon-option", "data-icon-name": icon.name, "data-icon-path": icon.path, onClick: () => onSelect(icon.path), onDoubleClick: () => onDoubleClick?.(icon.path), className: cn("flex flex-col items-center justify-center rounded-xl border border-transparent p-3 transition-all duration-200", "hover:border-border-default hover:bg-neutral-grey-5 hover:shadow-sm", isSelected &&
            "border-primary bg-primary/5 ring-primary/20 shadow-sm ring-2"), title: icon.name, children: [!isInView ? (_jsx("div", { className: "bg-neutral-grey-5 h-8 w-8 animate-pulse rounded", "aria-hidden": "true" })) : imageError ? (_jsx("div", { className: "bg-neutral-grey-5 flex h-8 w-8 items-center justify-center rounded", "aria-hidden": "true", children: _jsx(ImageOff, { className: "text-neutral-grey-50 h-5 w-5", strokeWidth: 1.5 }) })) : (_jsx("img", { src: imageUrl, alt: icon.name, className: "h-10 w-10 object-contain transition-transform duration-200 group-hover:scale-110", onError: handleImageError, loading: "lazy" }, imageUrl)), _jsx("span", { className: "text-neutral-grey-50 mt-2 w-full truncate text-center text-[10px] font-medium", children: icon.name })] }));
}
// Virtual scrolling hook
function useVirtualScroll(items, containerRef, itemHeight, itemWidth) {
    const [visibleRange, setVisibleRange] = useState({ start: 0, end: 50 });
    const [columnsPerRow, setColumnsPerRow] = useState(1);
    useEffect(() => {
        const container = containerRef.current;
        if (!container)
            return;
        let rafId = null;
        const updateVisibleRange = () => {
            const containerRect = container.getBoundingClientRect();
            const scrollTop = container.scrollTop;
            const containerHeight = containerRect.height;
            // Calculate columns per row based on container width,
            // taking the actual horizontal padding into account
            const computedStyle = window.getComputedStyle(container);
            const paddingLeft = parseFloat(computedStyle.paddingLeft || "0");
            const paddingRight = parseFloat(computedStyle.paddingRight || "0");
            const containerWidth = containerRect.width - paddingLeft - paddingRight;
            const cols = Math.floor(containerWidth / itemWidth) || 1;
            setColumnsPerRow(cols);
            // Calculate visible rows
            const rowHeight = itemHeight + 8; // item height + gap (gap-2 = 8px)
            const startRow = Math.max(0, Math.floor(scrollTop / rowHeight) - 2); // Buffer of 2 rows above
            const endRow = Math.min(Math.ceil(items.length / cols), Math.ceil((scrollTop + containerHeight) / rowHeight) + 2) - 1;
            const start = Math.max(0, startRow * cols);
            const end = Math.min(items.length, (endRow + 1) * cols);
            setVisibleRange({ start, end });
        };
        const handleScroll = () => {
            if (rafId !== null) {
                cancelAnimationFrame(rafId);
            }
            rafId = requestAnimationFrame(updateVisibleRange);
        };
        const handleResize = () => {
            if (rafId !== null) {
                cancelAnimationFrame(rafId);
            }
            rafId = requestAnimationFrame(updateVisibleRange);
        };
        updateVisibleRange();
        container.addEventListener("scroll", handleScroll, { passive: true });
        window.addEventListener("resize", handleResize);
        return () => {
            container.removeEventListener("scroll", handleScroll);
            window.removeEventListener("resize", handleResize);
            if (rafId !== null) {
                cancelAnimationFrame(rafId);
            }
        };
    }, [items.length, containerRef, itemHeight, itemWidth]);
    const visibleItems = useMemo(() => {
        return items.slice(visibleRange.start, visibleRange.end);
    }, [items, visibleRange.start, visibleRange.end]);
    // Calculate total height for the container
    const totalHeight = Math.ceil(items.length / columnsPerRow) * (itemHeight + 8);
    return {
        visibleItems,
        startIndex: visibleRange.start,
        totalHeight,
        columnsPerRow,
    };
}
export function IconSelectorDialog(props) {
    const editContext = useEditContext();
    const [icons, setIcons] = useState([]);
    const [loading, setLoading] = useState(true);
    const [searchQuery, setSearchQuery] = useState("");
    const [selectedIcon, setSelectedIcon] = useState(props.currentIconPath || null);
    const scrollContainerRef = useRef(null);
    const failedIconPaths = useRef(new Set());
    // Icon item dimensions (approximate)
    const ITEM_HEIGHT = 110; // p-3 (12px) + img (40px) + text (~20px) + margins
    const ITEM_WIDTH = 100; // minmax(100px, 1fr)
    useEffect(() => {
        const loadIcons = async () => {
            if (!editContext?.sessionId)
                return;
            setLoading(true);
            try {
                const result = await getAvailableIcons(editContext.sessionId);
                if (result.type === "success" && result.data) {
                    // Server-side filtering already removes problematic URLs,
                    // but we keep the data as-is for now
                    setIcons(result.data);
                }
                else {
                    console.error("Failed to load icons:", result.details);
                    setIcons([]);
                }
            }
            catch (error) {
                console.error("Error loading icons:", error);
                setIcons([]);
            }
            finally {
                setLoading(false);
            }
        };
        loadIcons();
    }, [editContext?.sessionId]);
    const handleIconLoadError = useCallback((iconPath) => {
        // Remove icon from the list if it fails to load
        setIcons((prevIcons) => {
            // Avoid removing the same icon multiple times
            if (failedIconPaths.current.has(iconPath)) {
                return prevIcons;
            }
            failedIconPaths.current.add(iconPath);
            return prevIcons.filter((icon) => icon.path !== iconPath);
        });
    }, []);
    const filteredIcons = useMemo(() => {
        // Filter out failed icons
        const validIcons = icons.filter((icon) => !failedIconPaths.current.has(icon.path));
        if (!searchQuery.trim())
            return validIcons;
        const query = searchQuery.toLowerCase();
        return validIcons.filter((icon) => icon.name.toLowerCase().includes(query) ||
            icon.path.toLowerCase().includes(query) ||
            (icon.category && icon.category.toLowerCase().includes(query)));
    }, [icons, searchQuery]);
    const { visibleItems, startIndex, totalHeight, columnsPerRow } = useVirtualScroll(filteredIcons, scrollContainerRef, ITEM_HEIGHT, ITEM_WIDTH);
    const handleOk = () => {
        props.onClose?.(selectedIcon ?? null);
    };
    const handleCancel = () => {
        props.onClose?.(null);
    };
    const handleIconClick = (iconPath) => {
        setSelectedIcon(iconPath);
    };
    const handleIconDoubleClick = (iconPath) => {
        setSelectedIcon(iconPath);
        // Close dialog immediately after double-click
        props.onClose?.(iconPath);
    };
    return (_jsx(Dialog, { open: true, onOpenChange: (open) => {
            if (!open) {
                props.onClose?.(null);
            }
        }, children: _jsxs(DialogContent, { "data-testid": "icon-selector-dialog", className: "flex flex-col overflow-hidden border-none p-0 shadow-2xl", style: { width: "94vw", maxWidth: "1400px", height: "90vh" }, children: [_jsx(StyledDialogTitle, { icon: _jsx(ImageIcon, { strokeWidth: 1 }), title: "Select Icon" }), _jsxs("div", { className: "flex min-h-0 flex-1 flex-col overflow-hidden", children: [_jsx("div", { className: "shrink-0 px-6 pt-6 pb-4", children: _jsxs("div", { className: "relative", children: [_jsx(Search, { className: "text-muted-foreground absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 transform", strokeWidth: 1.5 }), _jsx(Input, { type: "text", placeholder: "Search icons...", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), className: "bg-white pl-9", autoFocus: true, "data-testid": "icon-search-input" })] }) }), _jsx("div", { ref: scrollContainerRef, className: "min-h-0 flex-1 overflow-x-hidden overflow-y-auto px-6", children: loading ? (_jsx("div", { className: "flex h-full items-center justify-center", children: _jsx(Spinner, { size: "2xl" }) })) : filteredIcons.length === 0 ? (_jsx("div", { className: "text-muted-foreground flex h-full items-center justify-center text-sm", children: searchQuery
                                    ? "No icons found matching your search"
                                    : "No icons available" })) : (_jsx("div", { style: {
                                    height: totalHeight,
                                    position: "relative",
                                }, children: _jsx("div", { className: "grid gap-3 pb-6", style: {
                                        gridTemplateColumns: `repeat(auto-fill, minmax(100px, 1fr))`,
                                        position: "absolute",
                                        top: Math.floor(startIndex / columnsPerRow) *
                                            (ITEM_HEIGHT + 8),
                                        left: 0,
                                        right: 0,
                                    }, children: visibleItems.map((icon) => {
                                        const isSelected = selectedIcon === icon.path;
                                        return (_jsx(LazyIcon, { icon: icon, isSelected: isSelected, onSelect: handleIconClick, onDoubleClick: handleIconDoubleClick, onLoadError: handleIconLoadError }, icon.path));
                                    }) }) })) }), _jsx("div", { className: "shrink-0", children: _jsxs(DialogButtons, { hideBorder: true, className: "bg-neutral-grey-5/50", children: [_jsx(Button, { onClick: handleCancel, variant: "ghost", children: "Cancel" }), _jsx(Button, { onClick: handleOk, variant: "default", disabled: !selectedIcon, className: "min-w-[120px]", "data-testid": "icon-select-button", children: "Select Icon" })] }) })] })] }) }));
}
//# sourceMappingURL=IconSelectorDialog.js.map