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

/**
 * RightUtils 组件动画 Hook
 *
 * 功能说明：
 * 1. 监听显示/隐藏状态变化
 * 2. 执行从右侧进入、从右侧离开的动画效果
 * 3. 支持模式切换（港口工具 ↔ 分公司工具）
 * 4. 支持首次加载动画
 * 5. 初始化时就会显示并执行进入动画
 *
 * 动画效果：
 * - 进入动画：从右侧 100px 处滑入，同时淡入（x: 100 → 0, opacity: 0 → 1）
 * - 离开动画：向右侧 100px 处滑出，同时淡出（x: 0 → 100, opacity: 1 → 0）
 *
 * 模式切换流程：
 * 1. 旧模式执行离开动画（0.3秒）
 * 2. 离开动画完成后，更新 displayedMode 为新模式
 * 3. 设置元素到初始位置（右侧 100px）
 * 4. 执行新模式的进入动画（0.5秒）
 *
 * @param {boolean} shouldShowPort - 是否应该显示港口工具
 * @param {boolean} shouldShowBranchOffice - 是否应该显示分公司工具
 * @returns {object} 返回包含以下属性的对象：
 *   - controls: motion 动画控制器
 *   - displayedMode: 延迟显示的模式（'port' | 'branchOffice' | null）
 *   - isVisible: 元素可见性状态
 */
export function useRightUtilsAnimation(shouldShowPort, shouldShowBranchOffice) {
  // ==================== 状态和引用管理 ====================

  // motion 动画控制器，用于控制 motion.div 的动画
  const controls = useAnimation();

  // 标记是否是首次渲染
  // 作用：首次渲染时需要特殊处理，确保初始状态正确设置
  const isFirstRender = useRef(true);

  // 标记是否正在执行动画
  // 作用：防止在动画执行过程中触发新的动画，避免动画冲突
  const isAnimating = useRef(false);

  // 延迟显示的模式状态
  // 作用：在模式切换时，先显示旧模式，动画完成后再更新为新模式
  // 这样确保用户看到完整的离开动画和进入动画
  const [displayedMode, setDisplayedMode] = useState(
    shouldShowPort ? "port" : shouldShowBranchOffice ? "branchOffice" : null,
  );

  // 元素可见性状态
  // 注意：motion.div 始终挂载，isVisible 只控制内容的显示/隐藏
  // 这样可以保证动画控制器始终有效
  const [isVisible, setIsVisible] = useState(false);

  useEffect(() => {
    // ==================== 计算当前应该显示的模式 ====================
    // 根据 shouldShowPort 和 shouldShowBranchOffice 判断当前应该显示哪个模式
    const currentMode = shouldShowPort ? "port" : shouldShowBranchOffice ? "branchOffice" : null;

    // ==================== 首次渲染 ====================
    if (isFirstRender.current) {
      if (currentMode) {
        // ===== 步骤 1：立即更新显示的模式 =====
        // 设置 displayedMode 为当前模式，让组件知道应该渲染哪种内容
        setDisplayedMode(currentMode);

        // ===== 步骤 2：设置初始动画状态 =====
        // 此时 motion.div 还没有挂载（因为 isVisible 还是 false）
        // controls.set() 会将初始状态保存到 controls 对象中
        // 当 motion.div 挂载时，会读取这个初始状态
        controls.set({
          x: 100, // X 轴位置：从右侧 100px 处开始
          opacity: 0, // 透明度：完全透明
        });

        // ===== 步骤 3：显示元素，触发 motion.div 挂载 =====
        // 设置 isVisible 为 true，触发组件重渲染
        // motion.div 会挂载到 DOM，并读取 controls 的初始状态 { x: 100, opacity: 0 }
        setIsVisible(true);

        // ===== 步骤 4：执行进入动画 =====
        // 从初始位置（x: 100, opacity: 0）动画到正常位置（x: 0, opacity: 1）
        controls.start({
          x: 0, // 移动到正常位置（X 轴 0）
          opacity: 1, // 变为完全不透明
          transition: {
            duration: 0.5, // 动画时长：0.5 秒
            ease: "easeOut", // 缓动函数：快速开始，缓慢结束
          },
        }).then(() => {
          // ===== 步骤 5：动画完成，标记首次渲染结束 =====
          isFirstRender.current = false;
        });
      }
      else {
        // 如果首次渲染时不需要显示（currentMode 为 null）
        // 直接标记首次渲染完成，不需要执行动画
        isFirstRender.current = false;
      }
      return;
    }

    // ==================== 隐藏状态（当前没有工具应该显示）====================
    if (!currentMode) {
      if (isVisible && !isAnimating.current) {
        // ===== 执行离开动画 =====
        isAnimating.current = true;
        controls.start({
          x: 100, // 向右移动到 100px 处
          opacity: 0, // 同时淡出
          transition: {
            duration: 0.3, // 动画时长：0.3 秒（比进入动画短，显得更轻快）
            ease: "easeIn", // 缓动函数：缓慢开始，快速结束
          },
        }).then(() => {
          // ===== 离开动画完成后，隐藏元素 =====
          setIsVisible(false); // 隐藏内容（motion.div 仍然挂载）
          setDisplayedMode(null); // 清空显示的模式
          isAnimating.current = false; // 解除动画锁定
        });
      }
      return;
    }

    // ==================== 模式切换或显示 ====================

    // 检查模式是否发生了变化（港口 ↔ 分公司）
    const modeChanged = displayedMode !== currentMode;

    // ===== 场景 1：模式切换（港口 ↔ 分公司）=====
    if (modeChanged && !isAnimating.current && isVisible) {
      isAnimating.current = true;

      // -------- 步骤 1：执行离开动画（旧模式离开）--------
      controls.start({
        x: 100, // 向右移动到 100px 处
        opacity: 0, // 同时淡出
        transition: {
          duration: 0.3, // 动画时长：0.3 秒
          ease: "easeIn", // 缓动函数
        },
      }).then(() => {
        // -------- 步骤 2：离开动画完成后，更新显示的模式 --------
        setDisplayedMode(currentMode);

        // -------- 步骤 3：设置元素到初始位置（右侧外部）--------
        controls.set({
          x: 100, // 确保元素在右侧 100px 处
          opacity: 0, // 确保元素是透明的
        });

        // -------- 步骤 4：执行进入动画（新模式进入）--------
        controls.start({
          x: 0, // 移动到正常位置
          opacity: 1, // 淡入
          transition: {
            duration: 0.5, // 动画时长：0.5 秒
            ease: "easeOut", // 缓动函数
          },
        }).then(() => {
          // -------- 步骤 5：动画完成 --------
          isAnimating.current = false; // 解除动画锁定
        });
      });
    }

    // ===== 场景 2：元素不可见，但应该显示（例如：从隐藏状态切换到显示）=====
    else if (!isVisible && currentMode) {
      // -------- 步骤 1：显示元素 --------
      setIsVisible(true);

      // -------- 步骤 2：更新显示的模式 --------
      setDisplayedMode(currentMode);

      // -------- 步骤 3：设置元素到初始位置 --------
      controls.set({
        x: 100, // 右侧 100px 处
        opacity: 0, // 透明
      });

      // -------- 步骤 4：执行进入动画 --------
      controls.start({
        x: 0, // 移动到正常位置
        opacity: 1, // 淡入
        transition: {
          duration: 0.5, // 动画时长：0.5 秒
          ease: "easeOut", // 缓动函数
        },
      });
    }
  }, [shouldShowPort, shouldShowBranchOffice, controls, displayedMode, isVisible]);

  // ==================== 返回值 ====================
  // 返回动画控制器、延迟显示的模式和可见性状态
  return {
    controls, // motion 动画控制器，绑定到 motion.div 的 animate 属性
    displayedMode, // 延迟显示的模式，用于决定渲染 port 还是 branchOffice 内容
    isVisible, // 元素可见性状态，用于控制内容的显示/隐藏
  };
}
