"""
Automatic Cleanup Handlers for groknroll.

Registers cleanup functions to be called on:
- Normal exit
- SIGINT (Ctrl+C)
- SIGTERM
- Uncaught exceptions

Example:
    # Register cleanup function
    register_cleanup(lambda: print("Cleaning up..."))

    # Or use as decorator
    @cleanup_handler
    async def cleanup_database():
        await db.close()

    # Cleanup will run automatically on exit
"""

import asyncio
import atexit
import signal
import sys
from typing import Callable, List, Optional


class CleanupManager:
    """
    Manages cleanup handlers for graceful shutdown.

    Ensures resources are properly released on exit, even in case of crashes.
    """

    def __init__(self):
        """Initialize cleanup manager."""
        self.handlers: List[Callable] = []
        self._signal_handlers_registered = False
        self._running = False

    def register(self, handler: Callable):
        """
        Register a cleanup handler.

        Args:
            handler: Function to call on cleanup (can be sync or async)
        """
        if handler not in self.handlers:
            self.handlers.append(handler)

            # Register signal handlers on first cleanup
            if not self._signal_handlers_registered:
                self._register_signal_handlers()
                self._signal_handlers_registered = True

    def unregister(self, handler: Callable):
        """
        Unregister a cleanup handler.

        Args:
            handler: Handler to remove
        """
        if handler in self.handlers:
            self.handlers.remove(handler)

    def _register_signal_handlers(self):
        """Register signal handlers for graceful shutdown."""
        # Register atexit handler for normal exit
        atexit.register(self._run_cleanup_sync)

        # Register SIGINT handler (Ctrl+C)
        signal.signal(signal.SIGINT, self._signal_handler)

        # Register SIGTERM handler (kill)
        signal.signal(signal.SIGTERM, self._signal_handler)

    def _signal_handler(self, signum, frame):
        """Handle termination signals."""
        print(f"\nReceived signal {signum}, cleaning up...")
        self._run_cleanup_sync()
        sys.exit(0)

    def _run_cleanup_sync(self):
        """Run cleanup handlers synchronously (for atexit/signals)."""
        if self._running:
            return  # Avoid running cleanup multiple times

        self._running = True

        for handler in reversed(self.handlers):  # LIFO order
            try:
                if asyncio.iscoroutinefunction(handler):
                    # Run async handlers in new event loop
                    loop = asyncio.new_event_loop()
                    asyncio.set_event_loop(loop)
                    try:
                        loop.run_until_complete(handler())
                    finally:
                        loop.close()
                else:
                    # Run sync handlers directly
                    handler()
            except Exception as e:
                # Don't let cleanup failures crash the process
                print(f"Error in cleanup handler: {e}")

        self._running = False

    async def cleanup(self):
        """Run cleanup handlers asynchronously."""
        if self._running:
            return

        self._running = True

        for handler in reversed(self.handlers):  # LIFO order
            try:
                if asyncio.iscoroutinefunction(handler):
                    await handler()
                else:
                    handler()
            except Exception as e:
                print(f"Error in cleanup handler: {e}")

        self._running = False

    def clear(self):
        """Clear all registered handlers."""
        self.handlers.clear()


# Global cleanup manager
_cleanup_manager: Optional[CleanupManager] = None


def get_cleanup_manager() -> CleanupManager:
    """Get or create global cleanup manager."""
    global _cleanup_manager
    if _cleanup_manager is None:
        _cleanup_manager = CleanupManager()
    return _cleanup_manager


def register_cleanup(handler: Callable):
    """
    Register a cleanup handler.

    Args:
        handler: Function to call on cleanup (can be sync or async)

    Example:
        register_cleanup(lambda: print("Cleaning up..."))

        register_cleanup(async_cleanup_function)
    """
    manager = get_cleanup_manager()
    manager.register(handler)


def unregister_cleanup(handler: Callable):
    """
    Unregister a cleanup handler.

    Args:
        handler: Handler to remove
    """
    manager = get_cleanup_manager()
    manager.unregister(handler)


def cleanup_handler(func: Callable) -> Callable:
    """
    Decorator to register a function as a cleanup handler.

    Example:
        @cleanup_handler
        def cleanup_temp_files():
            shutil.rmtree("/tmp/groknroll")

        @cleanup_handler
        async def cleanup_database():
            await db.close()
    """
    register_cleanup(func)
    return func


async def run_cleanup():
    """Manually trigger cleanup (async)."""
    manager = get_cleanup_manager()
    await manager.cleanup()


def run_cleanup_sync():
    """Manually trigger cleanup (sync)."""
    manager = get_cleanup_manager()
    manager._run_cleanup_sync()
