"""
AsyncTkBridge - Bridge between Tkinter and asyncio
Shared module for all GUI integrations
"""

import asyncio
import threading
import tkinter as tk
from typing import Optional, Callable, Any
import logging
from concurrent.futures import Future

logger = logging.getLogger(__name__)


class AsyncTkBridge:
    """
    Bridge between Tkinter main thread and asyncio event loop
    Allows safe execution of async operations from Tkinter GUI
    """

    def __init__(self, root: tk.Tk):
        """
        Initialize the bridge

        Args:
            root: Tkinter root window
        """
        self.root = root
        self.loop: Optional[asyncio.AbstractEventLoop] = None
        self.thread: Optional[threading.Thread] = None
        self._running = False
        self._start_event = threading.Event()

        # Start the async thread
        self._start_async_thread()

    def _start_async_thread(self):
        """Start the async event loop in a separate thread"""

        def run_loop():
            # Create new event loop for this thread
            self.loop = asyncio.new_event_loop()
            asyncio.set_event_loop(self.loop)

            # Signal that loop is ready
            self._start_event.set()

            # Run the loop
            try:
                self.loop.run_forever()
            finally:
                self.loop.close()

        self.thread = threading.Thread(target=run_loop, daemon=True)
        self.thread.start()

        # Wait for loop to be ready
        self._start_event.wait(timeout=5.0)
        self._running = True

    def run_coroutine(
        self,
        coro,
        callback: Optional[Callable[[Any], None]] = None,
        error_callback: Optional[Callable[[Exception], None]] = None,
    ) -> Future:
        """
        Run a coroutine in the async thread

        Args:
            coro: Coroutine to run
            callback: Optional callback for successful result
            error_callback: Optional callback for exceptions

        Returns:
            Future object for the coroutine
        """
        if not self._running or not self.loop:
            raise RuntimeError("AsyncTkBridge is not running")

        # Schedule coroutine in async loop
        future = asyncio.run_coroutine_threadsafe(coro, self.loop)

        # Schedule periodic checks for completion
        self._check_future(future, callback, error_callback)

        return future

    def _check_future(
        self,
        future: Future,
        callback: Optional[Callable[[Any], None]] = None,
        error_callback: Optional[Callable[[Exception], None]] = None,
    ):
        """Check if future is done and handle results"""
        if future.done():
            try:
                result = future.result()
                if callback:
                    # Run callback in main thread
                    self.root.after(0, lambda: callback(result))
            except Exception as e:
                logger.error(f"Async operation failed: {e}")
                if error_callback:
                    # Run error callback in main thread
                    self.root.after(0, lambda err=e: error_callback(err))
                else:
                    # Show default error dialog
                    from tkinter import messagebox

                    self.root.after(
                        0,
                        lambda err=e: messagebox.showerror(
                            "Async Error", f"Operation failed: {str(err)}"
                        ),
                    )
        else:
            # Check again in 100ms
            self.root.after(
                100, lambda: self._check_future(future, callback, error_callback)
            )

    def create_task(self, coro) -> asyncio.Task:
        """
        Create an async task (fire and forget)

        Args:
            coro: Coroutine to run

        Returns:
            Task object
        """
        if not self._running or not self.loop:
            raise RuntimeError("AsyncTkBridge is not running")

        # Create task in the async loop
        future = asyncio.run_coroutine_threadsafe(
            self._create_task_wrapper(coro), self.loop
        )

        return future.result()

    async def _create_task_wrapper(self, coro):
        """Wrapper to create task in async context"""
        return asyncio.create_task(coro)

    def stop(self):
        """Stop the async thread"""
        if self._running and self.loop:
            self._running = False

            # Stop the event loop
            self.loop.call_soon_threadsafe(self.loop.stop)

            # Wait for thread to finish
            if self.thread:
                self.thread.join(timeout=5.0)

    def is_running(self) -> bool:
        """Check if bridge is running"""
        return self._running and self.loop is not None and self.loop.is_running()

    def call_soon(self, func: Callable, *args):
        """
        Schedule a function to be called in the async loop

        Args:
            func: Function to call
            *args: Arguments for the function
        """
        if not self._running or not self.loop:
            raise RuntimeError("AsyncTkBridge is not running")

        self.loop.call_soon_threadsafe(func, *args)

    def run_in_executor(self, func: Callable, *args) -> Future:
        """
        Run a blocking function in a thread pool

        Args:
            func: Blocking function to run
            *args: Arguments for the function

        Returns:
            Future for the result
        """
        if not self._running or not self.loop:
            raise RuntimeError("AsyncTkBridge is not running")

        # Run in default executor
        future = asyncio.run_coroutine_threadsafe(
            self.loop.run_in_executor(None, func, *args), self.loop
        )

        return future


class AsyncProgressDialog(tk.Toplevel):
    """
    Progress dialog for async operations
    """

    def __init__(
        self,
        parent: tk.Tk,
        title: str = "Processing...",
        message: str = "Please wait...",
    ):
        super().__init__(parent)

        self.title(title)
        self.geometry("400x150")
        self.resizable(False, False)

        # Make modal
        self.transient(parent)
        self.grab_set()

        # Center on parent
        self.update_idletasks()
        x = parent.winfo_x() + (parent.winfo_width() // 2) - (self.winfo_width() // 2)
        y = parent.winfo_y() + (parent.winfo_height() // 2) - (self.winfo_height() // 2)
        self.geometry(f"+{x}+{y}")

        # Create widgets
        self._create_widgets(message)

        # Progress tracking
        self._cancelled = False

    def _create_widgets(self, message: str):
        """Create dialog widgets"""
        from tkinter import ttk

        # Message label
        self.message_label = ttk.Label(self, text=message, padding=20)
        self.message_label.pack()

        # Progress bar
        self.progress_var = tk.DoubleVar()
        self.progress_bar = ttk.Progressbar(
            self, variable=self.progress_var, maximum=100, length=350
        )
        self.progress_bar.pack(padx=20, pady=(0, 10))

        # Status label
        self.status_label = ttk.Label(self, text="", foreground="gray")
        self.status_label.pack()

        # Cancel button
        self.cancel_button = ttk.Button(self, text="Cancel", command=self._on_cancel)
        self.cancel_button.pack(pady=10)

    def update_progress(self, progress: float, status: str = ""):
        """
        Update progress (0.0 to 1.0)

        Args:
            progress: Progress value (0.0 to 1.0)
            status: Optional status message
        """
        self.progress_var.set(progress * 100)
        if status:
            self.status_label.config(text=status)
        self.update_idletasks()

    def set_message(self, message: str):
        """Update the main message"""
        self.message_label.config(text=message)

    def _on_cancel(self):
        """Handle cancel button"""
        self._cancelled = True
        self.cancel_button.config(state="disabled", text="Cancelling...")

    def is_cancelled(self) -> bool:
        """Check if operation was cancelled"""
        return self._cancelled

    def close(self):
        """Close the dialog"""
        self.destroy()
