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 _resolve_realm


@mcp.tool(annotations=ToolAnnotations(readOnlyHint=True))
async def kc_get_user_profile_config(
    realm: str | None = None,
    instance: str | None = None,
) -> dict[str, Any]:
    """Get user profile configuration (attribute definitions, validations).

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

    Returns:
        dict with user profile 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}/users/profile")
            return result
    except KeycloakClientError as e:
        raise translate_error(e, resource_type="user profile config") from e


@mcp.tool()
async def kc_update_user_profile_config(
    config: dict[str, Any],
    realm: str | None = None,
    instance: str | None = None,
) -> dict[str, Any]:
    """Replace the full user profile configuration. Audited write operation.

    Args:
        config: Complete user profile configuration to set.
        realm: Realm name, or None for instance default.
        instance: Instance name, or None for auto-select.

    Returns:
        dict with realm, 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, "update", KcObjectType.USER_PROFILE, {"realm": r}
            )
            previous = await client.get(f"/realms/{r}/users/profile")
            await logger.save_rollback_state(
                audit_id, KcObjectType.USER_PROFILE, r, previous
            )
            await client.put(f"/realms/{r}/users/profile", data=config)
            outcome = {
                "realm": r,
                "status": "updated",
                "attribute_count": len(config.get("attributes", [])),
                "previous_attribute_count": len(previous.get("attributes", [])),
            }
            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="user profile config") from e


@mcp.tool()
async def kc_add_user_attribute(
    attribute: dict[str, Any],
    realm: str | None = None,
    instance: str | None = None,
) -> dict[str, Any]:
    """Add a new attribute to user profile config. Audited write operation.

    Args:
        attribute: Attribute definition (must include 'name').
        realm: Realm name, or None for instance default.
        instance: Instance name, or None for auto-select.

    Returns:
        dict with realm, attribute_name, status.
    """
    attr_name = attribute.get("name")
    if not attr_name:
        raise ToolError("Attribute must include a 'name' field.")
    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.USER_PROFILE,
                {"realm": r, "attribute_name": attr_name},
            )
            config = await client.get(f"/realms/{r}/users/profile")
            await logger.save_rollback_state(
                audit_id, KcObjectType.USER_PROFILE, r, config
            )
            attrs = config.get("attributes", [])
            existing_names = {a["name"] for a in attrs}
            if attr_name in existing_names:
                raise ToolError(
                    f"Attribute '{attr_name}' already exists. "
                    f"Use kc_update_user_attribute to modify it."
                )
            attrs.append(attribute)
            config["attributes"] = attrs
            await client.put(f"/realms/{r}/users/profile", data=config)
            outcome = {
                "realm": r,
                "attribute_name": attr_name,
                "status": "added",
            }
            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="user attribute",
            resource_id=attr_name,
        ) from e


@mcp.tool()
async def kc_update_user_attribute(
    attribute_name: str,
    updates: dict[str, Any],
    realm: str | None = None,
    instance: str | None = None,
) -> dict[str, Any]:
    """Update an existing attribute in user profile config. Audited write operation.

    Args:
        attribute_name: Name of attribute to update.
        updates: Fields to merge into the attribute definition.
        realm: Realm name, or None for instance default.
        instance: Instance name, or None for auto-select.

    Returns:
        dict with realm, attribute_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,
                "update",
                KcObjectType.USER_PROFILE,
                {"realm": r, "attribute_name": attribute_name},
            )
            config = await client.get(f"/realms/{r}/users/profile")
            await logger.save_rollback_state(
                audit_id, KcObjectType.USER_PROFILE, r, config
            )
            attrs = config.get("attributes", [])
            found = False
            for attr in attrs:
                if attr["name"] == attribute_name:
                    attr.update(updates)
                    attr["name"] = attribute_name  # prevent name overwrite
                    found = True
                    break
            if not found:
                raise ToolError(
                    f"Attribute '{attribute_name}' not found. "
                    f"Use kc_add_user_attribute to create it."
                )
            config["attributes"] = attrs
            await client.put(f"/realms/{r}/users/profile", data=config)
            outcome = {
                "realm": r,
                "attribute_name": attribute_name,
                "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="user attribute",
            resource_id=attribute_name,
        ) from e


@mcp.tool()
async def kc_remove_user_attribute(
    attribute_name: str,
    realm: str | None = None,
    instance: str | None = None,
) -> dict[str, Any]:
    """Remove an attribute from user profile config. Audited write operation.

    Args:
        attribute_name: Name of attribute to remove.
        realm: Realm name, or None for instance default.
        instance: Instance name, or None for auto-select.

    Returns:
        dict with realm, attribute_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,
                "delete",
                KcObjectType.USER_PROFILE,
                {"realm": r, "attribute_name": attribute_name},
            )
            config = await client.get(f"/realms/{r}/users/profile")
            await logger.save_rollback_state(
                audit_id, KcObjectType.USER_PROFILE, r, config
            )
            attrs = config.get("attributes", [])
            new_attrs = [a for a in attrs if a["name"] != attribute_name]
            if len(new_attrs) == len(attrs):
                raise ToolError(
                    f"Attribute '{attribute_name}' not found in user profile config."
                )
            config["attributes"] = new_attrs
            await client.put(f"/realms/{r}/users/profile", data=config)
            outcome = {
                "realm": r,
                "attribute_name": attribute_name,
                "status": "removed",
            }
            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="user attribute",
            resource_id=attribute_name,
        ) from e
