"""Session service handles authentication and session lifecycle."""

from __future__ import annotations

from typing import TYPE_CHECKING

import grpc

if TYPE_CHECKING:
    from ..session import Session
    from .service_context import ServiceContext


class SessionService:
    """Session service for authentication and session management."""

    def __init__(self, ctx: ServiceContext):
        """Initialize session service.

        Args:
            ctx: Service context with shared dependencies
        """
        self.ctx = ctx
        self._stub = None

    def _get_stub(self):
        """Get or initialize session stub."""
        if self._stub is None:
            from ..proto import gqldb_pb2_grpc

            channel = self.ctx.get_channel()
            self._stub = gqldb_pb2_grpc.SessionServiceStub(channel)
        return self._stub

    def login(self, username: str, password: str, default_graph: str) -> Session:
        """Authenticate the user and create a session.

        Args:
            username: Username for authentication
            password: Password for authentication
            default_graph: Default graph name

        Returns:
            The created Session

        Raises:
            GqldbError: If login fails
        """
        from ..errors import GqldbError
        from ..proto import gqldb_pb2

        try:
            request = gqldb_pb2.LoginRequest(
                username=username,
                password=password,
                default_graph=default_graph
            )
            response = self._get_stub().Login(
                request,
                timeout=self.ctx.config.timeout
            )
            return self.ctx.sessions.login(
                response.session_id,
                response.server_version,
                list(response.roles),
                default_graph
            )
        except grpc.RpcError as e:
            raise GqldbError(f"Login failed: {e.details()}") from e

    def logout(self) -> None:
        """Terminate the current session.

        Raises:
            GqldbError: If logout fails
        """
        if not self.ctx.sessions.is_logged_in():
            return

        if self._stub:
            from ..proto import gqldb_pb2

            try:
                metadata = self.ctx.get_session_metadata()
                request = gqldb_pb2.LogoutRequest()
                self._stub.Logout(
                    request,
                    metadata=metadata,
                    timeout=self.ctx.config.timeout
                )
            except grpc.RpcError:
                pass  # Best effort logout

        self.ctx.tx_manager.clear_all()
        self.ctx.sessions.logout()

    def ping(self) -> int:
        """Check the connection and return the latency in nanoseconds.

        Returns:
            Latency in nanoseconds

        Raises:
            GqldbError: If ping fails
        """
        from ..errors import GqldbError
        from ..proto import gqldb_pb2

        try:
            metadata = self.ctx.get_session_metadata()
            request = gqldb_pb2.PingRequest()
            response = self._get_stub().Ping(
                request,
                metadata=metadata,
                timeout=self.ctx.config.timeout
            )
            self.ctx.update_activity()
            return response.latency_ns
        except grpc.RpcError as e:
            raise GqldbError(f"Ping failed: {e.details()}") from e
