import { useEffect, useRef, RefObject, useCallback } from 'react';

type ResizeCallback = () => void;

const useResizeObserverAndMutationObserver = (
  targetRef: RefObject<HTMLElement | null>,
  scrollToRef: RefObject<HTMLElement | null>,
  callback: ResizeCallback
) => {
  const callbackRef = useRef(callback);
  const resizeObserverRef = useRef<ResizeObserver | null>(null);
  const mutationObserverRef = useRef<MutationObserver | null>(null);
  const debounceTimeout = useRef<NodeJS.Timeout | null>(null);

  useEffect(() => {
    callbackRef.current = callback;
  }, [callback]);

  useEffect(() => {
    const container = targetRef.current;
    const scrollTo = scrollToRef.current;
    if (!container) return;

    const debouncedCallback = (entries: ResizeObserverEntry[] | undefined) => {
      if (debounceTimeout.current) clearTimeout(debounceTimeout.current);
      debounceTimeout.current = setTimeout(() => {
        requestAnimationFrame(() => callbackRef.current());
      }, 50);
    };

    const resizeObserver = new ResizeObserver(debouncedCallback);
    const mutationObserver = new MutationObserver(() => { debouncedCallback(undefined); });

    // Observe container size
    resizeObserver.observe(container);
    // Observe TextField size if available
    if (scrollTo) {
      resizeObserver.observe(scrollTo);
    }

    // Observe child additions/removals and text content changes
    mutationObserver.observe(container, {
      childList: true,
      subtree: true,
      characterData: true,
    });

    resizeObserverRef.current = resizeObserver;
    mutationObserverRef.current = mutationObserver;

    return () => {
      if (debounceTimeout.current) clearTimeout(debounceTimeout.current);
      resizeObserver.disconnect();
      mutationObserver.disconnect();
    };
  }, [targetRef, scrollToRef]);
};

/**
 * Auto-scroll hook for scrollable containers.
 * Scrolls to the bottom of the container on paste or when TextField is fully/partially visible.
 */
export const useAutoScrollToBottom = (
  scrollToRef: RefObject<HTMLElement | null>,
  smooth: boolean = true,
  fallbackThreshold: number = 0.33,
  contentUpdateTrigger?: any
): RefObject<HTMLDivElement | null> => {
  const containerRef = useRef<HTMLDivElement | null>(null);
  const lastScrollTop = useRef(0);
  const scrollTimeout = useRef<NodeJS.Timeout | null>(null);
  const isUserScrollingUpRef = useRef(false);

  const checkAndScrollToBottom = useCallback((isPasteEvent: boolean = false) => {
    const container = containerRef.current;
    if (!container) return;

    let shouldScroll = false;
    const scrollTo = scrollToRef.current;

    if (scrollTo) {
      // Get positions
      const containerRect = container.getBoundingClientRect();
      const scrollToRect = scrollTo.getBoundingClientRect();
      const containerTop = containerRect.top;
      const containerBottom = containerTop + container.clientHeight;

      // Check if TextField is fully or partially visible (for non-paste events)
      const isTextFieldVisible =
        scrollToRect.top < containerBottom && scrollToRect.bottom > containerTop;

      // Scroll on paste or if TextField is visible and user isn't scrolling up
      shouldScroll = (isPasteEvent || isTextFieldVisible) && !isUserScrollingUpRef.current;

      if (shouldScroll) {
        requestAnimationFrame(() => {
          console.debug('Scrolling to container bottom:', {
            scrollHeight: container.scrollHeight,
            scrollToHeight: scrollToRect.height,
            containerHeight: container.clientHeight,
            isPasteEvent,
            isTextFieldVisible,
            isUserScrollingUp: isUserScrollingUpRef.current,
          });
            container.scrollTo({
              top: container.scrollHeight,
              behavior: smooth ? 'smooth' : 'auto',
            });
        });
      }
    } else {
      // Fallback to threshold-based check
      const scrollHeight = container.scrollHeight;
      const isNearBottom =
        scrollHeight - container.scrollTop - container.clientHeight <=
        container.clientHeight * fallbackThreshold;
      shouldScroll = isNearBottom && !isUserScrollingUpRef.current;

      if (shouldScroll) {
        requestAnimationFrame(() => {
          console.debug('Scrolling to container bottom (fallback):', { scrollHeight });
          container.scrollTo({
            top: container.scrollHeight,
            behavior: smooth ? 'smooth' : 'auto',
          });
        });
      }
    }
  }, [fallbackThreshold, smooth, scrollToRef]);

  useEffect(() => {
    const container = containerRef.current;
    const scrollTo = scrollToRef.current;
    if (!container) return;

    const handleScroll = () => {
      const currentScrollTop = container.scrollTop;
      isUserScrollingUpRef.current = currentScrollTop < lastScrollTop.current;
      lastScrollTop.current = currentScrollTop;

      if (scrollTimeout.current) clearTimeout(scrollTimeout.current);
      scrollTimeout.current = setTimeout(() => {
        isUserScrollingUpRef.current = false;
      }, 500);
    };

    const handlePaste = () => {
      // Delay scroll check to ensure DOM updates
      setTimeout(() => {
        requestAnimationFrame(() => checkAndScrollToBottom(true));
      }, 0);
    };

    container.addEventListener('scroll', handleScroll);
    if (scrollTo) {
      scrollTo.addEventListener('paste', handlePaste);
    }
    checkAndScrollToBottom();

    return () => {
      container.removeEventListener('scroll', handleScroll);
      if (scrollTo) {
        scrollTo.removeEventListener('paste', handlePaste);
      }
      if (scrollTimeout.current) clearTimeout(scrollTimeout.current);
    };
  }, [smooth, scrollToRef, fallbackThreshold, contentUpdateTrigger, checkAndScrollToBottom]);

  // Observe container and TextField size, plus DOM changes
  useResizeObserverAndMutationObserver(containerRef, scrollToRef, checkAndScrollToBottom);

  return containerRef;
};