from typing import Any

from fastmcp.exceptions import ToolError
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 (
    _REALM_UPDATABLE_FIELDS,
    _extract_items,
    _filter_update_fields,
    _resolve_realm,
    _validate_realm,
)


@mcp.tool(annotations=ToolAnnotations(readOnlyHint=True))
async def kc_list_realms(
    instance: str | None = None,
) -> dict[str, Any]:
    """List all Keycloak realms accessible to the authenticated user.

    Args:
        instance: Instance name, or None for auto-select.

    Returns:
        dict with realms, count.
    """
    try:
        async with KeycloakAdminClient.for_instance(instance) as client:
            realms = await client.get("/realms")
            items = _extract_items(realms)
            return {
                "realms": [
                    {
                        "id": r["id"],
                        "realm": r["realm"],
                        "displayName": r.get("displayName"),
                        "enabled": r.get("enabled"),
                    }
                    for r in items
                ],
                "count": len(items),
            }
    except KeycloakClientError as e:
        raise translate_error(e, resource_type="realm") from e


@mcp.tool(annotations=ToolAnnotations(readOnlyHint=True))
async def kc_get_realm(
    realm: str | None = None,
    instance: str | None = None,
) -> dict[str, Any]:
    """Get detailed configuration of a Keycloak realm.

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

    Returns:
        dict with realm configuration.
    """
    try:
        async with KeycloakAdminClient.for_instance(instance) as client:
            r = _resolve_realm(realm, instance, client)
            result: dict[str, Any] = await client.get(f"/realms/{r}")
            return result
    except KeycloakClientError as e:
        raise translate_error(e, resource_type="realm", resource_id=realm) from e


@mcp.tool()
async def kc_create_realm(
    realm_name: str,
    display_name: str | None = None,
    enabled: bool = True,
    instance: str | None = None,
) -> dict[str, Any]:
    """Create a new Keycloak realm. Audited write operation.

    Args:
        realm_name: Unique realm identifier.
        display_name: Human-readable name (default: realm_name).
        enabled: Whether realm is active (default: True).
        instance: Instance name, or None for auto-select.

    Returns:
        dict with id, realm, status.
    """
    _validate_realm(realm_name)
    logger = KcAuditLogger.for_instance(instance)
    audit_id = await logger.record_pending(
        None, "create", KcObjectType.REALM, {"realm": realm_name}
    )
    try:
        async with KeycloakAdminClient.for_instance(instance) as client:
            result = await client.post(
                "/realms",
                data={
                    "realm": realm_name,
                    "displayName": display_name or realm_name,
                    "enabled": enabled,
                },
            )
            outcome = {
                "realm": realm_name,
                "displayName": display_name or realm_name,
                "enabled": enabled,
                "status": "created",
                **result,
            }
            await logger.mark_success(audit_id, outcome)
            return outcome
    except KeycloakClientError as e:
        await logger.mark_failed(audit_id, str(e))
        raise translate_error(e, resource_type="realm", resource_id=realm_name) from e


@mcp.tool()
async def kc_update_realm(
    updates: dict[str, Any],
    realm: str | None = None,
    instance: str | None = None,
) -> dict[str, Any]:
    """Update realm settings. Audited write operation.

    Args:
        updates: Fields to update (see allowed fields below).
        realm: Realm name, or None for instance default.
        instance: Instance name, or None for auto-select.

    Returns:
        dict with realm, updated_fields, previous_values.
    """
    filtered = _filter_update_fields(updates, _REALM_UPDATABLE_FIELDS, "realm")
    if not filtered:
        raise ToolError("No valid fields to update.")
    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,
                "update",
                KcObjectType.REALM,
                {"realm": r, "fields": list(filtered.keys())},
            )
            previous = await client.get(f"/realms/{r}")
            await logger.save_rollback_state(audit_id, KcObjectType.REALM, r, previous)
            previous_values = {k: previous.get(k) for k in filtered}
            await client.put(f"/realms/{r}", data=filtered)
            outcome = {
                "realm": r,
                "updated_fields": list(filtered.keys()),
                "previous_values": previous_values,
                "status": "updated",
            }
            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="realm", resource_id=realm) from e


@mcp.tool()
async def kc_delete_realm(
    realm: str,
    confirm_delete: bool = False,
    instance: str | None = None,
) -> dict[str, Any]:
    """Delete a Keycloak realm. Audited write operation.

    Safety: requires confirm_delete=True if realm has users.

    Args:
        realm: Realm name to delete.
        confirm_delete: Must be True if realm has users (default: False).
        instance: Instance name, or None for auto-select.

    Returns:
        dict with realm, status.
    """
    _validate_realm(realm)
    logger = KcAuditLogger.for_instance(instance)
    audit_id = await logger.record_pending(
        None, "delete", KcObjectType.REALM, {"realm": realm}
    )
    try:
        async with KeycloakAdminClient.for_instance(instance) as client:
            previous = await client.get(f"/realms/{realm}")
            await logger.save_rollback_state(
                audit_id, KcObjectType.REALM, realm, previous
            )
            user_count = await client.get(f"/realms/{realm}/users/count")
            if user_count > 0 and not confirm_delete:
                raise ToolError(
                    f"Realm '{realm}' has {user_count} user(s). "
                    f"Set confirm_delete=True to proceed with deletion."
                )
            await client.delete(f"/realms/{realm}")
            outcome = {
                "realm": realm,
                "user_count_at_deletion": user_count,
                "status": "deleted",
            }
            await logger.mark_success(audit_id, outcome)
            return outcome
    except KeycloakClientError as e:
        await logger.mark_failed(audit_id, str(e))
        raise translate_error(e, resource_type="realm", resource_id=realm) from e
