""" — Session State Management
===================================================
Tracks conversation history within a single client lifecycle.

A session:
  - Has a unique session_id (UUID) generated at first use
  - Accumulates conversation turns in memory
  - Can be reset or seeded externally
  - Is capped at MAX_HISTORY_TURNS to prevent unbounded growth

Sessions are in-memory only. For durable sessions across process restarts,
persist session_id + history externally and pass them to Client.
"""

from __future__ import annotations

import uuid
from dataclasses import dataclass, field
from typing import Optional


MAX_HISTORY_TURNS = 20   # max turns kept in memory (older turns are dropped)


@dataclass
class Turn:
    """One conversation exchange."""
    role:    str    # "user" | "assistant"
    content: str

    def to_dict(self) -> dict[str, str]:
        return {"role": self.role, "content": self.content}


class Session:
    """
    Manages session_id and conversation history for one Client instance.

    Each call to record() appends both the user turn and the assistant
    response turn so the history stays correctly paired.

    Usage:
        session = Session()                        # fresh session
        session = Session(session_id="existing")   # resume known session
    """

    def __init__(
        self,
        session_id: Optional[str] = None,
        max_turns:  int = MAX_HISTORY_TURNS,
    ) -> None:
        self._session_id = session_id or str(uuid.uuid4())
        self._history:   list[Turn] = []
        self._max_turns  = max_turns

    # ── Properties ───────────────────────────────────────────────────────────

    @property
    def session_id(self) -> str:
        return self._session_id

    @property
    def history(self) -> list[Turn]:
        return list(self._history)

    @property
    def turn_count(self) -> int:
        """Number of individual messages (user + assistant) in history."""
        return len(self._history)

    # ── History management ────────────────────────────────────────────────────

    def record(self, user_message: str, assistant_response: str) -> None:
        """
        Append a completed exchange to history.
        Trims to max_turns if the cap is exceeded.
        """
        self._history.append(Turn(role="user",      content=user_message))
        self._history.append(Turn(role="assistant", content=assistant_response))
        # Keep only the most recent max_turns pairs (2 messages per turn)
        cap = self._max_turns * 2
        if len(self._history) > cap:
            self._history = self._history[-cap:]

    def history_as_dicts(self) -> list[dict[str, str]]:
        """Serialise history for the API request payload."""
        return [t.to_dict() for t in self._history]

    def reset(self) -> None:
        """
        Clear history and generate a fresh session_id.
        Use when starting a new conversation context.
        """
        self._history    = []
        self._session_id = str(uuid.uuid4())

    def seed(
        self,
        session_id: str,
        history: Optional[list[dict[str, str]]] = None,
    ) -> None:
        """
        Resume an existing session by setting a known session_id and
        optionally restoring prior history.

        Args:
            session_id  — previously stored session_id
            history     — list of {"role": "user"|"assistant", "content": "..."}
        """
        self._session_id = session_id
        self._history    = []
        if history:
            for item in history[-(self._max_turns * 2):]:
                role    = item.get("role", "user")
                content = item.get("content", "")
                if role in ("user", "assistant") and content:
                    self._history.append(Turn(role=role, content=content))

    def __repr__(self) -> str:
        return (
            f"Session(id={self._session_id[:8]}..., "
            f"turns={self.turn_count // 2})"
        )
