"""Sessions resource -- CRUD, lifecycle, cleanup, and stats."""

from __future__ import annotations

import builtins
from typing import Any
from uuid import UUID

from mcpgateway_sdk._http import HTTPClient
from mcpgateway_sdk.models.session import Session


class _Unset:
    """Sentinel to distinguish 'not provided' from ``None``."""

    def __repr__(self) -> str:  # pragma: no cover
        return "<UNSET>"


_UNSET = _Unset()

_ENDPOINTS = {
    "create": ("POST", "/api/v1/sessions"),
    "list": ("GET", "/api/v1/sessions"),
    "get": ("GET", "/api/v1/sessions/{session_id}"),
    "update": ("PATCH", "/api/v1/sessions/{session_id}"),
    "delete": ("DELETE", "/api/v1/sessions/{session_id}"),
    "touch": ("POST", "/api/v1/sessions/{session_id}/touch"),
    "cleanup": ("POST", "/api/v1/sessions/cleanup"),
    "stats": ("GET", "/api/v1/sessions/stats"),
}

# Type alias to avoid shadowing builtin ``list`` inside the class.
_DictResult = dict[str, Any]


class SessionsResource:
    """Interact with sessions managed by the MCP Gateway."""

    _endpoints = _ENDPOINTS

    def __init__(self, http: HTTPClient) -> None:
        self._http = http

    # ------------------------------------------------------------------
    # CRUD
    # ------------------------------------------------------------------

    async def create(
        self,
        *,
        server_id: str | UUID | None = None,
        bundle_id: str | UUID | None = None,
        allowed_tool_names: builtins.list[str] | None = None,
        denied_tool_names: builtins.list[str] | None = None,
    ) -> Session:
        """Create a new session.

        Args:
            server_id: Optional server UUID (mutually exclusive with bundle_id).
            bundle_id: Optional bundle UUID (mutually exclusive with server_id).
            allowed_tool_names: Optional list of tool names to allow.
            denied_tool_names: Optional list of tool names to deny.

        Returns:
            The created Session model.
        """
        if server_id is not None and bundle_id is not None:
            raise ValueError("server_id and bundle_id are mutually exclusive")

        body: dict[str, Any] = {}
        if server_id is not None:
            body["server_id"] = str(server_id)
        if bundle_id is not None:
            body["bundle_id"] = str(bundle_id)
        if allowed_tool_names is not None:
            body["allowed_tool_names"] = allowed_tool_names
        if denied_tool_names is not None:
            body["denied_tool_names"] = denied_tool_names

        data = await self._http.request("POST", "/api/v1/sessions", json=body)
        return Session.model_validate(data)

    async def list(
        self,
        *,
        server_id: str | UUID | None = None,
        bundle_id: str | UUID | None = None,
        include_inactive: bool = False,
    ) -> builtins.list[Session]:
        """List sessions with optional filtering.

        The backend requires at least ``server_id`` or ``bundle_id`` to
        return data.

        Args:
            server_id: Filter by server UUID.
            bundle_id: Filter by bundle UUID.
            include_inactive: Include expired/deleted sessions.

        Returns:
            A list of Session models.
        """
        params: dict[str, Any] = {}
        if server_id is not None:
            params["server_id"] = str(server_id)
        if bundle_id is not None:
            params["bundle_id"] = str(bundle_id)
        if include_inactive:
            params["include_inactive"] = True
        data = await self._http.request("GET", "/api/v1/sessions", params=params)
        items = data.get("items", []) if isinstance(data, dict) else data
        return [Session.model_validate(item) for item in items]

    async def get(self, session_id: str | UUID) -> Session:
        """Get a single session by ID.

        Args:
            session_id: The session UUID.

        Returns:
            The Session model.
        """
        data = await self._http.request("GET", f"/api/v1/sessions/{session_id}")
        return Session.model_validate(data)

    async def update(
        self,
        session_id: str | UUID,
        allowed_tool_names: builtins.list[str] | None | _Unset = _UNSET,
        *,
        denied_tool_names: builtins.list[str] | None | _Unset = _UNSET,
    ) -> Session:
        """Update a session's tool scope.

        Args:
            session_id: The session UUID.
            allowed_tool_names: New list of allowed tool names, or None to clear.
                Omit to leave unchanged.
            denied_tool_names: New list of denied tool names, or None to clear.
                Omit to leave unchanged.

        Returns:
            The updated Session model.
        """
        body: dict[str, Any] = {}
        if allowed_tool_names is not _UNSET:
            body["allowed_tool_names"] = allowed_tool_names
        if denied_tool_names is not _UNSET:
            body["denied_tool_names"] = denied_tool_names
        data = await self._http.request("PATCH", f"/api/v1/sessions/{session_id}", json=body)
        return Session.model_validate(data)

    async def delete(self, session_id: str | UUID, *, hard_delete: bool = False) -> None:
        """Delete a session.

        Args:
            session_id: The session UUID.
            hard_delete: If True, permanently remove the session instead of
                soft-deleting it.
        """
        params = {"hard_delete": "true"} if hard_delete else None
        await self._http.request("DELETE", f"/api/v1/sessions/{session_id}", params=params)

    # ------------------------------------------------------------------
    # Lifecycle
    # ------------------------------------------------------------------

    async def touch(self, session_id: str | UUID) -> Session:
        """Refresh session activity timestamp.

        Args:
            session_id: The session UUID.

        Returns:
            The refreshed Session model.
        """
        data = await self._http.request("POST", f"/api/v1/sessions/{session_id}/touch")
        return Session.model_validate(data)

    async def cleanup(self) -> _DictResult:
        """Cleanup expired sessions.

        Returns:
            A dict with ``cleaned_count`` key.
        """
        result: _DictResult = await self._http.request("POST", "/api/v1/sessions/cleanup")
        return result

    # ------------------------------------------------------------------
    # Stats
    # ------------------------------------------------------------------

    async def stats(self) -> _DictResult:
        """Get global session statistics (admin endpoint).

        Returns:
            Session stats dict with keys like ``total_active``,
            ``server_sessions``, ``bundle_sessions``, etc.
        """
        result: _DictResult = await self._http.request("GET", "/api/v1/sessions/stats")
        return result
