from __future__ import annotations

from datetime import datetime
from typing import Dict, Optional, Union

from synthetic_agents.database.entity.chat_memory import ChatMemory as DBChatMemory
from synthetic_agents.database.entity.memory import Memory as DBMemory
from synthetic_agents.model.entity.memory import Memory

CHAT_MEMORY_TYPE = "chat"


class ChatMemory(Memory):
    """
    This class represents a chat memory.
    """

    def __init__(
        self,
        chat_id: int,
        session_id: str,
        creation_timestamp: datetime,
        content: str,
        memory_id: Optional[int] = None,
        similarity_score: float = 0,
        relevance_score: float = 0,
    ):
        """
        Creates a chat memory.

        :param chat_id: the ID of the chat where the memory was produced.
        :param session_id: the ID of the chat session where the memory was produced.
        :param creation_timestamp: when the memory was created.
        :param content: textual content of the memory.
        :param memory_id: ID of the memory. It is optional because not persisted memories do not
            have an ID yet.
        :param similarity_score: similarity score between the memory and query used to retrieve the
            memory.
        :param relevance_score: score attributed to relevance during the memory ranking phase.
        :raise: ValueError: raised if the category if not a valid one.
        """
        super().__init__(
            named_type=CHAT_MEMORY_TYPE,
            creation_timestamp=creation_timestamp,
            content=content,
            memory_id=memory_id,
            similarity_score=similarity_score,
            relevance_score=relevance_score,
        )

        self.chat_id = chat_id
        self.session_id = session_id

    @property
    def metadata(self) -> Dict[str, Union[str, int, datetime, Dict[str, float]]]:
        metadata = super().metadata
        metadata["application_id"] = self.chat_id
        metadata["session_id"] = self.session_id
        return metadata

    def to_persistent_object(self, agent_id: int) -> DBMemory:
        """
        Gets a relational DB representation of the memory for persistence.

        :param agent_id: id of the agent associated with the memory.
        :return: a persistent memory object.
        """

        return DBChatMemory(
            memory_id=self.memory_id,
            agent_id=agent_id,
            creation_timestamp=self.creation_timestamp,
            content=self.content,
            chat_id=self.chat_id,
            session_id=self.session_id,
        )

    @classmethod
    def from_persistent_object(cls, db_memory: DBChatMemory) -> ChatMemory:
        """
        Gets a chat memory object from its relational DB representation.

        :param db_memory: relational DB instance of a chat memory.
        :return: a chat memory object.
        """

        return ChatMemory(
            memory_id=db_memory.memory_id,
            creation_timestamp=db_memory.creation_timestamp,
            content=db_memory.content,
            chat_id=db_memory.chat_id,
            session_id=db_memory.session_id,
        )
