import os
import sys
import shutil
import webbrowser
import requests
import json
from urllib.parse import urlparse
from collections import deque

from PySide6 import QtWidgets
from PySide6.QtCore import QTimer, Qt
from PySide6.QtGui import QIcon, QAction
from PySide6.QtWidgets import QMainWindow, QFileDialog, QApplication, QMessageBox

from ui.Ui_install import Ui_MainWindows
from core.animations import MultiStageAnimations
from data.config import (
    APP_NAME, APP_VERSION, PLUGIN, GAME_INFO, BLOCK_SIZE,
    PLUGIN_HASH, UA, CONFIG_URL, LOG_FILE
)
from utils import (
    load_base64_image, HashManager, AdminPrivileges, msgbox_frame,
    load_config, save_config, HostsManager, Logger
)
from workers.download import DownloadThread, ProgressWindow
from data.pic_data import img_data
from workers import (
    IpOptimizerThread, HashThread, ExtractionThread, ConfigFetchThread
)
from core import (
    MultiStageAnimations, UIManager, DownloadManager, DebugManager
)


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        
        # 初始化UI (从Ui_install.py导入)
        self.ui = Ui_MainWindows()
        self.ui.setupUi(self)

        icon_data = img_data.get("icon")
        if icon_data:
            pixmap = load_base64_image(icon_data)
        self.setWindowIcon(QIcon(pixmap))

        # 设置窗口标题为APP_NAME加版本号
        self.setWindowTitle(f"{APP_NAME} v{APP_VERSION}")
        
        # 初始化动画系统 (从animations.py导入)
        self.animator = MultiStageAnimations(self.ui, self)
        
        # 初始化功能变量
        self.selected_folder = ""
        self.installed_status = {f"NEKOPARA Vol.{i}": False for i in range(1, 5)}
        self.installed_status["NEKOPARA After"] = False  # 添加After的状态
        self.download_queue = deque()
        self.current_download_thread = None
        self.hash_manager = HashManager(BLOCK_SIZE)
        self.hash_thread = None
        self.extraction_thread = None
        self.hash_msg_box = None
        self.optimized_ip = None
        self.optimization_done = False # 标记是否已执行过优选
        self.logger = None
        self.hosts_manager = HostsManager() # 实例化HostsManager
        self.cloud_config = None
        self.config_fetch_thread = None
        
        # 加载配置
        self.config = load_config()
        
        # 检查管理员权限和进程
        self.admin_privileges = AdminPrivileges()
        self.admin_privileges.request_admin_privileges()
        self.admin_privileges.check_and_terminate_processes()
        
        # 备份hosts文件
        self.hosts_manager.backup()
        
        # 创建缓存目录
        if not os.path.exists(PLUGIN):
            try:
                os.makedirs(PLUGIN)
            except OSError as e:
                QtWidgets.QMessageBox.critical(
                    self,
                    f"错误 - {APP_NAME}",
                    f"\n无法创建缓存位置\n\n使用管理员身份运行或检查文件读写权限\n\n【错误信息】：{e}\n",
                )
                sys.exit(1)
        
        # 连接信号 (使用Ui_install.py中的组件名称)
        self.ui.start_install_btn.clicked.connect(self.download_manager.file_dialog)
        self.ui.exit_btn.clicked.connect(self.shutdown_app)

        # “帮助”菜单
        project_home_action = QAction("项目主页", self)
        project_home_action.triggered.connect(self.open_project_home_page)
        about_action = QAction("关于", self)
        about_action.triggered.connect(self.show_about_dialog)
        self.ui.menu_2.addAction(project_home_action)
        self.ui.menu_2.addAction(about_action)

        # “设置”菜单
        self.debug_action = QAction("Debug模式", self, checkable=True)
        self.debug_action.setChecked(self.config.get("debug_mode", False))
        self.debug_action.triggered.connect(self.toggle_debug_mode)
        self.ui.menu.addAction(self.debug_action)

        # 为未来功能预留的“切换下载源”按钮
        self.switch_source_action = QAction("切换下载源", self)
        self.switch_source_action.setEnabled(False)  # 暂时禁用
        self.ui.menu.addAction(self.switch_source_action)

        # 根据初始配置决定是否开启Debug模式
        if self.debug_action.isChecked():
            self.start_logging()
        
        # 在窗口显示前设置初始状态
        self.animator.initialize()
        
        # 窗口显示后延迟100ms启动动画
        QTimer.singleShot(100, self.start_animations)
    
    def start_animations(self):
        self.ui.exit_btn.setEnabled(False)
        self.animator.animation_finished.connect(self.on_animations_finished)
        self.animator.start_animations()
        self.fetch_cloud_config()

    def on_animations_finished(self):
        self.ui.exit_btn.setEnabled(True)

    def fetch_cloud_config(self):
        headers = {"User-Agent": UA}
        debug_mode = self.debug_action.isChecked()
        self.config_fetch_thread = ConfigFetchThread(CONFIG_URL, headers, debug_mode, self)
        self.config_fetch_thread.finished.connect(self.on_config_fetched)
        self.config_fetch_thread.start()

    def on_config_fetched(self, data, error_message):
        if error_message:
            if error_message == "update_required":
                msg_box = msgbox_frame(
                    f"更新提示 - {APP_NAME}",
                    "\n当前版本过低，请及时更新。\n",
                    QtWidgets.QMessageBox.StandardButton.Ok,
                )
                msg_box.exec()
                self.open_project_home_page()
                self.shutdown_app(force_exit=True)
            elif "missing_keys" in error_message:
                missing_versions = error_message.split(":")[1]
                msg_box = msgbox_frame(
                    f"配置缺失 - {APP_NAME}",
                    f'\n云端缺失下载链接，可能云服务器正在维护，不影响其他版本下载。\n当前缺失版本:"{missing_versions}"\n',
                    QtWidgets.QMessageBox.StandardButton.Ok,
                )
                msg_box.exec()
            else:
                # 其他错误暂时只在debug模式下打印
                if self.debug_action.isChecked():
                    print(f"获取云端配置失败: {error_message}")
        else:
            self.cloud_config = data
            if self.debug_action.isChecked():
                print("--- Cloud config fetched successfully ---")
                print(json.dumps(data, indent=2))

    def toggle_debug_mode(self, checked):
        self.config["debug_mode"] = checked
        save_config(self.config)
        if checked:
            self.start_logging()
        else:
            self.stop_logging()

    def start_logging(self):
        if self.logger is None:
            try:
                if os.path.exists(LOG_FILE):
                    os.remove(LOG_FILE)
                # 保存原始的 stdout 和 stderr
                self.original_stdout = sys.stdout
                self.original_stderr = sys.stderr
                # 创建 Logger 实例
                self.logger = Logger(LOG_FILE, self.original_stdout)
                sys.stdout = self.logger
                sys.stderr = self.logger
                print("--- Debug mode enabled ---")
            except (IOError, OSError) as e:
                QtWidgets.QMessageBox.critical(self, "错误", f"无法创建日志文件: {e}")
                self.logger = None

    def stop_logging(self):
        if self.logger:
            print("--- Debug mode disabled ---")
            sys.stdout = self.original_stdout
            sys.stderr = self.original_stderr
            self.logger.close()
            self.logger = None
        
    def get_install_paths(self):
        return {
            game: os.path.join(self.selected_folder, info["install_path"])
            for game, info in GAME_INFO.items()
        }

    def file_dialog(self):
        self.selected_folder = QFileDialog.getExistingDirectory(
            self, f"选择游戏所在【上级目录】 {APP_NAME}"
        )
        if not self.selected_folder:
            QtWidgets.QMessageBox.warning(
                self, f"通知 - {APP_NAME}", "\n未选择任何目录,请重新选择\n"
            )
            return
        self.download_action()

    def get_download_url(self) -> dict:
        try:
            if self.cloud_config:
                if self.debug_action.isChecked():
                    print("--- Using pre-fetched cloud config ---")
                config_data = self.cloud_config
            else:
                # 如果没有预加载的配置，则同步获取
                headers = {"User-Agent": UA}
                response = requests.get(CONFIG_URL, headers=headers, timeout=10)
                response.raise_for_status()
                config_data = response.json()

            if not config_data:
                raise ValueError("未能获取或解析配置数据")

            if self.debug_action.isChecked():
                print(f"DEBUG: Parsed JSON data: {json.dumps(config_data, indent=2)}")

            # 统一处理URL提取，确保返回扁平化的字典
            urls = {}
            for i in range(4):
                key = f"vol.{i+1}.data"
                if key in config_data and "url" in config_data[key]:
                    urls[f"vol{i+1}"] = config_data[key]["url"]
            
            if "after.data" in config_data and "url" in config_data["after.data"]:
                urls["after"] = config_data["after.data"]["url"]

            # 检查是否成功提取了所有URL
            if len(urls) != 5:
                missing_keys_map = {
                    f"vol{i+1}": f"vol.{i+1}.data" for i in range(4)
                }
                missing_keys_map["after"] = "after.data"
                
                extracted_keys = set(urls.keys())
                all_keys = set(missing_keys_map.keys())
                missing_simple_keys = all_keys - extracted_keys
                
                missing_original_keys = [missing_keys_map[k] for k in missing_simple_keys]
                raise ValueError(f"配置文件缺少必要的键: {', '.join(missing_original_keys)}")

            if self.debug_action.isChecked():
                print(f"DEBUG: Extracted URLs: {urls}")
                print("--- Finished getting download URL successfully ---")
            return urls
            if self.debug_action.isChecked():
                print(f"DEBUG: Extracted URLs: {urls}")
                print("--- Finished getting download URL successfully ---")
            return urls

        except requests.exceptions.RequestException as e:
            status_code = e.response.status_code if e.response is not None else "未知"
            try:
                error_response = e.response.json() if e.response else {}
                json_title = error_response.get("title", "无错误类型")
                json_message = error_response.get("message", "无附加错误信息")
            except (ValueError, AttributeError):
                json_title = "配置文件异常，无法解析错误类型"
                json_message = "配置文件异常，无法解析错误信息"

            if self.debug_action.isChecked():
                print(f"ERROR: Failed to get download config due to RequestException: {e}")
            
            QtWidgets.QMessageBox.critical(
                self,
                f"错误 - {APP_NAME}",
                f"\n下载配置获取失败\n\n【HTTP状态】：{status_code}\n【错误类型】：{json_title}\n【错误信息】：{json_message}\n",
            )
            return {}
        except ValueError as e:
            if self.debug_action.isChecked():
                print(f"ERROR: Failed to parse download config due to ValueError: {e}")

            QtWidgets.QMessageBox.critical(
                self,
                f"错误 - {APP_NAME}",
                f"\n配置文件格式异常\n\n【错误信息】：{e}\n",
            )
            return {}

    def download_setting(self, url, game_folder, game_version, _7z_path, plugin_path):
        game_exe = {
            game: os.path.join(
                self.selected_folder, info["install_path"].split("/")[0], info["exe"]
            )
            for game, info in GAME_INFO.items()
        }
        
        if (
            game_version not in game_exe
            or not os.path.exists(game_exe[game_version])
            or self.installed_status[game_version]
        ):
            self.installed_status[game_version] = False
            self.show_result()
            return
            
        self.progress_window = ProgressWindow(self)
        self.start_download_with_ip(self.optimized_ip, url, _7z_path, game_version, game_folder, plugin_path)


    def on_optimization_and_hosts_finished(self, ip):
        self.optimized_ip = ip
        self.optimization_done = True
        if hasattr(self, 'optimizing_msg_box') and self.optimizing_msg_box:
            if self.optimizing_msg_box.isVisible():
                self.optimizing_msg_box.accept()
            self.optimizing_msg_box = None

        if not ip:
            QtWidgets.QMessageBox.warning(self, f"优选失败 - {APP_NAME}", "\n未能找到合适的Cloudflare IP，将使用默认网络进行下载。\n")
        else:
            if self.download_queue:
                first_url = self.download_queue[0][0]
                hostname = urlparse(first_url).hostname
                if self.hosts_manager.apply_ip(hostname, ip):
                    QtWidgets.QMessageBox.information(self, f"成功 - {APP_NAME}", f"\n已将优选IP ({ip}) 应用到hosts文件。\n")
                else:
                    QtWidgets.QMessageBox.critical(self, f"错误 - {APP_NAME}", "\n修改hosts文件失败，请检查程序是否以管理员权限运行。\n")
        
        self.next_download_task()

    def start_download_with_ip(self, preferred_ip, url, _7z_path, game_version, game_folder, plugin_path):
        if preferred_ip:
            print(f"已为 {game_version} 获取到优选IP: {preferred_ip}")
        else:
            print(f"未能为 {game_version} 获取优选IP，将使用默认线路。")

        self.current_download_thread = DownloadThread(url, _7z_path, game_version, self)
        self.current_download_thread.progress.connect(self.progress_window.update_progress)
        self.current_download_thread.finished.connect(
            lambda success, error: self.install_setting(
                success,
                error,
                self.progress_window,
                url,
                game_folder,
                game_version,
                _7z_path,
                plugin_path,
            )
        )
        
        self.progress_window.stop_button.clicked.connect(self.current_download_thread.stop)
        self.current_download_thread.start()
        self.progress_window.exec()

    def install_setting(
        self,
        success,
        error,
        progress_window,
        url,
        game_folder,
        game_version,
        _7z_path,
        plugin_path,
    ):
        if progress_window.isVisible():
            progress_window.reject()

        if not success:
            print(f"--- Download Failed: {game_version} ---")
            print(error)
            print("------------------------------------")
            msg_box = QtWidgets.QMessageBox(self)
            msg_box.setWindowTitle(f"下载失败 - {APP_NAME}")
            msg_box.setText(f"\n文件获取失败: {game_version}\n错误: {error}\n\n是否重试？")
            
            retry_button = msg_box.addButton("重试", QtWidgets.QMessageBox.ButtonRole.YesRole)
            next_button = msg_box.addButton("下一个", QtWidgets.QMessageBox.ButtonRole.NoRole)
            end_button = msg_box.addButton("结束", QtWidgets.QMessageBox.ButtonRole.RejectRole)
            
            msg_box.exec()
            clicked_button = msg_box.clickedButton()

            if clicked_button == retry_button:
                self.download_setting(url, game_folder, game_version, _7z_path, plugin_path)
            elif clicked_button == next_button:
                self.next_download_task()
            else:
                self.on_download_stopped()
            return

        # --- Start Extraction in a new thread ---
        self.hash_msg_box = self.hash_manager.hash_pop_window()
        
        self.extraction_thread = ExtractionThread(_7z_path, game_folder, plugin_path, game_version, self)
        self.extraction_thread.finished.connect(self.on_extraction_finished)
        self.extraction_thread.start()

    def on_extraction_finished(self, success, error_message, game_version):
        if self.hash_msg_box and self.hash_msg_box.isVisible():
            self.hash_msg_box.close()

        if not success:
            QtWidgets.QMessageBox.critical(self, f"错误 - {APP_NAME}", error_message)
            self.installed_status[game_version] = False
        else:
            self.installed_status[game_version] = True
        
        self.next_download_task()

    def download_action(self):
        # 询问用户是否使用Cloudflare加速
        msg_box = QMessageBox(self)
        msg_box.setWindowTitle(f"下载优化 - {APP_NAME}")
        msg_box.setText("是否愿意通过Cloudflare加速来优化下载速度？\n\n这将临时修改系统的hosts文件，并需要管理员权限。")
        msg_box.setIcon(QMessageBox.Icon.Question)
        
        yes_button = msg_box.addButton("是，开启加速", QMessageBox.ButtonRole.YesRole)
        no_button = msg_box.addButton("否，直接下载", QMessageBox.ButtonRole.NoRole)
        
        msg_box.exec()

        use_optimization = msg_box.clickedButton() == yes_button

        self.hash_msg_box = self.hash_manager.hash_pop_window()

        install_paths = self.get_install_paths()
        
        self.hash_thread = HashThread("pre", install_paths, PLUGIN_HASH, self.installed_status, self)
        # 将用户选择传递给哈希完成后的处理函数
        self.hash_thread.pre_finished.connect(lambda status: self.on_pre_hash_finished(status, use_optimization))
        self.hash_thread.start()

    def on_pre_hash_finished(self, updated_status, use_optimization):
        self.installed_status = updated_status
        if self.hash_msg_box and self.hash_msg_box.isVisible():
            self.hash_msg_box.accept()
            self.hash_msg_box = None

        config = self.get_download_url()
        if not config:
            QtWidgets.QMessageBox.critical(
                self, f"错误 - {APP_NAME}", "\n网络状态异常或服务器故障，请重试\n"
            )
            return

        # --- 填充下载队列 ---
        for i in range(1, 5):
            game_version = f"NEKOPARA Vol.{i}"
            if not self.installed_status.get(game_version, False):
                url = config.get(f"vol{i}")
                if not url: continue
                game_folder = os.path.join(self.selected_folder, f"NEKOPARA Vol. {i}")
                _7z_path = os.path.join(PLUGIN, f"vol.{i}.7z")
                plugin_path = os.path.join(PLUGIN, GAME_INFO[game_version]["plugin_path"])
                self.download_queue.append((url, game_folder, game_version, _7z_path, plugin_path))

        game_version = "NEKOPARA After"
        if not self.installed_status.get(game_version, False):
            url = config.get("after")
            if url:
                game_folder = os.path.join(self.selected_folder, "NEKOPARA After")
                _7z_path = os.path.join(PLUGIN, "after.7z")
                plugin_path = os.path.join(PLUGIN, GAME_INFO[game_version]["plugin_path"])
                self.download_queue.append((url, game_folder, game_version, _7z_path, plugin_path))

        if not self.download_queue:
            self.after_hash_compare(PLUGIN_HASH)
            return

        if use_optimization and not self.optimization_done:
            first_url = self.download_queue[0][0]
            self.optimizing_msg_box = msgbox_frame(
                f"通知 - {APP_NAME}",
                "\n正在优选Cloudflare IP，请稍候...\n\n这可能需要5-10分钟，请耐心等待喵~"
            )
            # 我们不再提供“跳过”按钮，因为用户已经做出了选择
            self.optimizing_msg_box.setStandardButtons(QMessageBox.StandardButton.NoButton)
            self.optimizing_msg_box.setWindowModality(Qt.WindowModality.ApplicationModal)
            self.optimizing_msg_box.open()

            self.ip_optimizer_thread = IpOptimizerThread(first_url)
            # 优选完成后，需要修改hosts并开始下载
            self.ip_optimizer_thread.finished.connect(self.on_optimization_and_hosts_finished)
            self.ip_optimizer_thread.start()
        else:
            # 如果用户选择不优化，或已经优化过，直接开始下载
            self.next_download_task()

    def next_download_task(self):
        if not self.download_queue:
            self.after_hash_compare(PLUGIN_HASH)
            return
        # 检查下载线程是否仍在运行，以避免在手动停止后立即开始下一个任务
        if self.current_download_thread and self.current_download_thread.isRunning():
            return
        
        # 在开始下载前，确保hosts文件已修改（如果需要）
        # 这里的逻辑保持不变，因为hosts文件应该在队列开始前就被修改了
        url, game_folder, game_version, _7z_path, plugin_path = self.download_queue.popleft()
        self.download_setting(url, game_folder, game_version, _7z_path, plugin_path)

    def on_download_stopped(self):
        """当用户点击停止按钮或选择结束时调用的槽函数"""
        # 停止IP优选线程
        if hasattr(self, 'ip_optimizer_thread') and self.ip_optimizer_thread and self.ip_optimizer_thread.isRunning():
            self.ip_optimizer_thread.stop()
            self.ip_optimizer_thread.wait()
            if hasattr(self, 'optimizing_msg_box') and self.optimizing_msg_box:
                if self.optimizing_msg_box.isVisible():
                    self.optimizing_msg_box.accept()
                self.optimizing_msg_box = None

        # 停止当前可能仍在运行的下载线程
        if self.current_download_thread and self.current_download_thread.isRunning():
            self.current_download_thread.stop()
            self.current_download_thread.wait() # 等待线程完全终止
            
        # 清空下载队列，因为用户决定停止
        self.download_queue.clear()
        
        # 确保进度窗口已关闭
        if hasattr(self, 'progress_window') and self.progress_window.isVisible():
            self.progress_window.reject()

        # 可以在这里决定是否立即进行哈希比较或显示结果
        print("下载已全部停止。")
        self.setEnabled(True) # 恢复主窗口交互
        self.show_result()

    def after_hash_compare(self, plugin_hash):
        self.hash_msg_box = self.hash_manager.hash_pop_window()

        install_paths = self.get_install_paths()
        
        self.hash_thread = HashThread("after", install_paths, plugin_hash, self.installed_status, self)
        self.hash_thread.after_finished.connect(self.on_after_hash_finished)
        self.hash_thread.start()

    def on_after_hash_finished(self, result):
        if self.hash_msg_box and self.hash_msg_box.isVisible():
            self.hash_msg_box.close()

        if not result["passed"]:
            game = result.get("game", "未知游戏")
            message = result.get("message", "发生未知错误。")
            msg_box = msgbox_frame(
                f"文件校验失败 - {APP_NAME}",
                message,
                QtWidgets.QMessageBox.StandardButton.Ok,
            )
            msg_box.exec()

        self.show_result()

    def show_result(self):
        installed_version = "\n".join(
            [i for i in self.installed_status if self.installed_status[i]]
        )
        failed_ver = "\n".join(
            [i for i in self.installed_status if not self.installed_status[i]]
        )
        QtWidgets.QMessageBox.information(
            self,
            f"完成 - {APP_NAME}",
            f"\n安装结果：\n安装成功数：{len(installed_version.splitlines())}      安装失败数：{len(failed_ver.splitlines())}\n"
            f"安装成功的版本：\n{installed_version}\n尚未持有或未使用本工具安装补丁的版本：\n{failed_ver}\n",
        )

    def show_about_dialog(self):
        """显示关于对话框"""
        about_text = f"""
            <p><b>{APP_NAME} v{APP_VERSION}</b></p>
            <p>原作: <a href="https://github.com/Yanam1Anna">Yanam1Anna</a></p>
            <p>此应用根据 <a href="https://github.com/hyb-oyqq/FRAISEMOE2-Installer/blob/master/LICENSE">GPL-3.0 许可证</a> 授权。</p>
        """
        msg_box = msgbox_frame(
            f"关于 - {APP_NAME}",
            about_text,
            QtWidgets.QMessageBox.StandardButton.Ok,
        )
        msg_box.setTextFormat(Qt.TextFormat.RichText)  # 启用富文本
        msg_box.exec()

    def open_project_home_page(self):
        """打开项目主页"""
        webbrowser.open("https://github.com/hyb-oyqq/FRAISEMOE-Addons-Installer-NEXT")

    def closeEvent(self, event):
        self.shutdown_app(event)

    def shutdown_app(self, event=None, force_exit=False):
        self.hosts_manager.restore() # 恢复hosts文件
        self.stop_logging()  # 确保在退出时停止日志记录

        if not force_exit:
            reply = QtWidgets.QMessageBox.question(
                self,
                "退出程序",
                "\n是否确定退出?\n",
                QtWidgets.QMessageBox.StandardButton.Yes | QtWidgets.QMessageBox.StandardButton.No,
                QtWidgets.QMessageBox.StandardButton.No,
            )
            if reply != QtWidgets.QMessageBox.StandardButton.Yes:
                if event:
                    event.ignore()
                return

        if (
            self.current_download_thread
            and self.current_download_thread.isRunning()
        ):
            QtWidgets.QMessageBox.critical(
                self,
                f"错误 - {APP_NAME}",
                "\n当前有下载任务正在进行，完成后再试\n",
            )
            if event:
                event.ignore()
            return

        if os.path.exists(PLUGIN):
            for attempt in range(3):
                try:
                    shutil.rmtree(PLUGIN)
                    break
                except Exception as e:
                    if attempt == 2:
                        QtWidgets.QMessageBox.critical(
                            self,
                            f"错误 - {APP_NAME}",
                            f"\n清理缓存失败\n\n【错误信息】：{e}\n",
                        )
                        if event:
                            event.accept()
                        sys.exit(1)
        if event:
            event.accept()
        else:
            sys.exit(0)