from abc import ABC
from enum import Enum
from typing import Any, Dict

from bclearer_core.objects.registers.registers import Registers


class Registries(ABC):
    """
    Base class for registry objects that manage collections of registers.
    
    A Registry acts as a container for multiple Registers, typically grouping
    related data sources (e.g., SAP registry contains workorders, notifications, etc.).
    
    A registry can contain registers of different types (e.g., PandasTableRegisters,
    GraphDatabaseRegisters, etc.) as long as they inherit from the Registers base class.
        
    Attributes:
        owning_universe: Back-reference to the parent Universe object.
        _registers: Dictionary mapping enum keys to register instances.
    """
    
    def __init__(
            self,
            owning_universe):
        super().__init__()

        self.owning_universe = \
            owning_universe
        
        self._registers: Dict[Enum, Registers] = \
            {}
        
    @property
    def registers(
            self) \
            -> Dict[Enum, Registers]:
        """
        Get all registers in this registry.
        
        :returns: Dictionary mapping enum keys to register instances.
        """
        return \
            self._registers
    
    def get_register(
            self,
            name: Enum) \
            -> Registers:
        """
        Retrieve a register by its enum name.
        
        :param name: The enum key for the register.
        :returns: The register instance.
        :raises ValueError: If register not found.
        """
        if name not in self._registers:
            raise \
                ValueError(
                    f"Register '{name}' not found in {self.__class__.__name__}")
        
        return \
            self._registers[name]
    
    def set_register(
            self,
            name: Enum,
            register: Registers) \
            -> None:
        """
        Store a register by its enum name.
        
        :param name: The enum key for the register.
        :param register: The register instance to store (can be any Registers subclass).
        """
        self._registers[name] = \
            register
        
        # Also set as attribute for direct access (e.g., registry.workorders)
        setattr(
            self,
            name.value.lower(),
            register)
    
    def has_register(
            self,
            name: Enum) \
            -> bool:
        """
        Check if a register exists by name.
        
        :param name: The enum key to check.
        :returns: True if register exists, False otherwise.
        """
        return \
            name in self._registers
    
    def get_all_register_names(
            self) \
            -> list[Enum]:
        """
        Get a list of all register names in this registry.
        
        :returns: List of enum keys for all registers.
        """
        return \
            list(self._registers.keys())
    
    def set_register_content(
            self,
            register_name: Enum,
            content: Any) \
            -> None:
        """
        Load content into a register by name.
        
        This is a convenience method that gets the register and sets its content.
        
        :param register_name: The enum key for the register.
        :param content: The content to load into the register.
        :raises ValueError: If register not found.
        """
        register = \
            self.get_register(
                name=register_name)
        
        register.set_content(
            content=content)
    
    def get_register_content(
            self,
            register_name: Enum) \
            -> Any:
        """
        Retrieve content from a register by name.
        
        This is a convenience method that gets the register and returns its content.
        
        :param register_name: The enum key for the register.
        :returns: The content stored in the register.
        :raises ValueError: If register not found or has no content.
        """
        register = \
            self.get_register(
                name=register_name)
        
        return \
            register.get_content()
    
    def export_to_disk(
            self,
            folder_path: str) \
            -> None:
        """
        Export all registers to disk.
        
        Default implementation delegates to each register's export_to_disk method.
        Override this method if custom export logic is needed.
        
        :param folder_path: The base folder path for exports.
        """
        for register in self._registers.values():
            register.export_to_disk(
                base_folder_path=folder_path)
    
    def log_registry_state(
            self) \
            -> None:
        """
        Log the state of all registers in this registry.
        
        This method can be overridden for custom logging behavior,
        but the default implementation should work for most cases.
        """
        from bclearer_core.configurations.datastructure.logging_inspection_level_b_enums import \
            LoggingInspectionLevelBEnums
        from bclearer_orchestration_services.reporting_service.reporters.inspection_message_logger import \
            log_inspection_message
        
        registry_name = \
            self.__class__.__name__
        
        log_inspection_message(
            logging_inspection_level_b_enum=LoggingInspectionLevelBEnums.INFO,
            message=f"Registry '{registry_name}' contains {len(self._registers)} register(s).")
        
        for register_name, register in self._registers.items():
            has_content = \
                register.has_content()
            
            log_inspection_message(
                logging_inspection_level_b_enum=LoggingInspectionLevelBEnums.INFO,
                message=f"  - Register '{register_name.value}': {'Has content' if has_content else 'Empty'}")
