import yaml
import logging
import json
import uuid
from datetime import datetime
from rpclpy.CommunicationManager import CommunicationManager
from rpclpy.KnowledgeManager import KnowledgeManager
from rpclpy.LoggingAndTracking import LoggingAndTrackingHandler

# ---------------------------------------------------------------------------
# Configuration Keys
# ---------------------------------------------------------------------------
KEY_EVENT_MANAGER_CONFIG = "Event_Manager_Config"
KEY_MESSAGE_MANAGER_CONFIG = "Message_Manager_Config"
KEY_LOGGER_CONFIG = "Logger_Config"
KEY_KNOWLEDGE_CONFIG = "Knowledge_Config"
KEY_LOG_LEVEL = "log_level"
KEY_LOG_FORMAT = "format"
KEY_PROTOCOL = "protocol"

# ---------------------------------------------------------------------------
# Default Values and Messages
# ---------------------------------------------------------------------------
DEFAULT_EVENT_MESSAGE = {}
ENCODING_UTF8 = "utf-8"

WARN_EVENT_MANAGER_NOT_SET = "Event manager is not set for Event publishing."
WARN_REGISTER_EVENT_CALLBACK_NOT_SET = "Event manager is not set for registering event callbacks."

MSG_INITIALIZING_KNOWLEDGE = "Initializing Knowledge: {} knowledge"
MSG_INITIALIZING_EVENT_MANAGER = "Initializing Event Manager"
MSG_INITIALIZING_IO_MANAGER = "Initializing IO Manager"
MSG_USING_COMMUNICATION_MANAGER = "{} is using {} Communication Manager"
MSG_STARTING = "{} is starting..."
MSG_SHUTTING_DOWN = "{} is shutting down..."
MSG_REGISTER_EVENT_CALLBACK = "Registered callback for event: {}"


class Node:
    def __init__(self, config, verbose=False):
        # self.config = self.load_config(config)
        self.config = config
        self.logger = self._initialize_logger()
        self.knowledge = self._initialize_knowledge()  # Initialize knowledge within the component
        self.event_manager = self._initialize_event_manager()
        self.message_manager = self._initialize_message_manager()

        # Initialize MQTT and ROS2 Event
        if self.event_manager:
            self.logger.info(
                MSG_USING_COMMUNICATION_MANAGER.format(
                    self.__class__.__name__,
                    self.config[KEY_EVENT_MANAGER_CONFIG][KEY_PROTOCOL]
                )
            )
        if self.message_manager:
            self.logger.info(
                MSG_USING_COMMUNICATION_MANAGER.format(
                    self.__class__.__name__,
                    self.config[KEY_MESSAGE_MANAGER_CONFIG][KEY_PROTOCOL]
                )
            )

    def load_config(self, config_file):
        with open(config_file, 'r') as file:
            return yaml.safe_load(file)

    def _initialize_logger(self):
        # Create the main logger.
        logger = logging.getLogger(self.__class__.__name__)
        logger.setLevel(self.config[KEY_LOGGER_CONFIG][KEY_LOG_LEVEL])
        # Instantiate the custom logging handler.
        custom_handler = LoggingAndTrackingHandler(self.config[KEY_LOGGER_CONFIG])
        formatter = logging.Formatter(self.config[KEY_LOGGER_CONFIG][KEY_LOG_FORMAT])
        custom_handler.setFormatter(formatter)
        logger.addHandler(custom_handler)
        return logger

    def _initialize_knowledge(self):
        """Initialize the Knowledge object based on the config."""
        self.logger.info(
            MSG_INITIALIZING_KNOWLEDGE.format(
                self.config[KEY_KNOWLEDGE_CONFIG]["knowledge_type"]
            )
        )
        return KnowledgeManager(self.config[KEY_KNOWLEDGE_CONFIG])

    def _initialize_event_manager(self):
        """Initialize the Event Manager based on the config."""
        self.logger.info(MSG_INITIALIZING_EVENT_MANAGER)
        return CommunicationManager(config=self.config[KEY_EVENT_MANAGER_CONFIG])

    def _initialize_message_manager(self):
        """Initialize the IO Manager based on the config."""
        self.logger.info(MSG_INITIALIZING_IO_MANAGER)
        return CommunicationManager(config=self.config[KEY_MESSAGE_MANAGER_CONFIG])

    def start(self):
        """Start the component and enable Event."""
        self.logger.info(MSG_STARTING.format(self.__class__.__name__))
        if self.event_manager:
            self.event_manager.start()
        if self.message_manager:
            self.message_manager.start()

    def shutdown(self):
        """Shutdown the component and stop Event."""
        self.logger.info(MSG_SHUTTING_DOWN.format(self.__class__.__name__))
        if self.event_manager:
            self.event_manager.stop()
        if self.message_manager:
            self.message_manager.stop()
            

    def publish_event(self, event_key, message=DEFAULT_EVENT_MESSAGE):
        """Publish Event using the Event manager."""
        # Generate a unique ID for the message
        message_id = str(uuid.uuid4().hex[:8])
        # Get the current timestamp in ISO 8601 format
        timestamp = datetime.now().isoformat()
        
        message['uid'] = message_id
        message['timestamp'] = timestamp
    
        if self.event_manager:
            self.event_manager.publish(event_key,  json.dumps(message))
        else:
            self.logger.warning(WARN_EVENT_MANAGER_NOT_SET)

    def publish_message(self, event_key, message=DEFAULT_EVENT_MESSAGE):
        """Publish Event using the Event manager."""
        if self.message_manager:
            self.message_manager.publish(event_key, message)
        else:
            self.logger.warning(WARN_EVENT_MANAGER_NOT_SET)

    def register_event_callback(self, event_key, callback):
        """Register a callback for Event manager events (MQTT or Redis)."""
        if self.event_manager:
            self.event_manager.subscribe(event_key, callback)
            self.logger.info(MSG_REGISTER_EVENT_CALLBACK.format(event_key))
        else:
            self.logger.warning(WARN_REGISTER_EVENT_CALLBACK_NOT_SET)

    def register_message_callback(self, event_key, callback):
        """Register a callback for Event manager events (MQTT or Redis)."""
        if self.message_manager:
            self.message_manager.subscribe(event_key, callback)
            self.logger.info(MSG_REGISTER_EVENT_CALLBACK.format(event_key))
        else:
            self.logger.warning(WARN_REGISTER_EVENT_CALLBACK_NOT_SET)

    def read_knowledge(self, key, queueSize=1):
        """Read a value from the Knowledge Manager."""
        value = self.knowledge.read(key, queueSize)
        if value is not None:
            value = value.decode(ENCODING_UTF8)  # Convert bytes to string
            try:
                value = json.loads(value)  # Try to deserialize the value if it's a JSON string
            except json.JSONDecodeError:
                pass  # If it's not JSON, return it as a string
        return value

    def write_knowledge(self, key, value):
        """Write a value to the Knowledge Manager."""
        if isinstance(key, str):
            if isinstance(value, str):
                value = str(value)
            return self.knowledge.write(key, value)
        else:
            # Convert the class instance to a dictionary
            class_dict = {}
            for key_attr, attr_value in key.__dict__.items():
                public_key = key_attr.lstrip('_')
                class_dict[public_key] = attr_value
            value = json.dumps(class_dict)  # Serialize the dictionary to a JSON string
            return self.knowledge.write(key.name, value)