from __future__ import annotations

import asyncio
from typing import Any
from urllib.parse import quote

import yaml
from mcp.types import ToolAnnotations

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_export_realm_yaml(
    realm: str | None = None,
    include_roles: bool = True,
    include_themes: bool = True,
    instance: str | None = None,
) -> dict[str, Any]:
    """Export realm configuration as YAML.

    Args:
        realm: Realm name, or None for instance default.
        include_roles: Include roles and composites (default: True).
        include_themes: Include theme settings (default: True).
        instance: Instance name, or None for auto-select.

    Returns:
        dict with realm, yaml content.
    """
    try:
        async with KeycloakAdminClient.for_instance(instance) as client:
            r = _resolve_realm(realm, instance, client)

            tasks: list[Any] = [client.get(f"/realms/{r}")]
            if include_roles:
                tasks.append(client.get(f"/realms/{r}/roles"))

            results = await asyncio.gather(*tasks)
            realm_config = results[0]

            export_data: dict[str, Any] = {
                "realm": r,
                "displayName": realm_config.get("displayName"),
                "enabled": realm_config.get("enabled"),
            }

            if include_themes:
                export_data["themes"] = {
                    field: realm_config.get(field)
                    for field in (
                        "loginTheme",
                        "accountTheme",
                        "adminTheme",
                        "emailTheme",
                    )
                }

            if include_roles and len(results) > 1:
                roles_data = results[1]
                items = (
                    roles_data["items"]
                    if isinstance(roles_data, dict) and roles_data.get("truncated")
                    else roles_data
                )
                # Fetch composite children in parallel
                composite_roles = [
                    r_item for r_item in items if r_item.get("composite")
                ]
                if composite_roles:
                    composites = await asyncio.gather(
                        *[
                            client.get(
                                f"/realms/{r}/roles/"
                                f"{quote(cr['name'], safe='')}"
                                f"/composites"
                            )
                            for cr in composite_roles
                        ]
                    )
                    for role, children in zip(composite_roles, composites):
                        role["composites"] = [c["name"] for c in (children or [])]
                export_data["roles"] = [
                    {
                        "name": role["name"],
                        "description": role.get("description"),
                        "composite": role.get("composite", False),
                        "composites": role.get("composites", []),
                    }
                    for role in items
                ]

            yaml_content = yaml.dump(
                export_data,
                default_flow_style=False,
                sort_keys=False,
            )
            return {"realm": r, "yaml": yaml_content}
    except KeycloakClientError as e:
        raise translate_error(e, resource_type="realm export", resource_id=realm) from e


@mcp.tool(annotations=ToolAnnotations(readOnlyHint=True))
async def kc_compare_realms(
    instance_a: str,
    instance_b: str,
    realm_a: str | None = None,
    realm_b: str | None = None,
) -> dict[str, Any]:
    """Compare roles between two Keycloak instances.

    Args:
        instance_a: First instance name.
        instance_b: Second instance name.
        realm_a: Realm on instance A, or None for default.
        realm_b: Realm on instance B, or None for default.

    Returns:
        dict with only_in_a, only_in_b, common, composite_differences.
    """
    try:

        async def _get_roles(
            inst: str,
            realm: str | None,
        ) -> tuple[str, list[dict[str, Any]]]:
            async with KeycloakAdminClient.for_instance(inst) as client:
                r = _resolve_realm(realm, inst, client)
                roles = await client.get(f"/realms/{r}/roles")
                items = (
                    roles["items"]
                    if isinstance(roles, dict) and roles.get("truncated")
                    else roles
                )
                # Fetch composites
                composite_roles = [role for role in items if role.get("composite")]
                if composite_roles:
                    composites = await asyncio.gather(
                        *[
                            client.get(
                                f"/realms/{r}/roles/"
                                f"{quote(cr['name'], safe='')}"
                                f"/composites"
                            )
                            for cr in composite_roles
                        ]
                    )
                    for role, children in zip(composite_roles, composites):
                        role["_composites"] = sorted(
                            c["name"] for c in (children or [])
                        )
                return r, items

        (realm_a_name, roles_a), (realm_b_name, roles_b) = await asyncio.gather(
            _get_roles(instance_a, realm_a),
            _get_roles(instance_b, realm_b),
        )

        names_a = {r["name"] for r in roles_a}
        names_b = {r["name"] for r in roles_b}
        common = names_a & names_b

        # Check composite differences
        composites_a = {
            r["name"]: r.get("_composites", []) for r in roles_a if r["name"] in common
        }
        composites_b = {
            r["name"]: r.get("_composites", []) for r in roles_b if r["name"] in common
        }
        composite_diffs = {
            name: {"a": composites_a.get(name, []), "b": composites_b.get(name, [])}
            for name in common
            if composites_a.get(name, []) != composites_b.get(name, [])
        }

        return {
            "instance_a": {"instance": instance_a, "realm": realm_a_name},
            "instance_b": {"instance": instance_b, "realm": realm_b_name},
            "only_in_a": sorted(names_a - names_b),
            "only_in_b": sorted(names_b - names_a),
            "common": sorted(common),
            "composite_differences": composite_diffs,
        }
    except KeycloakClientError as e:
        raise translate_error(e, resource_type="realm comparison") from e
