import { useEffect, useRef } from "react";

interface VisibleTriggerProps {
    onVisible: () => void | Promise<void>;
    options?: IntersectionObserverInit;
    triggerOnce?: boolean;
    children?: React.ReactNode;
    className?: string;
    style?: React.CSSProperties;
}

/**
 * A component that calls a function when it becomes visible in the viewport
 * or a specified scrollable container.
 */
export default function VisibleTrigger({
    onVisible, // Function to call when the element becomes visible
    options = {}, // Optional: IntersectionObserver options (root, rootMargin, threshold)
    triggerOnce = true, // Optional: If true, trigger only the first time it becomes visible
    children,
    style,
    ...props
}: VisibleTriggerProps & React.ComponentProps<"div">) {
    const elementRef = useRef(null); // Ref to attach to the DOM element we want to observe

    useEffect(() => {
        const element = elementRef.current;

        // Only proceed if we have the DOM element and the function to call
        if (!element) {
            return;
        }

        // Default IntersectionObserver options
        const defaultOptions = {
            root: null, // default is the viewport
            rootMargin: "0px", // No margin by default
            threshold: 0, // Trigger as soon as any part of the element is visible
        };

        // Merge provided options with defaults
        const observerOptions = { ...defaultOptions, ...options };

        // Create the Intersection Observer instance
        const observer = new IntersectionObserver(
            (entries) => {
                const entry = entries[0]; // Assuming only one target element

                // If the element is intersecting (visible)...
                if (entry.isIntersecting) {
                    // console.log('VisibleTrigger: Element is intersecting.', entry);

                    // Call the provided function
                    onVisible();

                    // If triggerOnce is true, stop observing this element immediately
                    if (triggerOnce) {
                        // console.log('VisibleTrigger: Triggered once, disconnecting observer.');
                        observer.disconnect(); // Disconnect stops all observations by this instance
                    }
                } else {
                    // console.log('VisibleTrigger: Element is NOT intersecting.', entry);
                }
            },
            observerOptions, // Pass the options to the observer
        );

        // Start observing the element
        // console.log('VisibleTrigger: Starting observation.', element);
        observer.observe(element);

        // Cleanup function: Disconnect the observer when the component unmounts
        // or when the effect dependencies change.
        return () => {
            // console.log('VisibleTrigger: Cleaning up observer.');
            if (observer) {
                // Calling disconnect multiple times is safe.
                observer.disconnect();
            }
        };

        // Effect dependencies:
        // - elementRef: Need the DOM element reference.
        // - onVisible: If the function prop changes, we need a new observer with the new function.
        // - options: If observer options change, we need a new observer.
        // - triggerOnce: If triggerOnce changes, the logic inside the observer callback changes,
        //                so we need a new observer instance.
    }, [elementRef, onVisible, options, triggerOnce]);

    // Render a div that we will attach the ref to.
    // Ensure it has some minimal dimension if no children are provided,
    // so the observer can detect its presence.
    return (
        <div
            ref={elementRef}
            style={{ minHeight: children ? "auto" : "1px", ...style }}
            {...props}
        >
            {children} {/* Render any children passed to the component */}
        </div>
    );
}
