"""PySide2 GUI version of Pypack application."""

from __future__ import annotations

import asyncio
import contextlib
import json
import logging
import os
import sys
import threading
import time
from dataclasses import dataclass
from pathlib import Path
from typing import TYPE_CHECKING, ClassVar, cast

import PySide2
from PySide2.QtCore import Qt, QThread, Signal
from PySide2.QtWidgets import (
    QApplication,
    QCheckBox,
    QComboBox,
    QDialog,
    QDialogButtonBox,
    QFileDialog,
    QFormLayout,
    QGroupBox,
    QHBoxLayout,
    QLabel,
    QLineEdit,
    QListWidget,
    QListWidgetItem,
    QMainWindow,
    QMessageBox,
    QPushButton,
    QScrollArea,
    QSpinBox,
    QTabWidget,
    QTextEdit,
    QVBoxLayout,
    QWidget,
)

# Type-specific imports for better type checking
if TYPE_CHECKING:
    from sfi.pypack.pypack import (
        ConfigFactory,
        PackageWorkflow,
        WorkflowConfig,
    )

# Import from pypack module
from sfi.pypack.pypack import (
    ConfigFactory,
    PackageWorkflow,
    WorkflowConfig,
)

# Style constants for consistent UI design - Following checksum style
_UI_CONSTANTS = {
    # Dimensions
    "BUTTON_HEIGHT": 32,
    "INPUT_HEIGHT": 32,
    "COMPACT_SPACING": 4,
    "TIGHT_SPACING": 2,
    "MIN_MARGIN": 2,
    "SMALL_MARGIN": 8,
    "MEDIUM_MARGIN": 12,
    "LARGE_MARGIN": 16,
    # Widths
    "BUTTON_WIDTH_LARGE": 120,
    "BUTTON_WIDTH_MEDIUM": 100,
    "BUTTON_WIDTH_SMALL": 80,
    "INPUT_WIDTH_MIN": 280,
    "RESULT_WIDTH": 500,
    "RESULT_HEIGHT": 150,
    "LANGUAGE_COMBO_WIDTH": 100,
    # Fonts
    "FONT_SIZE_LARGE": 16,
    "FONT_SIZE_MEDIUM": 15,
    "FONT_SIZE_SMALL": 12,
    "FONT_FAMILY": "'Microsoft YaHei UI', 'Segoe UI', 'PingFang SC', sans-serif",
    "MONO_FONT": "'Consolas', 'Microsoft YaHei UI', monospace",
    # Colors (aligned with checksum style)
    "PRIMARY_BLUE": "#3b82f6",
    "PRIMARY_DARK": "#1d4ed8",
    "BORDER_COLOR": "#cbd5e1",
    "BACKGROUND_LIGHT": "#ffffff",
    "BACKGROUND_CARD": "#f8fafc",
    "TEXT_PRIMARY": "#0f172a",
    "TEXT_SECONDARY": "#64748b",
    # Label styling - Simple and clean design
    "LABEL_PADDING_VERTICAL": 4,
    "LABEL_PADDING_HORIZONTAL": 8,
    "LABEL_BORDER_RADIUS": 4,
    "LABEL_BACKGROUND": "transparent",
    "LABEL_TEXT_COLOR": "#0f172a",
    "LABEL_BORDER_COLOR": "transparent",
}


# Layout factory methods for consistent configuration
class LayoutFactory:
    @staticmethod
    def create_compact_layout(
        spacing: int = _UI_CONSTANTS["COMPACT_SPACING"],
        margins: tuple = (_UI_CONSTANTS["MIN_MARGIN"],) * 4,
    ) -> QHBoxLayout:
        """Create horizontally compact layout."""
        layout = QHBoxLayout()
        layout.setSpacing(spacing)
        layout.setContentsMargins(*margins)
        return layout

    @staticmethod
    def create_vertical_compact_layout(
        spacing: int = _UI_CONSTANTS["COMPACT_SPACING"],
        margins: tuple = (_UI_CONSTANTS["MIN_MARGIN"],) * 4,
    ) -> QVBoxLayout:
        """Create vertically compact layout."""
        layout = QVBoxLayout()
        layout.setSpacing(spacing)
        layout.setContentsMargins(*margins)
        return layout

    @staticmethod
    def configure_compact_widget(
        widget: QWidget,
        spacing: int = _UI_CONSTANTS["TIGHT_SPACING"],
        margins: tuple = (
            0,
            _UI_CONSTANTS["MIN_MARGIN"],
            0,
            _UI_CONSTANTS["MIN_MARGIN"],
        ),
    ) -> None:
        """Configure widget with compact layout settings."""
        if hasattr(widget, "layout") and widget.layout():
            layout = widget.layout()
            layout.setSpacing(spacing)
            layout.setContentsMargins(*margins)


# Import translation system
try:
    # First try relative import
    from .translation_pypack import translator
except (ImportError, ValueError):
    try:
        # Try absolute import
        from sfi.pypack.translation_pypack import translator
    except ImportError:
        try:
            # Try direct import
            from translation_pypack import translator
        except ImportError:
            # Minimal fallback translator
            class DummyTranslator:
                def tr(self, key, **kwargs):
                    translations = {
                        "window_title": "Pypack - Python Project Packaging Tool v1.0.0",
                        "config_panel": "Build Configuration",
                        "log_panel": "Build Log",
                        "build_button": "Start Build",
                        "clean_button": "Clean Artifacts",
                        "ready_status": "Ready",
                        "building_status": "Building...",
                        "build_success": "✓ Build completed successfully!",
                        "build_failed": "✗ Build failed",
                        "clean_success": "✓ Clean completed successfully!",
                        "build_starting": "Starting build process...",
                        "about_title": "About Pypack",
                        "about_content": "Pypack v1.0.0\nAdvanced Python project packaging tool",
                        "menu_file": "&File",
                        "menu_reset": "&Reset Parameters",
                        "menu_exit": "E&xit",
                        "menu_help": "&Help",
                        "menu_about": "&About",
                        "error_prefix": "Error: ",
                        "build_complete_status": "Build completed",
                        "build_failed_status": "Build failed",
                        "clean_complete_status": "Clean completed",
                        "param_project_name": "Project Name",
                        "param_python_version": "Python Version",
                        "param_loader_type": "Loader Type",
                        "param_entry_suffix": "Entry Suffix",
                        "param_generate_loader": "Generate Loader",
                        "param_offline": "Offline Mode",
                        "param_max_concurrent": "Max Concurrent Tasks",
                        "param_debug": "Debug Mode",
                        "param_cache_dir": "Cache Directory",
                        "param_archive_format": "Archive Format",
                        "param_mirror": "PyPI Mirror",
                        "param_archive_type": "Archive Type",
                    }
                    return translations.get(key, key)

            translator = DummyTranslator()

# Styles are now embedded directly in the code

logging.basicConfig(level=logging.INFO, format="%(message)s")
logger = logging.getLogger(__name__)

# Configure Qt platform plugins for Windows compatibility
qt_dir = Path(PySide2.__file__).parent
plugin_path = str(qt_dir / "plugins" / "platforms")
os.environ["QT_QPA_PLATFORM_PLUGIN_PATH"] = plugin_path


@dataclass
class PypackConfig:
    """Configuration class for Pypack GUI with persistent storage."""

    # Window geometry settings - 增大默认窗口尺寸
    window_width: int = 1400
    window_height: int = 900
    window_x: int = 100
    window_y: int = 100

    # Default build parameters
    directory: str = str(Path.cwd())
    python_version: str = "3.8.10"
    loader_type: str = "console"
    entry_suffix: str = ".ent"
    generate_loader: bool = True
    offline: bool = False
    max_concurrent: int = 4
    debug: bool = False
    cache_dir: str = ""
    archive_format: str = "zip"
    mirror: str = "aliyun"
    archive_type: str = ""
    project_name: str = ""


class ConfigManager:
    """Manager class for GUI configuration persistence."""

    CONFIG_FILE: ClassVar[Path] = Path.home() / ".pysfi" / "pypack.json"
    DEFAULT_CONFIG: ClassVar[PypackConfig] = PypackConfig()

    def __init__(self, config_file: Path | None = None):
        if config_file is not None:
            self.config_file = config_file
        else:
            self.config_file = self.CONFIG_FILE
            self.config_file.parent.mkdir(parents=True, exist_ok=True)

        self.config = self.load_config()

    def load_config(self) -> PypackConfig:
        if not self.config_file.exists():
            return self.DEFAULT_CONFIG

        try:
            with open(self.config_file, encoding="utf-8") as f:
                data = json.load(f)

            config_dict = {}
            for field_name in self.DEFAULT_CONFIG.__dataclass_fields__:
                default_value = getattr(self.DEFAULT_CONFIG, field_name)
                raw_value = data.get(field_name, default_value)

                # Ensure type consistency for numeric fields
                if (field_name == "max_concurrent" and isinstance(raw_value, str)) or (
                    field_name
                    in ["window_width", "window_height", "window_x", "window_y"]
                    and isinstance(raw_value, str)
                ):
                    try:
                        raw_value = int(raw_value)
                    except ValueError:
                        raw_value = default_value

                config_dict[field_name] = raw_value

            return PypackConfig(**config_dict)
        except (json.JSONDecodeError, KeyError, TypeError) as e:
            logger.warning(f"Could not load config from {self.config_file}: {e}")
            return self.DEFAULT_CONFIG

    def save_config(self) -> None:
        try:
            config_dict = {
                field_name: getattr(self.config, field_name)
                for field_name in self.config.__dataclass_fields__
            }
            with open(self.config_file, "w", encoding="utf-8") as f:
                json.dump(config_dict, f, indent=4)
        except Exception as e:
            logger.error(f"Failed to save config: {e}")

    def get_config(self) -> PypackConfig:
        return self.config

    def update_config(self, **kwargs) -> None:
        for key, value in kwargs.items():
            if hasattr(self.config, key):
                setattr(self.config, key, value)


class BuildWorker(QThread):
    """Worker thread for running build operations with pause/resume/stop support."""

    log_signal = Signal(str)
    finished_signal = Signal(bool, str)  # success, message
    status_signal = Signal(str)  # status updates
    progress_signal = Signal(int, int)  # current, total

    def __init__(self, config: WorkflowConfig, parent=None):
        super().__init__(parent)
        self.config = config
        self._paused = False
        self._stopped = False
        self._pause_condition = threading.Condition()
        self.loop = None
        self.build_task = None

    def pause(self):
        """Pause the build process."""
        self._paused = True
        self.status_signal.emit(translator.tr("build_paused"))

    def resume(self):
        """Resume the build process."""
        with self._pause_condition:
            self._paused = False
            self._pause_condition.notify_all()
        self.status_signal.emit(translator.tr("build_resumed"))

    def stop(self):
        """Stop the build process."""
        self._stopped = True
        self.status_signal.emit(translator.tr("build_stopped"))

        # Cancel the asyncio task if it exists
        if self.build_task and not self.build_task.done():
            self.build_task.cancel()

        # Stop the event loop
        if self.loop and self.loop.is_running():
            self.loop.call_soon_threadsafe(self.loop.stop)

    def is_paused(self) -> bool:
        """Check if build is currently paused."""
        return self._paused

    def is_stopped(self) -> bool:
        """Check if build has been stopped."""
        return self._stopped

    def _check_pause(self):
        """Check if we should pause and wait if necessary.

        This method should be called frequently during long operations
        to ensure responsive pause/resume behavior.
        """
        with self._pause_condition:
            while self._paused and not self._stopped:
                self._pause_condition.wait(timeout=0.1)  # Check every 100ms
                # Process Qt events to keep UI responsive
                from PySide2.QtWidgets import QApplication

                QApplication.processEvents()

    async def _run_with_control(self, workflow: PackageWorkflow):
        """Run build with pause/resume control."""
        try:
            # Check for stop before starting
            if self._stopped:
                return False, translator.tr("build_stopped")

            self.log_signal.emit("Starting packaging workflow execution")
            start_time = time.perf_counter()

            # Stage 1: Pack embed python (prerequisite for other tasks)
            self._check_pause()
            if self._stopped:
                return False, translator.tr("build_stopped")

            self.log_signal.emit("Packing embedded Python...")
            await self._run_task_with_pause_check(workflow.pack_embed_python)

            # Stage 2: Pack loaders, libraries, and source concurrently
            self._check_pause()
            if self._stopped:
                return False, translator.tr("build_stopped")

            self.log_signal.emit("=" * 50)
            self.log_signal.emit(
                "Running parallel tasks: loaders, libraries, source..."
            )
            await self._run_parallel_tasks_with_pause([
                workflow.pack_loaders,
                workflow.pack_libraries,
                workflow.pack_source,
            ])

            # Stage 3: Create archive (optional)
            self._check_pause()
            if self._stopped:
                return False, translator.tr("build_stopped")

            if workflow.config.archive_type:
                self.log_signal.emit(
                    f"Creating {workflow.config.archive_type} archive..."
                )
                await self._run_task_with_pause_check(
                    lambda: workflow.pack_archive(workflow.config.archive_type)
                )

            elapsed = time.perf_counter() - start_time
            self.log_signal.emit("=" * 50)
            self.log_signal.emit(f"Packaging workflow completed in {elapsed:.2f}s")
            return True, translator.tr("build_success")

        except asyncio.CancelledError:
            return False, translator.tr("build_stopped")
        except Exception as e:
            return False, f"{translator.tr('build_failed')}: {e!s}"

    async def _run_task_with_pause_check(self, task_func):
        """Run a single task with frequent pause checks.

        Args:
            task_func: Async function to execute
        """
        # Run the task in a way that allows frequent pause checks
        task = asyncio.create_task(task_func())

        while not task.done():
            # Check for pause/resume/stop every 100ms
            self._check_pause()
            if self._stopped:
                task.cancel()
                break

            try:
                # Wait for task with timeout to allow periodic checks
                await asyncio.wait_for(asyncio.shield(task), timeout=0.1)
                break  # Task completed
            except asyncio.TimeoutError:
                # Timeout means we need to check again
                continue
            except asyncio.CancelledError:
                # Task was cancelled
                break

        # If we get here and task isn't done, it was cancelled
        if not task.done():
            with contextlib.suppress(asyncio.CancelledError):
                await task

    async def _run_parallel_tasks_with_pause(self, task_funcs):
        """Run multiple tasks in parallel with pause support.

        Args:
            task_funcs: List of async functions to run concurrently
        """
        tasks = [asyncio.create_task(func()) for func in task_funcs]

        while not all(task.done() for task in tasks):
            # Check for pause/resume/stop
            self._check_pause()
            if self._stopped:
                for task in tasks:
                    if not task.done():
                        task.cancel()
                break

            # Wait a bit before checking again
            await asyncio.sleep(0.1)

        # Wait for remaining tasks to complete or be cancelled
        if not self._stopped:
            with contextlib.suppress(asyncio.CancelledError):
                await asyncio.gather(*tasks, return_exceptions=True)

    def run(self):
        try:
            import asyncio
            import logging

            # Create custom handler to capture logging output
            class GuiLogHandler(logging.Handler):
                def __init__(self, signal):
                    super().__init__()
                    self.signal = signal

                def emit(self, record):
                    try:
                        msg = self.format(record)
                        if msg.strip():
                            self.signal.emit(msg.strip())
                    except Exception:
                        pass

            # Set up logging handler
            gui_handler = GuiLogHandler(self.log_signal)
            gui_handler.setFormatter(logging.Formatter("%(levelname)s: %(message)s"))

            # Add handler to root logger
            root_logger = logging.getLogger()
            original_handlers = root_logger.handlers[:]
            root_logger.handlers.clear()
            root_logger.addHandler(gui_handler)

            # Set logging level based on config
            if self.config.debug:
                root_logger.setLevel(logging.DEBUG)
            else:
                root_logger.setLevel(logging.INFO)

            # Create workflow
            workflow = PackageWorkflow(
                root_dir=self.config.directory, config=self.config
            )

            # Use asyncio to run the build
            self.loop = asyncio.new_event_loop()
            asyncio.set_event_loop(self.loop)

            try:
                self.build_task = self.loop.create_task(
                    self._run_with_control(workflow)
                )
                success, message = self.loop.run_until_complete(self.build_task)
                self.finished_signal.emit(success, message)
            except Exception as e:
                self.finished_signal.emit(
                    False, f"{translator.tr('build_failed')}: {e!s}"
                )
            finally:
                if self.loop.is_running():
                    self.loop.close()
                self.loop = None
                self.build_task = None

        except Exception as e:
            self.finished_signal.emit(False, f"{translator.tr('build_failed')}: {e!s}")
        finally:
            # Restore original logging configuration
            if "root_logger" in locals():
                root_logger.handlers.clear()
                for handler in original_handlers:
                    root_logger.addHandler(handler)


class WidgetFactory:
    """工厂类用于创建不同类型的参数控件."""

    @staticmethod
    def create_widget(widget_type: str, default_value=None, options=None) -> QWidget:
        """根据控件类型创建相应的控件实例."""
        creators = {
            "text": WidgetFactory._create_text_widget,
            "directory": WidgetFactory._create_directory_widget,
            "combo": WidgetFactory._create_combo_widget,
            "checkbox": WidgetFactory._create_checkbox_widget,
        }

        creator = creators.get(widget_type)
        if not creator:
            raise ValueError(f"Unsupported widget type: {widget_type}")

        return creator(default_value, options)

    @staticmethod
    def _create_text_widget(default_value=None, options=None) -> QLineEdit:
        """创建文本输入控件."""
        widget = QLineEdit()
        if default_value:
            widget.setText(str(default_value))
        widget.setMinimumWidth(_UI_CONSTANTS["INPUT_WIDTH_MIN"])
        widget.setMinimumHeight(_UI_CONSTANTS["INPUT_HEIGHT"])
        widget.setObjectName("ParameterInput")
        widget.setStyleSheet(
            f"""
            padding: 2px 8px;
            border: 2px solid {_UI_CONSTANTS["BORDER_COLOR"]};
            border-radius: 8px;
            background-color: {_UI_CONSTANTS["BACKGROUND_LIGHT"]};
            font-size: {_UI_CONSTANTS["FONT_SIZE_MEDIUM"]}px;
            font-family: {_UI_CONSTANTS["MONO_FONT"]};
            """
        )
        return widget

    @staticmethod
    def _create_directory_widget(default_value=None, options=None) -> QWidget:
        """创建目录选择控件."""
        # 目录控件的复杂创建逻辑
        container = QWidget()
        container_layout = LayoutFactory.create_compact_layout(
            spacing=_UI_CONSTANTS["COMPACT_SPACING"],
            margins=(_UI_CONSTANTS["MIN_MARGIN"],) * 4,
        )
        from PySide2.QtCore import Qt

        container_layout.setAlignment(Qt.Alignment(Qt.AlignVCenter))
        container.setLayout(container_layout)

        line_edit = QLineEdit()
        if default_value:
            line_edit.setText(str(default_value))
        line_edit.setMinimumWidth(_UI_CONSTANTS["INPUT_WIDTH_MIN"])
        line_edit.setMinimumHeight(_UI_CONSTANTS["INPUT_HEIGHT"])
        line_edit.setObjectName("ParameterInput")
        line_edit.setAlignment(Qt.Alignment(Qt.AlignVCenter))
        line_edit.setStyleSheet(
            f"""
            padding: 2px 8px;
            border: 2px solid {_UI_CONSTANTS["BORDER_COLOR"]};
            border-radius: 8px;
            background-color: {_UI_CONSTANTS["BACKGROUND_LIGHT"]};
            font-size: {_UI_CONSTANTS["FONT_SIZE_MEDIUM"]}px;
            font-family: {_UI_CONSTANTS["MONO_FONT"]};
            """
        )
        container_layout.addWidget(line_edit, 1)

        browse_button = QPushButton("浏览...")
        browse_button.setObjectName("BrowseButton")
        browse_button.setProperty("class", "PrimaryButton")
        browse_button.setMinimumWidth(_UI_CONSTANTS["BUTTON_WIDTH_SMALL"])
        browse_button.setMinimumHeight(_UI_CONSTANTS["BUTTON_HEIGHT"])
        browse_button.setMaximumHeight(_UI_CONSTANTS["BUTTON_HEIGHT"])
        browse_button.setStyleSheet("padding: 2px 6px;")
        # 注意：这里需要在 ParameterWidget 中处理信号连接逻辑
        container_layout.addWidget(browse_button)

        return container

    @staticmethod
    def _create_combo_widget(default_value=None, options=None) -> QComboBox:
        """创建下拉选择控件."""
        widget = QComboBox()
        if options:
            widget.addItems(options)
        if default_value:
            index = widget.findText(str(default_value))
            if index >= 0:
                widget.setCurrentIndex(index)
        widget.setMinimumWidth(_UI_CONSTANTS["INPUT_WIDTH_MIN"])
        widget.setMinimumHeight(_UI_CONSTANTS["INPUT_HEIGHT"])
        widget.setObjectName("ParameterCombo")
        widget.setStyleSheet(
            f"""
            padding: 2px 8px;
            border: 2px solid {_UI_CONSTANTS["BORDER_COLOR"]};
            border-radius: 8px;
            background-color: {_UI_CONSTANTS["BACKGROUND_LIGHT"]};
            font-size: {_UI_CONSTANTS["FONT_SIZE_MEDIUM"]}px;
            font-family: {_UI_CONSTANTS["MONO_FONT"]};
            """
        )
        return widget

    @staticmethod
    def _create_checkbox_widget(default_value=None, options=None) -> QCheckBox:
        """创建复选框控件."""
        widget = QCheckBox()
        if default_value is not None:
            widget.setChecked(bool(default_value))
        widget.setMinimumHeight(_UI_CONSTANTS["INPUT_HEIGHT"])
        widget.setObjectName("ParameterCheckbox")
        widget.setStyleSheet("spacing: 4px;")
        return widget


class ParameterWidget(QWidget):
    """Widget for parameter input."""

    def __init__(
        self,
        name: str,
        label: str,
        widget_type: str,
        default_value=None,
        options=None,
        parent=None,
    ):
        super().__init__(parent)
        self.name = name
        self.label = label
        self.widget_type = widget_type

        layout = QHBoxLayout(self)
        layout.setSpacing(_UI_CONSTANTS["COMPACT_SPACING"])
        layout.setContentsMargins(
            _UI_CONSTANTS["MIN_MARGIN"],
            _UI_CONSTANTS["MIN_MARGIN"],
            _UI_CONSTANTS["MIN_MARGIN"],
            _UI_CONSTANTS["MIN_MARGIN"],
        )

        # 不再使用容器，直接创建标签以让 QFormLayout 正确控制对齐
        self.label_widget = QLabel(label)
        self.label_widget.setObjectName("ParameterLabel")
        self.label_widget.setStyleSheet(
            f"""
            QLabel#ParameterLabel {{
                font-size: {_UI_CONSTANTS["FONT_SIZE_MEDIUM"]}px;
                font-weight: 500;
                color: {_UI_CONSTANTS["LABEL_TEXT_COLOR"]};
                padding: {_UI_CONSTANTS["LABEL_PADDING_VERTICAL"]}px {_UI_CONSTANTS["LABEL_PADDING_HORIZONTAL"]}px;
                font-family: {_UI_CONSTANTS["FONT_FAMILY"]};
                background: {_UI_CONSTANTS["LABEL_BACKGROUND"]};
                min-height: {_UI_CONSTANTS["INPUT_HEIGHT"]}px;
            }}
            """
        )
        # QFormLayout 会自动处理垂直居中
        self.label_widget.setAlignment(Qt.AlignRight | Qt.AlignVCenter)

        # 使用工厂模式创建控件
        self.widget = WidgetFactory.create_widget(widget_type, default_value, options)
        layout.addWidget(self.widget)

        # 处理 directory 控件的特殊逻辑
        if widget_type == "directory":
            # 为目录控件设置特殊属性和信号连接
            from PySide2.QtWidgets import QLineEdit as QLineEditType
            from PySide2.QtWidgets import QPushButton as QPushButtonType

            line_edit: QLineEditType = cast(
                QLineEditType, self.widget.layout().itemAt(0).widget()
            )
            line_edit.textChanged.connect(self.on_directory_changed)
            browse_button: QPushButtonType = cast(
                QPushButtonType, self.widget.layout().itemAt(1).widget()
            )
            browse_button.clicked.connect(self.browse_directory)

        # Spacer with compact spacing
        spacer = QWidget()
        spacer.setMinimumWidth(_UI_CONSTANTS["TIGHT_SPACING"])
        spacer.setMaximumWidth(_UI_CONSTANTS["COMPACT_SPACING"])
        layout.addWidget(spacer)

    def on_directory_changed(self, text):
        """当目录路径发生变化时自动检测项目名称.

        Args:
            text: 新的目录路径文本
        """
        if text.strip():
            # 使用防抖机制避免频繁触发，同时防止重复解析
            from PySide2.QtCore import QDateTime, QTimer

            # 记录最后一次触发的时间和路径
            current_time = QDateTime.currentMSecsSinceEpoch()
            if not hasattr(self, "_last_trigger_time"):
                self._last_trigger_time = 0
                self._last_trigger_path = ""

            # 如果是相同路径且时间间隔很短，则忽略
            if (
                text == self._last_trigger_path
                and current_time - self._last_trigger_time < 1000
            ):  # 1秒内相同路径不重复处理
                return

            self._last_trigger_path = text
            self._last_trigger_time = current_time

            # 停止并清理之前的定时器
            if hasattr(self, "_change_timer"):
                self._change_timer.stop()
                self._change_timer.deleteLater()

            # 创建新的定时器
            self._change_timer = QTimer()
            self._change_timer.setSingleShot(True)
            self._change_timer.timeout.connect(
                lambda: self._delayed_detect_project_name(text)
            )
            self._change_timer.start(300)  # 300ms防抖延迟

    def _delayed_detect_project_name(self, directory_path: str):
        """延迟执行的项目名称检测，避免重复调用.

        Args:
            directory_path: 目录路径
        """
        # 再次检查是否是最新的一次调用
        if (
            hasattr(self, "_last_trigger_path")
            and self._last_trigger_path == directory_path
        ):
            self.auto_detect_project_name(directory_path)

    def browse_directory(self):
        """Open directory selection dialog."""
        from PySide2.QtWidgets import QLineEdit as QLineEditType

        current_path = cast(
            QLineEditType, self.widget.layout().itemAt(0).widget()
        ).text()

        logger.info(f"打开目录选择对话框，当前路径: {current_path}")

        directory = QFileDialog.getExistingDirectory(
            self,
            "选择项目目录",
            current_path,
            QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks,
        )

        if directory:
            logger.info(f"用户选择了目录: {directory}")
            line_edit: QLineEditType = cast(
                QLineEditType, self.widget.layout().itemAt(0).widget()
            )
            # 临时断开所有textChanged信号连接，避免setText触发信号
            with contextlib.suppress(RuntimeError):
                line_edit.textChanged.disconnect(self.on_directory_changed)
                # 断开项目列表解析信号（如果存在）
                try:
                    # 获取父窗口引用
                    parent_window = self.window()
                    if hasattr(parent_window, "_on_directory_changed_for_projects"):
                        line_edit.textChanged.disconnect(
                            parent_window._on_directory_changed_for_projects
                        )
                except (RuntimeError, AttributeError, TypeError):
                    pass  # 信号可能不存在或已被断开

            # 重新连接信号
            line_edit.textChanged.connect(self.on_directory_changed)
            # 重新连接项目列表解析信号
            try:
                parent_window = self.window()
                if hasattr(parent_window, "_on_directory_changed_for_projects"):
                    line_edit.textChanged.connect(
                        parent_window._on_directory_changed_for_projects
                    )
                    logger.info("已重新连接项目列表解析信号")
            except (AttributeError, TypeError):
                logger.warning("无法重新连接项目列表解析信号")
                pass  # 父窗口可能没有这个方法
            # 立即触发项目名称检测
            self.auto_detect_project_name(directory)

            # 手动触发项目列表解析（因为setText可能不会触发textChanged）
            try:
                parent_window = self.window()
                if hasattr(parent_window, "_on_directory_changed_for_projects"):
                    logger.info("手动调用 _on_directory_changed_for_projects")
                    parent_window._on_directory_changed_for_projects(directory)
            except Exception as e:
                logger.error(f"手动调用项目列表解析失败: {e}")

    def auto_detect_project_name(self, directory_path: str):
        """自动检测项目名称并填充到对应输入框.

        Args:
            directory_path: 项目目录路径
        """
        try:
            from pathlib import Path

            dir_path = Path(directory_path)
            logger.info(f"开始检测项目名称，目录路径: {dir_path}")
            logger.info(f"目录是否存在: {dir_path.exists()}")
            logger.info(f"是否为目录: {dir_path.is_dir()}")

            if not dir_path.exists() or not dir_path.is_dir():
                logger.warning(f"目录不存在或不是文件夹: {dir_path}")
                return

            # 方法1: 使用目录名作为项目名
            project_name = dir_path.name
            logger.info(f"初始项目名(来自目录名): {project_name}")

            # 方法2: 查找pyproject.toml文件获取项目名
            pyproject_file = dir_path / "pyproject.toml"
            logger.info(f"检查pyproject.toml文件: {pyproject_file}")
            logger.info(f"pyproject.toml是否存在: {pyproject_file.exists()}")

            if pyproject_file.exists():
                try:
                    import tomli

                    logger.info("尝试解析pyproject.toml文件")

                    with open(pyproject_file, "rb") as f:
                        pyproject_data = tomli.load(f)
                    logger.info(f"pyproject.toml内容: {pyproject_data}")
                    # 尝试从tool.poetry.name获取
                    if "tool" in pyproject_data and "poetry" in pyproject_data["tool"]:
                        project_name = pyproject_data["tool"]["poetry"].get(
                            "name", project_name
                        )
                        logger.info(f"从tool.poetry.name获取项目名: {project_name}")
                    # 尝试从project.name获取
                    elif "project" in pyproject_data:
                        project_name = pyproject_data["project"].get(
                            "name", project_name
                        )
                        logger.info(f"从project.name获取项目名: {project_name}")
                except Exception as e:
                    logger.error(f"解析pyproject.toml失败: {e}")
                    pass  # 如果解析失败，使用目录名

            # 方法3: 查找setup.py文件
            setup_file = dir_path / "setup.py"
            logger.info(f"检查setup.py文件: {setup_file}")
            logger.info(f"setup.py是否存在: {setup_file.exists()}")

            if setup_file.exists() and project_name == dir_path.name:
                try:
                    with open(setup_file, encoding="utf-8") as f:
                        content = f.read()
                        logger.info("读取setup.py内容成功")
                        # 简单的正则匹配查找name参数
                        import re

                        name_match = re.search(
                            r'name\s*=\s*["\']([^"\']+)["\']', content
                        )
                        if name_match:
                            project_name = name_match.group(1)
                            logger.info(f"从setup.py获取项目名: {project_name}")
                except Exception:
                    pass

            logger.info(f"最终确定的项目名: {project_name}")

            # 在GUI中找到对应的项目名称输入框并设置值
            parent_window = self.window()
            logger.info(f"父窗口对象: {parent_window}")
            logger.info(
                f"父窗口是否有param_widgets属性: {hasattr(parent_window, 'param_widgets')}"
            )

            if hasattr(parent_window, "param_widgets"):
                project_name_widget = parent_window.param_widgets.get("project_name")
                logger.info(f"找到project_name_widget: {project_name_widget}")
                if project_name_widget and project_name_widget.widget_type == "text":
                    logger.info(f"设置项目名为: {project_name}")
                    project_name_widget.widget.setText(project_name)
                else:
                    logger.warning(
                        f"project_name_widget类型不匹配或为空: {project_name_widget}"
                    )
            else:
                logger.warning("父窗口没有param_widgets属性")

        except Exception as e:
            logger.warning(f"自动检测项目名称失败: {e}")

    def get_value(self):
        if self.widget_type == "text":
            return self.widget.text()
        elif self.widget_type == "directory":
            # Extract text from the line edit within the container
            return self.widget.layout().itemAt(0).widget().text()
        elif self.widget_type == "combo":
            return self.widget.currentText()
        elif self.widget_type == "checkbox":
            return self.widget.isChecked()


class LogDisplayWidget(QWidget):
    """Widget for displaying build logs with tabbed interface."""

    def __init__(self, parent=None):
        super().__init__(parent)
        layout = QVBoxLayout(self)
        layout.setSpacing(8)
        layout.setContentsMargins(8, 8, 8, 8)

        # Container styling
        self.setObjectName("LogContainer")

        # Title
        title_label = QLabel(translator.tr("log_panel"))
        title_label.setObjectName("LogTitle")
        layout.addWidget(title_label)

        # Tab widget for different log levels
        self.tab_widget = QTabWidget()
        self.tab_widget.setObjectName("LogTabs")
        self.tab_widget.setStyleSheet(self._get_tab_stylesheet())
        layout.addWidget(self.tab_widget)

        # Initialize tab contents and counters
        self._init_tabs()

        # Store all messages for filtering
        self.all_messages = []

    def append_log(self, text: str):
        """Append text to appropriate log tab based on log level."""
        # Determine log level and color
        log_level = self._classify_log_level(text)
        color = self._get_log_color(text)

        # Store message with metadata
        message_data = {
            "text": text,
            "level": log_level,
            "color": color,
            "timestamp": self._get_timestamp(),
        }
        self.all_messages.append(message_data)

        # Format the text with HTML for coloring
        formatted_text = (
            f'<span style="color: {color};">[{message_data["timestamp"]}] {text}</span>'
        )

        # Append to appropriate tab
        if log_level == "ERROR":
            self.error_text.append(formatted_text)
            self.error_count += 1
            self._update_tab_labels()
        elif log_level == "WARNING":
            self.warning_text.append(formatted_text)
            self.warning_count += 1
            self._update_tab_labels()
        else:  # INFO
            self.info_text.append(formatted_text)
            self.info_count += 1
            self._update_tab_labels()

        # Auto-scroll to bottom for active tab
        self._scroll_to_bottom(log_level)

    def _classify_log_level(self, text: str) -> str:
        """Classify log text into ERROR, WARNING, or INFO levels."""
        text_lower = text.lower()

        # Error-related keywords (higher priority)
        error_keywords = [
            "error",
            "failed",
            "failure",
            "exception",
            "traceback",
            "critical",
            "✗",
            "err",
            "fatal",
            "abort",
            "crash",
            "segfault",
            "permission denied",
            "access denied",
            "not found",
            "does not exist",
            "timeout",
            "connection refused",
        ]
        if any(keyword in text_lower for keyword in error_keywords):
            return "ERROR"

        # Warning-related keywords
        warning_keywords = [
            "warning",
            "warn",
            "caution",
            "attention",
            "deprecated",
            "obsolete",
            "insecure",
            "vulnerable",
            "slow",
            "performance",
            "memory leak",
            "retry",
            "fallback",
        ]
        if any(keyword in text_lower for keyword in warning_keywords):
            return "WARNING"

        # Success-related keywords (classify as INFO but with green color)
        success_keywords = [
            "success",
            "successful",
            "completed",
            "finished",
            "done",
            "✓",
            "ok",
            "passed",
            "validated",
            "verified",
        ]
        if any(keyword in text_lower for keyword in success_keywords):
            return "INFO"

        # Info-related keywords
        info_keywords = [
            "info",
            "information",
            "starting",
            "begin",
            "process",
            "stage",
            "step",
            "phase",
            "initializing",
            "loading",
            "parsing",
            "analyzing",
            "building",
            "compiling",
            "linking",
            "packaging",
            "archiving",
        ]
        if any(keyword in text_lower for keyword in info_keywords):
            return "INFO"

        # Debug-related keywords
        debug_keywords = [
            "debug",
            "verbose",
            "detail",
            "internal",
            "diagnostic",
        ]
        if any(keyword in text_lower for keyword in debug_keywords):
            return "INFO"

        # Default to INFO for general messages
        return "INFO"

    def _get_log_color(self, text: str) -> str:
        """Determine appropriate color for log text based on content."""
        log_level = self._classify_log_level(text)

        if log_level == "ERROR":
            return "#ef4444"  # Red-500
        elif log_level == "WARNING":
            return "#f59e0b"  # Amber-500
        else:  # INFO
            # Additional classification for INFO level
            text_lower = text.lower()
            success_keywords = [
                "success",
                "successful",
                "completed",
                "finished",
                "done",
                "✓",
                "ok",
            ]
            if any(keyword in text_lower for keyword in success_keywords):
                return "#10b981"  # Emerald-500

            info_keywords = [
                "info",
                "information",
                "starting",
                "begin",
                "process",
                "stage",
            ]
            if any(keyword in text_lower for keyword in info_keywords):
                return "#3b82f6"  # Blue-500

            debug_keywords = ["debug", "verbose", "detail"]
            if any(keyword in text_lower for keyword in debug_keywords):
                return "#8b5cf6"  # Violet-500

            # Default color (light gray)
            return "#94a3b8"  # Slate-400

    def clear_log(self):
        """Clear all log displays and reset counters."""
        self.info_text.clear()
        self.warning_text.clear()
        self.error_text.clear()
        self.all_messages.clear()
        self.info_count = 0
        self.warning_count = 0
        self.error_count = 0
        self._update_tab_labels()

    def _init_tabs(self):
        """Initialize tab widgets and their content areas."""
        # Initialize counters
        self.info_count = 0
        self.warning_count = 0
        self.error_count = 0

        # Info Tab
        info_widget = QWidget()
        info_layout = QVBoxLayout(info_widget)
        info_layout.setContentsMargins(0, 0, 0, 0)

        self.info_text = QTextEdit()
        self.info_text.setReadOnly(True)
        self.info_text.setObjectName("InfoLogContent")
        info_layout.addWidget(self.info_text)

        self.tab_widget.addTab(info_widget, self._get_tab_label("Info", 0))

        # Warning Tab
        warning_widget = QWidget()
        warning_layout = QVBoxLayout(warning_widget)
        warning_layout.setContentsMargins(0, 0, 0, 0)

        self.warning_text = QTextEdit()
        self.warning_text.setReadOnly(True)
        self.warning_text.setObjectName("WarningLogContent")
        warning_layout.addWidget(self.warning_text)

        self.tab_widget.addTab(warning_widget, self._get_tab_label("Warning", 0))

        # Error Tab
        error_widget = QWidget()
        error_layout = QVBoxLayout(error_widget)
        error_layout.setContentsMargins(0, 0, 0, 0)

        self.error_text = QTextEdit()
        self.error_text.setReadOnly(True)
        self.error_text.setObjectName("ErrorLogContent")
        error_layout.addWidget(self.error_text)

        self.tab_widget.addTab(error_widget, self._get_tab_label("Error", 0))

    def _get_tab_label(self, base_name: str, count: int) -> str:
        """Generate tab label with message count."""
        return f"{base_name} ({count})"

    def _update_tab_labels(self):
        """Update tab labels with current message counts."""
        self.tab_widget.setTabText(0, self._get_tab_label("Info", self.info_count))
        self.tab_widget.setTabText(
            1, self._get_tab_label("Warning", self.warning_count)
        )
        self.tab_widget.setTabText(2, self._get_tab_label("Error", self.error_count))

    def _scroll_to_bottom(self, log_level: str):
        """Scroll to bottom of the appropriate log text area."""
        if log_level == "ERROR":
            scrollbar = self.error_text.verticalScrollBar()
        elif log_level == "WARNING":
            scrollbar = self.warning_text.verticalScrollBar()
        else:
            scrollbar = self.info_text.verticalScrollBar()
        scrollbar.setValue(scrollbar.maximum())

    def _get_timestamp(self) -> str:
        """Get current timestamp for log entries."""
        from datetime import datetime

        return datetime.now().strftime("%H:%M:%S")

    def _get_tab_stylesheet(self) -> str:
        """Return stylesheet for tab widget."""
        return """
        QTabWidget::pane {
            border: 2px solid #cbd5e1;
            border-radius: 8px;
            background-color: #0f172a;
        }

        QTabBar::tab {
            background-color: #1e293b;
            color: #cbd5e1;
            padding: 8px 16px;
            margin: 2px;
            border: 1px solid #334155;
            border-radius: 6px;
            font-family: 'Microsoft YaHei UI', 'Segoe UI', sans-serif;
            font-size: 14px;
            font-weight: 500;
        }

        QTabBar::tab:selected {
            background-color: #3b82f6;
            color: white;
            border-color: #2563eb;
        }

        QTabBar::tab:hover:!selected {
            background-color: #334155;
        }

        QTextEdit {
            background-color: #0f172a;
            border: none;
            border-radius: 6px;
            padding: 12px;
            font-family: 'Consolas', 'Courier New', monospace;
            font-size: 14px;
            color: #e2e8f0;
        }
        """


class SettingsDialog(QDialog):
    """Settings dialog for advanced configuration options."""

    def __init__(self, parent=None, config_manager=None):
        """Initialize settings dialog.

        Args:
            parent: Parent widget (should be PypackGUI instance)
            config_manager: ConfigManager instance
        """
        super().__init__(parent)
        self.config_manager = config_manager
        self.main_window = parent  # Store reference to main window
        self.setWindowTitle("首选项设置")
        self.setModal(True)
        self.resize(500, 600)
        self._create_ui()
        self._load_settings()

    def _create_ui(self):
        """Create settings dialog UI."""
        layout = QVBoxLayout()

        # Build Options Group
        build_group = QGroupBox("构建选项")
        build_layout = QFormLayout()
        build_layout.setSpacing(12)

        # Python Version
        self.python_version_edit = QLineEdit()
        build_layout.addRow("Python版本:", self.python_version_edit)

        # Loader Type
        self.loader_type_combo = QComboBox()
        self.loader_type_combo.addItems(["console", "gui"])
        build_layout.addRow("加载器类型:", self.loader_type_combo)

        # Entry Suffix
        self.entry_suffix_edit = QLineEdit()
        build_layout.addRow("入口文件后缀:", self.entry_suffix_edit)

        # Max Concurrent
        self.max_concurrent_spin = QSpinBox()
        self.max_concurrent_spin.setMinimum(1)
        self.max_concurrent_spin.setMaximum(16)
        build_layout.addRow("最大并发任务数:", self.max_concurrent_spin)

        build_group.setLayout(build_layout)
        layout.addWidget(build_group)

        # Advanced Options Group
        advanced_group = QGroupBox("高级选项")
        advanced_layout = QVBoxLayout()
        advanced_layout.setSpacing(10)

        self.generate_loader_checkbox = QCheckBox("生成加载器")
        self.offline_checkbox = QCheckBox("离线模式")
        self.debug_checkbox = QCheckBox("调试模式")

        advanced_layout.addWidget(self.generate_loader_checkbox)
        advanced_layout.addWidget(self.offline_checkbox)
        advanced_layout.addWidget(self.debug_checkbox)
        advanced_group.setLayout(advanced_layout)
        layout.addWidget(advanced_group)

        # Cache and Mirror Group
        cache_group = QGroupBox("缓存和镜像")
        cache_layout = QFormLayout()
        cache_layout.setSpacing(12)

        # Cache Directory
        cache_layout.addRow("缓存目录:", QLabel("(使用系统默认)"))

        # Mirror Source
        self.mirror_combo = QComboBox()
        self.mirror_combo.addItems([
            "pypi",
            "tsinghua",
            "aliyun",
            "ustc",
            "douban",
            "tencent",
        ])
        cache_layout.addRow("PyPI镜像源:", self.mirror_combo)

        cache_group.setLayout(cache_layout)
        layout.addWidget(cache_group)

        # Archive Settings Group
        archive_group = QGroupBox("归档设置")
        archive_layout = QFormLayout()
        archive_layout.setSpacing(12)

        # Note about automatic dependency analysis
        note_label = QLabel("依赖库格式由系统自动分析确定")
        note_label.setStyleSheet("color: #666; font-style: italic;")
        archive_layout.addRow(note_label)

        # Final Archive Type
        self.archive_type_combo = QComboBox()
        self.archive_type_combo.addItems([
            "(不创建)",
            "zip",
            "tar",
            "gztar",
            "bztar",
            "xztar",
            "7z",
            "nsis",
        ])
        archive_layout.addRow("最终归档格式:", self.archive_type_combo)

        archive_group.setLayout(archive_layout)
        layout.addWidget(archive_group)

        # Buttons
        button_layout = QHBoxLayout()
        button_layout.addStretch()

        # OK and Cancel buttons
        button_box = QDialogButtonBox()
        button_box.setStandardButtons(
            QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel
        )
        button_box.accepted.connect(self.accept)
        button_box.rejected.connect(self.reject)
        button_layout.addWidget(button_box)

        layout.addLayout(button_layout)
        self.setLayout(layout)

    def _load_settings(self):
        """Load current settings into dialog."""
        if self.config_manager:
            config = self.config_manager.get_config()
            self.python_version_edit.setText(config.python_version)
            index = self.loader_type_combo.findText(config.loader_type)
            if index >= 0:
                self.loader_type_combo.setCurrentIndex(index)
            self.entry_suffix_edit.setText(config.entry_suffix)

            # Ensure max_concurrent is integer
            max_concurrent = config.max_concurrent
            if isinstance(max_concurrent, str):
                try:
                    max_concurrent = int(max_concurrent)
                except ValueError:
                    max_concurrent = 4  # default value
            self.max_concurrent_spin.setValue(max_concurrent)

            self.generate_loader_checkbox.setChecked(config.generate_loader)
            self.offline_checkbox.setChecked(config.offline)
            self.debug_checkbox.setChecked(config.debug)
            index = self.mirror_combo.findText(config.mirror)
            if index >= 0:
                self.mirror_combo.setCurrentIndex(index)
            index = self.archive_type_combo.findText(config.archive_type or "(不创建)")
            if index >= 0:
                self.archive_type_combo.setCurrentIndex(index)

    def accept(self):
        """Save settings when OK is clicked."""
        if self.config_manager:
            # Update config
            config = self.config_manager.get_config()
            config.python_version = self.python_version_edit.text()
            config.loader_type = self.loader_type_combo.currentText()
            config.entry_suffix = self.entry_suffix_edit.text()
            config.max_concurrent = self.max_concurrent_spin.value()
            config.generate_loader = self.generate_loader_checkbox.isChecked()
            config.offline = self.offline_checkbox.isChecked()
            config.debug = self.debug_checkbox.isChecked()
            config.mirror = self.mirror_combo.currentText()
            archive_type = self.archive_type_combo.currentText()
            config.archive_type = archive_type if archive_type != "(不创建)" else None

            # Save to file
            self.config_manager.save_config()

        super().accept()


class PypackGUI(QMainWindow):
    """Main GUI window for Pypack application."""

    def __init__(self):
        super().__init__()
        self.config_manager = ConfigManager()
        self.config = self.config_manager.get_config()
        self.worker = None
        self.build_in_progress = False
        self.init_ui()
        self.load_settings()
        self.setup_connections()

    def init_ui(self):
        """Initialize user interface."""
        self.setWindowTitle(translator.tr("window_title"))
        self.setMinimumSize(1400, 900)  # 增大最小尺寸以确保所有控件可见

        # Apply modern styling
        self.setStyleSheet(self._get_modern_stylesheet())
        # Set hand cursor for interactive elements
        self.setCursor(Qt.ArrowCursor)

        # Create menu bar
        self.create_menu_bar()

        # Create status bar
        self.create_status_bar()

        # Central widget with compact layout
        central_widget = QWidget()
        central_widget.setObjectName("centralWidget")
        self.setCentralWidget(central_widget)
        main_layout = LayoutFactory.create_compact_layout(
            spacing=_UI_CONSTANTS["COMPACT_SPACING"],
            margins=(_UI_CONSTANTS["SMALL_MARGIN"],) * 4,
        )
        central_widget.setLayout(main_layout)

        # Left panel - configuration with compact layout
        left_panel = QGroupBox(translator.tr("config_panel"))
        left_panel.setObjectName("configPanel")
        left_layout = LayoutFactory.create_vertical_compact_layout(
            spacing=_UI_CONSTANTS["COMPACT_SPACING"],
            margins=(_UI_CONSTANTS["SMALL_MARGIN"],) * 4,
        )
        left_panel.setLayout(left_layout)

        # Scroll area for parameters (优化滚动区域尺寸)
        scroll_area = QScrollArea()
        scroll_area.setWidgetResizable(True)
        scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
        scroll_area.setObjectName("configScrollArea")
        scroll_area.setMinimumWidth(500)  # 增加最小宽度
        scroll_area.setStyleSheet("""
            QScrollArea {
                border: none;
                background-color: transparent;
            }
            QScrollArea > QWidget > QWidget {
                background-color: transparent;
            }
        """)

        scroll_widget = QWidget()
        scroll_widget.setObjectName("scrollWidget")
        self.params_layout = QFormLayout(scroll_widget)
        self.params_layout.setSpacing(_UI_CONSTANTS["COMPACT_SPACING"])
        self.params_layout.setContentsMargins(
            _UI_CONSTANTS["MIN_MARGIN"],
            _UI_CONSTANTS["MIN_MARGIN"],
            _UI_CONSTANTS["MIN_MARGIN"],
            _UI_CONSTANTS["MIN_MARGIN"],
        )
        self.params_layout.setLabelAlignment(Qt.AlignRight | Qt.AlignVCenter)
        self.params_layout.setFormAlignment(Qt.AlignLeft | Qt.AlignTop)
        self.params_layout.setRowWrapPolicy(QFormLayout.DontWrapRows)
        self.params_layout.setFieldGrowthPolicy(QFormLayout.AllNonFixedFieldsGrow)

        scroll_area.setWidget(scroll_widget)
        left_layout.addWidget(scroll_area)

        # Visual separator with compact spacing
        separator = QWidget()
        separator.setFixedHeight(2)
        separator.setStyleSheet("""
            background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
                                      stop:0 transparent, stop:0.5 #4a6fa5, stop:1 transparent);
            margin: 4px 0px;
            border-radius: 1px;
        """)
        left_layout.addWidget(separator)

        # Create parameter widgets
        self.create_parameter_widgets()

        # Visual separator
        separator2 = QWidget()
        separator2.setFixedHeight(2)
        separator2.setStyleSheet("""
            background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
                                      stop:0 transparent, stop:0.5 #4a6fa5, stop:1 transparent);
            margin: 8px 0px;
            border-radius: 1px;
        """)
        left_layout.addWidget(separator2)

        # Action buttons with compact sizing
        self.build_button = QPushButton(translator.tr("build_button"))
        self.build_button.setCursor(Qt.PointingHandCursor)
        self.build_button.setObjectName("BuildButton")
        self.build_button.setProperty("class", "PrimaryButton")
        self.build_button.setMinimumHeight(_UI_CONSTANTS["BUTTON_HEIGHT"])
        self.build_button.setStyleSheet("padding: 2px 8px;")
        left_layout.addWidget(self.build_button)

        # Control buttons (initially hidden)
        control_layout = QHBoxLayout()
        control_layout.setSpacing(_UI_CONSTANTS["COMPACT_SPACING"])

        self.pause_button = QPushButton(translator.tr("pause_button"))
        self.pause_button.setCursor(Qt.PointingHandCursor)
        self.pause_button.setObjectName("PauseButton")
        self.pause_button.setProperty("class", "ControlButton")
        self.pause_button.setMinimumHeight(_UI_CONSTANTS["BUTTON_HEIGHT"])
        self.pause_button.setStyleSheet("padding: 2px 8px;")
        self.pause_button.setVisible(False)
        control_layout.addWidget(self.pause_button)

        self.resume_button = QPushButton(translator.tr("resume_button"))
        self.resume_button.setCursor(Qt.PointingHandCursor)
        self.resume_button.setObjectName("ResumeButton")
        self.resume_button.setProperty("class", "ControlButton")
        self.resume_button.setMinimumHeight(_UI_CONSTANTS["BUTTON_HEIGHT"])
        self.resume_button.setStyleSheet("padding: 2px 8px;")
        self.resume_button.setVisible(False)
        control_layout.addWidget(self.resume_button)

        self.stop_button = QPushButton(translator.tr("stop_button"))
        self.stop_button.setCursor(Qt.PointingHandCursor)
        self.stop_button.setObjectName("StopButton")
        self.stop_button.setProperty("class", "ControlButton")
        self.stop_button.setMinimumHeight(_UI_CONSTANTS["BUTTON_HEIGHT"])
        self.stop_button.setStyleSheet("padding: 2px 8px;")
        self.stop_button.setVisible(False)
        control_layout.addWidget(self.stop_button)

        left_layout.addLayout(control_layout)

        self.clean_button = QPushButton(translator.tr("clean_button"))
        self.clean_button.setCursor(Qt.PointingHandCursor)
        self.clean_button.setObjectName("CleanButton")
        self.clean_button.setProperty("class", "PrimaryButton")
        self.clean_button.setMinimumHeight(_UI_CONSTANTS["BUTTON_HEIGHT"])
        self.clean_button.setStyleSheet("padding: 2px 6px;")
        left_layout.addWidget(self.clean_button)

        # Right panel - log display with compact layout
        right_panel = QWidget()
        right_panel.setObjectName("logPanel")
        right_layout = LayoutFactory.create_vertical_compact_layout(
            spacing=0, margins=(0, 0, 0, 0)
        )
        right_panel.setLayout(right_layout)

        self.log_widget = LogDisplayWidget()
        right_layout.addWidget(self.log_widget, 1)

        # Layout arrangement (调整面板比例，给左侧更多空间以适应长路径显示)
        main_layout.addWidget(left_panel, 3)  # 左侧占比增加到3份
        main_layout.addWidget(right_panel, 2)  # 右侧占2份

    def create_menu_bar(self):
        """Create application menu bar."""
        menubar = self.menuBar()

        # File menu
        file_menu = menubar.addMenu(translator.tr("menu_file"))

        reset_action = file_menu.addAction(translator.tr("menu_reset"))
        reset_action.triggered.connect(self.reset_parameters)
        reset_action.setShortcut("Ctrl+R")

        file_menu.addSeparator()

        # Preferences action
        preferences_action = file_menu.addAction("首选项设置...")
        preferences_action.triggered.connect(self._show_settings)
        preferences_action.setShortcut("Ctrl+P")

        file_menu.addSeparator()

        exit_action = file_menu.addAction(translator.tr("menu_exit"))
        exit_action.triggered.connect(self.close)
        exit_action.setShortcut("Ctrl+Q")

        # Help menu
        help_menu = menubar.addMenu(translator.tr("menu_help"))

        about_action = help_menu.addAction(translator.tr("menu_about"))
        about_action.triggered.connect(self.show_about)

    def _show_settings(self):
        """Show settings dialog."""
        settings_dialog = SettingsDialog(self, self.config_manager)
        settings_dialog.exec_()

    def create_status_bar(self):
        """Create status bar."""
        from PySide2.QtWidgets import QLabel

        self.status_bar = self.statusBar()

        self.status_label = QLabel(translator.tr("ready_status"))
        self.status_label.setObjectName("StatusLabelReady")
        self.status_bar.addPermanentWidget(self.status_label)
        self.status_bar.showMessage(translator.tr("ready_status"))

    def _set_ready_style(self):
        """Set status label style for ready state."""
        self.status_label.setObjectName("StatusLabelReady")

    def _set_building_style(self):
        """Set status label style for building state."""
        self.status_label.setObjectName("StatusLabelBuilding")

    def _set_success_style(self):
        """Set status label style for success state."""
        self.status_label.setObjectName("StatusLabelSuccess")

    def _set_error_style(self):
        """Set status label style for error state."""
        self.status_label.setObjectName("StatusLabelError")

    def _get_modern_stylesheet(self) -> str:
        """Generate modern stylesheet with embedded styles."""
        return self._get_embedded_styles()

    def _get_embedded_styles(self) -> str:
        """Return embedded stylesheet with modern design."""
        return """
/* Main window and global styles - Modern design */
QMainWindow {
    background-color: #f8fafc;
    font-family: 'Microsoft YaHei UI', 'Segoe UI', 'PingFang SC', sans-serif;
    font-size: 16px;
}

QWidget {
    font-family: 'Microsoft YaHei UI', 'Segoe UI', 'PingFang SC', sans-serif;
    font-size: 16px;
}

#centralWidget {
    background-color: transparent;
}

/* Configuration panel styles - Modern design */
#configPanel {
    background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1,
                                    stop:0 #ffffff, stop:1 #f1f5f9);
    border: 1px solid #e2e8f0;
    border-radius: 16px;
    font-weight: 500;
    color: #0f172a;
    margin: 0px;
    padding-top: 30px;
}

#configPanel::title {
    subcontrol-origin: margin;
    subcontrol-position: top center;
    padding: 14px 22px;
    background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
                              stop:0 #3b82f6, stop:1 #1d4ed8);
    color: white;
    border-top-left-radius: 15px;
    border-top-right-radius: 15px;
    border-bottom: 2px solid #1e40af;
    font-family: 'Microsoft YaHei UI', 'Segoe UI', sans-serif;
    font-size: 18px;
    font-weight: 600;
    letter-spacing: 0.5px;
    text-transform: uppercase;
}

#configScrollArea {
    border: none;
    background-color: transparent;
}

#scrollWidget {
    background-color: transparent;
}

QFormLayout QLabel {
    color: #0f172a;
    font-weight: 500;
    font-size: 16px;
    font-family: 'Microsoft YaHei UI', 'Segoe UI', sans-serif;
    padding: 8px 14px 8px 0px;
}

/* Menu bar and menu styles - Modern design */
QMenuBar {
    background-color: #1e293b;
    color: white;
    border-bottom: 1px solid #334155;
    padding: 6px;
    font-size: 14px;
    font-family: 'Microsoft YaHei UI', 'Segoe UI', sans-serif;
}

QMenuBar::item {
    background: transparent;
    padding: 8px 14px;
    border-radius: 6px;
    margin: 0 2px;
}

QMenuBar::item:selected {
    background-color: #3b82f6;
}

QMenuBar::item:pressed {
    background-color: #2563eb;
}

QMenu {
    background-color: white;
    color: #0f172a;
    border: 1px solid #e2e8f0;
    border-radius: 8px;
    padding: 6px 0px;
}

QMenu::item {
    padding: 10px 24px;
    font-size: 15px;
    font-family: 'Microsoft YaHei UI', 'Segoe UI', sans-serif;
}

QMenu::item:selected {
    background-color: #eff6ff;
    color: #1d4ed8;
}

QMenu::separator {
    height: 1px;
    background-color: #e2e8f0;
    margin: 4px 0;
}

/* Scrollbar styles */
QScrollBar:vertical {
    border: none;
    background-color: #f0f2f5;
    width: 14px;
    border-radius: 7px;
    margin: 2px;
}

QScrollBar::handle:vertical {
    background-color: #c5cad3;
    border-radius: 7px;
    min-height: 30px;
}

QScrollBar::handle:vertical:hover {
    background-color: #a0a8b5;
}

QScrollBar::handle:vertical:pressed {
    background-color: #8890a0;
}

QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {
    height: 0px;
}

/* Input controls styles - Modern design */
QLineEdit, QComboBox {
    font-size: 16px;
    font-family: 'Consolas', 'Microsoft YaHei UI', monospace;
    padding: 12px 16px;
    min-height: 32px;
    border: 2px solid #cbd5e1;
    border-radius: 8px;
    background-color: #ffffff;
}

QLineEdit:focus, QComboBox:focus {
    border-color: #3b82f6;
    outline: none;
}

QCheckBox {
    font-size: 16px;
    font-family: 'Microsoft YaHei UI', 'Segoe UI', sans-serif;
    spacing: 10px;
}

QCheckBox::indicator {
    width: 22px;
    height: 22px;
    border: 2px solid #cbd5e1;
    border-radius: 4px;
    background-color: #ffffff;
}

QCheckBox::indicator:checked {
    background-color: #3b82f6;
    border-color: #3b82f6;
}

/* Button styles - Compact design following checksum style */
.PrimaryButton {
    background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
                              stop:0 #3b82f6, stop:1 #2563eb);
    color: white;
    border: none;
    border-radius: 8px;
    font-size: 15px;
    font-weight: 500;
    font-family: 'Microsoft YaHei UI', 'Segoe UI', sans-serif;
    min-height: 32px;
    padding: 8px 16px;
}

.PrimaryButton:hover {
    background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
                              stop:0 #2563eb, stop:1 #1d4ed8);
    border: 2px solid #3b82f6;
}

.PrimaryButton:pressed {
    background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
                              stop:0 #1d4ed8, stop:1 #1e40af);
    border: 1px solid #3b82f6;
}

.PrimaryButton:disabled {
    background: #94a3b8;
    color: #cbd5e1;
}

.ControlButton {
    background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
                              stop:0 #f59e0b, stop:1 #d97706);
    color: white;
    border: none;
    border-radius: 8px;
    font-size: 15px;
    font-weight: 500;
    font-family: 'Microsoft YaHei UI', 'Segoe UI', sans-serif;
    min-height: 32px;
    padding: 8px 16px;
}

.ControlButton:hover {
    background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
                              stop:0 #d97706, stop:1 #b45309);
    border: 2px solid #f59e0b;
}

.ControlButton:pressed {
    background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
                              stop:0 #b45309, stop:1 #92400e);
    border: 1px solid #f59e0b;
}

.ControlButton:disabled {
    background: #94a3b8;
    color: #cbd5e1;
}

/* Specific button customizations - Compact checksum style */
#BuildButton {
    padding: 6px 16px;
    font-size: 15px;
    font-weight: 600;
    border-radius: 8px;
    min-height: 32px;
}

#CleanButton {
    padding: 6px 14px;
    font-size: 15px;
    min-height: 32px;
}

#BrowseButton {
    padding: 6px 12px;
    min-width: 80px;
    min-height: 32px;
    max-height: 32px;
}

/* Status bar label styles */
.StatusLabelBase {
    font-size: 16px;
    font-weight: 600;
    padding: 6px 16px;
    border-radius: 8px;
    font-family: 'Consolas', 'Microsoft YaHei UI', monospace;
}

.StatusLabelReady {
    color: #2c3e50;
    background-color: rgba(255, 255, 255, 0.9);
    border: 1px solid #e2e8f0;
}

.StatusLabelBuilding {
    color: #0c4a6e;
    background-color: rgba(224, 242, 254, 0.95);
    border: 1px solid #7dd3fc;
}

.StatusLabelSuccess {
    color: #0f5132;
    background-color: rgba(238, 255, 242, 0.95);
    border: 1px solid #a3d9a5;
}

.StatusLabelError {
    color: #721c24;
    background-color: rgba(255, 238, 238, 0.95);
    border: 1px solid #f5c6cb;
}

/* Log display widget styles */
.LogContainer {
    background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1,
                                    stop:0 #ffffff, stop:1 #f1f5f9);
    border: 2px solid #cbd5e1;
    border-radius: 18px;
}

.LogTitle {
    font-size: 24px;
    font-weight: 800;
    color: #1e293b;
    padding: 12px 0px 15px 0px;
    border-bottom: 3px solid #4a6fa5;
    font-family: 'Microsoft YaHei UI', 'Segoe UI', sans-serif;
    text-transform: uppercase;
    letter-spacing: 1px;
    background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
                              stop:0 transparent, stop:0.5 #4a6fa5, stop:1 transparent);
}

.LogContent {
    background-color: #0f172a;
    border: 2px solid #334155;
    border-radius: 14px;
    padding: 15px;
    font-family: 'Consolas', 'Courier New', monospace;
    font-size: 16px;
    color: #e2e8f0;
}

/* Parameter widget styles */
.ParameterLabel {
    color: #1a1a2e;
    font-weight: 600;
    font-size: 19px;
    font-family: 'Microsoft YaHei UI', 'Segoe UI', sans-serif;
    padding: 10px 18px 10px 8px;
    text-align: right;
}

.ParameterInput {
    font-size: 18px;
    padding: 12px 16px;
    border: 2px solid #cbd5e1;
    border-radius: 8px;
}

.ParameterInput:focus {
    border-color: #4a6fa5;
}

.ParameterCombo {
    font-size: 18px;
    padding: 12px 16px;
    border: 2px solid #cbd5e1;
    border-radius: 8px;
}

.ParameterCombo:focus {
    border-color: #4a6fa5;
}

.ParameterCombo::drop-down {
    border: none;
}

.ParameterCheckbox {
    font-size: 18px;
    font-weight: 500;
    spacing: 12px;
}

.ParameterCheckbox::indicator {
    width: 24px;
    height: 24px;
}

        """

    def show_about(self):
        """Show about dialog."""

        QMessageBox.about(
            self,
            translator.tr("about_title"),
            translator.tr("about_content"),
        )

    def create_parameter_widgets(self):
        """Create parameter input widgets - directory selection only."""
        # Define only directory parameter
        params = [
            (
                "directory",
                translator.tr("param_directory"),
                "directory",
                str(Path.cwd()),
            ),
        ]

        # Create widgets
        self.param_widgets = {}
        for param_def in params:
            if len(param_def) == 4:
                name, label, widget_type, default = param_def
                options = None
            else:
                name, label, widget_type, default, options = param_def

            widget = ParameterWidget(name, label, widget_type, default, options)
            self.param_widgets[name] = widget
            self.params_layout.addRow(widget.label_widget, widget)

        # Add projects list display area
        self._create_projects_display()

        # Add a note about advanced settings
        note_label = QLabel("更多设置请使用 菜单 → 首选项设置")
        note_label.setStyleSheet("color: #666; font-style: italic; padding: 10px 0px;")
        note_label.setAlignment(Qt.AlignCenter)
        self.params_layout.addRow(note_label)

    def _create_projects_display(self):
        """Create projects list display area."""
        # Projects section title
        projects_title = QLabel(translator.tr("projects_title"))
        projects_title.setStyleSheet("""
            font-size: 18px;
            font-weight: bold;
            color: #2d3748;
            padding: 15px 0px 5px 0px;
            border-bottom: 2px solid #e2e8f0;
        """)
        projects_title.setAlignment(Qt.AlignLeft)
        self.params_layout.addRow(projects_title)

        # Projects list widget
        self.projects_list = QListWidget()
        self.projects_list.setMinimumHeight(200)
        self.projects_list.setMaximumHeight(300)
        self.projects_list.setStyleSheet("""
            QListWidget {
                border: 2px solid #cbd5e1;
                border-radius: 8px;
                padding: 8px;
                background-color: #ffffff;
                font-size: 16px;
                font-family: 'Consolas', 'Microsoft YaHei UI', monospace;
            }
            QListWidget::item {
                padding: 8px;
                border-bottom: 1px solid #e2e8f0;
            }
            QListWidget::item:selected {
                background-color: #ebf8ff;
                color: #2b6cb0;
                border-radius: 4px;
            }
            QListWidget::item:hover {
                background-color: #f7fafc;
            }
        """)

        # Add placeholder text
        placeholder_item = QListWidgetItem(translator.tr("projects_placeholder"))
        placeholder_item.setFlags(Qt.NoItemFlags)  # Disable selection
        self.projects_list.addItem(placeholder_item)

        self.params_layout.addRow(self.projects_list)

        # Project details display area
        self.project_details = QTextEdit()
        self.project_details.setMaximumHeight(150)
        self.project_details.setReadOnly(True)
        self.project_details.setPlaceholderText(
            translator.tr("project_details_placeholder")
        )
        self.project_details.setStyleSheet("""
            QTextEdit {
                border: 2px solid #cbd5e1;
                border-radius: 8px;
                padding: 12px;
                background-color: #f8fafc;
                font-family: 'Consolas', 'Microsoft YaHei UI', monospace;
                font-size: 14px;
                color: #2d3748;
            }
        """)

        # Connect selection change to show details
        self.projects_list.itemSelectionChanged.connect(
            self._show_selected_project_details
        )

        self.params_layout.addRow(self.project_details)

        # Connect directory change signal to project parsing
        if "directory" in self.param_widgets:
            directory_widget = self.param_widgets["directory"]
            logger.info(f"directory_widget存在，类型: {type(directory_widget)}")
            if hasattr(directory_widget, "widget") and hasattr(
                directory_widget.widget, "layout"
            ):
                line_edit = directory_widget.widget.layout().itemAt(0).widget()
                logger.info(f"line_edit对象: {line_edit}, 类型: {type(line_edit)}")
                line_edit.textChanged.connect(self._on_directory_changed_for_projects)
                logger.info(
                    "已成功连接 textChanged 信号到 _on_directory_changed_for_projects"
                )
            else:
                logger.warning("directory_widget没有widget或layout属性")
        else:
            logger.warning("param_widgets中没有directory项")

    def _on_directory_changed_for_projects(self, directory_path: str):
        """Handle directory change and parse projects.

        Args:
            directory_path: Selected directory path
        """
        logger.info(f"_on_directory_changed_for_projects被调用，路径: {directory_path}")
        if not directory_path.strip():
            logger.info("目录路径为空，显示占位符")
            self._show_placeholder_projects()
            return

        try:
            from pathlib import Path

            dir_path = Path(directory_path)

            if not dir_path.exists() or not dir_path.is_dir():
                logger.warning(f"目录不存在或不是文件夹: {dir_path}")
                self._show_placeholder_projects(translator.tr("directory_not_exist"))
                return

            # Parse projects in the directory
            logger.info(f"开始解析目录中的项目: {dir_path}")
            projects = self._parse_projects_in_directory(dir_path)
            logger.info(f"解析完成，获得 {len(projects)} 个项目")
            self._display_projects(projects)

        except Exception as e:
            logger.error(f"解析项目时出错: {e}")
            import traceback

            logger.error(f"错误堆栈: {traceback.format_exc()}")
            self._show_placeholder_projects(translator.tr("parse_error", error=str(e)))

    def _parse_projects_in_directory(self, directory: Path) -> list:
        """Parse projects in the given directory using pyprojectparse.

        Args:
            directory: Directory to parse projects from

        Returns:
            List of project dictionaries with detailed information
        """
        try:
            # Use pyprojectparse to analyze the directory
            from sfi.pyprojectparse.models.solution import Solution

            logger.info(f"开始解析目录: {directory}")

            # 使用 pyprojectparse 的内置排除功能
            solution = Solution.from_directory(directory)
            logger.info(f"Solution创建成功，包含 {len(solution.projects)} 个项目")
            projects_info = solution.get_projects_info()
            logger.info(f"获取项目信息成功，共 {len(projects_info)} 条")

            # Add additional analysis that pyprojectparse doesn't cover
            enhanced_projects = []
            for project_info in projects_info:
                project_path = Path(project_info["path"])

                # Add file indicators
                indicators = []
                if (project_path / "pyproject.toml").exists():
                    indicators.append("pyproject.toml")
                if (project_path / "setup.py").exists():
                    indicators.append("setup.py")
                if (project_path / "requirements.txt").exists():
                    indicators.append("requirements.txt")
                if (project_path / "src").exists():
                    indicators.append("src/")

                python_files = list(project_path.glob("*.py"))
                if python_files:
                    indicators.append(f"{len(python_files)} .py files")

                project_info["indicators"] = indicators
                enhanced_projects.append(project_info)

            logger.info(f"增强后的项目列表包含 {len(enhanced_projects)} 个项目")
            return enhanced_projects

        except Exception as e:
            logger.error(f"使用pyprojectparse分析项目时出错: {e}")
            import traceback

            logger.error(f"错误堆栈: {traceback.format_exc()}")
            # Fallback to original method
            return self._parse_projects_in_directory_fallback(directory)

    def _parse_projects_in_directory_fallback(self, directory: Path) -> list:
        """Fallback method for parsing projects when pyprojectparse fails.

        Args:
            directory: Directory to parse projects from

        Returns:
            List of project dictionaries with name and path
        """
        projects = []

        try:
            # Look for common Python project indicators
            for item in directory.iterdir():
                if item.is_dir():
                    project_info = self._analyze_project_directory(item)
                    if project_info:
                        projects.append(project_info)

        except Exception as e:
            logger.error(f"扫描目录时出错: {e}")

        return projects

    def _analyze_project_directory(self, project_path: Path) -> dict | None:
        """Analyze a directory to determine if it's a Python project.

        Args:
            project_path: Path to analyze

        Returns:
            Project info dictionary or None if not a valid project
        """
        try:
            project_name = project_path.name
            indicators = []
            project_type = "未知"
            project_type_emoji = "❓"
            dependencies = []
            python_version = None
            entry_points = []

            # Check for pyproject.toml
            pyproject_file = project_path / "pyproject.toml"
            if pyproject_file.exists():
                indicators.append("pyproject.toml")
                # Try to read project name and metadata from pyproject.toml
                try:
                    import tomli

                    with open(pyproject_file, "rb") as f:
                        pyproject_data = tomli.load(f)

                    # Get project name
                    if (
                        "project" in pyproject_data
                        and "name" in pyproject_data["project"]
                    ):
                        project_name = pyproject_data["project"]["name"]
                    elif (
                        "tool" in pyproject_data
                        and "poetry" in pyproject_data["tool"]
                        and "name" in pyproject_data["tool"]["poetry"]
                    ):
                        project_name = pyproject_data["tool"]["poetry"]["name"]

                    # Get project type - Enhanced detection logic
                    if "project" in pyproject_data:
                        project_section = pyproject_data["project"]

                        # Check for GUI applications first
                        if "gui-scripts" in project_section or self._is_gui_application(
                            project_name, project_path
                        ):
                            project_type = "窗口应用"
                            project_type_emoji = "🖥️"

                        # Check for console applications
                        elif (
                            (
                                "scripts" in project_section
                                or "entry-points" in project_section
                                or "console-scripts" in project_section
                            )
                            or project_name.lower() == "bumpversion"
                            or self._is_cli_tool_project(project_name, project_path)
                        ):
                            project_type = "控制台应用"
                            project_type_emoji = "⌨️"

                        # Check for library/modules
                        else:
                            project_type = "库/模块"
                            project_type_emoji = "📚"

                    # Get Python version requirement
                    if (
                        "project" in pyproject_data
                        and "requires-python" in pyproject_data["project"]
                    ):
                        python_version = pyproject_data["project"]["requires-python"]

                    # Get entry points
                    if (
                        "project" in pyproject_data
                        and "scripts" in pyproject_data["project"]
                    ):
                        entry_points.extend(
                            list(pyproject_data["project"]["scripts"].keys())
                        )
                    if (
                        "project" in pyproject_data
                        and "gui-scripts" in pyproject_data["project"]
                    ):
                        entry_points.extend(
                            list(pyproject_data["project"]["gui-scripts"].keys())
                        )
                        deps = pyproject_data["project"]["dependencies"]
                        dependencies.extend(deps if isinstance(deps, list) else [deps])
                    elif (
                        "tool" in pyproject_data
                        and "poetry" in pyproject_data["tool"]
                        and "dependencies" in pyproject_data["tool"]["poetry"]
                    ):
                        poetry_deps = pyproject_data["tool"]["poetry"]["dependencies"]
                        if isinstance(poetry_deps, dict):
                            dependencies.extend(list(poetry_deps.keys()))
                        else:
                            dependencies.extend(
                                poetry_deps
                                if isinstance(poetry_deps, list)
                                else [poetry_deps]
                            )

                except Exception as e:
                    logger.warning(f"解析pyproject.toml失败: {e}")
                    pass

            # Check for setup.py
            setup_file = project_path / "setup.py"
            if setup_file.exists():
                indicators.append("setup.py")

                # Enhanced project type detection for setup.py projects
                if project_type == "未知":
                    # Check if it's a CLI tool
                    if (
                        self._is_cli_tool_project(project_name, project_path)
                        or project_name.lower() == "bumpversion"
                    ):
                        project_type = "控制台应用"
                        project_type_emoji = "⌨️"
                    else:
                        project_type = "传统项目"
                        project_type_emoji = "🔧"

                # Try to parse setup.py for dependencies
                try:
                    with open(setup_file, encoding="utf-8") as f:
                        content = f.read()
                        # Simple regex to find install_requires
                        import re

                        req_match = re.search(
                            r"install_requires\s*=\s*\[(.*?)\]", content, re.DOTALL
                        )
                        if req_match:
                            req_content = req_match.group(1)
                            # Extract package names
                            pkg_matches = re.findall(
                                r'["\']([^"\']*)["\']', req_content
                            )
                            dependencies.extend([
                                pkg
                                for pkg in pkg_matches
                                if pkg and not pkg.startswith("#")
                            ])
                except Exception:
                    pass

            # Check for requirements.txt
            requirements_file = project_path / "requirements.txt"
            if requirements_file.exists():
                indicators.append("requirements.txt")
                try:
                    with open(requirements_file, encoding="utf-8") as f:
                        for line in f:
                            line = line.strip()
                            if line and not line.startswith("#"):
                                # Extract package name (before ==, >=, etc.)
                                pkg_name = (
                                    line
                                    .split("==")[0]
                                    .split(">=")[0]
                                    .split("<=")[0]
                                    .split(">")[0]
                                    .split("<")[0]
                                )
                                if pkg_name:
                                    dependencies.append(pkg_name)
                except Exception:
                    pass

            # Check for src directory (common Python project structure)
            if (project_path / "src").exists() and (project_path / "src").is_dir():
                indicators.append("src/")

            # Check for __init__.py in root (indicates package)
            if any(
                (project_path / "__init__.py").exists()
                for py_file in project_path.glob("*.py")
            ):
                indicators.append("__init__.py")

            # Check for Python files
            python_files = list(project_path.glob("*.py"))
            if python_files:
                indicators.append(f"{len(python_files)} .py files")

            if indicators:
                return {
                    "name": project_name,
                    "path": str(project_path),
                    "indicators": indicators,
                    "type": project_type,
                    "type_emoji": project_type_emoji,
                    "dependencies": list(set(dependencies)),  # Remove duplicates
                    "python_version": python_version,
                    "entry_points": entry_points,
                    "file_count": len(python_files),
                }

        except Exception as e:
            logger.error(f"分析项目目录 {project_path} 时出错: {e}")

        return None

    def _is_cli_tool_project(self, project_name: str, project_path: Path) -> bool:
        """Determine if a project is a CLI tool based on various heuristics.

        Args:
            project_name: Name of the project
            project_path: Path to the project directory

        Returns:
            True if the project appears to be a CLI tool, False otherwise
        """
        # Known CLI projects whitelist - Direct recognition
        known_cli_projects = [
            "docdiff",  # 文档差异比较工具
            "condasetup",  # Conda环境设置工具
            "bumpversion",  # 版本升级工具
            "which",  # 命令查找工具
            "taskkill",  # 进程终止工具
            "filedate",  # 文件日期工具
            "img2pdf",  # 图片转PDF工具
            "pdfsplit",  # PDF分割工具
            "pdfcrypt",  # PDF加密工具
            "pyarchive",  # Python归档工具
            "regexvalidate",  # 正则表达式验证工具
            "makepython",  # Python项目生成工具
            "llmclient",  # LLM客户端工具
            "llmquantize",  # LLM量化工具
            "gittool",  # Git工具
            "cleanbuild",  # 构建清理工具
        ]

        if project_name.lower() in known_cli_projects:
            return True
        cli_tool_keywords = [
            "cli",
            "command",
            "tool",
            "util",
            "utility",
            "manager",
            "runner",
            "builder",
            "generator",
            "converter",
            "parser",
            "scanner",
            "analyzer",
            "processor",
            "packer",
            "installer",
            "setup",  # 特别添加setup关键词，匹配condasetup
            "diff",  # 特别添加diff关键词，匹配docdiff
            "compare",  # 文档比较相关
        ]

        # Check project name for CLI indicators
        name_lower = project_name.lower()
        if any(keyword in name_lower for keyword in cli_tool_keywords):
            return True

        # Check for common CLI tool files/directories - Enhanced
        cli_indicators = [
            project_path / "cli.py",
            project_path / "main.py",
            project_path / "__main__.py",
            project_path / "bin",
            project_path / "scripts",
            project_path / "commands",
            project_path / (project_name.replace("-", "_") + ".py"),  # 项目同名文件
        ]

        if any(indicator.exists() for indicator in cli_indicators):
            return True

        # Check for __main__.py with typical CLI structure - Enhanced
        main_file = project_path / "__main__.py"
        project_main_file = project_path / (project_name.replace("-", "_") + ".py")

        # Check both __main__.py and project-named main file
        main_files_to_check = [main_file, project_main_file]

        for main_file_path in main_files_to_check:
            if main_file_path.exists():
                try:
                    with open(main_file_path, encoding="utf-8") as f:
                        content = f.read()
                        # Look for argparse, click, typer, or fire imports
                        cli_imports = ["argparse", "click", "typer", "fire"]
                        if any(
                            f"import {imp}" in content or f"from {imp}" in content
                            for imp in cli_imports
                        ):
                            return True
                        # Look for if __name__ == '__main__' pattern
                        if 'if __name__ == "__main__"' in content:
                            return True
                        # Look for main() function call pattern
                        if "main()" in content and "def main" in content:
                            return True
                except Exception:
                    pass

        # Check for setup.py with console_scripts
        setup_file = project_path / "setup.py"
        if setup_file.exists():
            try:
                with open(setup_file, encoding="utf-8") as f:
                    content = f.read()
                    if "console_scripts" in content or "entry_points" in content:
                        return True
            except Exception:
                pass

        return False

    def _is_gui_application(self, project_name: str, project_path: Path) -> bool:
        """Determine if a project is a GUI application based on various heuristics.

        Args:
            project_name: Name of the project
            project_path: Path to the project directory

        Returns:
            True if the project appears to be a GUI application, False otherwise
        """
        # Known GUI projects whitelist - Direct recognition
        known_gui_projects = [
            "llmserver",  # LLM服务器GUI
            "lscopt",  # LSC优化器GUI (优先识别为GUI)
            "docscan",  # 文档扫描GUI
            "quizbase",  # 测验基础GUI
            "alarmclock",  # 闹钟GUI
            "regexvalidate",  # 正则验证GUI
            "lscopt_gui",  # LSC优化器GUI版本
        ]

        if project_name.lower() in known_gui_projects:
            return True

        # Check for GUI-specific files
        gui_indicators = [
            project_path / (project_name.replace("-", "_") + "_gui.py"),
            project_path / "gui.py",
            project_path / "mainwindow.py",
            project_path / "window.py",
            project_path / "interface.py",
        ]

        if any(indicator.exists() for indicator in gui_indicators):
            return True

        # Check for GUI framework imports in main files
        main_files_to_check = [
            project_path / (project_name.replace("-", "_") + ".py"),
            project_path / "__main__.py",
        ]

        gui_frameworks = ["PySide2", "PyQt5", "tkinter", "wx"]

        for main_file in main_files_to_check:
            if main_file.exists():
                try:
                    with open(main_file, encoding="utf-8") as f:
                        content = f.read()
                        # Check for GUI framework imports
                        if any(framework in content for framework in gui_frameworks):
                            # Also check for typical GUI classes
                            gui_classes = [
                                "QApplication",
                                "QMainWindow",
                                "QWidget",
                                "Tk",
                            ]
                            if any(gui_class in content for gui_class in gui_classes):
                                return True
                except Exception:
                    pass

        return False

    def _show_selected_project_details(self):
        """Show details of the selected project."""
        selected_items = self.projects_list.selectedItems()
        if not selected_items:
            self.project_details.clear()
            return

        item = selected_items[0]
        project_data = item.data(Qt.UserRole)

        if not project_data:
            self.project_details.clear()
            return

        # Format project details
        details_lines = []

        # Project name and type
        details_lines.append(f"📁 项目名称: {project_data['name']}")
        details_lines.append(
            f"{project_data.get('type_emoji', '❓')} 项目类型: {project_data.get('type', '未知')}"
        )

        # Description and version
        if project_data.get("description"):
            details_lines.append(f"📝 描述: {project_data['description']}")
        if project_data.get("version"):
            details_lines.append(f"🔢 版本: {project_data['version']}")

        # Path
        details_lines.append(f"📍 路径: {project_data['path']}")

        # Python version requirement
        python_version = project_data.get("python_version")
        if python_version:
            details_lines.append(f"🐍 Python版本: {python_version}")

        # Entry points
        entry_points = project_data.get("entry_points", [])
        gui_scripts = project_data.get("gui_scripts", [])
        all_entry_points = entry_points + gui_scripts
        if all_entry_points:
            details_lines.append(f"🚀 入口点 ({len(all_entry_points)}个):")
            for ep in all_entry_points[:5]:  # Show first 5
                details_lines.append(f"   • {ep}")
            if len(all_entry_points) > 5:
                details_lines.append(
                    f"   ... 和其他{len(all_entry_points) - 5}个入口点"
                )

        # Dependencies
        dependencies = project_data.get("dependencies", [])
        if dependencies:
            details_lines.append(f"📦 依赖库 ({len(dependencies)}个):")
            # Show first 10 dependencies, then "..." if more
            deps_to_show = dependencies[:10]
            for dep in deps_to_show:
                details_lines.append(f"   • {dep}")
            if len(dependencies) > 10:
                details_lines.append(f"   ... 和其他{len(dependencies) - 10}个依赖")
        else:
            details_lines.append("📦 依赖库: 无")

        # Qt dependencies
        if project_data.get("has_qt"):
            qt_deps = project_data.get("qt_deps", [])
            if qt_deps:
                details_lines.append(f"🎨 Qt框架: {', '.join(qt_deps)}")

        # File count
        file_count = project_data.get("file_count", 0)
        if file_count > 0:
            details_lines.append(f"📄 Python文件: {file_count}个")

        # Keywords
        keywords = project_data.get("keywords", [])
        if keywords:
            details_lines.append(f"🏷️ 关键词: {', '.join(keywords)}")

        # Indicators
        indicators = project_data.get("indicators", [])
        if indicators:
            details_lines.append(f"🔍 项目标识: {', '.join(indicators)}")

        # Set the formatted text
        self.project_details.setPlainText("\n".join(details_lines))

    def _display_projects(self, projects: list):
        """Display parsed projects in the list widget.

        Args:
            projects: List of project dictionaries to display
        """
        logger.info(
            f"_display_projects被调用，项目数量: {len(projects) if projects else 0}"
        )
        self.projects_list.clear()

        if not projects:
            placeholder_item = QListWidgetItem(translator.tr("projects_not_found"))
            placeholder_item.setFlags(Qt.NoItemFlags)
            self.projects_list.addItem(placeholder_item)
            logger.warning("项目列表为空，显示占位符")
            return

        # Add project count summary
        summary_item = QListWidgetItem(
            translator.tr("projects_count", count=len(projects))
        )
        summary_item.setFlags(Qt.NoItemFlags)
        summary_item.setBackground(Qt.lightGray)
        self.projects_list.addItem(summary_item)

        # Add each project
        for project in projects:
            display_text = f"{project.get('type_emoji', '📁')} {project['name']}"
            if project.get("indicators"):
                display_text += f" ({', '.join(project['indicators'][:3])})"  # Show first 3 indicators

            item = QListWidgetItem(display_text)
            item.setData(int(Qt.UserRole), project)  # Store complete project data
            item.setToolTip(f"路径: {project['path']}")
            self.projects_list.addItem(item)

        logger.info(f"成功显示了 {len(projects)} 个项目")

    def _show_placeholder_projects(self, message: str | None = None):
        """Show placeholder message in projects list.

        Args:
            message: Placeholder message to display
        """
        if message is None:
            message = translator.tr("projects_placeholder")
        self.projects_list.clear()
        placeholder_item = QListWidgetItem(message)
        placeholder_item.setFlags(Qt.NoItemFlags)
        self.projects_list.addItem(placeholder_item)

    # Removed create_archive_section method as archive settings are now in SettingsDialog

    def setup_connections(self):
        """Setup signal connections."""
        self.build_button.clicked.connect(self.start_build)
        self.clean_button.clicked.connect(self.clean_project)

        # Control button connections
        self.pause_button.clicked.connect(self.pause_build)
        self.resume_button.clicked.connect(self.resume_build)
        self.stop_button.clicked.connect(self.stop_build)

    def start_build(self):
        """Start build process."""
        try:
            # Collect essential parameters from UI
            params = {}
            for name, widget in self.param_widgets.items():
                value = widget.get_value()
                params[name] = value

            # Get advanced settings from config manager
            config_obj = self.config_manager.get_config()

            # Merge advanced settings
            params.update({
                "python_version": config_obj.python_version,
                "loader_type": config_obj.loader_type,
                "entry_suffix": config_obj.entry_suffix,
                "generate_loader": config_obj.generate_loader,
                "offline": config_obj.offline,
                "max_concurrent": config_obj.max_concurrent,
                "debug": config_obj.debug,
                "cache_dir": config_obj.cache_dir,
                "mirror": config_obj.mirror,
                "archive_type": config_obj.archive_type,
                "archive_format": "zip",  # System automatically determines this
            })

            # Create config using the selected directory
            import argparse

            args = argparse.Namespace(**params, command="build")
            config = ConfigFactory.create_from_args(
                args, Path(params.get("directory", Path.cwd()))
            )

            # Update UI state
            self._set_build_controls_state(building=True)
            self.log_widget.clear_log()
            self.log_widget.append_log(translator.tr("build_starting"))
            self.log_widget.append_log("=" * 50)
            self.status_label.setText(translator.tr("building_status"))
            self._set_building_style()

            # Start worker thread
            self.worker = BuildWorker(config)
            self.worker.log_signal.connect(self.log_widget.append_log)
            self.worker.finished_signal.connect(self.build_finished)
            self.worker.status_signal.connect(self._update_build_status)
            self.worker.start()

        except Exception as e:
            self.log_widget.append_log(f"{translator.tr('error_prefix')}{e!s}")
            self.status_label.setText(f"{translator.tr('error_prefix')}{e!s}")
            self._set_error_style()
            self._set_build_controls_state(building=False)

    def build_finished(self, success: bool, message: str):
        """Handle build completion."""
        self._set_build_controls_state(building=False)

        if success:
            self.log_widget.append_log(message)
            self.status_label.setText(translator.tr("build_complete_status"))
            self._set_success_style()
        else:
            self.log_widget.append_log(message)
            self.status_label.setText(translator.tr("build_failed_status"))
            self._set_error_style()

    def pause_build(self):
        """Pause the current build process."""
        if self.worker and not self.worker.is_paused():
            self.worker.pause()
            self.pause_button.setVisible(False)
            self.resume_button.setVisible(True)
            self.status_label.setText(translator.tr("build_paused"))
            self._set_building_style()
            self.log_widget.append_log("⚠️ 构建已暂停 - 点击'继续'按钮恢复构建")

    def resume_build(self):
        """Resume the paused build process."""
        if self.worker and self.worker.is_paused():
            self.worker.resume()
            self.pause_button.setVisible(True)
            self.resume_button.setVisible(False)
            self.status_label.setText(translator.tr("building_status"))
            self._set_building_style()
            self.log_widget.append_log("▶️ 构建已恢复")

    def stop_build(self):
        """Stop the current build process."""
        if self.worker:
            reply = QMessageBox.question(
                self,
                "确认停止",
                "确定要停止当前构建过程吗？此操作不可撤销。",
                QMessageBox.Yes | QMessageBox.No,
                QMessageBox.No,
            )

            if reply == QMessageBox.Yes:
                self.worker.stop()
                self._set_build_controls_state(building=False)
                self.status_label.setText(translator.tr("build_stopped"))
                self._set_error_style()
                self.log_widget.append_log("⏹️ 构建已被用户停止")
            else:
                self.log_widget.append_log("构建继续进行中")

    def _set_build_controls_state(self, building: bool):
        """Set the state of build control buttons.

        Args:
            building: True if build is in progress, False otherwise
        """
        self.build_in_progress = building

        if building:
            # Build in progress - show control buttons
            self.build_button.setEnabled(False)
            self.clean_button.setEnabled(False)
            self.pause_button.setVisible(True)
            self.resume_button.setVisible(False)
            self.stop_button.setVisible(True)
        else:
            # Build not in progress - hide control buttons
            self.build_button.setEnabled(True)
            self.clean_button.setEnabled(True)
            self.pause_button.setVisible(False)
            self.resume_button.setVisible(False)
            self.stop_button.setVisible(False)

    def _update_build_status(self, status: str):
        """Update build status display.

        Args:
            status: Status message to display
        """
        self.status_label.setText(status)
        if "paused" in status.lower():
            self._set_building_style()  # Orange for paused
        elif "stopped" in status.lower():
            self._set_error_style()
        elif "resumed" in status.lower():
            self._set_building_style()
        self.log_widget.append_log(f"Status: {status}")

    def clean_project(self):
        """Clean project build artifacts asynchronously to prevent UI freezing."""
        try:
            # Get the selected directory
            directory = self.param_widgets["directory"].get_value()

            import argparse

            args = argparse.Namespace(command="clean", debug=False)
            config = ConfigFactory.create_from_args(args, Path(directory))

            # Disable UI during cleanup
            self.build_button.setEnabled(False)
            self.clean_button.setEnabled(False)
            self.status_label.setText("清理中...")
            self._set_building_style()
            self.log_widget.append_log("开始清理构建产物...")

            # Run cleanup in background thread
            self._run_cleanup_async(config)

        except Exception as e:
            self.log_widget.append_log(f"{translator.tr('error_prefix')}{e!s}")
            self.status_label.setText(f"{translator.tr('error_prefix')}{e!s}")
            self._set_error_style()
            self.build_button.setEnabled(True)
            self.clean_button.setEnabled(True)

    def _run_cleanup_async(self, config: WorkflowConfig):
        """Run cleanup operation in background thread.

        Args:
            config: Workflow configuration for cleanup
        """

        def cleanup_worker():
            try:
                workflow = PackageWorkflow(root_dir=config.directory, config=config)
                workflow.clean_project()

                # Update UI in main thread
                self.log_widget.append_log(translator.tr("clean_success"))
                self.status_label.setText(translator.tr("clean_complete_status"))
                self._set_success_style()

            except Exception as e:
                self.log_widget.append_log(f"{translator.tr('error_prefix')}{e!s}")
                self.status_label.setText(f"{translator.tr('error_prefix')}{e!s}")
                self._set_error_style()
            finally:
                # Re-enable UI
                self.build_button.setEnabled(True)
                self.clean_button.setEnabled(True)

        # Start cleanup in separate thread
        import threading

        cleanup_thread = threading.Thread(target=cleanup_worker, daemon=True)
        cleanup_thread.start()

    def reset_parameters(self):
        """Reset parameters to defaults."""
        for name, widget in self.param_widgets.items():
            default_value = getattr(self.config_manager.DEFAULT_CONFIG, name)
            if widget.widget_type == "text":
                widget.widget.setText(str(default_value))
            elif widget.widget_type == "combo":
                index = widget.widget.findText(str(default_value))
                if index >= 0:
                    widget.widget.setCurrentIndex(index)
            elif widget.widget_type == "checkbox":
                widget.widget.setChecked(bool(default_value))

        # Reset archive settings - handled by SettingsDialog
        self.log_widget.clear_log()
        self.status_label.setText(translator.tr("ready_status"))
        self._set_ready_style()
        self.status_bar.showMessage(translator.tr("ready_status"))

    def load_settings(self):
        """Load window geometry and parameter values."""
        self.resize(self.config.window_width, self.config.window_height)
        self.move(self.config.window_x, self.config.window_y)

        # Apply saved parameter values
        for name, widget in self.param_widgets.items():
            value = getattr(self.config, name)
            if widget.widget_type == "text":
                widget.widget.setText(str(value))
            elif widget.widget_type == "combo":
                index = widget.widget.findText(str(value))
                if index >= 0:
                    widget.widget.setCurrentIndex(index)
            elif widget.widget_type == "checkbox":
                widget.widget.setChecked(bool(value))

        # Load archive settings - handled by SettingsDialog
        # archive_type is loaded/saved via SettingsDialog

    def closeEvent(self, event):  # noqa: N802
        """Handle window closing."""
        # Stop any ongoing build process
        if self.worker and self.build_in_progress:
            reply = QMessageBox.question(
                self,
                "确认退出",
                "构建正在进行中，确定要退出吗？这将停止当前构建过程。",
                QMessageBox.Yes | QMessageBox.No,
                QMessageBox.No,
            )

            if reply == QMessageBox.No:
                event.ignore()
                return
            else:
                self.worker.stop()
                # Wait a moment for cleanup
                import time

                time.sleep(0.5)

        # Save window geometry
        self.config_manager.update_config(
            window_width=self.width(),
            window_height=self.height(),
            window_x=self.x(),
            window_y=self.y(),
        )

        # Save parameter values
        params = {}
        for name, widget in self.param_widgets.items():
            params[name] = widget.get_value()
        self.config_manager.update_config(**params)

        # Save archive settings - handled by SettingsDialog
        # archive_type is loaded/saved via SettingsDialog

        # Save to file
        self.config_manager.save_config()

        super().closeEvent(event)

    def keyPressEvent(self, event):  # noqa: N802
        """Handle keyboard shortcuts."""
        if event.key() == Qt.Key_Return or event.key() == Qt.Key_Enter:
            self.start_build()
        elif event.key() == Qt.Key_Escape:
            self.reset_parameters()
        else:
            super().keyPressEvent(event)


def main():
    """Main entry point."""
    app = QApplication(sys.argv)
    app.setApplicationName("Pypack")
    app.setApplicationVersion("1.0.0")

    window = PypackGUI()
    window.show()

    sys.exit(app.exec_())


if __name__ == "__main__":
    main()
