"""Session and Message data models for multi-turn conversations."""

from __future__ import annotations

import uuid
from dataclasses import dataclass, field
from datetime import datetime, timezone
from enum import Enum
from typing import Any


class SessionStatus(str, Enum):
    """Session lifecycle states."""

    ACTIVE = "active"
    PAUSED = "paused"
    CLOSED = "closed"


class MessageRole(str, Enum):
    """Message role — mirrors OpenAI chat message roles."""

    SYSTEM = "system"
    USER = "user"
    ASSISTANT = "assistant"
    TOOL = "tool"


@dataclass
class Message:
    """A single message within a conversation session.

    Maps directly to the ``messages`` list consumed by the ReAct engine.
    """

    message_id: str
    session_id: str
    role: MessageRole
    content: str
    tool_call_id: str | None = None
    agent_name: str | None = None
    created_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
    metadata: dict[str, Any] = field(default_factory=dict)

    def to_dict(self) -> dict[str, Any]:
        return {
            "message_id": self.message_id,
            "session_id": self.session_id,
            "role": self.role.value,
            "content": self.content,
            "tool_call_id": self.tool_call_id,
            "agent_name": self.agent_name,
            "created_at": self.created_at.isoformat(),
            "metadata": self.metadata,
        }

    @classmethod
    def from_dict(cls, data: dict[str, Any]) -> Message:
        return cls(
            message_id=data["message_id"],
            session_id=data["session_id"],
            role=MessageRole(data["role"]),
            content=data["content"],
            tool_call_id=data.get("tool_call_id"),
            agent_name=data.get("agent_name"),
            created_at=datetime.fromisoformat(data["created_at"]) if data.get("created_at") else datetime.now(timezone.utc),
            metadata=data.get("metadata", {}),
        )

    def to_chat_message(self) -> dict[str, str]:
        """Convert to OpenAI-compatible chat message dict.

        Returns a dict suitable for the ``messages`` parameter of LLM chat APIs.
        """
        msg: dict[str, str] = {"role": self.role.value, "content": self.content}
        if self.tool_call_id is not None:
            msg["tool_call_id"] = self.tool_call_id
        return msg


@dataclass
class Session:
    """A conversation session binding a user to an Agent.

    Sessions track lifecycle state and accumulate Messages. They are
    persisted via :class:`SessionStore` backends.
    """

    session_id: str
    agent_name: str
    status: SessionStatus = SessionStatus.ACTIVE
    metadata: dict[str, Any] = field(default_factory=dict)
    created_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
    updated_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc))

    def to_dict(self) -> dict[str, Any]:
        return {
            "session_id": self.session_id,
            "agent_name": self.agent_name,
            "status": self.status.value,
            "metadata": self.metadata,
            "created_at": self.created_at.isoformat(),
            "updated_at": self.updated_at.isoformat(),
        }

    @classmethod
    def from_dict(cls, data: dict[str, Any]) -> Session:
        return cls(
            session_id=data["session_id"],
            agent_name=data["agent_name"],
            status=SessionStatus(data.get("status", "active")),
            metadata=data.get("metadata", {}),
            created_at=datetime.fromisoformat(data["created_at"]) if data.get("created_at") else datetime.now(timezone.utc),
            updated_at=datetime.fromisoformat(data["updated_at"]) if data.get("updated_at") else datetime.now(timezone.utc),
        )

    @staticmethod
    def new_session_id() -> str:
        """Generate a new session ID."""
        return str(uuid.uuid4())

    @staticmethod
    def new_message_id() -> str:
        """Generate a new message ID."""
        return str(uuid.uuid4())
