import React, { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';

type OverflowMarqueeProps = {
  children: React.ReactNode;
  className?: string;
  textClassName?: string;
  title?: string;
  speedPxPerSec?: number;
  minDurationSec?: number;
};

type MarqueeMetrics = {
  isOverflowing: boolean;
  overflowPx: number;
  durationSec: number;
};

const DEFAULT_SPEED_PX_PER_SEC = 24;
const DEFAULT_MIN_DURATION_SEC = 4;

const OverflowMarquee: React.FC<OverflowMarqueeProps> = ({
  children,
  className,
  textClassName,
  title,
  speedPxPerSec = DEFAULT_SPEED_PX_PER_SEC,
  minDurationSec = DEFAULT_MIN_DURATION_SEC,
}) => {
  const containerRef = useRef<HTMLSpanElement>(null);
  const textRef = useRef<HTMLSpanElement>(null);
  const [metrics, setMetrics] = useState<MarqueeMetrics>({
    isOverflowing: false,
    overflowPx: 0,
    durationSec: minDurationSec,
  });

  const fallbackTitle = useMemo(() => {
    if (title) return title;
    return typeof children === 'string' ? children : undefined;
  }, [children, title]);

  const recompute = () => {
    const container = containerRef.current;
    const text = textRef.current;
    if (!container || !text) return;

    // Ensure we measure with current layout.
    const available = container.clientWidth;
    const content = text.scrollWidth;
    const overflowPx = Math.ceil(content - available);

    if (overflowPx > 1) {
      const durationSec = Math.max(minDurationSec, overflowPx / Math.max(1, speedPxPerSec));
      setMetrics({ isOverflowing: true, overflowPx, durationSec });
    } else {
      // Avoid re-render loops if already not overflowing.
      setMetrics((prev) => (prev.isOverflowing ? { isOverflowing: false, overflowPx: 0, durationSec: minDurationSec } : prev));
    }
  };

  useLayoutEffect(() => {
    recompute();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [children]);

  useEffect(() => {
    recompute();

    const container = containerRef.current;
    const text = textRef.current;
    if (!container || !text) return;

    const ro = new ResizeObserver(() => recompute());
    ro.observe(container);
    ro.observe(text);

    const onResize = () => recompute();
    window.addEventListener('resize', onResize);

    return () => {
      ro.disconnect();
      window.removeEventListener('resize', onResize);
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  const textStyle: React.CSSProperties | undefined = metrics.isOverflowing
    ? ({
        ['--marquee-distance' as any]: `${metrics.overflowPx}px`,
        ['--marquee-duration' as any]: `${metrics.durationSec}s`,
      } satisfies React.CSSProperties)
    : undefined;

  return (
    <span
      ref={containerRef}
      className={['overflow-marquee', className].filter(Boolean).join(' ')}
      title={fallbackTitle}
    >
      <span
        ref={textRef}
        className={[
          'overflow-marquee__text',
          metrics.isOverflowing ? 'overflow-marquee__text--animate' : '',
          textClassName,
        ]
          .filter(Boolean)
          .join(' ')}
        style={textStyle}
      >
        {children}
      </span>
    </span>
  );
};

export default OverflowMarquee;
