# 中文注释：
# 文件：echobot/session/manager.py
# 说明：会话状态与持久化管理。

"""Session management for conversation history."""

import json
from pathlib import Path
from dataclasses import dataclass, field
from datetime import datetime
from typing import Any

from loguru import logger

from echobot.utils.helpers import ensure_dir, safe_filename, truncate_string


@dataclass
class Session:
    """
    A conversation session.

    Stores messages in JSONL format for easy reading and persistence.
    """

    key: str  # channel:chat_id
    messages: list[dict[str, Any]] = field(default_factory=list)
    created_at: datetime = field(default_factory=datetime.now)
    updated_at: datetime = field(default_factory=datetime.now)
    metadata: dict[str, Any] = field(default_factory=dict)

    def add_message(self, role: str, content: str, **kwargs: Any) -> None:
        """Add a message to the session."""
        msg = {"role": role, "content": content, "timestamp": datetime.now().isoformat(), **kwargs}
        self.messages.append(msg)
        self.updated_at = datetime.now()

    def get_history(
        self, max_messages: int = 50, include_summary: bool = False
    ) -> list[dict[str, Any]]:
        """
        Get message history for LLM context.

        Args:
            max_messages: Maximum messages to return.
            include_summary: Whether to prepend compressed history summary.

        Returns:
            List of messages in LLM format.
        """
        # Get recent messages
        recent = (
            self.messages[-max_messages:] if len(self.messages) > max_messages else self.messages
        )

        # Convert to LLM format (just role and content)
        history = [{"role": m["role"], "content": m["content"]} for m in recent]

        if include_summary:
            # 摘要放在历史最前，帮助模型先快速建立全局语境。
            summary = str(self.metadata.get("history_summary", "") or "").strip()
            if summary:
                history = [{"role": "assistant", "content": f"[Conversation Summary]\n{summary}"}] + history

        return history

    def compress_history(
        self,
        max_messages: int = 40,
        keep_recent_messages: int = 16,
        summary_max_chars: int = 2000,
    ) -> bool:
        """
        Compress old history into session metadata summary.

        Returns:
            bool: True if compression changed session data.
        """
        max_messages = max(6, max_messages)
        keep_recent_messages = max(2, min(keep_recent_messages, max_messages))

        if len(self.messages) <= max_messages:
            # 未超过阈值，不做压缩。
            return False

        to_summarize = self.messages[:-keep_recent_messages]
        if not to_summarize:
            return False

        summary_lines: list[str] = []
        for msg in to_summarize:
            # 简版压缩策略：保留角色 + 截断后的单行内容。
            # 好处是稳定、便宜、无额外模型依赖。
            role = str(msg.get("role", "unknown"))
            content = " ".join(str(msg.get("content", "")).split())
            content = truncate_string(content, max_len=180)
            summary_lines.append(f"- {role}: {content}")

        generated_summary = truncate_string(
            "\n".join(summary_lines), max_len=max(500, summary_max_chars)
        )

        existing_summary = str(self.metadata.get("history_summary", "") or "").strip()
        if existing_summary:
            # 多次压缩时把历史摘要向前滚动拼接，并再次截断。
            generated_summary = truncate_string(
                f"{existing_summary}\n{generated_summary}",
                max_len=max(500, summary_max_chars),
            )

        # 写回摘要并裁剪原始消息，形成“摘要 + 最近原文”双层结构。
        self.metadata["history_summary"] = generated_summary
        self.messages = self.messages[-keep_recent_messages:]
        self.updated_at = datetime.now()
        return True

    def clear(self) -> None:
        """Clear all messages in the session."""
        self.messages = []
        self.updated_at = datetime.now()


class SessionManager:
    """
    Manages conversation sessions.

    Sessions are stored as JSONL files in the sessions directory.

    支持多实例隔离架构。可以通过 instance 参数指定要使用的实例，
    每个实例拥有独立的会话存储目录。
    """

    def __init__(self, workspace: Path, instance: str | None = None):
        """
        Initialize the session manager.

        Args:
            workspace: The workspace path for the session manager.
            instance: Optional instance name. If provided, sessions are stored
                     in the instance-specific sessions directory.
        """
        self.workspace = workspace
        self.instance = instance

        # Determine the sessions directory based on instance
        if instance:
            # Use instance-specific sessions directory
            self.sessions_dir = ensure_dir(
                Path.home() / ".echobot" / "instances" / instance / "sessions"
            )
        else:
            # Use default sessions directory (backward compatible)
            self.sessions_dir = ensure_dir(Path.home() / ".echobot" / "sessions")

        self._cache: dict[str, Session] = {}

    def _get_session_path(self, key: str) -> Path:
        """Get the file path for a session."""
        safe_key = safe_filename(key.replace(":", "_"))
        return self.sessions_dir / f"{safe_key}.jsonl"

    def get_or_create(self, key: str) -> Session:
        """
        Get an existing session or create a new one.

        Args:
            key: Session key (usually channel:chat_id).

        Returns:
            The session.
        """
        # Check cache
        if key in self._cache:
            return self._cache[key]

        # Try to load from disk
        session = self._load(key)
        if session is None:
            session = Session(key=key)

        self._cache[key] = session
        return session

    def _load(self, key: str) -> Session | None:
        """Load a session from disk."""
        path = self._get_session_path(key)

        if not path.exists():
            return None

        try:
            messages = []
            metadata = {}
            created_at = None

            with open(path) as f:
                for line in f:
                    line = line.strip()
                    if not line:
                        continue

                    data = json.loads(line)

                    if data.get("_type") == "metadata":
                        metadata = data.get("metadata", {})
                        created_at = (
                            datetime.fromisoformat(data["created_at"])
                            if data.get("created_at")
                            else None
                        )
                    else:
                        messages.append(data)

            return Session(
                key=key,
                messages=messages,
                created_at=created_at or datetime.now(),
                metadata=metadata,
            )
        except Exception as e:
            logger.warning(f"Failed to load session {key}: {e}")
            return None

    def save(self, session: Session) -> None:
        """Save a session to disk."""
        path = self._get_session_path(session.key)

        with open(path, "w") as f:
            # Write metadata first
            metadata_line = {
                "_type": "metadata",
                "created_at": session.created_at.isoformat(),
                "updated_at": session.updated_at.isoformat(),
                "metadata": session.metadata,
            }
            f.write(json.dumps(metadata_line) + "\n")

            # Write messages
            for msg in session.messages:
                f.write(json.dumps(msg) + "\n")

        self._cache[session.key] = session

    def delete(self, key: str) -> bool:
        """
        Delete a session.

        Args:
            key: Session key.

        Returns:
            True if deleted, False if not found.
        """
        # Remove from cache
        self._cache.pop(key, None)

        # Remove file
        path = self._get_session_path(key)
        if path.exists():
            path.unlink()
            return True
        return False

    def list_sessions(self) -> list[dict[str, Any]]:
        """
        List all sessions.

        Returns:
            List of session info dicts.
        """
        sessions = []

        for path in self.sessions_dir.glob("*.jsonl"):
            try:
                # Read just the metadata line
                with open(path) as f:
                    first_line = f.readline().strip()
                    if first_line:
                        data = json.loads(first_line)
                        if data.get("_type") == "metadata":
                            sessions.append(
                                {
                                    "key": path.stem.replace("_", ":"),
                                    "created_at": data.get("created_at"),
                                    "updated_at": data.get("updated_at"),
                                    "path": str(path),
                                }
                            )
            except Exception:
                continue

        return sorted(sessions, key=lambda x: x.get("updated_at", ""), reverse=True)
