"""Multi-user session manager for MCPBridge instances."""

from __future__ import annotations

import asyncio
import time
import uuid
from typing import Any

from mcpbridge.exceptions import SessionLimitError


class SessionManager:
    """Manages a pool of `MCPBridge` instances, one per session/user.

    Thread-safe via an `asyncio.Lock`.
    """

    def __init__(self, base_config: dict[str, Any], max_sessions: int = 1000) -> None:
        self._base_config = base_config
        self._max_sessions = int(max_sessions)

        self._sessions: dict[str, Any] = {}
        self._last_access: dict[str, float] = {}

        self._lock = asyncio.Lock()
        self._cleanup_task: asyncio.Task[None] | None = None
        self._cleanup_ttl_seconds: int = 3600

    async def get_or_create(self, session_id: str) -> Any:
        """Get existing bridge for session or create a new one."""
        async with self._lock:
            if session_id in self._sessions:
                self._last_access[session_id] = time.time()
                return self._sessions[session_id]

            if len(self._sessions) >= self._max_sessions:
                raise SessionLimitError(f"Session limit reached: {self._max_sessions}")

            # Lazy import to avoid circular dependency.
            from mcpbridge.bridge import MCPBridge

            bridge = MCPBridge(self._base_config)
            await bridge.connect()

            self._sessions[session_id] = bridge
            self._last_access[session_id] = time.time()
            return bridge

    async def destroy(self, session_id: str) -> None:
        """Close and remove a session."""
        async with self._lock:
            bridge = self._sessions.pop(session_id, None)
            self._last_access.pop(session_id, None)

        if bridge is not None:
            await bridge.close()

    async def destroy_all(self) -> None:
        """Shutdown all sessions."""
        async with self._lock:
            ids = list(self._sessions.keys())
        for sid in ids:
            await self.destroy(sid)

    def active_count(self) -> int:
        """Return number of active sessions."""
        return len(self._sessions)

    async def start_cleanup_task(self, ttl_seconds: int = 3600, interval_seconds: int = 30) -> None:
        """Start a background cleanup task for idle sessions."""
        async with self._lock:
            if self._cleanup_task and not self._cleanup_task.done():
                return
            self._cleanup_ttl_seconds = int(ttl_seconds)

        async def _cleanup() -> None:
            while True:
                await asyncio.sleep(interval_seconds)
                now = time.time()
                async with self._lock:
                    stale = [
                        sid
                        for sid, last in self._last_access.items()
                        if now - last > self._cleanup_ttl_seconds
                    ]
                for sid in stale:
                    try:
                        await self.destroy(sid)
                    except Exception:
                        # Cleanup should be best-effort.
                        pass

        self._cleanup_task = asyncio.create_task(_cleanup(), name="mcpbridge-session-cleanup")

