import clsx from "clsx";
import { motion } from "motion/react";
import React, { useEffect, useRef, useState } from "react";

interface TooltipProps {
  label: string;
  children: React.ReactNode;
  position?: "top" | "bottom" | "left" | "right" | "cursor";
  showDelay?: number;
  className?: string;
}

interface TooltipPosition {
  top?: number | string;
  left?: number | string;
  bottom?: number | string;
  right?: number | string;
  transform?: string;
}

export default function Tooltip({
  label,
  children,
  position = "top",
  showDelay = 500,
  className,
}: TooltipProps) {
  const [isVisible, setIsVisible] = useState(false);
  const [tooltipPosition, setTooltipPosition] = useState<TooltipPosition>({});
  const tooltipWrapperRef = useRef<HTMLDivElement>(null);
  const tooltipRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (!tooltipWrapperRef.current) return;
    const current = tooltipWrapperRef.current;

    current.addEventListener("mouseenter", () => setIsVisible(true));
    current.addEventListener("mouseleave", () => setIsVisible(false));
    return () => {
      current?.removeEventListener("mouseenter", () => setIsVisible(true));
      current?.removeEventListener("mouseleave", () => setIsVisible(false));
    };
  }, []);

  useEffect(() => {
    if (!isVisible || !tooltipRef.current || !tooltipWrapperRef.current) return;

    const updatePosition = () => {
      const pos = calculatePosition(position, tooltipRef, tooltipWrapperRef);
      setTooltipPosition(pos);
    };

    const handleScroll = () => {
      setIsVisible(false);
    };

    updatePosition();

    // Скрываем tooltip при скролле, обновляем позицию при ресайзе
    window.addEventListener("scroll", handleScroll, true);
    window.addEventListener("resize", updatePosition);

    return () => {
      window.removeEventListener("scroll", handleScroll, true);
      window.removeEventListener("resize", updatePosition);
    };
  }, [isVisible, position]);

  return (
    <div ref={tooltipWrapperRef} className={clsx("cursor-pointer", className)}>
      {children}
      {isVisible && (
        <motion.div
          initial={{ opacity: 0 }}
          animate={{ opacity: 1 }}
          transition={{ delay: showDelay / 1000, duration: 0.3 }}
        >
          <TooltipContent
            label={label}
            position={tooltipPosition || {}}
            ref={tooltipRef as React.RefObject<HTMLDivElement>}
          />
        </motion.div>
      )}
    </div>
  );
}

function TooltipContent({
  label,
  position,
  ref,
}: {
  label: string;
  position: TooltipPosition;
  ref: React.RefObject<HTMLDivElement>;
}) {
  return (
    <div
      ref={ref}
      style={{ ...position, transform: position.transform || "none" }}
      className="z-[9999] fixed 2xl:px-[0.694vw] 2xl:py-[0.417vw] px-[10px] py-[6px] bg-[#00000073] caption-s text-white 2xl:rounded-[0.556vw] rounded-lg whitespace-nowrap backdrop-blur-[4px] shadow-[0_2px_4px_0_#00000059] cursor-default"
    >
      {label}
    </div>
  );
}

function calculatePosition(
  position: TooltipProps["position"],
  tooltipRef: React.RefObject<HTMLDivElement | null>,
  tooltipWrapperRef: React.RefObject<HTMLDivElement | null>
) {
  if (!tooltipWrapperRef.current || !tooltipRef.current) return {};

  const wrapperRect = tooltipWrapperRef.current.getBoundingClientRect();
  const tooltipRect = tooltipRef.current.getBoundingClientRect();
  const GAP = 8;

  function adjustHorizontal(baseLeft: number) {
    let left = baseLeft;
    let transform = "translateX(-50%)";

    const tooltipLeft = left - tooltipRect.width / 2;
    const tooltipRight = tooltipLeft + tooltipRect.width;

    // Если не влезает горизонтально, то сдвигаем
    if (tooltipLeft < GAP) {
      left = GAP + tooltipRect.width / 2;
      transform = "translateX(-50%)";
    } else if (tooltipRight > window.innerWidth - GAP) {
      left = window.innerWidth - GAP - tooltipRect.width / 2;
      transform = "translateX(-50%)";
    }

    return { left, transform };
  }

  function adjustVertical(baseTop: number) {
    return { top: baseTop, transform: "translateY(-50%)" };
  }

  switch (position) {
    case "top": {
      const top = wrapperRect.top - tooltipRect.height - GAP;
      const left = wrapperRect.left + wrapperRect.width / 2;

      // Если не влезает сверху, показываем снизу
      if (top < GAP) {
        const bottomTop = wrapperRect.bottom + GAP;
        return { top: bottomTop, ...adjustHorizontal(left) };
      }

      return { top, ...adjustHorizontal(left) };
    }
    case "bottom": {
      const top = wrapperRect.bottom + GAP;
      const left = wrapperRect.left + wrapperRect.width / 2;

      // Если не влезает снизу, показываем сверху
      if (top + tooltipRect.height > window.innerHeight - GAP) {
        const topTop = wrapperRect.top - tooltipRect.height - GAP;
        return { top: topTop, ...adjustHorizontal(left) };
      }

      return { top, ...adjustHorizontal(left) };
    }
    case "left": {
      const left = wrapperRect.left - tooltipRect.width - GAP;
      const top = wrapperRect.top + wrapperRect.height / 2;

      // Если не влезает слева, показываем справа
      if (left < GAP) {
        const rightLeft = wrapperRect.right + GAP;
        return { left: rightLeft, ...adjustVertical(top) };
      }

      return { left, ...adjustVertical(top) };
    }
    case "right": {
      const left = wrapperRect.right + GAP;
      const top = wrapperRect.top + wrapperRect.height / 2;

      // Если не влезает справа, показываем слева
      if (left + tooltipRect.width > window.innerWidth - GAP) {
        const leftLeft = wrapperRect.left - tooltipRect.width - GAP;
        return { left: leftLeft, ...adjustVertical(top) };
      }

      return { left, ...adjustVertical(top) };
    }
    case "cursor": {
      return { transform: "none" };
    }
  }
  return {};
}
