"""Admin service handles administrative operations."""

from __future__ import annotations

from typing import TYPE_CHECKING

import grpc

if TYPE_CHECKING:
    from ..types import CacheStats, CacheType, Statistics
    from .service_context import ServiceContext


class AdminService:
    """Admin service for administrative operations."""

    def __init__(self, ctx: ServiceContext):
        self.ctx = ctx
        self._stub = None

    def _get_stub(self):
        if self._stub is None:
            from ..proto import gqldb_pb2_grpc
            channel = self.ctx.get_channel()
            self._stub = gqldb_pb2_grpc.AdminServiceStub(channel)
        return self._stub

    def warmup_parser(self, count: int) -> None:
        from ..errors import GqldbError
        from ..proto import gqldb_pb2

        try:
            request = gqldb_pb2.WarmupParserRequest(count=count)
            metadata = self.ctx.get_session_metadata()
            response = self._get_stub().WarmupParser(
                request, metadata=metadata, timeout=self.ctx.config.timeout
            )
            if not response.success:
                raise GqldbError(response.message)
        except grpc.RpcError as e:
            raise GqldbError(f"Warmup parser failed: {e.details()}") from e

    def get_cache_stats(self, cache_type: CacheType) -> CacheStats:
        from ..errors import GqldbError
        from ..proto import gqldb_pb2
        from ..types import ASTCacheStats, CacheStats, PlanCacheStats

        try:
            request = gqldb_pb2.GetCacheStatsRequest(cache_type=cache_type.value)
            metadata = self.ctx.get_session_metadata()
            response = self._get_stub().GetCacheStats(
                request, metadata=metadata, timeout=self.ctx.config.timeout
            )

            ast_stats = None
            plan_stats = None

            if response.ast_stats:
                ast_stats = ASTCacheStats(
                    hits=response.ast_stats.hits,
                    misses=response.ast_stats.misses,
                    evictions=response.ast_stats.evictions,
                    entries=response.ast_stats.entries,
                    hit_rate=response.ast_stats.hit_rate
                )

            if response.plan_stats:
                plan_stats = PlanCacheStats(
                    size=response.plan_stats.size,
                    capacity=response.plan_stats.capacity,
                    hits=response.plan_stats.hits,
                    misses=response.plan_stats.misses,
                    hit_rate=response.plan_stats.hit_rate
                )

            return CacheStats(ast_stats=ast_stats, plan_stats=plan_stats)
        except grpc.RpcError as e:
            raise GqldbError(f"Get cache stats failed: {e.details()}") from e

    def clear_cache(self, cache_type: CacheType) -> None:
        from ..errors import GqldbError
        from ..proto import gqldb_pb2

        try:
            request = gqldb_pb2.ClearCacheRequest(cache_type=cache_type.value)
            metadata = self.ctx.get_session_metadata()
            response = self._get_stub().ClearCache(
                request, metadata=metadata, timeout=self.ctx.config.timeout
            )
            if not response.success:
                raise GqldbError(response.message)
        except grpc.RpcError as e:
            raise GqldbError(f"Clear cache failed: {e.details()}") from e

    def get_statistics(self, graph_name: str) -> Statistics:
        from ..errors import GqldbError
        from ..proto import gqldb_pb2
        from ..types import Statistics

        try:
            request = gqldb_pb2.GetStatisticsRequest(graph_name=graph_name)
            metadata = self.ctx.get_session_metadata()
            response = self._get_stub().GetStatistics(
                request, metadata=metadata, timeout=self.ctx.config.timeout
            )

            return Statistics(
                node_count=response.node_count,
                edge_count=response.edge_count,
                label_counts=dict(response.label_counts),
                edge_label_counts=dict(response.edge_label_counts)
            )
        except grpc.RpcError as e:
            raise GqldbError(f"Get statistics failed: {e.details()}") from e

    def invalidate_permission_cache(self, username: str) -> None:
        from ..errors import GqldbError
        from ..proto import gqldb_pb2

        try:
            request = gqldb_pb2.InvalidatePermissionCacheRequest(username=username)
            metadata = self.ctx.get_session_metadata()
            response = self._get_stub().InvalidatePermissionCache(
                request, metadata=metadata, timeout=self.ctx.config.timeout
            )
            if not response.success:
                raise GqldbError(response.message)
        except grpc.RpcError as e:
            raise GqldbError(f"Invalidate permission cache failed: {e.details()}") from e

    def compact(self) -> "CompactResult":
        """Trigger manual compaction of the database storage.

        Returns:
            CompactResult with success status and message.

        Raises:
            GqldbError: If compact fails.
        """
        from ..errors import GqldbError
        from ..proto import gqldb_pb2
        from ..types import CompactResult

        try:
            request = gqldb_pb2.CompactRequest()
            metadata = self.ctx.get_session_metadata()
            response = self._get_stub().Compact(
                request, metadata=metadata, timeout=self.ctx.config.timeout
            )
            return CompactResult(
                success=response.success,
                message=response.message
            )
        except grpc.RpcError as e:
            raise GqldbError(f"Compact failed: {e.details()}") from e

    def wait_for_compute_topology(self, graph_name: str, timeout_ms: int = 0) -> "ComputeTopologyResult":
        """Wait for the computing engine topology to be ready.

        Args:
            graph_name: Graph to check.
            timeout_ms: Timeout in milliseconds (0 = check current status only).

        Returns:
            ComputeTopologyResult with ready status and message.

        Raises:
            GqldbError: If the call fails.
        """
        from ..errors import GqldbError
        from ..proto import gqldb_pb2
        from ..types import ComputeTopologyResult

        try:
            request = gqldb_pb2.WaitForComputeTopologyRequest(
                graph_name=graph_name,
                timeout_ms=timeout_ms
            )
            metadata = self.ctx.get_session_metadata()
            response = self._get_stub().WaitForComputeTopology(
                request, metadata=metadata, timeout=self.ctx.config.timeout
            )
            return ComputeTopologyResult(
                ready=response.ready,
                message=response.message
            )
        except grpc.RpcError as e:
            raise GqldbError(f"Wait for compute topology failed: {e.details()}") from e
