from abc import ABC, abstractmethod
from typing import Generic, TypeVar

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

T = \
    TypeVar('T')

class Registers(ABC, Generic[T]):
    """
    Generic base class for register objects that store typed content.
    
    A Register is the lowest level in the hierarchy, storing a single piece
    of content (e.g., a DataFrame, a graph database, etc.).
    
    Type Parameters:
        T: The type of content this register will store.
        
    Attributes:
        owning_registry: Back-reference to the parent Registry object.
        name: The name/identifier for this register.
        _content: The stored content of type T.
    """
    
    def __init__(
            self,
            owning_registry,
            name: str,
            content: T | None = None):
        super().__init__()

        self.owning_registry = \
            owning_registry
        
        self.name = \
            name
        
        self._content: T | None = \
            content
        
        self._content_type: type = \
            T

    def has_content(
            self)\
            -> bool:
        """
        Check if this register contains content.
        
        :returns: True if content is set, False otherwise.
        """
        return \
            self._content is not None

    @abstractmethod
    def get_model(self):
        """
        Get metadata/schema information about the content.
        
        This should return a dictionary or object describing the structure
        of the content without returning the content itself.
        
        :returns: Metadata about the content.
        :raises NotImplementedError: Must be implemented by subclasses.
        """
        raise \
            NotImplementedError()

    def get_content(
            self)\
            -> T:
        """
        Retrieve the content from this register.
        
        :returns: The stored content of type T.
        :raises ValueError: If content is not set.
        """
        if self.has_content() is False:
            raise \
                ValueError(
                    f"Content for register '{self.name}' is not set.")
        
        # COMMENT: This is here so there is no typing error. get_content does not understand
        # that has_content ensures _content is not None.
        assert \
            self._content is not None

        return \
            self._content
    
    def get_content_type(
            self) \
            -> type:
        """
        Get the type of content this register stores.
        :returns: The type of the content.
        """
        return \
            self._content_type

    def set_content(
        self, 
        content: T):
        """
        Store content in this register.
        
        If content already exists, it will be overwritten with a warning.
        
        :param content: The content to store.
        """
        if self.has_content():
            log_inspection_message(
                message=f"Warning: Overwriting content for register '{self.name}'.",
                logging_inspection_level_b_enum=LoggingInspectionLevelBEnums.WARNING)
            
        self._content = \
            content

    @abstractmethod
    def export_to_disk(
            self,
            base_folder_path: str):
        """
        Export the register's content to disk.
        
        :param base_folder_path: The base folder path for export.
        :raises NotImplementedError: Must be implemented by subclasses.
        """
        raise \
            NotImplementedError()
    
    def log_register_state(
            self) \
            -> None:
        """
        Log the state of this register.
        
        Default implementation logs basic information about the register.
        Override this method for custom logging behavior specific to content type.
        """
        has_content = \
            self.has_content()
        
        status = \
            "has content" if has_content else "is empty"
        
        log_inspection_message(
            logging_inspection_level_b_enum=LoggingInspectionLevelBEnums.INFO,
            message=f"Register '{self.name}' {status}.")