#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
UOS 全能系统升级助手 V2.0
新增功能：预检、快照回滚（Timeshift）、设置、关于
"""

import sys
import os
import subprocess
import shutil
import json
from datetime import datetime
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
                             QHBoxLayout, QPushButton, QTextEdit, QLabel,
                             QProgressBar, QMessageBox, QCheckBox, QRadioButton,
                             QButtonGroup, QGroupBox, QFileDialog, QDialog,
                             QDialogButtonBox, QTabWidget, QLineEdit,
                             QFormLayout, QSpinBox)   # 注意：没有 QSettings
from PyQt5.QtCore import QThread, pyqtSignal, Qt, QSettings   # QSettings 来自 QtCore

# ==================== 配置管理 ====================
class SettingsManager:
    """使用 QSettings 保存用户配置"""
    def __init__(self):
        self.settings = QSettings("UOS", "UpgradeAssistant")
    
    def get(self, key, default=None):
        return self.settings.value(key, default)
    
    def set(self, key, value):
        self.settings.setValue(key, value)
    
    def sync(self):
        self.settings.sync()

# ==================== 预检线程 ====================
class PrecheckThread(QThread):
    output_signal = pyqtSignal(str)
    finished_signal = pyqtSignal(bool, str)
    
    def __init__(self):
        super().__init__()
    
    def run(self):
        self.output_signal.emit("========== 开始预检 ==========\n")
        all_passed = True
        messages = []
        
        # 1. 检查磁盘空间 (根分区至少 500MB)
        self.output_signal.emit("1. 检查磁盘空间...\n")
        try:
            stat = shutil.disk_usage('/')
            free_gb = stat.free / (1024**3)
            if stat.free < 500 * 1024 * 1024:
                self.output_signal.emit(f"   ✗ 根分区剩余空间不足 500MB (当前 {free_gb:.1f} GB)\n")
                all_passed = False
                messages.append("磁盘空间不足")
            else:
                self.output_signal.emit(f"   ✓ 根分区剩余 {free_gb:.1f} GB\n")
        except Exception as e:
            self.output_signal.emit(f"   ✗ 检查失败: {e}\n")
        
        # 2. 检查网络连通性 (ping 华为云镜像源)
        self.output_signal.emit("2. 检查网络连通性...\n")
        try:
            result = subprocess.run(['ping', '-c', '1', '-W', '2', 'mirrors.huaweicloud.com'],
                                    stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
            if result.returncode == 0:
                self.output_signal.emit("   ✓ 网络可达\n")
            else:
                self.output_signal.emit("   ✗ 网络不可达 (ping 失败)\n")
                all_passed = False
                messages.append("网络不通")
        except Exception:
            self.output_signal.emit("   ✗ 网络检查异常\n")
            all_passed = False
        
        # 3. 检查电池电量 (仅笔记本电脑)
        self.output_signal.emit("3. 检查电池状态...\n")
        try:
            result = subprocess.run(['upower', '-e'], capture_output=True, text=True)
            battery_paths = [line for line in result.stdout.splitlines() if 'BAT' in line]
            if battery_paths:
                for path in battery_paths:
                    info = subprocess.run(['upower', '-i', path], capture_output=True, text=True)
                    for line in info.stdout.splitlines():
                        if 'percentage' in line.lower():
                            pct = line.split(':')[1].strip().replace('%', '')
                            pct = int(float(pct))
                            self.output_signal.emit(f"   电池电量: {pct}%\n")
                            if pct < 20:
                                self.output_signal.emit("   ✗ 电量低于20%，建议接通电源\n")
                                all_passed = False
                                messages.append("电池电量低")
                            else:
                                self.output_signal.emit("   ✓ 电量充足\n")
                            break
                    break
            else:
                self.output_signal.emit("   未检测到电池 (可能是台式机)\n")
        except Exception as e:
            self.output_signal.emit(f"   电池检查失败: {e}\n")
        
        # 4. 检查 apt 锁
        self.output_signal.emit("4. 检查 apt 是否被占用...\n")
        lock_files = ['/var/lib/apt/lists/lock', '/var/cache/apt/archives/lock']
        locked = False
        for lock in lock_files:
            if os.path.exists(lock):
                try:
                    f = open(lock, 'r')
                    f.close()
                except PermissionError:
                    self.output_signal.emit(f"   ✗ {lock} 被其他进程锁定\n")
                    locked = True
        if not locked:
            self.output_signal.emit("   ✓ 无 apt 锁冲突\n")
        else:
            all_passed = False
            messages.append("apt 进程冲突")
        
        self.output_signal.emit("========== 预检结束 ==========\n")
        if all_passed:
            self.finished_signal.emit(True, "预检通过，可以安全升级")
        else:
            self.finished_signal.emit(False, f"预检发现问题: {', '.join(messages)}")

# ==================== 快照线程 ====================
class SnapshotThread(QThread):
    output_signal = pyqtSignal(str)
    finished_signal = pyqtSignal(bool, str)
    
    def __init__(self, comment="升级前自动快照"):
        super().__init__()
        self.comment = comment
    
    def run(self):
        self.output_signal.emit("========== 创建 Timeshift 快照 ==========\n")
        if shutil.which('timeshift') is None:
            self.output_signal.emit("✗ 未安装 timeshift，请执行: sudo apt install timeshift\n")
            self.finished_signal.emit(False, "timeshift 未安装")
            return
        
        self.output_signal.emit(f"正在创建快照，备注: {self.comment}\n")
        self.output_signal.emit("这可能需要几分钟，请耐心等待...\n")
        try:
            cmd = ['timeshift', '--create', '--comments', self.comment, '--yes']
            process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
                                       text=True, bufsize=1)
            for line in process.stdout:
                self.output_signal.emit(line)
            process.wait()
            if process.returncode == 0:
                self.output_signal.emit("✓ 快照创建成功\n")
                self.finished_signal.emit(True, "快照创建完成")
            else:
                self.output_signal.emit(f"✗ 快照创建失败，返回码: {process.returncode}\n")
                self.finished_signal.emit(False, "快照创建失败")
        except Exception as e:
            self.output_signal.emit(f"✗ 异常: {str(e)}\n")
            self.finished_signal.emit(False, str(e))

# ==================== 设置对话框 ====================
class SettingsDialog(QDialog):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setWindowTitle("设置")
        self.setMinimumWidth(400)
        self.settings = SettingsManager()
        self.initUI()
        self.loadSettings()
    
    def initUI(self):
        layout = QVBoxLayout(self)
        tabs = QTabWidget()
        
        general_tab = QWidget()
        general_layout = QFormLayout(general_tab)
        self.default_mode_combo = QLineEdit()
        self.default_mode_combo.setPlaceholderText("upgrade 或 dist-upgrade")
        general_layout.addRow("默认升级模式:", self.default_mode_combo)
        
        self.noninteractive_check = QCheckBox("默认启用非交互模式")
        general_layout.addRow(self.noninteractive_check)
        
        self.backup_dir_edit = QLineEdit()
        general_layout.addRow("备份目录:", self.backup_dir_edit)
        
        extra_tab = QWidget()
        extra_layout = QFormLayout(extra_tab)
        self.enable_precheck = QCheckBox("升级前自动执行预检")
        extra_layout.addRow(self.enable_precheck)
        self.enable_snapshot = QCheckBox("升级前自动创建系统快照")
        extra_layout.addRow(self.enable_snapshot)
        
        tabs.addTab(general_tab, "通用")
        tabs.addTab(extra_tab, "高级")
        layout.addWidget(tabs)
        
        buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        buttons.accepted.connect(self.saveSettings)
        buttons.rejected.connect(self.reject)
        layout.addWidget(buttons)
    
    def loadSettings(self):
        self.default_mode_combo.setText(self.settings.get("default_mode", "dist-upgrade"))
        self.noninteractive_check.setChecked(self.settings.get("noninteractive", True) == "true")
        self.backup_dir_edit.setText(self.settings.get("backup_dir", os.path.expanduser("~/uos_upgrade_backup")))
        self.enable_precheck.setChecked(self.settings.get("enable_precheck", True) == "true")
        self.enable_snapshot.setChecked(self.settings.get("enable_snapshot", False) == "true")
    
    def saveSettings(self):
        self.settings.set("default_mode", self.default_mode_combo.text())
        self.settings.set("noninteractive", "true" if self.noninteractive_check.isChecked() else "false")
        self.settings.set("backup_dir", self.backup_dir_edit.text())
        self.settings.set("enable_precheck", "true" if self.enable_precheck.isChecked() else "false")
        self.settings.set("enable_snapshot", "true" if self.enable_snapshot.isChecked() else "false")
        self.settings.sync()
        self.accept()

# ==================== 关于对话框 ====================
class AboutDialog(QDialog):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setWindowTitle("关于")
        self.setMinimumSize(350, 250)
        layout = QVBoxLayout(self)
        
        title = QLabel("<h2>UOS 全能系统升级助手</h2>")
        title.setAlignment(Qt.AlignCenter)
        layout.addWidget(title)
        
        version = QLabel("版本 2.0")
        version.setAlignment(Qt.AlignCenter)
        layout.addWidget(version)
        
        desc = QLabel("一款专为统信UOS设计的图形化系统升级工具，"
                      "支持预检、快照、非交互升级等功能。")
        desc.setWordWrap(True)
        desc.setAlignment(Qt.AlignCenter)
        layout.addWidget(desc)
        
        license_label = QLabel("许可证: GPL v3")
        license_label.setAlignment(Qt.AlignCenter)
        layout.addWidget(license_label)
        
        copyright_label = QLabel("© 2025 UOS Upgrade Assistant Team")
        copyright_label.setAlignment(Qt.AlignCenter)
        layout.addWidget(copyright_label)
        
        btn = QPushButton("确定")
        btn.clicked.connect(self.accept)
        layout.addWidget(btn)

# ==================== 升级线程 ====================
class UpgradeThread(QThread):
    output_signal = pyqtSignal(str)
    progress_signal = pyqtSignal(int)
    finished_signal = pyqtSignal(bool, str)

    def __init__(self, upgrade_mode, auto_confirm, backup_dir=None):
        super().__init__()
        self.upgrade_mode = upgrade_mode
        self.auto_confirm = auto_confirm
        self.backup_dir = backup_dir

    def run_command(self, cmd, description):
        self.output_signal.emit(f"\n>>> {description}\n")
        self.output_signal.emit(f">>> 命令: {' '.join(cmd)}\n")
        env = os.environ.copy()
        if self.auto_confirm:
            env['DEBIAN_FRONTEND'] = 'noninteractive'
            cmd = cmd + ['-o', 'Dpkg::Options::=--force-confdef',
                         '-o', 'Dpkg::Options::=--force-confold']
        try:
            process = subprocess.Popen(cmd, stdout=subprocess.PIPE,
                                       stderr=subprocess.STDOUT,
                                       text=True, bufsize=1, env=env)
            for line in iter(process.stdout.readline, ''):
                if line:
                    self.output_signal.emit(line.rstrip())
            process.wait()
            if process.returncode == 0:
                self.output_signal.emit(f"\n✓ {description} 完成\n")
                return True
            else:
                self.output_signal.emit(f"\n✗ {description} 失败，返回码: {process.returncode}\n")
                return False
        except Exception as e:
            self.output_signal.emit(f"\n✗ 执行异常: {str(e)}\n")
            return False

    def create_backup(self):
        if not self.backup_dir:
            return True
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        backup_path = os.path.join(self.backup_dir, f"sources_backup_{timestamp}")
        self.output_signal.emit(f"\n>>> 正在备份软件源配置到: {backup_path}\n")
        try:
            os.makedirs(backup_path, exist_ok=True)
            src_dir = '/etc/apt/sources.list.d'
            dst_dir = os.path.join(backup_path, 'sources.list.d')
            if os.path.exists(src_dir):
                if os.path.exists(dst_dir):
                    shutil.rmtree(dst_dir)
                shutil.copytree(src_dir, dst_dir)
            shutil.copy2('/etc/apt/sources.list', os.path.join(backup_path, 'sources.list'))
            self.output_signal.emit("✓ 软件源备份完成\n")
            return True
        except Exception as e:
            self.output_signal.emit(f"✗ 备份失败: {str(e)}\n")
            return False

    def run(self):
        success = True
        if self.backup_dir:
            self.create_backup()
        self.progress_signal.emit(10)
        if not self.run_command(['apt', 'update'], '更新软件源列表'):
            self.finished_signal.emit(False, "软件源更新失败")
            return
        self.progress_signal.emit(30)
        try:
            result = subprocess.run(['apt', 'list', '--upgradable'], capture_output=True, text=True)
            upgradable = [l for l in result.stdout.splitlines() if l and 'Listing...' not in l]
            if upgradable:
                self.output_signal.emit(f"\n发现 {len(upgradable)} 个软件包可升级\n")
            else:
                self.output_signal.emit("\n当前系统已是最新\n")
        except Exception as e:
            self.output_signal.emit(f"\n无法获取升级列表: {e}\n")
        self.progress_signal.emit(50)
        upgrade_cmd = ['apt', self.upgrade_mode, '-y']
        desc = '执行系统升级' if self.upgrade_mode == 'upgrade' else '执行完整系统升级(dist-upgrade)'
        if not self.run_command(upgrade_cmd, desc):
            self.finished_signal.emit(False, f"{desc}失败")
            return
        self.progress_signal.emit(80)
        self.run_command(['apt', 'autoremove', '-y'], '清理无用依赖包')
        self.run_command(['apt', 'autoclean'], '清理软件包缓存')
        self.progress_signal.emit(90)
        try:
            subprocess.run(['needrestart', '-r', 'l'], capture_output=True)
        except:
            self.output_signal.emit("needrestart 未安装，跳过服务检查\n")
        self.progress_signal.emit(100)
        self.finished_signal.emit(True, "系统升级完成！")

# ==================== 主窗口 ====================
class UpgradeWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.upgrade_thread = None
        self.precheck_thread = None
        self.snapshot_thread = None
        self.settings = SettingsManager()
        self.initUI()
        self.applySettingsToUI()
    
    def initUI(self):
        self.setWindowTitle("UOS 全能系统升级助手 V2.0")
        self.setMinimumSize(800, 600)
        
        menubar = self.menuBar()
        file_menu = menubar.addMenu("文件")
        settings_action = file_menu.addAction("设置")
        settings_action.triggered.connect(self.openSettings)
        file_menu.addSeparator()
        exit_action = file_menu.addAction("退出")
        exit_action.triggered.connect(self.close)
        help_menu = menubar.addMenu("帮助")
        about_action = help_menu.addAction("关于")
        about_action.triggered.connect(self.openAbout)
        
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        main_layout = QVBoxLayout(central_widget)
        
        tool_layout = QHBoxLayout()
        self.precheck_btn = QPushButton("🔍 预检系统")
        self.precheck_btn.clicked.connect(self.startPrecheck)
        tool_layout.addWidget(self.precheck_btn)
        
        self.snapshot_btn = QPushButton("📸 创建系统快照")
        self.snapshot_btn.clicked.connect(self.startSnapshot)
        tool_layout.addWidget(self.snapshot_btn)
        
        self.manage_snapshot_btn = QPushButton("📂 管理快照")
        self.manage_snapshot_btn.clicked.connect(self.manageSnapshots)
        tool_layout.addWidget(self.manage_snapshot_btn)
        tool_layout.addStretch()
        main_layout.addLayout(tool_layout)
        
        mode_group = QGroupBox("升级模式")
        mode_layout = QHBoxLayout()
        self.radio_upgrade = QRadioButton("标准升级 (upgrade)")
        self.radio_dist = QRadioButton("完整升级 (dist-upgrade) *推荐")
        self.radio_upgrade.setChecked(True)
        mode_layout.addWidget(self.radio_upgrade)
        mode_layout.addWidget(self.radio_dist)
        mode_group.setLayout(mode_layout)
        main_layout.addWidget(mode_group)
        
        backup_group = QGroupBox("预检查与备份")
        backup_layout = QVBoxLayout()
        self.check_before_btn = QCheckBox("升级前备份软件源配置 (推荐)")
        self.check_before_btn.setChecked(True)
        backup_layout.addWidget(self.check_before_btn)
        path_layout = QHBoxLayout()
        path_layout.addWidget(QLabel("备份目录:"))
        self.backup_path_edit = QTextEdit()
        self.backup_path_edit.setMaximumHeight(30)
        path_layout.addWidget(self.backup_path_edit)
        self.browse_btn = QPushButton("浏览...")
        self.browse_btn.clicked.connect(self.browse_backup_dir)
        path_layout.addWidget(self.browse_btn)
        backup_layout.addLayout(path_layout)
        backup_group.setLayout(backup_layout)
        main_layout.addWidget(backup_group)
        
        auto_group = QGroupBox("自动化设置")
        auto_layout = QHBoxLayout()
        self.auto_confirm_btn = QCheckBox("非交互式运行 (自动确认所有提示)")
        self.auto_confirm_btn.setChecked(True)
        auto_layout.addWidget(self.auto_confirm_btn)
        auto_group.setLayout(auto_layout)
        main_layout.addWidget(auto_group)
        
        btn_layout = QHBoxLayout()
        self.start_btn = QPushButton("开始升级")
        self.start_btn.clicked.connect(self.start_upgrade)
        self.start_btn.setStyleSheet("background-color: #4CAF50; font-weight: bold; padding: 8px;")
        self.stop_btn = QPushButton("停止")
        self.stop_btn.clicked.connect(self.stop_upgrade)
        self.stop_btn.setEnabled(False)
        self.stop_btn.setStyleSheet("background-color: #f44336; padding: 8px;")
        btn_layout.addWidget(self.start_btn)
        btn_layout.addWidget(self.stop_btn)
        main_layout.addLayout(btn_layout)
        
        self.progress_bar = QProgressBar()
        self.progress_bar.setValue(0)
        main_layout.addWidget(self.progress_bar)
        
        output_label = QLabel("执行日志 (实时输出):")
        main_layout.addWidget(output_label)
        self.output_text = QTextEdit()
        self.output_text.setReadOnly(True)
        self.output_text.setFontFamily("Monospace")
        main_layout.addWidget(self.output_text)
        
        self.statusBar().showMessage("就绪")
    
    def applySettingsToUI(self):
        mode = self.settings.get("default_mode", "dist-upgrade")
        if mode == "upgrade":
            self.radio_upgrade.setChecked(True)
        else:
            self.radio_dist.setChecked(True)
        self.auto_confirm_btn.setChecked(self.settings.get("noninteractive", "true") == "true")
        self.backup_path_edit.setText(self.settings.get("backup_dir", os.path.expanduser("~/uos_upgrade_backup")))
    
    def browse_backup_dir(self):
        dir_path = QFileDialog.getExistingDirectory(self, "选择备份目录",
                                                    self.backup_path_edit.toPlainText())
        if dir_path:
            self.backup_path_edit.setText(dir_path)
    
    def log(self, message):
        self.output_text.append(message)
        cursor = self.output_text.textCursor()
        cursor.movePosition(cursor.End)
        self.output_text.setTextCursor(cursor)
    
    def update_progress(self, value):
        self.progress_bar.setValue(value)
    
    def startPrecheck(self):
        if self.precheck_thread and self.precheck_thread.isRunning():
            self.log("预检已在运行中\n")
            return
        self.log("\n")
        self.precheck_thread = PrecheckThread()
        self.precheck_thread.output_signal.connect(self.log)
        self.precheck_thread.finished_signal.connect(self.onPrecheckFinished)
        self.precheck_thread.start()
        self.precheck_btn.setEnabled(False)
        self.statusBar().showMessage("预检进行中...")
    
    def onPrecheckFinished(self, success, message):
        self.precheck_btn.setEnabled(True)
        if success:
            self.statusBar().showMessage("预检通过")
            self.log(f"\n✓ {message}\n")
        else:
            self.statusBar().showMessage("预检发现问题")
            self.log(f"\n⚠ {message}\n")
            QMessageBox.warning(self, "预检警告", message)
    
    def startSnapshot(self):
        if self.snapshot_thread and self.snapshot_thread.isRunning():
            self.log("快照创建已在运行中\n")
            return
        self.log("\n")
        self.snapshot_thread = SnapshotThread(comment="UOS升级助手预升级快照")
        self.snapshot_thread.output_signal.connect(self.log)
        self.snapshot_thread.finished_signal.connect(self.onSnapshotFinished)
        self.snapshot_thread.start()
        self.snapshot_btn.setEnabled(False)
        self.statusBar().showMessage("创建快照中...")
    
    def onSnapshotFinished(self, success, message):
        self.snapshot_btn.setEnabled(True)
        if success:
            self.statusBar().showMessage("快照创建完成")
            self.log(f"\n✓ {message}\n")
        else:
            self.statusBar().showMessage("快照创建失败")
            self.log(f"\n✗ {message}\n")
            QMessageBox.critical(self, "快照失败", message)
    
    def manageSnapshots(self):
        if shutil.which('timeshift-gtk'):
            try:
                subprocess.Popen(['pkexec', 'timeshift-gtk'])
                self.log("启动 timeshift-gtk 管理界面...\n")
            except Exception as e:
                self.log(f"启动失败: {e}\n")
                QMessageBox.critical(self, "错误", f"无法启动 timeshift-gtk: {e}")
        else:
            self.log("未安装 timeshift-gtk，使用命令行显示快照列表：\n")
            try:
                result = subprocess.run(['timeshift', '--list'], capture_output=True, text=True)
                self.log(result.stdout)
                if result.stderr:
                    self.log(result.stderr)
            except Exception as e:
                self.log(f"无法列出快照: {e}\n")
                QMessageBox.information(self, "提示", "请安装 timeshift-gtk 获得图形化管理界面：\nsudo apt install timeshift-gtk")
    
    def openSettings(self):
        dlg = SettingsDialog(self)
        if dlg.exec_() == QDialog.Accepted:
            self.applySettingsToUI()
            self.log("设置已保存\n")
    
    def openAbout(self):
        dlg = AboutDialog(self)
        dlg.exec_()
    
    def start_upgrade(self):
        upgrade_mode = 'dist-upgrade' if self.radio_dist.isChecked() else 'upgrade'
        auto_confirm = self.auto_confirm_btn.isChecked()
        backup_dir = self.backup_path_edit.toPlainText().strip() if self.check_before_btn.isChecked() else None
        
        if self.settings.get("enable_precheck", "true") == "true":
            self.log("\n根据设置，升级前自动执行预检...\n")
            reply = QMessageBox.question(self, "预检建议",
                                         "建议先点击“预检系统”按钮检查环境。\n是否跳过预检直接升级？",
                                         QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
            if reply == QMessageBox.No:
                return
        
        if self.settings.get("enable_snapshot", "false") == "true":
            reply = QMessageBox.question(self, "创建快照",
                                         "根据设置，升级前将创建系统快照。是否立即创建？",
                                         QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)
            if reply == QMessageBox.Yes:
                self.snapshot_thread = SnapshotThread(comment="升级前自动快照")
                self.snapshot_thread.output_signal.connect(self.log)
                self.snapshot_thread.finished_signal.connect(lambda ok, msg: self.continueUpgradeAfterSnapshot(ok, msg, upgrade_mode, auto_confirm, backup_dir))
                self.snapshot_thread.start()
                self.snapshot_btn.setEnabled(False)
                self.start_btn.setEnabled(False)
                self.statusBar().showMessage("创建快照中，请稍候...")
                return
        
        self._do_upgrade(upgrade_mode, auto_confirm, backup_dir)
    
    def continueUpgradeAfterSnapshot(self, success, message, upgrade_mode, auto_confirm, backup_dir):
        self.snapshot_btn.setEnabled(True)
        if success:
            self.log("快照创建成功，继续升级流程...\n")
            self._do_upgrade(upgrade_mode, auto_confirm, backup_dir)
        else:
            self.log(f"快照创建失败: {message}，升级已取消\n")
            self.start_btn.setEnabled(True)
            self.statusBar().showMessage("升级已取消")
    
    def _do_upgrade(self, upgrade_mode, auto_confirm, backup_dir):
        msg = f"即将执行{'完整' if upgrade_mode == 'dist-upgrade' else '标准'}系统升级。\n"
        if upgrade_mode == 'dist-upgrade':
            msg += "⚠️ 注意: dist-upgrade 可能会安装新包或卸载有冲突的包。\n"
        if auto_confirm:
            msg += "🤖 非交互模式已启用，过程不会出现交互提示。\n"
        msg += "\n是否继续？"
        reply = QMessageBox.question(self, "确认升级", msg,
                                     QMessageBox.Yes | QMessageBox.No,
                                     QMessageBox.No)
        if reply != QMessageBox.Yes:
            return
        
        self.output_text.clear()
        self.progress_bar.setValue(0)
        self.upgrade_thread = UpgradeThread(upgrade_mode, auto_confirm, backup_dir)
        self.upgrade_thread.output_signal.connect(self.log)
        self.upgrade_thread.progress_signal.connect(self.update_progress)
        self.upgrade_thread.finished_signal.connect(self.on_upgrade_finished)
        self.upgrade_thread.start()
        self.start_btn.setEnabled(False)
        self.stop_btn.setEnabled(True)
        self.statusBar().showMessage("升级进行中...")
    
    def stop_upgrade(self):
        if self.upgrade_thread and self.upgrade_thread.isRunning():
            reply = QMessageBox.question(self, "确认停止",
                                         "正在运行的升级可能被中断，是否确定停止？",
                                         QMessageBox.Yes | QMessageBox.No,
                                         QMessageBox.No)
            if reply == QMessageBox.Yes:
                self.upgrade_thread.terminate()
                self.upgrade_thread.wait()
                self.log("\n⚠️ 用户手动停止了升级流程。")
                self.on_upgrade_finished(False, "升级已被用户中断")
    
    def on_upgrade_finished(self, success, message):
        self.start_btn.setEnabled(True)
        self.stop_btn.setEnabled(False)
        self.progress_bar.setValue(100 if success else 0)
        if success:
            self.statusBar().showMessage("升级完成")
            QMessageBox.information(self, "完成", message)
        else:
            self.statusBar().showMessage("升级失败")
            QMessageBox.critical(self, "错误", f"{message}\n请查看日志信息。")
        if success and self.radio_dist.isChecked():
            QMessageBox.information(self, "提示",
                                    "完整升级已完成。\n"
                                    "如系统运行异常，建议重启系统以确保所有更新生效。")

# ==================== 程序入口 ====================
def main():
    if os.geteuid() != 0:
        app = QApplication(sys.argv)
        QMessageBox.critical(None, "权限不足",
                             "本程序需要 root 权限才能执行系统升级操作。\n\n"
                             "请在终端中使用 sudo 运行：\n"
                             "sudo python3 uos_upgrade_gui_v2.py")
        sys.exit(1)
    
    app = QApplication(sys.argv)
    window = UpgradeWindow()
    window.show()
    sys.exit(app.exec_())

if __name__ == "__main__":
    main()