/**
 * Collabora Online 文档查看器组件
 *
 * 功能：
 * - 加载 Collabora Online iframe
 * - 管理文档加载状态
 * - 提供 UNO 命令接口
 * - 支持只读和编辑模式
 *
 * @encoding UTF-8
 */

import { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react';
import { useCollaboraConfig, useCollaboraUnoCommands, useDocumentReady } from './hooks';
import {
  customGotoPage,
  highlightText as pythonHighlightText,
  replaceTextInPage,
  requestPageInfo,
  unoCancelSearch,
  unoHighlightText,
  unoReplaceAll,
  unoReplaceCurrent,
  unoScrollToTop,
  unoSearchNext,
  type GotoPageResponse,
  type PageInfo
} from './lib';
import { clearHighlights } from './lib/ClearHighlight';
import type { CollaboraViewerHandle, CollaboraViewerProps } from './types';
import { sendUnoCommand } from './Uno';

/**
 * Collabora 文档查看器组件
 * @param props - 组件属性
 * @param ref - 父组件传入的 ref，用于暴露命令接口
 */
export const CollaboraViewer = forwardRef<CollaboraViewerHandle, CollaboraViewerProps>(
  function CollaboraViewer(
    {
      fileId,
      mode = 'view',
      userId = 'guest',
      userName = '',
      targetPage,
      highlightText,
      aiSuggestionReplace,
    },
    ref
  ) {
    const iframeRef = useRef<HTMLIFrameElement>(null);
    // 保存 iframe 的 contentWindow 引用，用于组件卸载时清除高亮
    const iframeWindowRef = useRef<Window | null>(null);

    // 搜索替换面板显示状态
    const [showSearchReplacePanel, setShowSearchReplacePanel] = useState(false);
    // 标记是否应该自动执行搜索
    const shouldAutoSearchRef = useRef(false);
    // 标记是否应该自动执行替换（静默模式）
    const shouldAutoReplaceRef = useRef(false);

    // 高亮测试面板状态
    const [highlightTextInput, setHighlightTextInput] = useState('');
    const [highlightPage, setHighlightPage] = useState('');
    const [previousHighlightText, setPreviousHighlightText] = useState<string | null>(null);
    const [highlightResult, setHighlightResult] = useState<string | null>(null);

    // 清除高亮测试面板状态
    const [clearHighlightResult, setClearHighlightResult] = useState<string | null>(null);
    const [isClearing, setIsClearing] = useState(false);

    // 页数信息测试状态
    const [pageInfoResult, setPageInfoResult] = useState<string | null>(null);
    const [isLoadingPageInfo, setIsLoadingPageInfo] = useState(false);

    // 页面跳转测试状态
    const [gotoPageInput, setGotoPageInput] = useState('');
    const [gotoPageResult, setGotoPageResult] = useState<string | null>(null);
    const [isJumping, setIsJumping] = useState(false);

    // UNO 命令测试面板状态
    const [unoCmd, setUnoCmd] = useState('.uno:Bold');
    const [unoArgs, setUnoArgs] = useState('');
    const [unoResult, setUnoResult] = useState<string | null>(null);

    // 搜索替换测试面板状态
    const [searchText, setSearchText] = useState('');
    const [replaceText, setReplaceText] = useState('');
    const [searchReplaceResult, setSearchReplaceResult] = useState<string | null>(null);
    const [replaceAllMode, setReplaceAllMode] = useState(false);
    const [searchReplacePageNumber, setSearchReplacePageNumber] = useState('');
    // 记录上一次搜索的参数，用于判断是否需要重新跳转页面
    const [lastSearchParams, setLastSearchParams] = useState<{
      text: string;
      page: string;
    }>({ text: '', page: '' });

    // 页面定点替换状态
    const [replaceInPageNumber, setReplaceInPageNumber] = useState('');
    const [replaceInPageSearch, setReplaceInPageSearch] = useState('');
    const [replaceInPageReplace, setReplaceInPageReplace] = useState('');
    const [replaceInPageResult, setReplaceInPageResult] = useState<string | null>(null);
    const [isReplacingInPage, setIsReplacingInPage] = useState(false);

    // 1. 加载 Collabora 配置
    const { config, loading, error } = useCollaboraConfig(fileId, mode, userId, userName);

    // 2. 监听文档加载状态
    const { isDocumentLoaded } = useDocumentReady(iframeRef);

    // 2.5. 保存 iframe window 引用并在文档加载时清除所有高亮
    useEffect(() => {
      if (isDocumentLoaded && iframeRef.current?.contentWindow) {
        iframeWindowRef.current = iframeRef.current.contentWindow;
        // console.log('[CollaboraViewer] 已保存 iframe window 引用');

        // 🔥 文档加载完成后主动清除一次高亮（防止缓存的高亮状态）
        // console.log('[CollaboraViewer] 🧹 文档加载完成，清除可能存在的缓存高亮');
        clearHighlights(iframeRef.current.contentWindow, {
          color: 16776960,
          timeout: 5000,
        }).then((result) => {
          if (result.count && result.count > 0) {
            // console.log(`[CollaboraViewer] ✓ 清除了 ${result.count} 个缓存的高亮区域`);
          } else {
            // console.log('[CollaboraViewer] ✓ 文档无缓存高亮，已确认干净');
          }
        }).catch(error => {
          console.warn('[CollaboraViewer] ⚠️ 清除缓存高亮失败:', error);
        });
      }
    }, [isDocumentLoaded]);

    // 3. UNO 命令封装
    const unoCommands = useCollaboraUnoCommands(iframeRef);

    // 4. 暴露接口给父组件（包括清除高亮方法和保存方法）
    useImperativeHandle(ref, () => ({
      unoCommands,
      isReady: isDocumentLoaded,
      mode,
      getIframeWindow: () => iframeRef.current?.contentWindow || null,
      clearAllHighlights: async () => {
        const savedWindow = iframeWindowRef.current || iframeRef.current?.contentWindow;
        if (savedWindow) {
          // console.log('[CollaboraViewer] 🧹 父组件调用清除高亮');
          await clearHighlights(savedWindow, {
            color: 16776960,
            timeout: 5000,
          });
          // console.log('[CollaboraViewer] ✓ 清除高亮完成');
        } else {
          console.warn('[CollaboraViewer] ⚠️ 无法清除高亮：iframe window 不可用');
        }
      },
      saveDocument: async () => {
        const savedWindow = iframeWindowRef.current || iframeRef.current?.contentWindow;
        if (savedWindow) {
          console.log('[CollaboraViewer] 💾 父组件调用保存文档');
          try {
            // 步骤1：发送保存命令
            sendUnoCommand(savedWindow, '.uno:Save', {});
            // console.log('[CollaboraViewer] ✓ 保存命令已发送');

            // 步骤2：等待 WOPI PutFile 请求完成（增加到 2000ms）
            // await new Promise(resolve => setTimeout(resolve, 2000));

            // 步骤3：再次发送保存命令确保完全保存
            sendUnoCommand(savedWindow, '.uno:Save', {});
            // console.log('[CollaboraViewer] ✓ 二次保存命令已发送');

            // 步骤4：再等待一段时间确保保存完成
            // await new Promise(resolve => setTimeout(resolve, 1000));

            // console.log('[CollaboraViewer] ✓ 文档保存完成（总等待 3000ms）');
          } catch (error) {
            console.error('[CollaboraViewer] ✗ 保存文档失败:', error);
            throw error;
          }
        } else {
          console.warn('[CollaboraViewer] ⚠️ 无法保存文档：iframe window 不可用');
          throw new Error('iframe window 不可用');
        }
      },
    }), [unoCommands, isDocumentLoaded, mode]);

    // 5. 监听 targetPage 和 highlightText 变化，自动跳转并高亮
    // 根据是否有 targetPage 参数选择高亮方式：
    // - 有 targetPage：使用 Python 脚本（支持跨页搜索和精确跳转）
    // - 无 targetPage：使用 UNO 命令（当前页面高亮，性能更好）
    useEffect(() => {
      // 如果文档未加载完成，不执行跳转和高亮
      if (!isDocumentLoaded || !iframeRef.current?.contentWindow) {
        return;
      }

      // 如果有高亮文本，执行高亮操作
      if (highlightText && highlightText.trim() !== '') {
        const performHighlight = async () => {
          const iframeWindow = iframeRef.current!.contentWindow!;
          const textToHighlight = highlightText.trim();

          try {
            // 步骤1：清除之前的所有高亮
            console.log('[CollaboraViewer] 步骤1：清除旧高亮...');
            await clearHighlights(iframeWindow, {
              color: 16776960, // 黄色
              timeout: 5000,
            });

            // 短暂延迟，确保清除操作完成
            await new Promise(resolve => setTimeout(resolve, 100));

            // 步骤2：根据是否有 targetPage 选择高亮方式
            if (targetPage !== undefined && targetPage !== null) {
              // 方案A：有 targetPage - 使用 Python 脚本（跨页搜索 + 高亮 + 跳转）
              console.log(`[CollaboraViewer] 步骤2A：使用 Python 脚本跳转到第 ${targetPage} 页并高亮 "${textToHighlight}"`);

              const result = await pythonHighlightText(iframeWindow, textToHighlight, {
                color: 16776960, // 黄色
                page: targetPage,
              });

              if (result.success) {
                console.log(`[CollaboraViewer] ✓ Python 高亮成功: "${textToHighlight}" (第${targetPage}页, 共${result.highlightedCount}处)`);
              } else {
                console.error('[CollaboraViewer] ✗ Python 高亮失败:', result.message);
              }
            } else {
              // 方案B：无 targetPage - 使用 UNO 命令（当前页面高亮）
              console.log(`[CollaboraViewer] 步骤2B：使用 UNO 命令在当前页高亮 "${textToHighlight}"`);

              unoHighlightText(iframeWindow, textToHighlight, 16776960); // 黄色

              // 短暂延迟，确保高亮操作完成
              await new Promise(resolve => setTimeout(resolve, 100));

              // 取消选中状态（避免高亮后文本仍被选中）
              console.log('[CollaboraViewer] 步骤3：取消选中状态...');
              sendUnoCommand(iframeWindow, '.uno:Escape', {});

              console.log(`[CollaboraViewer] ✓ UNO 高亮完成: "${textToHighlight}"`);
            }
          } catch (error) {
            console.error('[CollaboraViewer] 高亮失败:', error);
          }
        };

        performHighlight();
      }
    }, [targetPage, highlightText, isDocumentLoaded]);

    // 6. 组件销毁时保存文档并清除所有高亮（使用保存的 window 引用）
    useEffect(() => {
      // 返回清理函数，在组件卸载时执行
      return () => {
        const savedWindow = iframeWindowRef.current;
        if (savedWindow) {
          // console.log('[CollaboraViewer] 🔥 组件即将销毁，触发文档保存和清除高亮');

          // 步骤1：发送保存命令（如果是编辑模式）
          if (mode === 'edit') {
            try {
              console.log('[CollaboraViewer] 💾 组件销毁时发送保存命令');
              sendUnoCommand(savedWindow, '.uno:Save', {});
              // 再次发送确保保存
              setTimeout(() => {
                sendUnoCommand(savedWindow, '.uno:Save', {});
                console.log('[CollaboraViewer] ✓ 二次保存命令已发送');
              }, 100);
            } catch (error) {
              console.error('[CollaboraViewer] ✗ 组件销毁时保存失败:', error);
            }
          }

          // 步骤2：清除高亮
          void clearHighlights(savedWindow, {
            color: 16776960, // 黄色
            timeout: 3000,
          }).then(() => {
            console.log('[CollaboraViewer] ✓ 组件销毁时高亮清除成功');
          }).catch(error => {
            console.error('[CollaboraViewer] ✗ 组件销毁时清除高亮失败:', error);
          });

          // 清空引用
          iframeWindowRef.current = null;
        } else {
          console.warn('[CollaboraViewer] ⚠️ 组件销毁时未找到保存的 window 引用');
        }
      };
    }, [mode]);

    // 7. 监听 AI 建议替换参数变化，设置搜索参数
    useEffect(() => {
      if (!aiSuggestionReplace || !isDocumentLoaded) {
        return;
      }

      console.log('[CollaboraViewer] 收到 AI 建议替换参数:', aiSuggestionReplace);

      const { searchText: newSearchText, replaceText: newReplaceText, pageNumber, silentReplace } = aiSuggestionReplace;

      // 根据 silentReplace 标志决定是否显示面板
      if (silentReplace) {
        // 静默替换模式：不显示面板，直接执行替换
        console.log('[CollaboraViewer] 静默替换模式，不显示面板，直接执行替换');

        // 延迟执行替换，确保 DOM 更新完成
        const timer = setTimeout(async () => {
          if (!iframeRef.current?.contentWindow) {
            console.error('[CollaboraViewer] iframe 未就绪，无法执行替换');
            return;
          }

          try {
            // 步骤1：搜索文本（确保文本被选中，会自动跳转到匹配位置）
            console.log(`[CollaboraViewer] 步骤1：搜索文本 "${newSearchText}"`);
            unoSearchNext(iframeRef.current.contentWindow, newSearchText);

            // 等待搜索完成
            await new Promise(resolve => setTimeout(resolve, 300));

            // 步骤2：执行替换（替换后光标保留在当前位置）
            console.log(`[CollaboraViewer] 步骤2：替换为 "${newReplaceText}"`);
            unoReplaceCurrent(iframeRef.current.contentWindow, newSearchText, newReplaceText);

            // 等待替换完成
            await new Promise(resolve => setTimeout(resolve, 200));

            // 步骤3：自动搜索下一个相同的占位符（如果还有的话）
            console.log(`[CollaboraViewer] 步骤3：定位到下一个 "${newSearchText}"`);
            unoSearchNext(iframeRef.current.contentWindow, newSearchText);

            console.log('[CollaboraViewer] ✓ 静默替换完成，已定位到下一个占位符（如有）');
          } catch (error) {
            console.error('[CollaboraViewer] 静默替换失败:', error);
          }
        }, 300);

        return () => clearTimeout(timer);
      } else {
        // 显示搜索替换面板
        setShowSearchReplacePanel(true);

        // 设置搜索、替换和页码输入框的值
        setSearchText(newSearchText);
        setReplaceText(newReplaceText);
        setSearchReplacePageNumber(String(pageNumber));

        // 普通模式：仅自动执行查找
        shouldAutoSearchRef.current = true;
        console.log('[CollaboraViewer] 已设置搜索参数，等待状态更新后自动执行查找');
      }
    }, [aiSuggestionReplace, isDocumentLoaded]);

    // 8. 当搜索参数更新完成后，自动执行查找
    useEffect(() => {
      if (shouldAutoSearchRef.current && searchText && searchReplacePageNumber && isDocumentLoaded) {
        console.log('[CollaboraViewer] 状态更新完成，执行自动查找:', { searchText, searchReplacePageNumber });

        // 重置标志
        shouldAutoSearchRef.current = false;

        // 延迟执行，确保 DOM 更新完成
        const timer = setTimeout(() => {
          // console.log('[CollaboraViewer] 开始执行自动查找操作');
          handleSearchNext();
        }, 100);

        return () => clearTimeout(timer);
      }
    }, [searchText, searchReplacePageNumber, isDocumentLoaded]); // eslint-disable-line react-hooks/exhaustive-deps

    // 9. 当搜索参数更新完成后，自动执行替换（静默模式）
    // 不跳转页面，直接在当前位置搜索并替换，替换后自动定位到下一个相同占位符
    useEffect(() => {
      if (shouldAutoReplaceRef.current && searchText && replaceText && isDocumentLoaded) {
        console.log('[CollaboraViewer] 静默替换模式：自动执行替换:', { searchText, replaceText });

        // 重置标志
        shouldAutoReplaceRef.current = false;

        // 延迟执行，确保 DOM 更新完成
        const timer = setTimeout(async () => {
          if (!iframeRef.current?.contentWindow) {
            console.error('[CollaboraViewer] iframe 未就绪，无法执行替换');
            return;
          }

          try {
            // 步骤1：搜索文本（确保文本被选中，会自动跳转到匹配位置）
            console.log(`[CollaboraViewer] 步骤1：搜索文本 "${searchText}"`);
            unoSearchNext(iframeRef.current.contentWindow, searchText);

            // 等待搜索完成
            await new Promise(resolve => setTimeout(resolve, 300));

            // 步骤2：执行替换（替换后光标保留在当前位置）
            console.log(`[CollaboraViewer] 步骤2：替换为 "${replaceText}"`);
            unoReplaceCurrent(iframeRef.current.contentWindow, searchText, replaceText);

            // 等待替换完成
            await new Promise(resolve => setTimeout(resolve, 200));

            // 步骤3：自动搜索下一个相同的占位符（如果还有的话）
            console.log(`[CollaboraViewer] 步骤3：定位到下一个 "${searchText}"`);
            unoSearchNext(iframeRef.current.contentWindow, searchText);

            console.log('[CollaboraViewer] ✓ 静默替换完成，已定位到下一个占位符（如有）');
          } catch (error) {
            console.error('[CollaboraViewer] 静默替换失败:', error);
          }
        }, 300);

        return () => clearTimeout(timer);
      }
    }, [searchText, replaceText, isDocumentLoaded]); // eslint-disable-line react-hooks/exhaustive-deps

    // 加载中状态
    if (loading) {
      return (
        <div className="flex justify-center items-center h-full min-h-[600px]">
          <div className="text-center">
            <div className="inline-block animate-spin rounded-full h-12 w-12 border-b-2 border-primary"></div>
            <p className="mt-4 text-gray-600">加载文档配置中...</p>
          </div>
        </div>
      );
    }

    // 错误状态
    if (error || !config) {
      return (
        <div className="flex justify-center items-center h-full min-h-[600px]">
          <div className="text-center text-red-500">
            <i className="ri-error-warning-line text-4xl mb-2"></i>
            <p className="text-lg">{error}</p>
            {/* <p className="text-sm text-gray-500 mt-2">请刷新页面重试或联系管理员</p> */}
          </div>
        </div>
      );
    }

    // 发送 UNO 命令的处理函数（测试用）
    const sendUno = () => {
      if (!iframeRef.current?.contentWindow) {
        setUnoResult('iframe 不可用');
        return;
      }

      let args: Record<string, unknown> = {};
      const raw = (unoArgs || '').trim();
      if (raw !== '') {
        try {
          args = JSON.parse(raw) as Record<string, unknown>;
        } catch (err) {
          try {
            // fallback: replace single quotes with double quotes and parse
            args = JSON.parse(raw.replace(/'(.*?)'/g, '"$1"')) as Record<string, unknown>;
          } catch (err2) {
            console.error('解析 UNO Args 失败:', err2);
            setUnoResult('Args 解析失败，请使用有效 JSON');
            return;
          }
        }
      }

      try {
        // 使用正确的 sendUnoCommand 方法
        sendUnoCommand(iframeRef.current.contentWindow, unoCmd, args);
        setUnoResult(`已发送: ${unoCmd}`);
        console.log('[UNO Debug] 发送命令:', unoCmd, args);
      } catch (e) {
        console.error('发送 UNO 失败:', e);
        setUnoResult('发送失败，请查看控制台');
      }
    };

    // 高亮测试处理函数
    const handleSwitchHighlight = async () => {
      if (!iframeRef.current?.contentWindow) {
        // setHighlightResult('iframe 未就绪');
        return;
      }

      if (!highlightTextInput || highlightTextInput.trim() === '') {
        setHighlightResult('请输入要高亮的文本');
        return;
      }

      try {
        // 解析页码 (可选)
        const page = highlightPage && highlightPage.trim() !== ''
          ? parseInt(highlightPage.trim(), 10)
          : undefined;

        // 验证页码
        if (page !== undefined && (isNaN(page) || page < 1)) {
          setHighlightResult('页码必须是大于0的整数');
          return;
        }

        const iframeWindow = iframeRef.current.contentWindow;
        const textToHighlight = highlightTextInput.trim();

        // 先清除旧高亮
        await clearHighlights(iframeWindow, {
          color: 16776960,
          timeout: 5000,
        });

        await new Promise(resolve => setTimeout(resolve, 100));

        // 根据是否有页码选择高亮方式
        if (page !== undefined) {
          // 使用 Python 脚本（跨页搜索 + 高亮 + 跳转）
          const result = await pythonHighlightText(iframeWindow, textToHighlight, {
            color: 16776960,
            page: page,
          });

          if (result.success) {
            setPreviousHighlightText(textToHighlight);
            setHighlightResult(`✓ 已切换高亮: ${textToHighlight} (第${page}页, 共${result.highlightedCount}处)`);
          } else {
            setHighlightResult(`✗ 高亮失败: ${result.message}`);
          }
        } else {
          // 使用 UNO 命令（当前页面高亮）
          unoHighlightText(iframeWindow, textToHighlight, 16776960);
          await new Promise(resolve => setTimeout(resolve, 100));
          sendUnoCommand(iframeWindow, '.uno:Escape', {});

          setPreviousHighlightText(textToHighlight);
          setHighlightResult(`✓ 已切换高亮: ${textToHighlight} (当前页)`);
        }
      } catch (e) {
        console.error('切换高亮失败:', e);
        setHighlightResult(`切换失败: ${e instanceof Error ? e.message : '未知错误'}`);
      }
    };

    // 滚动到顶部处理函数
    const handleScrollToTop = async () => {
      if (!iframeRef.current?.contentWindow) {
        console.error('iframe 未就绪');
        return;
      }

      try {
        await unoScrollToTop(iframeRef.current.contentWindow);
        // console.log('[CollaboraViewer] 已滚动到文档顶部');
      } catch (e) {
        console.error('滚动到顶部失败:', e);
      }
    };

    // 获取页数信息处理函数
    const handleGetPageInfo = async () => {
      if (!iframeRef.current?.contentWindow) {
        // setPageInfoResult('iframe 未就绪');
        return;
      }

      setIsLoadingPageInfo(true);
      setPageInfoResult('正在获取页数信息...');

      try {
        const info: PageInfo = await requestPageInfo(iframeRef.current.contentWindow);
        setPageInfoResult(
          `✓ 总页数: ${info.totalPages} | 当前页: ${info.currentPage + 1} (索引: ${info.currentPage})`
        );
        console.log('[CollaboraViewer] 页数信息:', info);

        // 3秒后清除提示
        setTimeout(() => {
          setPageInfoResult(null);
        }, 3000);
      } catch (e) {
        console.error('获取页数信息失败:', e);
        setPageInfoResult(`✗ 获取失败: ${e instanceof Error ? e.message : '未知错误'}`);

        // 5秒后清除错误提示
        setTimeout(() => {
          setPageInfoResult(null);
        }, 5000);
      } finally {
        setIsLoadingPageInfo(false);
      }
    };

    // 页面跳转处理函数
    const handleGotoPage = async () => {
      if (!iframeRef.current?.contentWindow) {
        // setGotoPageResult('iframe 未就绪');
        return;
      }

      const pageNumber = parseInt(gotoPageInput.trim(), 10);
      if (isNaN(pageNumber) || pageNumber < 1) {
        setGotoPageResult('请输入有效的页码 (大于0的整数)');
        return;
      }

      setIsJumping(true);
      // setGotoPageResult(`正在跳转到第 ${pageNumber} 页...`);

      try {
        const response: GotoPageResponse = await customGotoPage(
          iframeRef.current.contentWindow,
          pageNumber
        );
        setGotoPageResult(
          `✓ 已跳转到第 ${response.pageNumber} 页 (总页数: ${response.totalPages})`
        );
        // console.log('[CollaboraViewer] 页面跳转成功:', response);

        // 3秒后清除提示
        setTimeout(() => {
          setGotoPageResult(null);
        }, 3000);
      } catch (e) {
        console.error('页面跳转失败:', e);
        setGotoPageResult(`✗ 跳转失败: ${e instanceof Error ? e.message : '未知错误'}`);

        // 5秒后清除错误提示
        setTimeout(() => {
          setGotoPageResult(null);
        }, 5000);
      } finally {
        setIsJumping(false);
      }
    };

    // 清除高亮处理函数
    const handleClearHighlights = async () => {
      if (!iframeRef.current?.contentWindow) {
        // setClearHighlightResult('iframe 未就绪');
        return;
      }

      setIsClearing(true);
      setClearHighlightResult('正在清除高亮...');

      try {
        const result = await clearHighlights(iframeRef.current.contentWindow, {
          color: 16776960, // 黄色
          timeout: 10000,
        });

        if (result.success) {
          setClearHighlightResult(`✓ ${result.message}`);
          // console.log('[CollaboraViewer] 清除高亮成功:', result);

          // 清空之前记录的高亮文本
          setPreviousHighlightText(null);
        } else {
          setClearHighlightResult(`✗ 清除失败: ${result.message}`);
          console.error('[CollaboraViewer] 清除高亮失败:', result);
        }

        // 3秒后清除提示
        setTimeout(() => {
          setClearHighlightResult(null);
        }, 3000);
      } catch (e) {
        console.error('清除高亮失败:', e);
        setClearHighlightResult(`✗ 清除失败: ${e instanceof Error ? e.message : '未知错误'}`);

        // 5秒后清除错误提示
        setTimeout(() => {
          setClearHighlightResult(null);
        }, 5000);
      } finally {
        setIsClearing(false);
      }
    };

    // 搜索下一个处理函数
    const handleSearchNext = async () => {
      if (!iframeRef.current?.contentWindow) {
        // setSearchReplaceResult('iframe 未就绪');
        return;
      }

      if (!searchText.trim()) {
        setSearchReplaceResult('请输入搜索文本');
        return;
      }

      try {
        const currentSearchText = searchText.trim();
        const currentPageNumber = searchReplacePageNumber.trim();

        // 判断是否需要跳转页面（搜索文本或页码发生变化）
        const needsPageJump =
          lastSearchParams.text !== currentSearchText ||
          lastSearchParams.page !== currentPageNumber;

        // 如果指定了页码且需要跳转，先跳转到该页
        if (needsPageJump && currentPageNumber !== '') {
          const pageNumber = parseInt(currentPageNumber, 10);
          if (isNaN(pageNumber) || pageNumber < 1) {
            setSearchReplaceResult('✗ 页码必须是大于0的整数');
            return;
          }

          // setSearchReplaceResult(`正在跳转到第 ${pageNumber} 页...`);
          try {
            await customGotoPage(iframeRef.current.contentWindow, pageNumber);
            // 短暂延迟等待页面渲染
            await new Promise(resolve => setTimeout(resolve, 300));
          } catch (e) {
            console.error('跳转页面失败:', e);
            setSearchReplaceResult(`✗ 跳转失败: ${e instanceof Error ? e.message : '未知错误'}`);
            return;
          }
        }

        // 执行搜索（如果是页面内循环查找，会自动找到下一个匹配项）
        unoSearchNext(iframeRef.current.contentWindow, currentSearchText);

        // 更新搜索参数记录
        setLastSearchParams({
          text: currentSearchText,
          page: currentPageNumber
        });

        // const pageInfo = currentPageNumber !== ''
        //   ? ` (第${currentPageNumber}页内循环查找)`
        //   : '';
        // const jumpInfo = needsPageJump && currentPageNumber !== '' ? ' [已跳转]' : '';
        // setSearchReplaceResult(`✓ 搜索: "${currentSearchText}"${pageInfo}${jumpInfo}`);

        // 3秒后清除提示
        setTimeout(() => setSearchReplaceResult(null), 3000);
      } catch (e) {
        console.error('搜索失败:', e);
        setSearchReplaceResult(`✗ 搜索失败: ${e instanceof Error ? e.message : '未知错误'}`);
      }
    };

    // 替换处理函数
    const handleReplace = async () => {
      if (!iframeRef.current?.contentWindow) {
        // setSearchReplaceResult('iframe 未就绪');
        return;
      }

      if (!searchText.trim()) {
        setSearchReplaceResult('请输入搜索文本');
        return;
      }

      try {
        // 如果指定了页码，先跳转到该页
        if (searchReplacePageNumber && searchReplacePageNumber.trim() !== '') {
          const pageNumber = parseInt(searchReplacePageNumber.trim(), 10);
          if (isNaN(pageNumber) || pageNumber < 1) {
            setSearchReplaceResult('✗ 页码必须是大于0的整数');
            return;
          }

          // setSearchReplaceResult(`正在跳转到第 ${pageNumber} 页...`);
          try {
            await customGotoPage(iframeRef.current.contentWindow, pageNumber);
            // 短暂延迟等待页面渲染
            await new Promise(resolve => setTimeout(resolve, 300));
          } catch (e) {
            console.error('跳转页面失败:', e);
            setSearchReplaceResult(`✗ 跳转失败: ${e instanceof Error ? e.message : '未知错误'}`);
            return;
          }
        }

        const pageInfo = searchReplacePageNumber && searchReplacePageNumber.trim() !== ''
          ? ` (第${searchReplacePageNumber}页)`
          : '';

        if (replaceAllMode) {
          // 替换全部
          unoReplaceAll(iframeRef.current.contentWindow, searchText.trim(), replaceText);
          setSearchReplaceResult(`✓ 已替换全部: "${searchText.trim()}" → "${replaceText}"${pageInfo}`);
        } else {
          // 替换当前选中项：先搜索，再替换
          console.log('[handleReplace] 开始替换流程：先搜索，再替换');

          // 步骤1：先执行搜索，确保文本被选中
          unoSearchNext(iframeRef.current.contentWindow, searchText.trim());

          // 步骤2：等待搜索完成后执行替换
          await new Promise(resolve => setTimeout(resolve, 300));

          // 步骤3：执行替换
          unoReplaceCurrent(iframeRef.current.contentWindow, searchText.trim(), replaceText);

          setSearchReplaceResult(`✓ 已替换: "${searchText.trim()}" → "${replaceText}"${pageInfo}`);

          console.log('[handleReplace] 替换完成');
        }

        // 3秒后清除提示
        setTimeout(() => setSearchReplaceResult(null), 3000);
      } catch (e) {
        console.error('替换失败:', e);
        setSearchReplaceResult(`✗ 替换失败: ${e instanceof Error ? e.message : '未知错误'}`);
      }
    };

    // 取消搜索处理函数
    const handleCancelSearch = () => {
      if (!iframeRef.current?.contentWindow) {
        return;
      }

      try {
        unoCancelSearch(iframeRef.current.contentWindow);
        // 重置搜索参数记录，下次搜索会重新定位
        setLastSearchParams({ text: '', page: '' });
        setSearchReplaceResult('✓ 已取消搜索');
        setTimeout(() => setSearchReplaceResult(null), 2000);
      } catch (e) {
        console.error('取消搜索失败:', e);
      }
    };

    // 页面定点替换处理函数
    const handleReplaceInPage = async () => {
      if (!iframeRef.current?.contentWindow) {
        // setReplaceInPageResult('iframe 未就绪');
        return;
      }

      const pageNum = parseInt(replaceInPageNumber.trim(), 10);
      if (isNaN(pageNum) || pageNum < 1) {
        setReplaceInPageResult('请输入有效的页码 (大于0的整数)');
        return;
      }

      if (!replaceInPageSearch.trim()) {
        setReplaceInPageResult('请输入原文');
        return;
      }

      setIsReplacingInPage(true);
      setReplaceInPageResult(`正在第 ${pageNum} 页执行替换...`);

      try {
        const result = await replaceTextInPage(
          iframeRef.current.contentWindow,
          pageNum,
          replaceInPageSearch.trim(),
          replaceInPageReplace
        );

        if (result.success) {
          setReplaceInPageResult(`✓ ${result.message}`);
        } else {
          setReplaceInPageResult(`✗ ${result.message}`);
        }

        // 5秒后清除提示
        setTimeout(() => setReplaceInPageResult(null), 5000);
      } catch (e) {
        console.error('页面定点替换失败:', e);
        setReplaceInPageResult(`✗ 执行失败: ${e instanceof Error ? e.message : '未知错误'}`);

        // 5秒后清除错误提示
        setTimeout(() => setReplaceInPageResult(null), 5000);
      } finally {
        setIsReplacingInPage(false);
      }
    };

    return (
      <div className="collabora-viewer relative w-full h-full min-h-[600px]">
        {/* UNO 命令测试面板 - 临时隐藏 */}
        {/* <div className="absolute top-2 right-2 z-50 bg-white bg-opacity-95 px-3 py-2 rounded shadow-lg flex items-center gap-2 border border-gray-200">
        <span className="text-xs text-gray-500 font-medium">UNO:</span>
        <input
          className="px-2 py-1 border rounded text-sm w-48 font-mono"
          value={unoCmd}
          onChange={(e) => setUnoCmd(e.target.value)}
          placeholder="UNO 命令 (如 .uno:Bold)"
          aria-label="UNO 命令"
          onKeyPress={(e) => {
            if (e.key === 'Enter') {
              sendUno();
            }
          }}
        />
        <input
          className="px-2 py-1 border rounded text-sm w-64 font-mono"
          value={unoArgs}
          onChange={(e) => setUnoArgs(e.target.value)}
          placeholder='Args (JSON, 如 {"SearchItem.SearchString":{"type":"string","value":"test"}})'
          aria-label="UNO Args (JSON)"
          onKeyPress={(e) => {
            if (e.key === 'Enter') {
              sendUno();
            }
          }}
        />
        <button
          className="px-3 py-1.5 bg-indigo-600 text-white rounded hover:bg-indigo-700 text-sm font-medium disabled:bg-gray-400"
          onClick={sendUno}
          disabled={!isDocumentLoaded}
        >
          发送
        </button>
        {unoResult && (
          <span className={`text-xs ml-1 ${unoResult.startsWith('已发送') ? 'text-green-600' : 'text-red-500'}`}>
            {unoResult}
          </span>
        )}
      </div> */}

        {/* 高亮测试面板 - 临时隐藏 */}
        {/* <div className="absolute top-2 left-2 z-50 bg-white bg-opacity-90 px-3 py-2 rounded shadow flex items-center gap-2">
        <input
          className="px-3 py-1.5 border rounded text-sm w-64"
          value={highlightTextInput}
          onChange={(e) => setHighlightTextInput(e.target.value)}
          placeholder="输入要高亮的文本 (如: 合同编号)"
          aria-label="高亮文本"
          onKeyPress={(e) => {
            if (e.key === 'Enter') {
              handleSwitchHighlight();
            }
          }}
        />
        <input
          className="px-3 py-1.5 border rounded text-sm w-20"
          value={highlightPage}
          onChange={(e) => setHighlightPage(e.target.value)}
          placeholder="页码"
          aria-label="页码"
          type="number"
          min="1"
          onKeyPress={(e) => {
            if (e.key === 'Enter') {
              handleSwitchHighlight();
            }
          }}
        />
        <button
          className="px-4 py-1.5 bg-yellow-500 text-white rounded hover:bg-yellow-600 text-sm font-medium"
          onClick={handleSwitchHighlight}
          disabled={!isDocumentLoaded}
        >
          切换高亮
        </button>
        {previousHighlightText && (
          <span className="text-xs text-gray-600">
            上次: {previousHighlightText}
          </span>
        )}
        {highlightResult && (
          <span className="text-xs text-gray-700 ml-2">{highlightResult}</span>
        )}
      </div> */}

        {/* 清除高亮测试面板 - 临时隐藏 */}
        {/* <div className="absolute top-16 left-2 z-50 bg-white bg-opacity-90 px-3 py-2 rounded shadow flex items-center gap-2">
        <button
          className={`px-4 py-1.5 rounded text-sm font-medium transition-colors ${
            isClearing
              ? 'bg-gray-400 text-white cursor-not-allowed'
              : 'bg-red-500 text-white hover:bg-red-600'
          }`}
          onClick={handleClearHighlights}
          disabled={!isDocumentLoaded || isClearing}
        >
          {isClearing ? '清除中...' : '清除所有高亮'}
        </button>
        {clearHighlightResult && (
          <span className={`text-xs ml-2 ${
            clearHighlightResult.startsWith('✓') ? 'text-green-600' :
            clearHighlightResult.startsWith('✗') ? 'text-red-600' :
            'text-gray-600'
          }`}>
            {clearHighlightResult}
          </span>
        )}
      </div> */}

        {/* 搜索替换测试面板 - 移动到左上角并添加关闭按钮 */}
        {showSearchReplacePanel && (
          <div className="absolute top-2 left-2 z-50 bg-white bg-opacity-70 px-3 py-2 rounded shadow-lg border border-gray-200">
            {/* 标题栏和关闭按钮 */}
            <div className="flex items-center justify-between mb-2">
              <span className="text-xs text-gray-700 font-medium">搜索替换</span>
              <button
                onClick={() => setShowSearchReplacePanel(false)}
                className="text-gray-400 hover:text-gray-600 transition-colors"
                title="关闭面板"
                aria-label="关闭搜索替换面板"
              >
                <i className="ri-close-line text-lg"></i>
              </button>
            </div>

            <div className="flex items-center gap-2 mb-2">
              <span className="text-xs text-gray-500 font-medium w-12">搜索:</span>
              <input
                className="px-2 py-1 border rounded text-sm w-48"
                value={searchText}
                onChange={(e) => setSearchText(e.target.value)}
                placeholder="输入要搜索的文本"
                aria-label="搜索文本"
                onKeyDown={(e) => {
                  if (e.key === 'Enter') {
                    handleSearchNext();
                  }
                }}
              />
              <span className="text-xs text-gray-500 font-medium w-12">页码:</span>
              <input
                className="px-2 py-1 border rounded text-sm w-16"
                value={searchReplacePageNumber}
                onChange={(e) => setSearchReplacePageNumber(e.target.value)}
                placeholder="页码"
                aria-label="页码"
                type="number"
                min="1"
                title="可选：指定搜索/替换的页码"
              />
              <button
                className="px-3 py-1 bg-blue-500 text-white rounded hover:bg-blue-600 text-sm font-medium disabled:bg-gray-400"
                onClick={handleSearchNext}
                disabled={!isDocumentLoaded}
              >
                查找
              </button>
              <button
                className="px-3 py-1 bg-gray-500 text-white rounded hover:bg-gray-600 text-sm font-medium disabled:bg-gray-400"
                onClick={handleCancelSearch}
                disabled={!isDocumentLoaded}
                title="取消搜索选中"
              >
                取消搜索选中
              </button>
            </div>
            <div className="flex items-center gap-8 justify-between">
              <div className="flex items-center gap-2 flex-1">
                <span className="text-xs text-gray-500 font-medium w-12">替换:</span>
                <input
                  className="px-2 py-1 border rounded text-sm flex-1"
                  value={replaceText}
                  onChange={(e) => setReplaceText(e.target.value)}
                  placeholder="输入替换后的文本"
                  aria-label="替换文本"
                  onKeyDown={(e) => {
                    if (e.key === 'Enter') {
                      handleReplace();
                    }
                  }}
                />
                <button
                  className="px-3 py-1 bg-green-500 text-white rounded hover:bg-green-600 text-sm font-medium disabled:bg-gray-400"
                  onClick={handleReplace}
                  disabled={!isDocumentLoaded}
                >
                  {replaceAllMode ? '全部确认替换' : '确认替换'}
                </button>
                {/* <label className="flex items-center gap-1 text-xs text-gray-600 cursor-pointer">
              <input
                type="checkbox"
                checked={replaceAllMode}
                onChange={(e) => setReplaceAllMode(e.target.checked)}
                className="w-3 h-3"
              />
              全部
            </label> */}
              </div>

              <button
                className="px-3 py-1 bg-gray-500 text-white rounded hover:bg-gray-600 text-sm font-medium transition-colors"
                onClick={() => setShowSearchReplacePanel(false)}
                title="关闭搜索替换面板"
                aria-label="关闭"
              >
                关闭
              </button>
            </div>
            {searchReplaceResult && (
              <div className={`mt-2 text-xs ${searchReplaceResult.startsWith('✓') ? 'text-green-600' :
                searchReplaceResult.startsWith('✗') ? 'text-red-600' :
                  'text-gray-600'
                }`}>
                {searchReplaceResult}
              </div>
            )}
          </div>
        )}

        {/* 页面定点替换测试面板 - 临时隐藏 */}
        {/* <div className="absolute top-56 left-2 z-50 bg-white bg-opacity-95 px-3 py-2 rounded shadow-lg border border-purple-200">
        <div className="text-xs text-purple-600 font-medium mb-2">页面定点替换</div>
        <div className="flex items-center gap-2 mb-2">
          <span className="text-xs text-gray-500 font-medium w-12">页码:</span>
          <input
            className="px-2 py-1 border rounded text-sm w-16"
            value={replaceInPageNumber}
            onChange={(e) => setReplaceInPageNumber(e.target.value)}
            placeholder="如: 12"
            aria-label="页码"
            type="number"
            min="1"
          />
          <span className="text-xs text-gray-500 font-medium w-12">原文:</span>
          <input
            className="px-2 py-1 border rounded text-sm w-32"
            value={replaceInPageSearch}
            onChange={(e) => setReplaceInPageSearch(e.target.value)}
            placeholder="要替换的文本"
            aria-label="原文"
          />
        </div>
        <div className="flex items-center gap-2">
          <span className="text-xs text-gray-500 font-medium w-12">新文本:</span>
          <input
            className="px-2 py-1 border rounded text-sm w-48"
            value={replaceInPageReplace}
            onChange={(e) => setReplaceInPageReplace(e.target.value)}
            placeholder="替换后的文本"
            aria-label="新文本"
            onKeyDown={(e) => {
              if (e.key === 'Enter') {
                handleReplaceInPage();
              }
            }}
          />
          <button
            className={`px-4 py-1 rounded text-sm font-medium transition-colors ${
              isReplacingInPage
                ? 'bg-gray-400 text-white cursor-not-allowed'
                : 'bg-purple-500 text-white hover:bg-purple-600'
            }`}
            onClick={handleReplaceInPage}
            disabled={!isDocumentLoaded || isReplacingInPage}
          >
            {isReplacingInPage ? '执行中...' : '定点替换'}
          </button>
        </div>
        {replaceInPageResult && (
          <div className={`mt-2 text-xs ${
            replaceInPageResult.startsWith('✓') ? 'text-green-600' :
            replaceInPageResult.startsWith('✗') ? 'text-red-600' :
            'text-gray-600'
          }`}>
            {replaceInPageResult}
          </div>
        )}
      </div> */}

        {/* 文档加载提示 */}
        {!isDocumentLoaded && (
          <div className="absolute inset-0 flex items-center justify-center bg-white bg-opacity-90 z-10">
            <div className="text-center">
              <div className="inline-block animate-spin rounded-full h-12 w-12 border-b-2 border-primary"></div>
              <p className="mt-4 text-gray-600">正在加载文档...</p>
              <p className="text-sm text-gray-500 mt-2">{config.fileName}</p>
            </div>
          </div>
        )}

        {/* Collabora iframe - tabIndex is needed for keyboard navigation */}
        {/* eslint-disable jsx-a11y/no-noninteractive-tabindex */}
        <iframe
          ref={iframeRef}
          src={config.iframeUrl}
          className="w-full h-full border-0"
          style={{
            minHeight: '600px',
            height: '100%',
          }}
          allow="clipboard-read; clipboard-write"
          title={`Collabora Online - ${config.fileName}`}
          sandbox="allow-same-origin allow-scripts allow-forms allow-popups allow-modals"
          tabIndex={0}
        />
        {/* eslint-enable jsx-a11y/no-noninteractive-tabindex */}
      </div>
    );
  });

// 导出类型和 hook
export { useCollaboraUnoCommands };
export type { CollaboraViewerHandle };

