"""Build worker for GUI operations."""

from __future__ import annotations

import asyncio
import contextlib
import threading
import time

from PySide2.QtCore import QThread, Signal

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


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) -> 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("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("Build resumed")

    def stop(self) -> None:
        """Stop the build process."""
        self._stopped = True
        self.status_signal.emit("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, "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, "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, "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, "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, "Build completed successfully!"

        except asyncio.CancelledError:
            return False, "Build stopped"
        except Exception as e:
            return False, f"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"))

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