"""Resource manager — centralized cleanup and shutdown logic.

Tracks registered resources (executors, timers, file descriptors, etc.)
and provides safe cleanup with LIFO ordering and error handling.
"""

from __future__ import annotations

import contextlib
import logging
import threading
from collections.abc import Callable
from typing import Any

log = logging.getLogger(__name__)


class ResourceManager:
    """Manages resource cleanup and shutdown.

    Thread-safe. All operations are protected by locks.

    Example:
        >>> rm = ResourceManager()
        >>> rm.register_resource("executor", lambda: executor.shutdown(wait=True))
        >>> rm.register_resource("timer", lambda: timer.stop())
        >>> rm.cleanup_all()  # or use as context manager
    """

    def __init__(self) -> None:
        self._resources: dict[str, Callable[[], None]] = {}
        self._lock = threading.RLock()
        self._cleaned_up = False
        log.info("ResourceManager initialized")

    def register_resource(
        self,
        name: str,
        cleanup_callback: Callable[[], None],
        *,
        replace: bool = False,
    ) -> None:
        """Register a resource for cleanup.

        Args:
            name: Unique identifier for the resource.
            cleanup_callback: Callable that performs cleanup.
            replace: If True, replace existing resource with same name.
        """
        with self._lock:
            if name in self._resources and not replace:
                raise ValueError(
                    f"Resource '{name}' already registered. Use replace=True to override."
                )
            self._resources[name] = cleanup_callback
            log.debug("Resource registered: %s", name)

    def unregister_resource(self, name: str) -> bool:
        """Unregister a resource without cleaning it up."""
        with self._lock:
            if name in self._resources:
                del self._resources[name]
                log.debug("Resource unregistered: %s", name)
                return True
            return False

    def cleanup_specific(self, name: str) -> bool:
        """Cleanup a specific resource and remove it from the registry."""
        with self._lock:
            if name not in self._resources:
                log.warning("Cannot cleanup '%s': resource not found", name)
                return False
            cleanup_callback = self._resources[name]
            success = self._safe_cleanup(name, cleanup_callback)
            del self._resources[name]
            return success

    def cleanup_all(self) -> dict[str, bool]:
        """Cleanup all registered resources in reverse order (LIFO)."""
        with self._lock:
            if self._cleaned_up:
                log.warning("cleanup_all() called but already cleaned up")
                return {}

            log.info("Starting cleanup of %d resources", len(self._resources))
            results: dict[str, bool] = {}

            for name in reversed(list(self._resources.keys())):
                cleanup_callback = self._resources[name]
                success = self._safe_cleanup(name, cleanup_callback)
                results[name] = success

            self._resources.clear()
            self._cleaned_up = True

            succeeded = sum(1 for v in results.values() if v)
            failed = len(results) - succeeded
            log.info("Cleanup complete: %d succeeded, %d failed", succeeded, failed)
            return results

    def is_cleaned_up(self) -> bool:
        with self._lock:
            return self._cleaned_up

    def get_registered_resources(self) -> list[str]:
        with self._lock:
            return list(self._resources.keys())

    def __enter__(self) -> ResourceManager:
        return self

    def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
        self.cleanup_all()

    def _safe_cleanup(self, name: str, cleanup_callback: Callable[[], None]) -> bool:
        try:
            log.debug("Cleaning up resource: %s", name)
            cleanup_callback()
            log.debug("Resource cleanup successful: %s", name)
            return True
        except (OSError, RuntimeError, TimeoutError, AttributeError):
            log.exception("Resource cleanup failed: %s", name)
            return False

    def __del__(self) -> None:
        if not self._cleaned_up:
            with contextlib.suppress(Exception):
                self.cleanup_all()
