from typing import Any

from mcp.types import ToolAnnotations

from mcp_eregistrations_keycloak.audit.logger import KcAuditLogger
from mcp_eregistrations_keycloak.audit.models import KcObjectType
from mcp_eregistrations_keycloak.keycloak_client import (
    KeycloakAdminClient,
    KeycloakClientError,
    translate_error,
)
from mcp_eregistrations_keycloak.server import mcp
from mcp_eregistrations_keycloak.tools.validation import _resolve_realm


@mcp.tool(annotations=ToolAnnotations(readOnlyHint=True))
async def kc_list_client_mappers(
    client_id: str,
    realm: str | None = None,
    instance: str | None = None,
) -> dict[str, Any]:
    """List protocol mappers for a client.

    Args:
        client_id: Keycloak client UUID.
        realm: Realm name, or None for instance default.
        instance: Instance name, or None for auto-select.

    Returns:
        dict with client_id, mappers.
    """
    try:
        async with KeycloakAdminClient.for_instance(instance) as client:
            r = _resolve_realm(realm, instance, client)
            mappers = await client.get(
                f"/realms/{r}/clients/{client_id}/protocol-mappers/models"
            )
            return {
                "client_id": client_id,
                "mappers": mappers or [],
            }
    except KeycloakClientError as e:
        raise translate_error(
            e,
            resource_type="client mapper",
            resource_id=client_id,
        ) from e


@mcp.tool()
async def kc_add_client_mapper(
    client_id: str,
    mapper: dict[str, Any],
    realm: str | None = None,
    instance: str | None = None,
) -> dict[str, Any]:
    """Add a protocol mapper to a client. Audited write operation.

    Args:
        client_id: Keycloak client UUID.
        mapper: Mapper definition (name, protocol, protocolMapper, config).
        realm: Realm name, or None for instance default.
        instance: Instance name, or None for auto-select.

    Returns:
        dict with client_id, mapper_name, status.
    """
    logger = KcAuditLogger.for_instance(instance)
    audit_id: str | None = None
    try:
        async with KeycloakAdminClient.for_instance(instance) as client:
            r = _resolve_realm(realm, instance, client)
            audit_id = await logger.record_pending(
                None,
                "create",
                KcObjectType.CLIENT_MAPPER,
                {"realm": r, "client_id": client_id, "mapper_name": mapper.get("name")},
            )
            result = await client.post(
                f"/realms/{r}/clients/{client_id}/protocol-mappers/models",
                data=mapper,
            )
            outcome = {
                "client_id": client_id,
                "mapper_name": mapper.get("name"),
                "realm": r,
                "status": "created",
                **result,
            }
            await logger.mark_success(audit_id, outcome)
            return outcome
    except KeycloakClientError as e:
        if audit_id is not None:
            await logger.mark_failed(audit_id, str(e))
        raise translate_error(
            e, resource_type="client mapper", resource_id=mapper.get("name")
        ) from e


@mcp.tool()
async def kc_delete_client_mapper(
    client_id: str,
    mapper_id: str,
    realm: str | None = None,
    instance: str | None = None,
) -> dict[str, Any]:
    """Delete a protocol mapper from a client. Audited write operation.

    Args:
        client_id: Keycloak client UUID.
        mapper_id: Mapper UUID.
        realm: Realm name, or None for instance default.
        instance: Instance name, or None for auto-select.

    Returns:
        dict with client_id, mapper_id, status.
    """
    logger = KcAuditLogger.for_instance(instance)
    audit_id: str | None = None
    try:
        async with KeycloakAdminClient.for_instance(instance) as client:
            r = _resolve_realm(realm, instance, client)
            audit_id = await logger.record_pending(
                None,
                "delete",
                KcObjectType.CLIENT_MAPPER,
                {"realm": r, "client_id": client_id, "mapper_id": mapper_id},
            )
            previous = await client.get(
                f"/realms/{r}/clients/{client_id}/protocol-mappers/models/{mapper_id}"
            )
            await logger.save_rollback_state(
                audit_id, KcObjectType.CLIENT_MAPPER, mapper_id, previous
            )
            await client.delete(
                f"/realms/{r}/clients/{client_id}/protocol-mappers/models/{mapper_id}"
            )
            outcome = {
                "client_id": client_id,
                "mapper_id": mapper_id,
                "realm": r,
                "status": "deleted",
            }
            await logger.mark_success(audit_id, outcome)
            return outcome
    except KeycloakClientError as e:
        if audit_id is not None:
            await logger.mark_failed(audit_id, str(e))
        raise translate_error(
            e,
            resource_type="client mapper",
            resource_id=mapper_id,
        ) from e
