"""BLCE Conversation Store — trace persistence for agent conversations.

Stores ``AgentConversation`` objects in-memory with optional file
persistence to ``data/blce_runs/{conv_id}.json``.
"""
from __future__ import annotations

import json
import logging
import time
from pathlib import Path
from typing import Any, Dict, List, Optional

from .contracts import AgentConversation

logger = logging.getLogger(__name__)


class ConversationStore:
    """In-memory conversation store with optional file persistence.

    Parameters
    ----------
    store_dir : str
        Directory for persisted conversation JSON files.
    """

    def __init__(self, store_dir: str = "data/blce_runs"):
        self.store_dir = store_dir
        self._conversations: Dict[str, AgentConversation] = {}
        self._start_times: Dict[str, float] = {}

    # -- lifecycle ---------------------------------------------------------

    def start_conversation(
        self,
        agent_name: str,
        provider: str = "deterministic",
    ) -> str:
        """Start a new conversation and return its ID."""
        conv = AgentConversation(
            agent_name=agent_name,
            provider=provider,
        )
        self._conversations[conv.conversation_id] = conv
        self._start_times[conv.conversation_id] = time.monotonic()
        return conv.conversation_id

    def add_turn(
        self,
        conversation_id: str,
        role: str,
        content: str,
        metadata: Optional[Dict[str, Any]] = None,
    ) -> None:
        """Append a turn to an existing conversation."""
        conv = self._conversations.get(conversation_id)
        if conv is None:
            logger.warning("Conversation %s not found", conversation_id)
            return

        turn: Dict[str, Any] = {
            "role": role,
            "content": content,
        }
        if metadata:
            turn["metadata"] = metadata
        conv.turns.append(turn)

    def end_conversation(
        self,
        conversation_id: str,
        success: bool = True,
        error: Optional[str] = None,
    ) -> None:
        """Finalize a conversation with duration and status."""
        conv = self._conversations.get(conversation_id)
        if conv is None:
            logger.warning("Conversation %s not found", conversation_id)
            return

        conv.success = success
        conv.error = error
        start = self._start_times.get(conversation_id)
        if start is not None:
            conv.duration_seconds = round(time.monotonic() - start, 4)

    # -- queries -----------------------------------------------------------

    def get_conversation(self, conversation_id: str) -> Optional[AgentConversation]:
        """Return a conversation by ID, or ``None``."""
        return self._conversations.get(conversation_id)

    def list_conversations(
        self,
        agent_name: Optional[str] = None,
    ) -> List[AgentConversation]:
        """List conversations, optionally filtered by agent name."""
        convs = list(self._conversations.values())
        if agent_name:
            convs = [c for c in convs if c.agent_name == agent_name]
        return convs

    # -- persistence -------------------------------------------------------

    def save_to_file(self, conversation_id: str) -> str:
        """Persist a conversation to ``{store_dir}/{conv_id}.json``.

        Returns the file path.
        """
        conv = self._conversations.get(conversation_id)
        if conv is None:
            raise ValueError(f"Conversation {conversation_id} not found")

        out_dir = Path(self.store_dir)
        out_dir.mkdir(parents=True, exist_ok=True)
        path = out_dir / f"{conversation_id}.json"
        path.write_text(
            json.dumps(conv.model_dump(), indent=2, default=str),
            encoding="utf-8",
        )
        return str(path)

    @staticmethod
    def load_from_file(path: str) -> AgentConversation:
        """Load a conversation from a JSON file."""
        p = Path(path)
        if not p.exists():
            raise FileNotFoundError(f"Conversation file not found: {path}")
        data = json.loads(p.read_text(encoding="utf-8"))
        return AgentConversation(**data)
