"""Graphical user interface for pypack."""

from __future__ import annotations

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

import PySide2
from PySide2.QtCore import Qt, QThread, Signal
from PySide2.QtWidgets import (
    QApplication,
    QFileDialog,
    QGroupBox,
    QHBoxLayout,
    QLabel,
    QLineEdit,
    QListWidget,
    QListWidgetItem,
    QMainWindow,
    QMessageBox,
    QPushButton,
    QTextEdit,
    QVBoxLayout,
    QWidget,
)

# Type-specific imports for better type checking
if TYPE_CHECKING:
    from ..core.config import ConfigFactory, WorkflowConfig
    from ..core.workflow import PackageWorkflow

# Import from pypack core modules
from pytola.core.logger import get_logger

from ..core.config import ConfigFactory, WorkflowConfig
from ..core.workflow import PackageWorkflow

logger = get_logger(__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

# Style constants for consistent UI design
_UI_CONSTANTS = {
    "BUTTON_HEIGHT": 32,
    "INPUT_HEIGHT": 32,
    "COMPACT_SPACING": 4,
    "TIGHT_SPACING": 2,
    "MIN_MARGIN": 2,
    "SMALL_MARGIN": 8,
    "MEDIUM_MARGIN": 12,
    "LARGE_MARGIN": 16,
    "BUTTON_WIDTH_LARGE": 120,
    "BUTTON_WIDTH_MEDIUM": 100,
    "BUTTON_WIDTH_SMALL": 80,
    "INPUT_WIDTH_MIN": 280,
    "RESULT_WIDTH": 500,
    "RESULT_HEIGHT": 150,
    "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",
    "PRIMARY_BLUE": "#3b82f6",
    "PRIMARY_DARK": "#1d4ed8",
    "BORDER_COLOR": "#cbd5e1",
    "BACKGROUND_LIGHT": "#ffffff",
    "BACKGROUND_CARD": "#f8fafc",
    "TEXT_PRIMARY": "#0f172a",
    "TEXT_SECONDARY": "#64748b",
}


# Layout factory methods for consistent configuration
class LayoutFactory:
    """Factory class for creating consistent UI layouts and configurations."""

    @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


# Simple translation system
class DummyTranslator:
    """Minimal fallback translator for missing translations."""

    def tr(self, key, **kwargs):
        """Translate a string key to current language."""
        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_directory": "Project Directory",
            "projects_title": "Projects",
            "projects_placeholder": "No projects found",
            "projects_count": "Found {count} projects",
            "projects_not_found": "No projects found",
            "directory_not_exist": "Directory does not exist",
            "parse_error": "Failed to parse projects: {error}",
            "project_details_placeholder": "Select a project to view details",
            "pause_button": "Pause",
            "resume_button": "Resume",
            "stop_button": "Stop",
            "build_paused": "Build paused",
            "build_resumed": "Build resumed",
            "build_stopped": "Build stopped",
        }
        return translations.get(key, key)


translator = DummyTranslator()


@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() / ".pytola" / "pypack.json"
    DEFAULT_CONFIG: ClassVar[PypackConfig] = PypackConfig()

    def __init__(self, config_file: Path | None = 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:
        """Load configuration from a JSON file."""
        if not self.config_file.exists():
            return self.DEFAULT_CONFIG

        try:
            with Path(self.config_file).open(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:
        """Save configuration to a JSON file."""
        try:
            config_dict = {
                field_name: getattr(self.config, field_name) for field_name in self.config.__dataclass_fields__
            }
            with Path(self.config_file).open("w", encoding="utf-8") as f:
                json.dump(config_dict, f, indent=4)
        except Exception as e:
            logger.exception(f"Failed to save config: {e}")

    def get_config(self) -> PypackConfig:
        """Get the current configuration."""
        return self.config

    def update_config(self, **kwargs) -> None:
        """Update configuration with key-value pairs."""
        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

    def __init__(self, config: WorkflowConfig, parent=None) -> 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) -> None:
        """Pause the build process."""
        self._paused = True
        self.status_signal.emit(translator.tr("build_paused"))

    def resume(self) -> None:
        """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) -> None:
        """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) -> None:
        """Check if we should pause and wait if necessary."""
        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) -> None:
        """Run a single task with frequent pause checks."""
        # 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) -> None:
        """Run multiple tasks in parallel with pause support."""
        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) -> None:
        """Run the build process with support for pause, resume, and stop."""
        try:
            import asyncio
            import logging

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

                def emit(self, record) -> None:
                    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"))

            # 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}")


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

    def __init__(self) -> None:
        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) -> None:
        """Initialize user interface."""
        self.setWindowTitle(translator.tr("window_title"))
        self.setMinimumSize(1200, 800)

        # Central widget
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        main_layout = QHBoxLayout(central_widget)

        # Left panel - configuration
        left_panel = QGroupBox(translator.tr("config_panel"))
        left_layout = QVBoxLayout(left_panel)

        # Directory selection
        dir_layout = QHBoxLayout()
        dir_layout.addWidget(QLabel("Project Directory:"))
        self.dir_edit = QLineEdit(str(Path.cwd()))
        dir_layout.addWidget(self.dir_edit)
        browse_button = QPushButton("Browse...")
        browse_button.clicked.connect(self._browse_directory)
        dir_layout.addWidget(browse_button)
        left_layout.addLayout(dir_layout)

        # Projects list
        left_layout.addWidget(QLabel(translator.tr("projects_title")))
        self.projects_list = QListWidget()
        self.projects_list.setMinimumHeight(200)
        left_layout.addWidget(self.projects_list)

        # Project details
        self.project_details = QTextEdit()
        self.project_details.setMaximumHeight(150)
        self.project_details.setReadOnly(True)
        left_layout.addWidget(self.project_details)

        # Action buttons
        self.build_button = QPushButton(translator.tr("build_button"))
        self.build_button.clicked.connect(self.start_build)
        left_layout.addWidget(self.build_button)

        self.clean_button = QPushButton(translator.tr("clean_button"))
        self.clean_button.clicked.connect(self.clean_project)
        left_layout.addWidget(self.clean_button)

        # Control buttons (initially hidden)
        control_layout = QHBoxLayout()
        self.pause_button = QPushButton(translator.tr("pause_button"))
        self.pause_button.clicked.connect(self.pause_build)
        self.pause_button.setVisible(False)
        control_layout.addWidget(self.pause_button)

        self.resume_button = QPushButton(translator.tr("resume_button"))
        self.resume_button.clicked.connect(self.resume_build)
        self.resume_button.setVisible(False)
        control_layout.addWidget(self.resume_button)

        self.stop_button = QPushButton(translator.tr("stop_button"))
        self.stop_button.clicked.connect(self.stop_build)
        self.stop_button.setVisible(False)
        control_layout.addWidget(self.stop_button)

        left_layout.addLayout(control_layout)

        # Right panel - log display
        right_panel = QGroupBox(translator.tr("log_panel"))
        right_layout = QVBoxLayout(right_panel)
        self.log_text = QTextEdit()
        self.log_text.setReadOnly(True)
        right_layout.addWidget(self.log_text)

        # Clear log button
        clear_button = QPushButton("Clear Log")
        clear_button.clicked.connect(self.log_text.clear)
        right_layout.addWidget(clear_button)

        # Add panels to main layout
        main_layout.addWidget(left_panel, 1)
        main_layout.addWidget(right_panel, 2)

        # Status bar
        self.status_bar = self.statusBar()
        self.status_label = QLabel(translator.tr("ready_status"))
        self.status_bar.addPermanentWidget(self.status_label)

        # Connect directory change to project parsing
        self.dir_edit.textChanged.connect(self._on_directory_changed)

    def _browse_directory(self) -> None:
        """Open directory selection dialog."""
        current_path = self.dir_edit.text()
        directory = QFileDialog.getExistingDirectory(
            self,
            "Select Project Directory",
            current_path,
            QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks,
        )
        if directory:
            self.dir_edit.setText(directory)

    def _on_directory_changed(self, directory_path: str) -> None:
        """Handle directory change and parse projects."""
        if not directory_path.strip():
            self._show_placeholder_projects()
            return

        try:
            from pathlib import Path

            from ..models.solution import Solution

            dir_path = Path(directory_path)
            if not dir_path.exists() or not dir_path.is_dir():
                self._show_placeholder_projects(translator.tr("directory_not_exist"))
                return

            # Parse projects using pyprojectparse
            solution = Solution.from_directory(dir_path)
            projects_info = solution.get_projects_info()

            # Display projects
            self.projects_list.clear()
            if not projects_info:
                self._show_placeholder_projects(translator.tr("projects_not_found"))
                return

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

            # Add each project
            for project_info in projects_info:
                display_text = f"📁 {project_info['name']}"
                item = QListWidgetItem(display_text)
                item.setData(Qt.UserRole, project_info)
                self.projects_list.addItem(item)

        except Exception as e:
            logger.exception(f"Error parsing projects: {e}")
            self._show_placeholder_projects(translator.tr("parse_error", error=str(e)))

    def _show_placeholder_projects(self, message: str | None = None) -> None:
        """Show placeholder message in projects list."""
        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)

    def setup_connections(self) -> None:
        """Set up signal connections."""
        self.projects_list.itemSelectionChanged.connect(self._show_project_details)

    def _show_project_details(self) -> None:
        """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 = [
            f"📁 Project Name: {project_data['name']}",
            f"📍 Path: {project_data['path']}",
        ]

        if project_data.get("description"):
            details_lines.append(f"📝 Description: {project_data['description']}")
        if project_data.get("version"):
            details_lines.append(f"🔢 Version: {project_data['version']}")

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

    def start_build(self) -> None:
        """Start build process."""
        try:
            # Get configuration from UI
            directory = self.dir_edit.text()
            if not directory:
                self.log_text.append("Error: Please select a project directory")
                return

            # Create workflow config
            import argparse

            args = argparse.Namespace(
                directory=directory,
                command="build",
                python_version="3.8.10",
                loader_type="console",
                entry_suffix=".ent",
                generate_loader=True,
                offline=False,
                jobs=4,
                debug=False,
                cache_dir=None,
                archive_format="zip",
                mirror="aliyun",
                archive=None,
            )

            config = ConfigFactory.create_from_args(args, Path(directory))

            # Update UI state
            self._set_build_controls_state(building=True)
            self.log_text.clear()
            self.log_text.append(translator.tr("build_starting"))
            self.log_text.append("=" * 50)
            self.status_label.setText(translator.tr("building_status"))

            # Start worker thread
            self.worker = BuildWorker(config)
            self.worker.log_signal.connect(self.log_text.append)
            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_text.append(f"{translator.tr('error_prefix')}{e!s}")
            self.status_label.setText(f"{translator.tr('error_prefix')}{e!s}")
            self._set_build_controls_state(building=False)

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

        if success:
            self.log_text.append(message)
            self.status_label.setText(translator.tr("build_complete_status"))
        else:
            self.log_text.append(message)
            self.status_label.setText(translator.tr("build_failed_status"))

    def pause_build(self) -> None:
        """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.log_text.append("WARNING: Build paused - click 'Resume' to continue")

    def resume_build(self) -> None:
        """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.log_text.append("INFO: Build resumed")

    def stop_build(self) -> None:
        """Stop the current build process."""
        if self.worker:
            reply = QMessageBox.question(
                self,
                "Confirm Stop",
                "Are you sure you want to stop the current build process?",
                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.log_text.append("STOPPED: Build stopped by user")

    def _set_build_controls_state(self, building: bool) -> None:
        """Set the state of build control buttons."""
        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) -> None:
        """Update build status display."""
        self.status_label.setText(status)
        self.log_text.append(f"Status: {status}")

    def clean_project(self) -> None:
        """Clean project build artifacts."""
        try:
            directory = self.dir_edit.text()
            if not directory:
                self.log_text.append("Error: Please select a project directory")
                return

            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("Cleaning...")
            self.log_text.append("Starting cleanup...")

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

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

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

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

                # Update UI in main thread
                self.log_text.append(translator.tr("clean_success"))
                self.status_label.setText(translator.tr("clean_complete_status"))

            except Exception as e:
                self.log_text.append(f"{translator.tr('error_prefix')}{e!s}")
                self.status_label.setText(f"{translator.tr('error_prefix')}{e!s}")
            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 load_settings(self) -> None:
        """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)
        self.dir_edit.setText(self.config.directory)

    def closeEvent(self, event) -> None:
        """Handle window closing."""
        # Stop any ongoing build process
        if self.worker and self.build_in_progress:
            reply = QMessageBox.question(
                self,
                "Confirm Exit",
                "Build is in progress. Are you sure you want to exit?",
                QMessageBox.Yes | QMessageBox.No,
                QMessageBox.No,
            )

            if reply == QMessageBox.No:
                event.ignore()
                return
            self.worker.stop()

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

        super().closeEvent(event)


def main() -> None:
    """Run entry point for the command-line interface.

    Command-line interface for gui

    Examples
    --------
      main [options] <arguments>
    """
    app = QApplication(sys.argv)
    app.setApplicationName("Pypack")
    app.setApplicationVersion("1.0.0")

    window = PypackGUI()
    window.show()

    sys.exit(app.exec_())


if __name__ == "__main__":
    main()
