import os
import hashlib
import shutil
import tempfile
import py7zr
import traceback
from PySide6 import QtWidgets, QtCore
from PySide6.QtCore import QTimer
from PySide6.QtWidgets import QMessageBox

from config.config import PLUGIN, PLUGIN_HASH, GAME_INFO
from utils import msgbox_frame
from utils.logger import setup_logger

# 初始化logger
logger = setup_logger("offline_mode_manager")

class OfflineModeManager:
    """离线模式管理器，用于管理离线模式下的补丁安装和检测"""
    
    def __init__(self, main_window):
        """初始化离线模式管理器
        
        Args:
            main_window: 主窗口实例，用于访问UI和状态
        """
        self.main_window = main_window
        self.app_name = main_window.APP_NAME if hasattr(main_window, 'APP_NAME') else ""
        self.offline_patches = {}  # 存储离线补丁信息 {补丁名称: 文件路径}
        self.is_offline_mode = False
        self.installed_games = []  # 跟踪本次实际安装的游戏
        # 保持对哈希线程的引用，避免运行中被销毁
        self.hash_thread = None
        # 解压线程与进度窗口引用，避免运行中被销毁，且确保UI可更新
        self.extraction_thread = None
        self.extraction_progress_window = None
        
    def _is_debug_mode(self):
        """检查是否处于调试模式
        
        Returns:
            bool: 是否处于调试模式
        """
        try:
            if hasattr(self.main_window, 'debug_manager') and self.main_window.debug_manager:
                if hasattr(self.main_window.debug_manager, '_is_debug_mode'):
                    # 尝试直接从debug_manager获取状态
                    return self.main_window.debug_manager._is_debug_mode()
                elif hasattr(self.main_window, 'config'):
                    # 如果debug_manager还没准备好，尝试从配置中获取
                    return self.main_window.config.get('debug_mode', False)
            # 如果以上都不可行，返回False
            return False
        except Exception:
            # 捕获任何异常，默认返回False
            return False
        
    def scan_for_offline_patches(self, directory=None):
        """扫描指定目录（默认为软件所在目录）查找离线补丁文件
        
        Args:
            directory: 要扫描的目录，如果为None则使用软件所在目录
            
        Returns:
            dict: 找到的补丁文件 {补丁名称: 文件路径}
        """
        if directory is None:
            # 获取软件所在目录 - 直接使用最简单的方式
            try:
                import sys
                
                if getattr(sys, 'frozen', False):
                    # 如果是PyInstaller打包的环境，使用可执行文件所在目录
                    directory = os.path.dirname(sys.executable)
                else:
                    # 直接取当前工作目录
                    directory = os.getcwd()
                    
                    # 对于开发环境的特殊处理：
                    # 如果当前目录路径中包含'source'，则可能是在开发模式下从source目录运行
                    # 尝试找到项目根目录
                    if 'source' in directory:
                        # 尝试向上一级查找补丁文件
                        parent_dir = os.path.dirname(directory)
                        # 看看父目录是否存在补丁文件
                        potential_patches = ["vol.1.7z", "vol.2.7z", "vol.3.7z", "vol.4.7z", "after.7z"]
                        for patch_file in potential_patches:
                            if os.path.exists(os.path.join(parent_dir, patch_file)):
                                # 如果在父目录找到了补丁文件，使用父目录作为扫描目录
                                directory = parent_dir
                                break
                
                if self._is_debug_mode():
                    logger.debug(f"DEBUG: 使用目录 {directory} 扫描离线补丁文件")
                    current_dir = os.getcwd()
                    logger.debug(f"DEBUG: 当前工作目录: {current_dir}")
                    logger.debug(f"DEBUG: 是否为打包环境: {getattr(sys, 'frozen', False)}")
                    if getattr(sys, 'frozen', False):
                        logger.debug(f"DEBUG: 可执行文件路径: {sys.executable}")
            except Exception as e:
                # 如果出现异常，使用当前工作目录
                directory = os.getcwd()
                if self._is_debug_mode():
                    logger.debug(f"DEBUG: 路径计算出错，使用工作目录: {directory}, 错误: {e}")
            
        debug_mode = self._is_debug_mode()
        
        # 记录扫描操作
        logger.debug(f"扫描离线补丁文件，目录: {directory}")
            
        # 要查找的补丁文件名
        patch_files = ["vol.1.7z", "vol.2.7z", "vol.3.7z", "vol.4.7z", "after.7z"]
        
        # 只在指定目录中查找，不查找父目录和其他目录
        search_dirs = [directory]
        
        if debug_mode:
            logger.debug(f"DEBUG: 将在以下目录中查找补丁文件: {search_dirs}")
        
        found_patches = {}
        
        # 扫描目录查找补丁文件
        try:
            # 搜索指定目录
            search_dir = directory
            if debug_mode:
                logger.debug(f"DEBUG: 正在搜索目录: {search_dir}")
                
            if not os.path.exists(search_dir):
                if debug_mode:
                    logger.debug(f"DEBUG: 目录不存在，跳过: {search_dir}")
            else:
                for file in os.listdir(search_dir):
                    if file.lower() in patch_files:
                        file_path = os.path.join(search_dir, file)
                        if os.path.isfile(file_path):
                            patch_name = file.lower()
                            found_patches[patch_name] = file_path
                            # 无论是否为调试模式，都记录找到的补丁文件
                            logger.info(f"找到离线补丁文件: {patch_name} 路径: {file_path}")
                            if debug_mode:
                                logger.debug(f"DEBUG: 找到离线补丁文件: {patch_name} 路径: {file_path}")
        except Exception as e:
            logger.error(f"扫描目录时出错: {str(e)}")
                        
        self.offline_patches = found_patches
        
        # 记录扫描结果
        if found_patches:
            logger.info(f"共找到 {len(found_patches)} 个离线补丁文件: {list(found_patches.keys())}")
        else:
            logger.debug("未找到任何离线补丁文件")
            
        return found_patches
        
    def has_offline_patches(self):
        """检查是否有可用的离线补丁文件
        
        Returns:
            bool: 是否有可用的离线补丁
        """
        if not self.offline_patches:
            self.scan_for_offline_patches()
            
        return len(self.offline_patches) > 0
        
    def set_offline_mode(self, enabled):
        """设置离线模式状态
        
        Args:
            enabled: 是否启用离线模式
            
        Returns:
            bool: 是否成功设置离线模式
        """
        debug_mode = self._is_debug_mode()
        
        if enabled:
            # 检查是否有离线补丁文件
            if not self.has_offline_patches() and not debug_mode:
                msgbox_frame(
                    f"离线模式错误 - {self.app_name}",
                    "\n未找到任何离线补丁文件，无法启用离线模式。\n\n请将补丁文件放置在软件所在目录后再尝试。\n",
                    QMessageBox.StandardButton.Ok
                ).exec()
                logger.warning("尝试启用离线模式失败：未找到任何离线补丁文件")
                return False
                
            if debug_mode:
                logger.debug("DEBUG: 已启用离线模式（调试模式下允许强制启用）")
                
        self.is_offline_mode = enabled
        
        # 更新窗口标题
        if hasattr(self.main_window, 'setWindowTitle'):
            from config.config import APP_NAME, APP_VERSION
            mode_indicator = "[离线模式]" if enabled else "[在线模式]"
            self.main_window.setWindowTitle(f"{APP_NAME} v{APP_VERSION} {mode_indicator}")
            
            # 同时更新UI中的标题标签
            if hasattr(self.main_window, 'ui') and hasattr(self.main_window.ui, 'title_label'):
                self.main_window.ui.title_label.setText(f"{APP_NAME} v{APP_VERSION} {mode_indicator}")
                
        # 同步更新UI菜单中的模式选择状态
        if hasattr(self.main_window, 'ui_manager'):
            ui_manager = self.main_window.ui_manager
            # 使用专门的同步方法确保菜单状态正确更新
            if hasattr(ui_manager, 'sync_work_mode_menu_state'):
                ui_manager.sync_work_mode_menu_state()
            elif hasattr(ui_manager, 'online_mode_action') and hasattr(ui_manager, 'offline_mode_action'):
                # 兼容旧版本的直接设置方式
                if ui_manager.online_mode_action and ui_manager.offline_mode_action:
                    ui_manager.online_mode_action.setChecked(not enabled)
                    ui_manager.offline_mode_action.setChecked(enabled)
        
        # 记录离线模式状态变化
        logger.debug(f"离线模式已{'启用' if enabled else '禁用'}")
        if debug_mode:
            logger.debug(f"DEBUG: 离线模式已{'启用' if enabled else '禁用'}")
            
        return True
        
    def get_offline_patch_path(self, game_version):
        """根据游戏版本获取对应的离线补丁文件路径
        
        Args:
            game_version: 游戏版本名称，如"NEKOPARA Vol.1"
            
        Returns:
            str: 离线补丁文件路径，如果没有找到则返回None
        """
        # 确保已扫描过补丁文件
        if not self.offline_patches:
            self.scan_for_offline_patches()
            
        # 根据游戏版本获取对应的补丁文件名
        patch_file = None
        
        if "Vol.1" in game_version:
            patch_file = "vol.1.7z"
        elif "Vol.2" in game_version:
            patch_file = "vol.2.7z"
        elif "Vol.3" in game_version:
            patch_file = "vol.3.7z"
        elif "Vol.4" in game_version:
            patch_file = "vol.4.7z"
        elif "After" in game_version:
            patch_file = "after.7z"
            
        # 检查是否有对应的补丁文件
        if patch_file and patch_file in self.offline_patches:
            return self.offline_patches[patch_file]
            
        return None
        
    def prepare_offline_patch(self, game_version, target_path):
        """准备离线补丁文件，复制到缓存目录
        
        Args:
            game_version: 游戏版本名称
            target_path: 目标路径（通常是缓存目录中的路径）
            
        Returns:
            bool: 是否成功准备补丁文件
        """
        source_path = self.get_offline_patch_path(game_version)
        
        if not source_path:
            return False
            
        debug_mode = self._is_debug_mode()
        
        try:
            # 确保目标目录存在
            os.makedirs(os.path.dirname(target_path), exist_ok=True)
            
            # 复制文件
            shutil.copy2(source_path, target_path)
            
            if debug_mode:
                logger.debug(f"DEBUG: 已复制离线补丁文件 {source_path} 到 {target_path}")
                
            return True
        except Exception as e:
            if debug_mode:
                logger.error(f"DEBUG: 复制离线补丁文件失败: {e}")
            return False
            
    def verify_patch_hash(self, game_version, file_path):
        """验证补丁文件的哈希值，使用patch_detector模块
        
        Args:
            game_version: 游戏版本名称
            file_path: 补丁压缩包文件路径
            
        Returns:
            bool: 哈希值是否匹配
        """
        debug_mode = self._is_debug_mode()
        
        if debug_mode:
            logger.debug(f"DEBUG: 开始验证补丁文件哈希: {file_path}")
            
        # 创建进度对话框
        from utils.helpers import ProgressHashVerifyDialog
        from config.config import PLUGIN_HASH
        from workers.hash_thread import OfflineHashVerifyThread
        
        # 创建并显示进度对话框
        progress_dialog = ProgressHashVerifyDialog(
            f"验证补丁文件 - {self.app_name}",
            f"正在验证 {game_version} 的补丁文件完整性...",
            self.main_window
        )
        
        # 创建哈希验证线程
        hash_thread = OfflineHashVerifyThread(game_version, file_path, PLUGIN_HASH, self.main_window)
        
        # 连接信号
        hash_thread.progress.connect(progress_dialog.update_progress)
        hash_thread.finished.connect(lambda result, error, extracted_path: self._on_hash_verify_finished(result, error, extracted_path, progress_dialog))
        
        # 启动线程
        hash_thread.start()
        
        # 显示对话框，阻塞直到对话框关闭
        result = progress_dialog.exec()
        
        # 如果用户取消了验证，停止线程
        if result == ProgressHashVerifyDialog.Rejected and hash_thread.isRunning():
            if debug_mode:
                logger.debug(f"DEBUG: 用户取消了哈希验证")
            hash_thread.terminate()
            hash_thread.wait()
            return False
            
        # 返回对话框中存储的验证结果
        return hasattr(progress_dialog, 'hash_result') and progress_dialog.hash_result
        
    def _on_hash_verify_finished(self, result, error, extracted_path, dialog):
        """哈希验证线程完成后的回调
        
        Args:
            result: 验证结果
            error: 错误信息
            extracted_path: 解压后的补丁文件路径，如果哈希验证成功则包含此路径
            dialog: 进度对话框
        """
        debug_mode = self._is_debug_mode()
        
        # 存储结果到对话框，以便在exec()返回后获取
        dialog.hash_result = result
        
        if result:
            if debug_mode:
                logger.debug(f"DEBUG: 哈希验证成功")
                if extracted_path:
                    logger.debug(f"DEBUG: 解压后的补丁文件路径: {extracted_path}")
            dialog.set_status("验证成功")
            # 短暂延时后关闭对话框
            QTimer.singleShot(500, dialog.accept)
        else:
            if debug_mode:
                logger.debug(f"DEBUG: 哈希验证失败: {error}")
            dialog.set_status(f"验证失败: {error}")
            dialog.set_message("补丁文件验证失败，可能已损坏或被篡改。")
            # 将取消按钮改为关闭按钮
            dialog.cancel_button.setText("关闭")
            # 不自动关闭，让用户查看错误信息
            
    def _on_offline_install_hash_finished(self, result, error, extracted_path, dialog, game_version, _7z_path, game_folder, plugin_path, install_tasks):
        """离线安装哈希验证线程完成后的回调
        
        Args:
            result: 验证结果
            error: 错误信息
            extracted_path: 解压后的补丁文件路径
            dialog: 进度对话框
            game_version: 游戏版本
            _7z_path: 7z文件路径
            game_folder: 游戏文件夹路径
            plugin_path: 插件路径
            install_tasks: 剩余的安装任务列表
        """
        debug_mode = self._is_debug_mode()
        
        # 导入所需模块
        from config.config import GAME_INFO, PLUGIN
        
        # 存储结果到对话框，以便在exec()返回后获取
        dialog.hash_result = result
        
        # 关闭哈希验证窗口
        self.main_window.close_hash_msg_box()
            
        if not result:
            # 哈希验证失败
            if debug_mode:
                logger.warning(f"DEBUG: 补丁文件哈希验证失败: {error}")
                
            # 显示错误消息
            msgbox_frame(
                f"哈希验证失败 - {self.app_name}",
                f"\n{game_version} 的补丁文件哈希验证失败，可能已损坏或被篡改。\n\n跳过此游戏的安装。\n",
                QMessageBox.StandardButton.Ok
            ).exec()
            
            # 继续下一个任务
            self.process_next_offline_install_task(install_tasks)
            return
            
        # 哈希验证成功，直接进行安装
        if debug_mode:
            logger.debug(f"DEBUG: 哈希验证成功，开始安装")
                
        # 显示安装进度窗口
        self.main_window.hash_msg_box = self.main_window.hash_manager.hash_pop_window(check_type="offline_installation", is_offline=True)
        
        try:
            # 确保游戏目录存在
            os.makedirs(game_folder, exist_ok=True)
            
            # 根据游戏版本确定目标文件名
            target_filename = None
            if "Vol.1" in game_version:
                target_filename = "adultsonly.xp3"
            elif "Vol.2" in game_version:
                target_filename = "adultsonly.xp3"
            elif "Vol.3" in game_version:
                target_filename = "update00.int"
            elif "Vol.4" in game_version:
                target_filename = "vol4adult.xp3"
            elif "After" in game_version:
                target_filename = "afteradult.xp3"
                
            if not target_filename:
                raise ValueError(f"未知的游戏版本: {game_version}")
            
            # 直接解压文件到游戏目录
            import py7zr
            
            if debug_mode:
                logger.debug(f"DEBUG: 直接解压文件 {_7z_path} 到游戏目录 {game_folder}")
            
            # 解压文件
            with py7zr.SevenZipFile(_7z_path, mode="r") as archive:
                # 获取压缩包内的文件列表
                file_list = archive.getnames()
                if debug_mode:
                    logger.debug(f"DEBUG: 压缩包内文件列表: {file_list}")
                
                # 解析压缩包内的文件结构
                target_file_in_archive = None
                for file_path in file_list:
                    if target_filename in file_path:
                        target_file_in_archive = file_path
                        break
                
                if not target_file_in_archive:
                    if debug_mode:
                        logger.warning(f"DEBUG: 在压缩包中未找到目标文件 {target_filename}")
                    raise FileNotFoundError(f"在压缩包中未找到目标文件 {target_filename}")
                
                # 准备解压特定文件到游戏目录
                target_path = os.path.join(game_folder, target_filename)
                
                # 创建一个临时目录用于解压单个文件
                with tempfile.TemporaryDirectory() as temp_dir:
                    # 解压特定文件到临时目录
                    archive.extract(path=temp_dir, targets=[target_file_in_archive])
                    
                    # 找到解压后的文件
                    extracted_file_path = os.path.join(temp_dir, target_file_in_archive)
                    
                    # 复制到目标位置
                    shutil.copy2(extracted_file_path, target_path)
                    
                    if debug_mode:
                        logger.debug(f"DEBUG: 已解压并复制文件到 {target_path}")
                    
                    # 对于NEKOPARA After，还需要复制签名文件
                    if game_version == "NEKOPARA After":
                        sig_filename = f"{target_filename}.sig"
                        sig_file_in_archive = None
                        
                        # 查找签名文件
                        for file_path in file_list:
                            if sig_filename in file_path:
                                sig_file_in_archive = file_path
                                break
                        
                        if sig_file_in_archive:
                            # 解压签名文件
                            archive.extract(path=temp_dir, targets=[sig_file_in_archive])
                            extracted_sig_path = os.path.join(temp_dir, sig_file_in_archive)
                            sig_target = os.path.join(game_folder, sig_filename)
                            shutil.copy2(extracted_sig_path, sig_target)
                            
                            if debug_mode:
                                logger.debug(f"DEBUG: 已解压并复制签名文件到 {sig_target}")
                        else:
                            if debug_mode:
                                logger.warning(f"DEBUG: 未找到签名文件 {sig_filename}")
            
            # 进行安装后的哈希校验
            self._perform_hash_check(game_version, install_tasks)
            
        except Exception as e:
            if debug_mode:
                logger.error(f"DEBUG: 安装补丁文件失败: {e}")
                import traceback
                logger.error(f"DEBUG: 错误堆栈: {traceback.format_exc()}")
                
            # 关闭安装进度窗口
            self.main_window.close_hash_msg_box()
                
            # 显示错误消息
            msgbox_frame(
                f"安装错误 - {self.app_name}",
                f"\n{game_version} 的安装过程中发生错误: {str(e)}\n\n跳过此游戏的安装。\n",
                QMessageBox.StandardButton.Ok
            ).exec()
            
            # 继续下一个任务
            self.process_next_offline_install_task(install_tasks)
            
    def _perform_hash_check(self, game_version, install_tasks):
        """安装完成后进行哈希校验
        
        Args:
            game_version: 游戏版本
            install_tasks: 剩余的安装任务列表
        """
        debug_mode = self._is_debug_mode()
        
        # 导入所需模块
        from config.config import GAME_INFO, PLUGIN_HASH
        from workers.hash_thread import HashThread
        
        # 获取安装路径
        install_paths = {}
        game_dirs = self.main_window.game_detector.identify_game_directories_improved(
            self.main_window.download_manager.selected_folder
        )
        
        for game, info in GAME_INFO.items():
            if game in game_dirs and game == game_version:
                game_dir = game_dirs[game]
                install_path = os.path.join(game_dir, os.path.basename(info["install_path"]))
                install_paths[game] = install_path
                break
        
        if not install_paths:
            # 如果找不到安装路径，直接认为安装成功
            logger.warning(f"未找到 {game_version} 的安装路径，跳过哈希校验")
            self.main_window.installed_status[game_version] = True
            
            # 添加到已安装游戏列表
            if game_version not in self.installed_games:
                self.installed_games.append(game_version)
                
            # 关闭安装进度窗口
            self.main_window.close_hash_msg_box()
                
            # 继续下一个任务
            self.process_next_offline_install_task(install_tasks)
            return
            
        # 关闭可能存在的哈希校验窗口，然后创建新窗口
        self.main_window.close_hash_msg_box()
        
        # 显示哈希校验窗口
        self.main_window.hash_msg_box = self.main_window.hash_manager.hash_pop_window(check_type="post", is_offline=True)
        
        # 直接创建并启动哈希线程进行校验，而不是通过主窗口
        hash_thread = HashThread(
            "after", 
            install_paths, 
            PLUGIN_HASH, 
            self.main_window.installed_status,
            self.main_window
        )
        hash_thread.after_finished.connect(
            lambda result: self._on_hash_check_finished(result, game_version, install_tasks)
        )
        
        # 保存引用以便后续使用
        self.hash_thread = hash_thread
        try:
            self.hash_thread.finished.connect(lambda: setattr(self, 'hash_thread', None))
        except Exception:
            pass
        hash_thread.start()
        
    def _on_hash_check_finished(self, result, game_version, install_tasks):
        """哈希校验完成后的处理
        
        Args:
            result: 校验结果，包含通过状态、游戏版本和消息
            game_version: 游戏版本
            install_tasks: 剩余的安装任务列表
        """
        debug_mode = self._is_debug_mode()
        
        # 关闭哈希检查窗口
        self.main_window.close_hash_msg_box()
            
        if not result["passed"]:
            # 记录校验失败信息
            logger.error(f"===== {game_version} 哈希校验失败 =====")
            logger.error(f"校验失败消息: {result.get('message', '无错误消息')}")
            
            # 校验失败，删除已解压的文件并提示重新安装
            error_message = result["message"]
            
            # 获取安装路径
            install_path = None
            game_dirs = self.main_window.game_detector.identify_game_directories_improved(
                self.main_window.download_manager.selected_folder
            )
            
            from config.config import GAME_INFO
            if game_version in game_dirs and game_version in GAME_INFO:
                game_dir = game_dirs[game_version]
                install_path = os.path.join(game_dir, os.path.basename(GAME_INFO[game_version]["install_path"]))
                logger.info(f"找到安装路径: {install_path}")
                
                # 记录安装路径的文件信息
                if os.path.exists(install_path):
                    file_size = os.path.getsize(install_path)
                    logger.info(f"文件存在，大小: {file_size} 字节")
                    
                    # 检查是否为NEKOPARA After，记录签名文件信息
                    if game_version == "NEKOPARA After":
                        sig_path = f"{install_path}.sig"
                        if os.path.exists(sig_path):
                            sig_size = os.path.getsize(sig_path)
                            logger.info(f"签名文件存在: {sig_path}, 大小: {sig_size} 字节")
                        else:
                            logger.info(f"签名文件不存在: {sig_path}")
                else:
                    logger.warning(f"文件不存在: {install_path}")
            else:
                logger.warning(f"未找到 {game_version} 的安装路径")
            
            # 如果找到安装路径，尝试删除已解压的文件
            if install_path and os.path.exists(install_path):
                try:
                    os.remove(install_path)
                    logger.debug(f"已删除校验失败的文件: {install_path}")
                    
                    # 检查是否为NEKOPARA After，同时删除签名文件
                    if game_version == "NEKOPARA After":
                        sig_path = f"{install_path}.sig"
                        if os.path.exists(sig_path):
                            os.remove(sig_path)
                            logger.debug(f"已删除签名文件: {sig_path}")
                except Exception as e:
                    logger.error(f"删除文件失败: {e}")
            
            # 显示错误消息
            msgbox_frame(
                f"校验失败 - {self.app_name}",
                f"{error_message}\n\n跳过此游戏的安装。",
                QMessageBox.StandardButton.Ok
            ).exec()
            
            # 更新安装状态
            self.main_window.installed_status[game_version] = False
        else:
            # 校验通过，更新安装状态
            self.main_window.installed_status[game_version] = True
            logger.info(f"===== {game_version} 哈希校验通过 =====")
            
            # 添加到已安装游戏列表
            if game_version not in self.installed_games:
                self.installed_games.append(game_version)
                
            # 显示安装成功消息
            if debug_mode:
                logger.debug(f"DEBUG: {game_version} 安装成功并通过哈希校验")
        
        # 继续处理下一个任务
        self.process_next_offline_install_task(install_tasks)
        
    def _on_extraction_finished_with_hash_check(self, success, error_message, game_version, install_tasks):
        """解压完成后进行哈希校验（后台线程回调）"""
        # 关闭解压进度窗口
        try:
            if self.extraction_progress_window and self.extraction_progress_window.isVisible():
                self.extraction_progress_window.close()
        except Exception:
            pass
        self.extraction_progress_window = None
        
        # 清理线程引用
        self.extraction_thread = None
        
        if not success:
            # 解压失败，提示并继续下一个任务
            msgbox_frame(
                f"安装错误 - {self.app_name}",
                error_message or f"\n{game_version} 的安装过程中发生错误。\n",
                QMessageBox.StandardButton.Ok
            ).exec()
            self.process_next_offline_install_task(install_tasks)
            return
        
        # 解压成功，进入安装后哈希校验
        self._perform_hash_check(game_version, install_tasks)
        
    def on_extraction_thread_finished(self, success, error_message, game_version, install_tasks):
        """解压线程完成后的处理（兼容旧版本）
        
        Args:
            success: 是否解压成功
            error_message: 错误信息
            game_version: 游戏版本
            install_tasks: 剩余的安装任务列表
        """
        # 这个方法已不再使用，但为了兼容性，我们直接处理下一个任务
        if success:
            # 更新安装状态
            self.main_window.installed_status[game_version] = True
            
            # 添加到已安装游戏列表
            if game_version not in self.installed_games:
                self.installed_games.append(game_version)
        else:
            # 更新安装状态
            self.main_window.installed_status[game_version] = False
            
            # 显示错误消息
            debug_mode = self._is_debug_mode()
            if debug_mode:
                logger.error(f"DEBUG: 解压失败: {error_message}")
                
        # 继续下一个任务
        self.process_next_offline_install_task(install_tasks)
            
    def install_offline_patches(self, selected_games):
        """直接安装离线补丁，完全绕过下载模块
        
        Args:
            selected_games: 用户选择安装的游戏列表
            
        Returns:
            bool: 是否成功启动安装流程
        """
        debug_mode = self._is_debug_mode()
        
        if debug_mode:
            logger.debug(f"DEBUG: 开始离线安装流程，选择的游戏: {selected_games}")
            
        if not self.is_in_offline_mode():
            if debug_mode:
                logger.warning("DEBUG: 当前不是离线模式，无法使用离线安装")
            return False
            
        # 确保已扫描过补丁文件
        if not self.offline_patches:
            self.scan_for_offline_patches()
            
        if not self.offline_patches:
            if debug_mode:
                logger.warning("DEBUG: 未找到任何离线补丁文件")
            msgbox_frame(
                f"离线安装错误 - {self.app_name}",
                "\n未找到任何离线补丁文件，无法进行离线安装。\n\n请将补丁文件放置在软件所在目录后再尝试。\n",
                QMessageBox.StandardButton.Ok
            ).exec()
            return False
            
        # 获取游戏目录
        game_dirs = self.main_window.game_detector.identify_game_directories_improved(
            self.main_window.download_manager.selected_folder
        )
        
        if not game_dirs:
            if debug_mode:
                logger.warning("DEBUG: 未识别到任何游戏目录")
            return False
            
        self.main_window.setEnabled(False)
        
        # 重置已安装游戏列表
        self.installed_games = []
        
        # 设置到主窗口，供结果显示使用
        self.main_window.download_queue_history = selected_games
        
        # 记录未找到离线补丁文件的游戏
        self.missing_offline_patches = []
            
        # 创建安装任务列表
        install_tasks = []
        for game_version in selected_games:
            # 获取离线补丁文件路径
            patch_file = self.get_offline_patch_path(game_version)
            if not patch_file:
                if debug_mode:
                    logger.warning(f"DEBUG: 未找到 {game_version} 的离线补丁文件，跳过")
                # 记录未找到离线补丁文件的游戏
                self.missing_offline_patches.append(game_version)
                continue
                
            # 获取游戏目录
            game_folder = game_dirs.get(game_version)
            if not game_folder:
                if debug_mode:
                    logger.warning(f"DEBUG: 未找到 {game_version} 的游戏目录，跳过")
                continue
                
            # 获取目标路径
            if "Vol.1" in game_version:
                _7z_path = os.path.join(PLUGIN, "vol.1.7z")
                plugin_path = os.path.join(PLUGIN, GAME_INFO[game_version]["plugin_path"])
            elif "Vol.2" in game_version:
                _7z_path = os.path.join(PLUGIN, "vol.2.7z")
                plugin_path = os.path.join(PLUGIN, GAME_INFO[game_version]["plugin_path"])
            elif "Vol.3" in game_version:
                _7z_path = os.path.join(PLUGIN, "vol.3.7z")
                plugin_path = os.path.join(PLUGIN, GAME_INFO[game_version]["plugin_path"])
            elif "Vol.4" in game_version:
                _7z_path = os.path.join(PLUGIN, "vol.4.7z")
                plugin_path = os.path.join(PLUGIN, GAME_INFO[game_version]["plugin_path"])
            elif "After" in game_version:
                _7z_path = os.path.join(PLUGIN, "after.7z")
                plugin_path = os.path.join(PLUGIN, GAME_INFO[game_version]["plugin_path"])
            else:
                if debug_mode:
                    logger.warning(f"DEBUG: {game_version} 不是支持的游戏版本，跳过")
                continue
                
            # 添加到安装任务列表
            install_tasks.append((patch_file, game_folder, game_version, _7z_path, plugin_path))
        
        # 开始执行第一个安装任务
        if install_tasks:
            if debug_mode:
                logger.debug(f"开始离线安装流程，安装游戏数量: {len(install_tasks)}")
            self.process_next_offline_install_task(install_tasks)
            return True
        else:
            if debug_mode:
                logger.warning("DEBUG: 没有可安装的游戏，安装流程结束")
                
            # 如果没有找到任何可安装的游戏，显示一般消息
            msgbox_frame(
                f"离线安装信息 - {self.app_name}",
                "\n没有可安装的游戏或未找到对应的离线补丁文件。\n",
                QMessageBox.StandardButton.Ok
            ).exec()
            self.main_window.setEnabled(True)
            if hasattr(self.main_window, 'window_manager'):
                self.main_window.window_manager.change_window_state(self.main_window.window_manager.STATE_READY)
            
        return True
        
    def _start_online_download(self, games_to_download):
        """启动在线下载流程
        
        Args:
            games_to_download: 要下载的游戏列表
        """
        debug_mode = self._is_debug_mode()
        if debug_mode:
            logger.debug(f"DEBUG: 启动在线下载流程，游戏列表: {games_to_download}")
        
        # 确保下载管理器已初始化
        if hasattr(self.main_window, 'download_manager'):
            # 使用直接下载方法，绕过补丁判断
            self.main_window.download_manager.direct_download_action(games_to_download)
        else:
            if debug_mode:
                logger.error("DEBUG: 下载管理器未初始化，无法启动下载流程")
            # 显示错误消息
            msgbox_frame(
                f"错误 - {self.app_name}",
                "\n下载管理器未初始化，无法启动下载流程。\n",
                QMessageBox.StandardButton.Ok
            ).exec()
            
    def process_next_offline_install_task(self, install_tasks):
        """处理下一个离线安装任务
        
        Args:
            install_tasks: 安装任务列表，每个任务是一个元组 (patch_file, game_folder, game_version, _7z_path, plugin_path)
        """
        debug_mode = self._is_debug_mode()
        
        if not install_tasks:
            # 所有任务完成，进行后检查
            if debug_mode:
                logger.debug("所有离线安装任务完成，进行后检查")
                
            # 使用patch_detector进行安装后哈希比较
            self.main_window.patch_detector.after_hash_compare()
            
            # 检查是否有未找到离线补丁文件的游戏
            if hasattr(self, 'missing_offline_patches') and self.missing_offline_patches:
                if debug_mode:
                    logger.debug(f"DEBUG: 有未找到离线补丁文件的游戏: {self.missing_offline_patches}")
                
                # 先显示已安装的结果
                if self.installed_games:
                    installed_msg = f"已成功安装以下补丁：\n\n{chr(10).join(self.installed_games)}\n\n"
                else:
                    installed_msg = ""
                
                # 在安装完成后询问用户是否切换到在线模式
                self._show_missing_patches_dialog(installed_msg)
            else:
                # 恢复UI状态
                self.main_window.setEnabled(True)
                if hasattr(self.main_window, 'window_manager'):
                    self.main_window.window_manager.change_window_state(self.main_window.window_manager.STATE_READY)
                
            return
            
        # 获取下一个任务
        patch_file, game_folder, game_version, _7z_path, plugin_path = install_tasks.pop(0)
        
        if debug_mode:
            logger.debug(f"DEBUG: 处理离线安装任务: {game_version}")
            logger.debug(f"DEBUG: 补丁文件: {patch_file}")
            logger.debug(f"DEBUG: 游戏目录: {game_folder}")
            
        # 使用后台线程进行解压，避免阻塞UI
        try:
            # 确保游戏目录存在
            os.makedirs(game_folder, exist_ok=True)

            # 创建非阻塞的解压进度窗口
            self.extraction_progress_window = self.main_window.create_extraction_progress_window()
            try:
                self.extraction_progress_window.show()
                QtWidgets.QApplication.processEvents()
            except Exception:
                pass

            # 启动解压线程
            from workers.extraction_thread import ExtractionThread
            self.extraction_thread = ExtractionThread(
                patch_file, game_folder, plugin_path, game_version, self.main_window
            )

            # 连接进度更新到窗口控件
            if self.extraction_thread and self.extraction_progress_window:
                self.extraction_thread.progress.connect(
                    lambda percent, status: (
                        self.extraction_progress_window.progress_bar.setValue(percent),
                        self.extraction_progress_window.status_label.setText(status)
                    )
                )

            # 完成后进入哈希校验
            self.extraction_thread.finished.connect(
                lambda success, error, gv: self._on_extraction_finished_with_hash_check(success, error, gv, install_tasks)
            )

            # 线程结束时清理引用
            try:
                self.extraction_thread.finished.connect(lambda *_: setattr(self, 'extraction_thread', None))
            except Exception:
                pass

            self.extraction_thread.start()

        except Exception as e:
            if debug_mode:
                logger.error(f"DEBUG: 离线安装任务处理失败: {e}")
                logger.error(f"DEBUG: 错误堆栈: {traceback.format_exc()}")

            # 关闭可能存在的解压进度窗口
            try:
                if self.extraction_progress_window and self.extraction_progress_window.isVisible():
                    self.extraction_progress_window.close()
            except Exception:
                pass
            self.extraction_progress_window = None

            # 显示错误消息
            msgbox_frame(
                f"安装错误 - {self.app_name}",
                f"\n{game_version} 的安装过程中发生错误: {str(e)}\n\n跳过此游戏的安装。\n",
                QMessageBox.StandardButton.Ok
            ).exec()

            # 继续下一个任务
            self.process_next_offline_install_task(install_tasks)
            
    def is_offline_mode_available(self):
        """检查是否可以使用离线模式
        
        Returns:
            bool: 是否可以使用离线模式
        """
        # 在调试模式下始终允许离线模式
        if self._is_debug_mode():
            return True
            
        # 检查是否有离线补丁文件
        return self.has_offline_patches()
        
    def is_in_offline_mode(self):
        """检查当前是否处于离线模式
        
        Returns:
            bool: 是否处于离线模式
        """
        return self.is_offline_mode 

    def _show_missing_patches_dialog(self, installed_msg):
        """显示缺少离线补丁文件的对话框
        
        Args:
            installed_msg: 已安装的补丁信息
        """
        debug_mode = self._is_debug_mode()
        
        # 在安装完成后询问用户是否切换到在线模式
        msg_box = msgbox_frame(
            f"离线安装完成 - {self.app_name}",
            f"\n{installed_msg}以下游戏未找到对应的离线补丁文件：\n\n{chr(10).join(self.missing_offline_patches)}\n\n是否切换到在线模式继续安装？\n",
            QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
        )
        result = msg_box.exec()
        
        if result == QMessageBox.StandardButton.Yes:
            if debug_mode:
                logger.debug("DEBUG: 用户选择切换到在线模式")
            
            # 切换到在线模式
            if hasattr(self.main_window, 'ui_manager'):
                self.main_window.ui_manager.switch_work_mode("online")
                
                # 直接启动下载流程
                self.main_window.setEnabled(True)
                # 保存当前选择的游戏列表，以便在线模式使用
                missing_games = self.missing_offline_patches.copy()
                # 启动下载流程
                QTimer.singleShot(500, lambda: self._start_online_download(missing_games))
        else:
            if debug_mode:
                logger.debug("DEBUG: 用户选择不切换到在线模式")
            
            # 恢复UI状态
            self.main_window.setEnabled(True)
            if hasattr(self.main_window, 'window_manager'):
                self.main_window.window_manager.change_window_state(self.main_window.window_manager.STATE_READY) 