import React, { useRef, useState, useEffect, useMemo } from 'react';
import ReactDOM from 'react-dom';
import { DynamicTooltipProps } from './DynamicTooltip.types';
import './DynamicTooltip.scss';

/**
 * DynamicTooltip component: displays a customizable dynamic tooltip (like a tooltip, but with rich content).
 * Can be triggered by hover or click, and optionally shows a close button.
 *
 * @param trigger - The element that triggers the DynamicTooltip (text, button, icon, etc).
 * @param openOn - How the DynamicTooltip opens: 'hover' or 'click'. Defaults to 'hover'.
 * @param children - The content to display inside the DynamicTooltip.
 * @param onOpenChange - Callback called whenever the open state changes.
 * @param showCloseButton - Whether to show the close (X) button. Defaults to true.
 * @param size - Size of the DynamicTooltip: 'small', 'medium', or 'large'. Defaults to 'medium'.
 * @param position - Position of the DynamicTooltip: 'top', 'bottom', 'left', or 'right'. Defaults to 'top'.
 */
export const DynamicTooltip: React.FC<DynamicTooltipProps> = ({ trigger, openOn = 'hover', children, onOpenChange, showCloseButton = true, size = 'medium', position = 'top', className, closeOnScroll = true, hideArrow = false }) => {
  // Internal open state
  const [open, setOpen] = useState(false);
  // Track hover state for trigger and bubble
  const [isHoveringTrigger, setIsHoveringTrigger] = useState(false);
  const [isHoveringBubble, setIsHoveringBubble] = useState(false);
  // Ref for close timeout
  const closeTimeoutRef = useRef<NodeJS.Timeout | null>(null);
  // Refs for trigger and bubble elements
  const triggerRef = useRef<HTMLSpanElement>(null);
  const bubbleRef = useRef<HTMLDivElement>(null);
  // State for bubble position
  const [bubbleStyle, setBubbleStyle] = useState<React.CSSProperties>({});
  const ARROW_OFFSET = 2; // px (adjusted so the arrow stays closer)
  const scrollContainerRef = useRef<HTMLElement | null>(null);

  // Function to find the scrollable container
  function findScrollContainer(element: HTMLElement | null): HTMLElement | null {
    if (!element) return null;
    let el: HTMLElement | null = element.parentElement;
    while (el) {
      if (el.classList.contains('MuiTableContainer-root')) return el;
      const style = window.getComputedStyle(el);
      if (/(auto|scroll)/.test(style.overflow + style.overflowX + style.overflowY)) {
        return el;
      }
      el = el.parentElement;
    }
    return null;
  }

  // Function to calculate bubble position
  function updateBubblePosition() {
    if (triggerRef.current) {
      const triggerRect = triggerRef.current.getBoundingClientRect();
      let style: React.CSSProperties = { position: 'absolute' };
      if (scrollContainerRef.current) {
        // Use only getBoundingClientRect() from trigger
        switch (position) {
          case 'bottom':
            style.top = triggerRect.bottom + ARROW_OFFSET;
            style.left = triggerRect.left + triggerRect.width / 2;
            style.transform = 'translateX(-50%)';
            break;
          case 'top':
            style.top = triggerRect.top - ARROW_OFFSET;
            style.left = triggerRect.left + triggerRect.width / 2;
            style.transform = 'translate(-50%, -100%)';
            break;
          case 'left':
            style.top = triggerRect.top + triggerRect.height / 2;
            style.left = triggerRect.left;
            style.transform = 'translate(-100%, -50%)';
            break;
          case 'right':
            style.top = triggerRect.top + triggerRect.height / 2;
            style.left = triggerRect.right;
            style.transform = 'translateY(-50%)';
            break;
          default:
            style.top = triggerRect.bottom + ARROW_OFFSET;
            style.left = triggerRect.left + triggerRect.width / 2;
            style.transform = 'translateX(-50%)';
        }
      } else {
        // Fallback: position relative to viewport + global scroll
        let scrollX = window.scrollX || window.pageXOffset;
        let scrollY = window.scrollY || window.pageYOffset;
        switch (position) {
          case 'bottom':
            style.top = triggerRect.bottom + scrollY + ARROW_OFFSET;
            style.left = triggerRect.left + scrollX + triggerRect.width / 2;
            style.transform = 'translateX(-50%)';
            break;
          case 'top':
            style.top = triggerRect.top + scrollY - ARROW_OFFSET;
            style.left = triggerRect.left + scrollX + triggerRect.width / 2;
            style.transform = 'translate(-50%, -100%)';
            break;
          case 'left':
            style.top = triggerRect.top + scrollY + triggerRect.height / 2;
            style.left = triggerRect.left + scrollX;
            style.transform = 'translate(-100%, -50%)';
            break;
          case 'right':
            style.top = triggerRect.top + scrollY + triggerRect.height / 2;
            style.left = triggerRect.right + scrollX;
            style.transform = 'translateY(-50%)';
            break;
          default:
            style.top = triggerRect.bottom + scrollY + ARROW_OFFSET;
            style.left = triggerRect.left + scrollX + triggerRect.width / 2;
            style.transform = 'translateX(-50%)';
        }
      }
      style.zIndex = 9999;
      setBubbleStyle(style);
    }
  }

  /**
   * Notify parent when open state changes
   */
  useEffect(() => {
    if (onOpenChange) {
      onOpenChange(open);
    }
  }, [open]);

  // Robust hover handling: opens if mouse is over trigger or bubble, closes if not over either (with delay)
  useEffect(() => {
    if (openOn === 'hover') {
      if (isHoveringTrigger || isHoveringBubble) {
        if (closeTimeoutRef.current) {
          clearTimeout(closeTimeoutRef.current);
          closeTimeoutRef.current = null;
        }
        setOpen(true);
      } else {
        // Delay to prevent accidental closing
        closeTimeoutRef.current = setTimeout(() => {
          setOpen(false);
        }, 100);
      }
    }
    // Cleanup on unmount
    return () => {
      if (closeTimeoutRef.current) {
        clearTimeout(closeTimeoutRef.current);
        closeTimeoutRef.current = null;
      }
    };
  }, [isHoveringTrigger, isHoveringBubble, openOn]);

  /**
   * Close the DynamicTooltip when clicking outside (only for click mode)
   */
  useEffect(() => {
    if (openOn === 'click' && open) {
      const handleClickOutside = (event: MouseEvent) => {
        if (
          bubbleRef.current &&
          !bubbleRef.current.contains(event.target as Node) &&
          triggerRef.current &&
          !triggerRef.current.contains(event.target as Node)
        ) {
          setOpen(false);
        }
      };
      document.addEventListener('mousedown', handleClickOutside);
      return () => document.removeEventListener('mousedown', handleClickOutside);
    }
  }, [open, openOn]);

  // Effect to handle scroll/resize and recalculate position
  useEffect(() => {
    if (open && triggerRef.current) {
      // Find the scrollable container
      scrollContainerRef.current = findScrollContainer(triggerRef.current);
      updateBubblePosition();
      setTimeout(() => {
        updateBubblePosition();
      }, 0);
      // Handler for scroll/resize (only recalculates position)
      const handler = () => updateBubblePosition();
      // Handler to close DynamicTooltip on scroll or just update position
      const closeOnScrollHandler = () => {
        if (closeOnScroll) setOpen(false);
        else updateBubblePosition();
      };
      window.addEventListener('resize', handler);
      if (scrollContainerRef.current) {
        scrollContainerRef.current.addEventListener('scroll', handler);
        scrollContainerRef.current.addEventListener('scroll', closeOnScrollHandler);
      } else {
        window.addEventListener('scroll', handler);
        window.addEventListener('scroll', closeOnScrollHandler);
      }
      return () => {
        window.removeEventListener('resize', handler);
        if (scrollContainerRef.current) {
          scrollContainerRef.current.removeEventListener('scroll', handler);
          scrollContainerRef.current.removeEventListener('scroll', closeOnScrollHandler);
        } else {
          window.removeEventListener('scroll', handler);
          window.removeEventListener('scroll', closeOnScrollHandler);
        }
      };
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [open, position, closeOnScroll]);

  // Handlers for hover tracking
  const handleTriggerMouseEnter = () => openOn === 'hover' && setIsHoveringTrigger(true);
  const handleTriggerMouseLeave = () => openOn === 'hover' && setIsHoveringTrigger(false);
  const handleBubbleMouseEnter = () => openOn === 'hover' && setIsHoveringBubble(true);
  const handleBubbleMouseLeave = () => openOn === 'hover' && setIsHoveringBubble(false);

  // Handler for click
  const handleClick = () => openOn === 'click' && setOpen((prev) => !prev);

  // Compute size class
  const sizeClass = `gtc-rc-dynamic-tooltip--${size}`;
  // Compute position class
  const positionClass = `gtc-rc-dynamic-tooltip--${position}`;

  // Memoize content to avoid unnecessary rerenders when scrolling
  const memoizedContent = useMemo(
    () => (typeof children === 'function' ? children(open) : children),
    [open, children]
  );

  return (
    <span
      className="gtc-rc-dynamic-tooltip-trigger"
      ref={triggerRef}
      onMouseEnter={handleTriggerMouseEnter}
      onMouseLeave={handleTriggerMouseLeave}
      onClick={handleClick}
      tabIndex={0}
      style={{ display: 'inline-block' }}
    >
      {trigger}
      {open && ReactDOM.createPortal(
        <div
          className={`gtc-rc-dynamic-tooltip ${sizeClass} ${positionClass} gtc-rc-dynamic-tooltip--${openOn} ${className || ''}`.trim()}
          ref={bubbleRef}
          onMouseEnter={handleBubbleMouseEnter}
          onMouseLeave={handleBubbleMouseLeave}
          style={bubbleStyle}
        >
          {showCloseButton && (
            <button
              className="gtc-rc-dynamic-tooltip__close"
              aria-label="Close"
              onClick={e => {
                e.stopPropagation();
                setOpen(false);
              }}
              type="button"
            >
              ×
            </button>
          )}
          {/* Show arrow only if hideArrow is false */}
          {!hideArrow && <div className="gtc-rc-dynamic-tooltip__arrow" />}
          <div className="gtc-rc-dynamic-tooltip__content">
            {memoizedContent}
          </div>
        </div>,
        document.body
      )}
    </span>
  );
};
