"""Component manager for LSCSIM plugin architecture."""

from __future__ import annotations

from typing import TYPE_CHECKING, Any
from weakref import WeakValueDictionary

from typing_extensions import Self

from pytola_sim.lscsim.utils.logger import get_logger

if TYPE_CHECKING:
    from pytola_sim.lscsim.interfaces.icomponent import IComponent

logger = get_logger(__name__)


class ComponentManager:
    """Manages component lifecycle and discovery."""

    def __init__(self) -> None:
        self._components: dict[str, IComponent] = {}
        self._weak_components: WeakValueDictionary[str, IComponent] = WeakValueDictionary()
        self._component_factories: dict[str, type[IComponent]] = {}
        self._component_dependencies: dict[str, list[str]] = {}

    def register_component_type(
        self,
        component_id: str,
        component_class: type[IComponent],
    ) -> None:
        """Register a component type for dynamic creation."""
        self._component_factories[component_id] = component_class
        logger.debug(f"Registered component type: {component_id}")

    def create_component(
        self,
        component_id: str,
        **kwargs: object,
    ) -> IComponent | None:
        """Create a component instance by ID."""
        if component_id not in self._component_factories:
            logger.error(f"Component type not registered: {component_id}")
            return None

        try:
            component_class = self._component_factories[component_id]
            component = component_class(**kwargs)
            logger.info(f"Created component: {component_id}")
        except (OSError, RuntimeError, ValueError):
            logger.exception(f"Failed to create component {component_id}")
            return None
        else:
            return component

    def add_component(self, component: IComponent) -> bool:
        """Add an existing component instance."""
        component_id = component.get_id()

        if component_id in self._components:
            logger.warning(f"Component {component_id} already exists")
            return False

        self._components[component_id] = component
        self._weak_components[component_id] = component
        logger.info(f"Added component: {component_id}")
        return True

    def remove_component(self, component_id: str) -> bool:
        """Remove a component by ID."""
        component = self._components.pop(component_id, None)
        self._weak_components.pop(component_id, None)

        if component:
            logger.info(f"Removed component: {component_id}")
            return True
        logger.warning(f"Component not found: {component_id}")
        return False

    def get_component(self, component_id: str) -> IComponent | None:
        """Get component by ID."""
        component = self._components.get(component_id)
        if component is None:
            component = self._weak_components.get(component_id)
        return component

    def has_component(self, component_id: str) -> bool:
        """Check if component exists."""
        return component_id in self._components or component_id in self._weak_components

    def get_all_components(self) -> list[IComponent]:
        """Get all components."""
        return list(self._components.values())

    def get_component_ids(self) -> list[str]:
        """Get all component IDs."""
        return list(self._components.keys())

    def clear(self) -> None:
        """Clear all components."""
        # Release all components properly
        for component in self._components.values():
            self._safe_release_component(component)

        self._components.clear()
        self._weak_components.clear()
        logger.info("Component manager cleared")

    def _safe_release_component(self, component: object) -> None:
        """Safely release a component with exception handling."""
        try:
            component.release()  # type: ignore[attr-defined]
        except (OSError, RuntimeError, ValueError):
            logger.exception("Error releasing component")


class ComponentRegistry:
    """Global component registry for easy access."""

    _instance: ComponentRegistry | None = None
    _manager: ComponentManager

    def __new__(cls) -> Self:
        """Create a new instance if one doesn't exist."""
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            cls._manager = ComponentManager()
        return cls._instance

    @classmethod
    def get_instance(cls) -> ComponentRegistry:
        """Get singleton instance."""
        if cls._instance is None:
            cls._instance = cls()
        return cls._instance

    @classmethod
    def register_component_type(
        cls,
        component_id: str,
        component_class: type[IComponent],
    ) -> None:
        """Register component type globally."""
        cls.get_instance()._manager.register_component_type(
            component_id,
            component_class,
        )

    @classmethod
    def create_component(cls, component_id: str, **kwargs: Any) -> IComponent | None:  # noqa: ANN401
        """Create component globally."""
        return cls.get_instance()._manager.create_component(component_id, **kwargs)

    @classmethod
    def add_component(cls, component: IComponent) -> bool:
        """Add component globally."""
        return cls.get_instance()._manager.add_component(component)

    @classmethod
    def get_component(cls, component_id: str) -> IComponent | None:
        """Get component globally."""
        return cls.get_instance()._manager.get_component(component_id)

    @classmethod
    def has_component(cls, component_id: str) -> bool:
        """Check if component exists globally."""
        return cls.get_instance()._manager.has_component(component_id)
