"""McpServer group endpoint handlers for the REST API.

Implements GET/POST endpoints for mcp_server group operations,
reading directly from the application context groups dict.
"""

from starlette.concurrency import run_in_threadpool
from starlette.requests import Request
from starlette.routing import Route

from ...application.commands.crud_commands import (
    AddGroupMemberCommand,
    CreateGroupCommand,
    DeleteGroupCommand,
    RemoveGroupMemberCommand,
    UpdateGroupCommand,
)
from ...domain.exceptions import McpServerNotFoundError
from ..context import get_context
from .middleware import dispatch_command
from .serializers import HangarJSONResponse
from .request_body import missing_fields


async def list_groups(request: Request) -> HangarJSONResponse:
    """List all mcp_server groups with summary info.

    Returns:
        JSON with {"groups": [...]} array of group summaries.
    """
    ctx = get_context()
    groups = ctx.groups
    return HangarJSONResponse({"groups": [g.to_status_dict() for g in groups.values()]})


async def get_group(request: Request) -> HangarJSONResponse:
    """Get detailed info for a single mcp_server group.

    Path params:
        group_id: Group identifier.

    Returns:
        JSON with group detail including members and circuit breaker state.

    Raises:
        McpServerNotFoundError: If group_id is not found.
    """
    group_id = request.path_params["group_id"]
    ctx = get_context()
    group = ctx.groups.get(group_id)
    if group is None:
        raise McpServerNotFoundError(mcp_server_id=group_id)
    return HangarJSONResponse(group.to_status_dict())


async def rebalance_group(request: Request) -> HangarJSONResponse:
    """Trigger a rebalance on a mcp_server group.

    Path params:
        group_id: Group identifier.

    Returns:
        JSON with {"status": "rebalanced", "group_id": ...}.

    Raises:
        McpServerNotFoundError: If group_id is not found.
    """
    group_id = request.path_params["group_id"]
    ctx = get_context()
    group = ctx.groups.get(group_id)
    if group is None:
        raise McpServerNotFoundError(mcp_server_id=group_id)
    await run_in_threadpool(group.rebalance)
    return HangarJSONResponse({"status": "rebalanced", "group_id": group_id})


async def create_group(request: Request) -> HangarJSONResponse:
    """Create a new mcp_server group.

    Body:
        group_id: Unique identifier for the group.
        strategy: Load balancing strategy (e.g. round_robin).
        min_healthy: Optional minimum healthy members.
        description: Optional human-readable description.

    Returns:
        JSON with {"group_id": ..., "created": true} and HTTP 201.
    """
    body = await request.json()
    if (invalid := missing_fields(body, "group_id")) is not None:
        return invalid
    result = await dispatch_command(
        CreateGroupCommand(
            group_id=body["group_id"],
            strategy=body.get("strategy", "round_robin"),
            min_healthy=body.get("min_healthy", 1),
            description=body.get("description"),
        )
    )
    return HangarJSONResponse(result, status_code=201)


async def update_group(request: Request) -> HangarJSONResponse:
    """Update an existing mcp_server group.

    Path params:
        group_id: Group identifier.

    Body:
        strategy: Optional new load balancing strategy.
        min_healthy: Optional new minimum healthy members.
        description: Optional new description.

    Returns:
        JSON with {"group_id": ..., "updated": true} and HTTP 200.
    """
    group_id = request.path_params["group_id"]
    body = await request.json()
    result = await dispatch_command(
        UpdateGroupCommand(
            group_id=group_id,
            strategy=body.get("strategy"),
            min_healthy=body.get("min_healthy"),
            description=body.get("description"),
        )
    )
    return HangarJSONResponse(result)


async def delete_group(request: Request) -> HangarJSONResponse:
    """Delete a mcp_server group.

    Path params:
        group_id: Group identifier.

    Returns:
        JSON with {"group_id": ..., "deleted": true} and HTTP 200.
    """
    group_id = request.path_params["group_id"]
    result = await dispatch_command(DeleteGroupCommand(group_id=group_id))
    return HangarJSONResponse(result)


async def add_group_member(request: Request) -> HangarJSONResponse:
    """Add a mcp_server as a member of a group.

    Path params:
        group_id: Group identifier.

    Body:
        member_id: McpServer ID to add (mapped to mcp_server_id in command).
        weight: Optional routing weight.
        priority: Optional routing priority.

    Returns:
        JSON with {"group_id": ..., "mcp_server_id": ..., "added": true} and HTTP 201.
    """
    group_id = request.path_params["group_id"]
    body = await request.json()
    if (invalid := missing_fields(body, "member_id")) is not None:
        return invalid
    result = await dispatch_command(
        AddGroupMemberCommand(
            group_id=group_id,
            mcp_server_id=body["member_id"],
            weight=body.get("weight", 1),
            priority=body.get("priority", 1),
        )
    )
    return HangarJSONResponse(result, status_code=201)


async def remove_group_member(request: Request) -> HangarJSONResponse:
    """Remove a mcp_server from a group.

    Path params:
        group_id: Group identifier.
        member_id: McpServer ID to remove.

    Returns:
        JSON with {"group_id": ..., "mcp_server_id": ..., "removed": true} and HTTP 200.
    """
    group_id = request.path_params["group_id"]
    member_id = request.path_params["member_id"]
    result = await dispatch_command(RemoveGroupMemberCommand(group_id=group_id, mcp_server_id=member_id))
    return HangarJSONResponse(result)


# Route definitions for mounting in the API router
group_routes = [
    Route("/", list_groups, methods=["GET"]),
    Route("/", create_group, methods=["POST"]),
    Route("/{group_id:str}", get_group, methods=["GET"]),
    Route("/{group_id:str}", update_group, methods=["PUT"]),
    Route("/{group_id:str}", delete_group, methods=["DELETE"]),
    Route("/{group_id:str}/rebalance", rebalance_group, methods=["POST"]),
    Route("/{group_id:str}/members", add_group_member, methods=["POST"]),
    Route("/{group_id:str}/members/{member_id:str}", remove_group_member, methods=["DELETE"]),
]
