"""A component that automatically scrolls to the bottom when new content is added."""

from __future__ import annotations

import dataclasses

from reflex.components.el.elements.typography import Div
from reflex.constants.compiler import MemoizationDisposition, MemoizationMode
from reflex.utils.imports import ImportDict
from reflex.vars.base import Var, get_unique_variable_name


class AutoScroll(Div):
    """A div that automatically scrolls to the bottom when new content is added."""

    _memoization_mode = MemoizationMode(
        disposition=MemoizationDisposition.ALWAYS, recursive=False
    )

    @classmethod
    def create(cls, *children, **props):
        """Create an AutoScroll component.

        Args:
            *children: The children of the component.
            **props: The props of the component.

        Returns:
            An AutoScroll component.
        """
        props.setdefault("overflow", "auto")
        props.setdefault("id", get_unique_variable_name())
        component = super().create(*children, **props)
        if "key" in props:
            component._memoization_mode = dataclasses.replace(
                component._memoization_mode, recursive=True
            )
        return component

    def add_imports(self) -> ImportDict | list[ImportDict]:
        """Add imports required for the component.

        Returns:
            The imports required for the component.
        """
        return {"react": ["useEffect", "useRef"]}

    def add_hooks(self) -> list[str | Var]:
        """Add hooks required for the component.

        Returns:
            The hooks required for the component.
        """
        ref_name = self.get_ref()
        return [
            "const wasNearBottom = useRef(false);",
            "const hadScrollbar = useRef(false);",
            f"""
const checkIfNearBottom = () => {{
    if (!{ref_name}.current) return;

    const container = {ref_name}.current;
    const nearBottomThreshold = 50; // pixels from bottom to trigger auto-scroll

    const distanceFromBottom = container.scrollHeight - container.scrollTop - container.clientHeight;

    wasNearBottom.current = distanceFromBottom <= nearBottomThreshold;

    // Track if container had a scrollbar
    hadScrollbar.current = container.scrollHeight > container.clientHeight;
}};
""",
            f"""
const scrollToBottomIfNeeded = () => {{
    if (!{ref_name}.current) return;

    const container = {ref_name}.current;
    const hasScrollbarNow = container.scrollHeight > container.clientHeight;

    // Scroll if:
    // 1. User was near bottom, OR
    // 2. Container didn't have scrollbar before but does now
    if (wasNearBottom.current || (!hadScrollbar.current && hasScrollbarNow)) {{
      container.scrollTop = container.scrollHeight;
    }}

    // Update scrollbar state for next check
    hadScrollbar.current = hasScrollbarNow;
}};
""",
            f"""
useEffect(() => {{
    const container = {ref_name}.current;
    if (!container) return;

    scrollToBottomIfNeeded();

    // Create ResizeObserver to detect height changes
    const resizeObserver = new ResizeObserver(() => {{
        scrollToBottomIfNeeded();
    }});

    // Track scroll position before height changes
    container.addEventListener('scroll', checkIfNearBottom);

    // Initial check
    checkIfNearBottom();

    // Observe container for size changes
    resizeObserver.observe(container);

    return () => {{
        container.removeEventListener('scroll', checkIfNearBottom);
        resizeObserver.disconnect();
    }};
}});
""",
        ]


auto_scroll = AutoScroll.create
