import React, { useRef, useState, useEffect, useCallback } from "react";

interface ScrollAreaProps {
    children: React.ReactNode;
    className?: string;
    fadeDelay?: number; // ms before scrollbar fades out
    scrollbarOffset?: number; // px to offset scrollbar to the right (into padding area)
}

export default function ScrollArea({
    children,
    className = "",
    fadeDelay = 1000,
    scrollbarOffset = 0,
}: ScrollAreaProps) {
    const containerRef = useRef<HTMLDivElement>(null);
    const thumbRef = useRef<HTMLDivElement>(null);
    const [isHovered, setIsHovered] = useState(false);
    const [isScrolling, setIsScrolling] = useState(false);
    const [isDragging, setIsDragging] = useState(false);
    const [isThumbHovered, setIsThumbHovered] = useState(false);
    const [thumbHeight, setThumbHeight] = useState(0);
    const [thumbTop, setThumbTop] = useState(0);
    const [canScroll, setCanScroll] = useState(false);
    const fadeTimeoutRef = useRef<number | null>(null);
    const dragStartRef = useRef({ y: 0, scrollTop: 0 });

    // Check if content is scrollable and calculate thumb dimensions
    const updateScrollbar = useCallback(() => {
        const container = containerRef.current;
        if (!container) return;

        const { scrollHeight, clientHeight, scrollTop } = container;
        const hasScroll = scrollHeight > clientHeight;
        setCanScroll(hasScroll);

        if (hasScroll) {
            // Calculate thumb height as a proportion of visible content
            const ratio = clientHeight / scrollHeight;
            const minThumbHeight = 30;
            const calculatedHeight = Math.max(
                clientHeight * ratio,
                minThumbHeight,
            );
            setThumbHeight(calculatedHeight);

            // Calculate thumb position
            const maxScrollTop = scrollHeight - clientHeight;
            const scrollRatio = scrollTop / maxScrollTop;
            const maxThumbTop = clientHeight - calculatedHeight;
            setThumbTop(scrollRatio * maxThumbTop);
        }
    }, []);

    // Handle scroll events
    const handleScroll = useCallback(() => {
        updateScrollbar();
        setIsScrolling(true);

        // Clear existing timeout
        if (fadeTimeoutRef.current) {
            clearTimeout(fadeTimeoutRef.current);
        }

        // Set new timeout to hide scrollbar
        fadeTimeoutRef.current = window.setTimeout(() => {
            if (!isHovered && !isDragging) {
                setIsScrolling(false);
            }
        }, fadeDelay);
    }, [updateScrollbar, fadeDelay, isHovered, isDragging]);

    // Handle mouse enter/leave
    const handleMouseEnter = useCallback(() => {
        setIsHovered(true);
        updateScrollbar();
    }, [updateScrollbar]);

    const handleMouseLeave = useCallback(() => {
        setIsHovered(false);
        if (!isDragging) {
            // Start fade timer when mouse leaves
            if (fadeTimeoutRef.current) {
                clearTimeout(fadeTimeoutRef.current);
            }
            fadeTimeoutRef.current = window.setTimeout(() => {
                setIsScrolling(false);
            }, fadeDelay);
        }
    }, [fadeDelay, isDragging]);

    // Handle thumb drag
    const handleThumbMouseDown = useCallback((e: React.MouseEvent) => {
        e.preventDefault();
        e.stopPropagation();
        setIsDragging(true);
        dragStartRef.current = {
            y: e.clientY,
            scrollTop: containerRef.current?.scrollTop || 0,
        };
    }, []);

    // Handle track click (jump to position)
    const handleTrackClick = useCallback((e: React.MouseEvent) => {
        const container = containerRef.current;
        const track = e.currentTarget;
        if (!container || e.target === thumbRef.current) return;

        const rect = track.getBoundingClientRect();
        const clickY = e.clientY - rect.top;
        const trackHeight = rect.height;
        const ratio = clickY / trackHeight;
        const maxScrollTop = container.scrollHeight - container.clientHeight;
        container.scrollTop = ratio * maxScrollTop;
    }, []);

    // Global mouse move/up handlers for dragging
    useEffect(() => {
        if (!isDragging) return;

        const handleMouseMove = (e: MouseEvent) => {
            const container = containerRef.current;
            if (!container) return;

            const deltaY = e.clientY - dragStartRef.current.y;
            const { scrollHeight, clientHeight } = container;
            const maxScrollTop = scrollHeight - clientHeight;
            const trackHeight = clientHeight - thumbHeight;
            const scrollDelta = (deltaY / trackHeight) * maxScrollTop;

            container.scrollTop = dragStartRef.current.scrollTop + scrollDelta;
        };

        const handleMouseUp = () => {
            setIsDragging(false);
            // Start fade timer after drag ends
            if (!isHovered) {
                fadeTimeoutRef.current = window.setTimeout(() => {
                    setIsScrolling(false);
                }, fadeDelay);
            }
        };

        document.addEventListener("mousemove", handleMouseMove);
        document.addEventListener("mouseup", handleMouseUp);

        return () => {
            document.removeEventListener("mousemove", handleMouseMove);
            document.removeEventListener("mouseup", handleMouseUp);
        };
    }, [isDragging, thumbHeight, isHovered, fadeDelay]);

    // Update scrollbar on mount and resize
    useEffect(() => {
        updateScrollbar();

        const container = containerRef.current;
        if (!container) return;

        const resizeObserver = new ResizeObserver(() => {
            updateScrollbar();
        });
        resizeObserver.observe(container);

        // Also observe children for content changes
        const mutationObserver = new MutationObserver(() => {
            updateScrollbar();
        });
        mutationObserver.observe(container, {
            childList: true,
            subtree: true,
            characterData: true,
        });

        return () => {
            resizeObserver.disconnect();
            mutationObserver.disconnect();
        };
    }, [updateScrollbar]);

    // Cleanup timeout on unmount
    useEffect(() => {
        return () => {
            if (fadeTimeoutRef.current) {
                clearTimeout(fadeTimeoutRef.current);
            }
        };
    }, []);

    const showScrollbar = canScroll && (isHovered || isScrolling || isDragging);
    const isThumbExpanded = isThumbHovered || isDragging;

    return (
        <div
            className={`relative ${className}`}
            onMouseEnter={handleMouseEnter}
            onMouseLeave={handleMouseLeave}
        >
            <div
                ref={containerRef}
                className="h-full overflow-y-auto overflow-x-hidden"
                onScroll={handleScroll}
            >
                {children}
            </div>

            {/* Custom scrollbar track */}
            <div
                className="absolute top-0 w-2 h-full cursor-pointer"
                onClick={handleTrackClick}
                onMouseEnter={() => setIsThumbHovered(true)}
                onMouseLeave={() => setIsThumbHovered(false)}
                style={{
                    right: `-${scrollbarOffset}px`,
                    opacity: showScrollbar ? 1 : 0,
                    transition: "opacity 200ms ease-out",
                    pointerEvents: showScrollbar ? "auto" : "none",
                }}
            >
                {/* Scrollbar thumb */}
                <div
                    ref={thumbRef}
                    className="absolute right-0.5 rounded-full bg-gray-400/60 dark:bg-slate-500/60 hover:bg-gray-500/80 dark:hover:bg-slate-400/80 cursor-grab active:cursor-grabbing"
                    style={{
                        width: isThumbExpanded ? "6px" : "3px",
                        height: `${thumbHeight}px`,
                        top: `${thumbTop}px`,
                        transition: isDragging
                            ? "width 150ms ease"
                            : "width 150ms ease, background-color 150ms ease",
                    }}
                    onMouseDown={handleThumbMouseDown}
                />
            </div>
        </div>
    );
}
