"""Query classes for CQRS read operations.

Query classes represent requests for data without side effects.
They are immutable and should be named as questions (GetMcpServer, ListMcpServers).
"""

from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import Any


@dataclass(frozen=True)
class Query(ABC):
    """Base class for all queries.

    Queries are immutable and represent a request for data.
    They should be named as questions (GetMcpServer, ListMcpServers).
    """

    pass


class QueryHandler(ABC):
    """Base class for query handlers."""

    @abstractmethod
    def handle(self, query: Query) -> Any:
        """Handle the query and return result."""
        pass


@dataclass(frozen=True)
class ListMcpServersQuery(Query):
    """Query to list all mcp_servers."""

    state_filter: str | None = None  # Filter by state (cold, ready, degraded, etc.)


@dataclass(frozen=True)
class GetMcpServerQuery(Query):
    """Query to get a specific mcp_server's details."""

    mcp_server_id: str


@dataclass(frozen=True)
class GetL7PolicyQuery(Query):
    """Query for a server's attached L7 egress policy (wire form), or None.

    Added with #991: the route table only had writes, so an operator could not
    see which policy (if any) a gateway held -- while the EgressPolicySet event
    docstring claimed the rule set "is already retrievable from the server."
    """

    mcp_server_id: str


@dataclass(frozen=True)
class GetMcpServerToolsQuery(Query):
    """Query to get tools for a specific mcp_server."""

    mcp_server_id: str


@dataclass(frozen=True)
class GetMcpServerHealthQuery(Query):
    """Query to get health status of a mcp_server."""

    mcp_server_id: str


@dataclass(frozen=True)
class GetSystemMetricsQuery(Query):
    """Query to get overall system metrics."""

    pass


@dataclass(frozen=True)
class GetToolInvocationHistoryQuery(Query):
    """Query to get tool invocation history for a mcp_server.

    ``tenant_id`` confines the answer to invocations made by a caller in that
    tenant, and is set when the caller's grant is tenant-scoped. An invocation
    that names no tenant, or a different one, is not returned. ``None`` returns
    every tenant's invocations and is for a caller whose grant reaches the whole
    fleet.
    """

    mcp_server_id: str
    limit: int = 100
    from_position: int = 0
    tenant_id: str | None = None


# legacy aliases
globals().update(
    {
        "".join(("ListPro", "vidersQuery")): ListMcpServersQuery,
        "".join(("GetPro", "viderQuery")): GetMcpServerQuery,
        "".join(("GetPro", "viderToolsQuery")): GetMcpServerToolsQuery,
        "".join(("GetPro", "viderHealthQuery")): GetMcpServerHealthQuery,
    }
)
