import { forwardRef, useEffect, useImperativeHandle, useRef, useState } from "react";
import { throttle } from "throttle-debounce";

/**
 * 无缝滚动组件
 */
const SeamlessScroll = forwardRef((props, ref) => {
  const {
    isAutoScroll = true,
    list = [],
    step = 1,
    limitScrollNum = 3,
    hover = true,
    direction = "up",
    singleHeight = 0,
    singleWidth = 0,
    singleWaitTime = 1000,
    isRemUnit = false,
    isWatch = true,
    delay = 0,
    ease = "ease-in",
    count = -1,
    copyNum = 1,
    wheel = false,
    singleLine = false,
    children,
    className,
  } = props;

  // ==================== DOM Refs ====================
  const scrollRef = useRef(null); // 最外层容器 ref
  const slotListRef = useRef(null); // 原始列表 ref（用于计算宽度）
  const realBoxRef = useRef(null); // 实际滚动的容器 ref（包含原始+拷贝内容）

  // ==================== Animation Refs ====================
  const reqFrame = useRef(null); // requestAnimationFrame 的 ID，用于取消动画
  const singleWaitTimeout = useRef(null); // 单步滚动等待定时器的 ID
  const isHoverRef = useRef(false); // 使用 ref 同步跟踪 hover 状态，避免 React state 异步更新问题

  // ==================== States ====================
  const [realBoxWidth, setRealBoxWidth] = useState(0); // 滚动容器的实际宽度
  const [realBoxHeight, setRealBoxHeight] = useState(0); // 滚动容器的实际高度（包含原始+拷贝）
  const [xPos, setXPos] = useState(0); // 当前 X 轴偏移量
  const [yPos, setYPos] = useState(0); // 当前 Y 轴偏移量
  const [_count, setCount] = useState(0); // 当前已滚动的循环次数

  // ==================== Computed Values ====================
  const isScroll = list.length >= limitScrollNum; // 是否需要滚动（列表长度超过限制）

  const realBoxStyle = {
    width: realBoxWidth ? `${realBoxWidth}px` : "auto",
    transform: `translate(${xPos}px,${yPos}px)`,
    transition: `all ${typeof ease === "string" ? ease : `cubic-bezier(${ease.x1}, ${ease.y1}, ${ease.x2}, ${ease.y2})`} ${delay}ms`,
    overflow: "hidden",
    display: singleLine ? "flex" : "block",
  };

  const isHorizontal = direction === "left" || direction === "right";

  const floatStyle = isHorizontal
    ? {
        float: "left",
        overflow: "hidden",
        display: singleLine ? "flex" : "block",
        flexShrink: singleLine ? 0 : 1,
      }
    : { overflow: "hidden" };

  const baseFontSize = isRemUnit
    ? Number.parseInt(window.getComputedStyle(document.documentElement, null).fontSize)
    : 1;

  const realSingleStopWidth = singleWidth * baseFontSize;
  const realSingleStopHeight = singleHeight * baseFontSize;

  useEffect(() => {
    if (realSingleStopHeight > 0 && realSingleStopHeight % step > 0) {
      console.error(
        "如果设置了单步滚动，step 需是单步大小的约数，否则无法保证单步滚动结束的位置是否准确。~~~~~",
      );
    }
  }, [realSingleStopHeight, step]);

  /**
   * 取消当前动画帧
   */
  const cancle = () => {
    if (reqFrame.current) {
      cancelAnimationFrame(reqFrame.current);
      reqFrame.current = null;
    }
  };

  /**
   * 核心动画函数
   *
   * @description
   * 负责执行一帧的滚动动画，包括：
   * 1. 检查是否到达边界（滚动到一半的高度）
   * 2. 如果到达边界，重置位置到开头/结尾
   * 3. 继续滚动一步
   * 4. 处理单步滚动的等待逻辑
   *
   * @param {string} _direction - 滚动方向：'up' | 'down' | 'left' | 'right'
   * @param {number} _step - 每次滚动的步进距离
   * @param {boolean} isWheel - 是否由滚轮触发
   */
  const animation = (
    _direction,
    _step,
    isWheel,
  ) => {
    reqFrame.current = requestAnimationFrame(() => {
      // 计算一半的高度/宽度（即原始内容的高度/宽度）
      const h = realBoxHeight / 2;
      const w = realBoxWidth / 2;

      // 根据滚动方向确保尺寸有效
      if ((_direction === "up" || _direction === "down") && h <= 0) {
        return;
      }
      if ((_direction === "left" || _direction === "right") && w <= 0) {
        return;
      }

      // 记录新位置，用于后续判断是否需要单步等待
      let newYPos = yPos;
      let newXPos = xPos;

      // ==================== 向上滚动 ====================
      if (_direction === "up") {
        setYPos((prev) => {
          // 当向上滚动到达边界（已经滚动了一半的高度）时
          if (Math.abs(prev) >= h) {
            setCount(c => c + 1);
            newYPos = -_step;
            return -_step; // 重置到第一步的位置（而不是0，因为还要继续滚动一步）
          }
          newYPos = prev - _step;
          return prev - _step; // 继续向上滚动一步
        });
      }
      // ==================== 向下滚动 ====================
      else if (_direction === "down") {
        setYPos((prev) => {
          if (prev >= 0) {
            setCount(c => c + 1);
            newYPos = (h * -1) + _step;
            return (h * -1) + _step; // 重置到倒数第一步的位置
          }
          newYPos = prev + _step;
          return prev + _step; // 继续向下滚动一步
        });
      }
      // ==================== 向左滚动 ====================
      else if (_direction === "left") {
        setXPos((prev) => {
          if (Math.abs(prev) >= w) {
            setCount(c => c + 1);
            newXPos = -_step;
            return -_step;
          }
          newXPos = prev - _step;
          return prev - _step; // 继续向左滚动一步
        });
      }
      // ==================== 向右滚动 ====================
      else if (_direction === "right") {
        setXPos((prev) => {
          if (prev >= 0) {
            setCount(c => c + 1);
            newXPos = (w * -1) + _step;
            return (w * -1) + _step;
          }
          newXPos = prev + _step;
          return prev + _step; // 继续向右滚动一步
        });
      }

      // 如果是滚轮触发，不继续下一帧
      if (isWheel) {
        return;
      }

      // ==================== 单步滚动等待逻辑 ====================
      // 清除之前的等待定时器
      if (singleWaitTimeout.current) {
        clearTimeout(singleWaitTimeout.current);
      }

      // 如果设置了单步停止高度（垂直方向）
      if (realSingleStopHeight) {
        // 如果当前位置到达了单步停止点，等待一段时间后再继续
        if (Math.abs(newYPos) % realSingleStopHeight < _step) {
          singleWaitTimeout.current = window.setTimeout(() => {
            move();
          }, singleWaitTime);
        }
        else {
          move(); // 立即继续下一帧
        }
      }
      // 如果设置了单步停止宽度（水平方向）
      else if (realSingleStopWidth) {
        if (Math.abs(newXPos) % realSingleStopWidth < _step) {
          singleWaitTimeout.current = window.setTimeout(() => {
            move();
          }, singleWaitTime);
        }
        else {
          move();
        }
      }
      // 没有设置单步停止，持续滚动
      else {
        move();
      }
    });
  };

  /**
   * 启动下一帧动画
   *
   * @description
   * 检查是否满足滚动条件：
   * 1. 没有鼠标悬停
   * 2. 列表长度超过限制
   * 3. 没有达到最大循环次数
   * 如果满足条件，调用 animation 执行下一帧
   */
  const move = () => {
    cancle();
    if (isHoverRef.current || !isScroll || _count === count) {
      setCount(0);
      return;
    }
    animation(direction, step, false);
  };

  /**
   * 初始化滚动
   *
   * @description
   * 1. 计算并设置容器的宽度和高度
   * 2. 如果需要滚动且 isAutoScroll 为 true，启动滚动动画
   *
   * @note
   * - React 在 useEffect 中调用，可能存在 DOM 未完全渲染的情况
   * - 因此使用 requestAnimationFrame 确保 DOM 已渲染
   */
  const initMove = () => {
    if (list && typeof list !== "boolean" && list.length > 100) {
      console.warn(`数据达到了${list.length}条有点多哦~,可能会造成部分老旧浏览器卡顿。`);
    }

    if (isHorizontal) {
      let slotListWidth = slotListRef.current?.offsetWidth || 0;
      slotListWidth = slotListWidth * 2 + 1;
      setRealBoxWidth(slotListWidth);
    }

    if (isScroll) {
      // 使用 requestAnimationFrame 确保 DOM 完全渲染后再获取高度
      requestAnimationFrame(() => {
        const height = realBoxRef.current?.offsetHeight || 0;
        if (height > 0) {
          setRealBoxHeight(height);
        }
        else {
          // 如果获取失败，延迟重试
          setTimeout(() => {
            const retryHeight = realBoxRef.current?.offsetHeight || 0;
            if (retryHeight > 0) {
              setRealBoxHeight(retryHeight);
            }
          }, 100);
        }
      });
    }
    else {
      cancle();
      setXPos(0);
      setYPos(0);
    }
  };

  /**
   * 开始滚动（鼠标移出时调用）
   *
   * @description
   * 1. 更新 hover 状态为 false
   * 2. 启动滚动动画
   *
   * @note
   * 使用 isHoverRef.current 立即更新状态，避免 React state 异步更新导致的问题
   */
  const startMove = () => {
    isHoverRef.current = false; // 立即更新 ref
    move();
  };

  /**
   * 停止滚动（鼠标移入时调用）
   *
   * @description
   * 1. 更新 hover 状态为 true
   * 2. 清除单步等待定时器
   * 3. 取消当前动画帧
   */
  const stopMove = () => {
    isHoverRef.current = true; // 立即更新 ref
    if (singleWaitTimeout.current) {
      clearTimeout(singleWaitTimeout.current);
    }
    cancle();
  };

  const hoverStop = hover && isAutoScroll && isScroll; // 是否启用悬停停止功能

  /**
   * 重置滚动状态
   *
   * @description
   * 1. 取消当前动画
   * 2. 清除 hover 状态
   * 3. 重新初始化滚动
   *
   * @note
   * 对外暴露的方法，可通过 ref 调用
   */
  const reset = () => {
    cancle();
    initMove();
  };

  /**
   * 滚轮事件处理（节流）
   */
  const onWheel = throttle(30, (e) => {
    cancle();
    const singleHeight = realSingleStopHeight || 15;
    if (e.deltaY < 0) {
      animation("down", singleHeight, true);
    }
    if (e.deltaY > 0) {
      animation("up", singleHeight, true);
    }
  });

  // 对外暴露方法
  useImperativeHandle(ref, () => ({
    reset,
  }));

  // ==================== Effects ====================

  /**
   * 初始化效果
   * 当 isScroll 状态改变时，重新初始化滚动
   */
  useEffect(() => {
    if (isScroll) {
      initMove();
    }
  }, [isScroll]);

  /**
   * 高度/宽度变化后启动滚动
   * 当 realBoxHeight 或 realBoxWidth 设置完成后，自动启动滚动
   *
   * @note
   * 这个 useEffect 解决了 DOM 渲染延迟导致的问题：
   * - initMove 中异步获取高度
   * - 高度设置后触发此 useEffect
   * - 然后启动滚动
   */
  useEffect(() => {
    if (isScroll && isAutoScroll && (realBoxHeight > 0 || realBoxWidth > 0) && !isHoverRef.current) {
      move();
    }
  }, [realBoxHeight, realBoxWidth]);

  /**
   * 监听 isAutoScroll 属性变化
   * 当 isAutoScroll 变化时，启动或停止滚动
   */
  useEffect(() => {
    if (isAutoScroll) {
      startMove();
    }
    else {
      stopMove();
    }
  }, [isAutoScroll]);

  /**
   * 监听 count 属性变化
   * 当 count 不为 0 时，重新启动滚动
   *
   * @note
   * count 表示最大循环次数，达到后会停止
   * 通过改变 count 属性可以重新启动滚动
   */
  useEffect(() => {
    if (count !== 0) {
      startMove();
    }
  }, [count]);

  /**
   * 监听 list 数据变化
   * 当 list 或 isWatch 变化时，重置滚动
   */
  useEffect(() => {
    if (isWatch) {
      reset();
    }
  }, [list, isWatch]);

  /**
   * 清理效果
   * 组件卸载时，清除所有定时器和动画帧
   */
  useEffect(() => {
    return () => {
      cancle();
      if (singleWaitTimeout.current) {
        clearTimeout(singleWaitTimeout.current);
      }
    };
  }, []);

  /**
   * 生成滚动内容的 HTML
   *
   * @description
   * 1. 渲染原始列表
   * 2. 如果需要滚动，额外渲染一份拷贝
   * 3. 这样当滚动到一半时，可以无缝重置到开头
   */
  const getHtml = () => {
    return (
      <>
        <div ref={slotListRef} style={floatStyle}>
          {children && typeof children === "function" ? children() : children}
        </div>
        {isScroll
          ? Array.from({ length: copyNum }).map((_, index) => (
              <div key={`copy-${index}`} style={floatStyle}>
                {children && typeof children === "function" ? children() : children}
              </div>
            ))
          : null}
      </>
    );
  };

  /**
   * 渲染组件
   *
   * @description
   * 1. 根据是否启用滚轮功能，渲染不同的结构
   * 2. 处理鼠标悬停事件
   */
  return (
    <div ref={scrollRef} className={className}>
      {/* 如果启用了滚轮和悬停功能，添加 onWheel 事件 */}
      {wheel && hover
        ? (
            <div
              ref={realBoxRef}
              style={realBoxStyle}
              onMouseEnter={() => {
                if (hoverStop) {
                  stopMove();
                }
              }}
              onMouseLeave={() => {
                if (hoverStop) {
                  startMove();
                }
              }}
              onWheel={(e) => {
                if (hoverStop) {
                  onWheel(e);
                }
              }}
            >
              {getHtml()}
            </div>
          )
        : (
            <div
              ref={realBoxRef}
              style={realBoxStyle}
              onMouseEnter={() => {
                if (hoverStop) {
                  stopMove();
                }
              }}
              onMouseLeave={() => {
                if (hoverStop) {
                  startMove();
                }
              }}
            >
              {getHtml()}
            </div>
          )}
    </div>
  );
});

SeamlessScroll.displayName = "SeamlessScroll";

export default SeamlessScroll;
