"""Microsoft Graph object to ResourceNode mapping helpers."""

from __future__ import annotations

from typing import Any, Dict, Optional

from ..adt_types import ResourceNode


def _safe_get(obj: Any, attr: str) -> Optional[Any]:
    return getattr(obj, attr, None)


def _graph_id(kind: str, object_id: str) -> str:
    # Prefix Graph object IDs so they never collide with ARM resource IDs.
    return f"graph://{kind}/{object_id}"


def _base_tags(raw: Any) -> Dict[str, str]:
    tags: Dict[str, str] = {}
    object_id = _safe_get(raw, "id")
    if object_id:
        tags["graph_id"] = str(object_id)
    return tags


def organization_to_node(org: Any, tenant_id: str) -> ResourceNode:
    object_id = str(_safe_get(org, "id") or tenant_id)
    display_name = _safe_get(org, "display_name") or _safe_get(org, "displayName")
    tenant_type = _safe_get(org, "tenant_type") or _safe_get(org, "tenantType")

    tags = _base_tags(org)
    if tenant_type:
        tags["tenantType"] = str(tenant_type)

    return ResourceNode(
        id=_graph_id("organization", object_id),
        name=str(display_name or object_id),
        type="Microsoft.Graph/Organization",
        subscription_id="Tenant",
        tags=tags,
    )


def domain_to_node(domain: Any) -> ResourceNode:
    object_id = str(_safe_get(domain, "id") or _safe_get(domain, "name") or "domain")
    name = _safe_get(domain, "id") or _safe_get(domain, "name") or object_id

    tags = _base_tags(domain)
    is_default = _safe_get(domain, "is_default")
    if is_default is not None:
        tags["isDefault"] = str(bool(is_default)).lower()

    return ResourceNode(
        id=_graph_id("domain", object_id),
        name=str(name),
        type="Microsoft.Graph/Domain",
        subscription_id="Tenant",
        tags=tags,
    )


def user_to_node(user: Any) -> ResourceNode:
    object_id = str(_safe_get(user, "id") or "user")
    display_name = _safe_get(user, "display_name") or _safe_get(user, "displayName")
    upn = _safe_get(user, "user_principal_name") or _safe_get(user, "userPrincipalName")

    tags = _base_tags(user)
    if upn:
        tags["userPrincipalName"] = str(upn)

    return ResourceNode(
        id=_graph_id("user", object_id),
        name=str(display_name or upn or object_id),
        type="Microsoft.Graph/User",
        subscription_id="Tenant",
        tags=tags,
    )


def group_to_node(group: Any) -> ResourceNode:
    object_id = str(_safe_get(group, "id") or "group")
    display_name = _safe_get(group, "display_name") or _safe_get(group, "displayName")

    tags = _base_tags(group)

    mail = _safe_get(group, "mail")
    if mail:
        tags["mail"] = str(mail)

    group_types = _safe_get(group, "group_types") or _safe_get(group, "groupTypes")
    if group_types:
        if isinstance(group_types, list):
            if group_types:
                tags["groupType"] = str(group_types[0])
            tags["groupTypes"] = ",".join(str(t) for t in group_types)
        else:
            tags["groupType"] = str(group_types)

    return ResourceNode(
        id=_graph_id("group", object_id),
        name=str(display_name or object_id),
        type="Microsoft.Graph/Group",
        subscription_id="Tenant",
        tags=tags,
    )


def application_to_node(app: Any) -> ResourceNode:
    object_id = str(_safe_get(app, "id") or "application")
    display_name = _safe_get(app, "display_name") or _safe_get(app, "displayName")
    app_id = _safe_get(app, "app_id") or _safe_get(app, "appId")

    tags = _base_tags(app)
    if app_id:
        tags["appId"] = str(app_id)

    return ResourceNode(
        id=_graph_id("application", object_id),
        name=str(display_name or object_id),
        type="Microsoft.Graph/Application",
        subscription_id="Tenant",
        tags=tags,
    )


def service_principal_to_node(sp: Any) -> ResourceNode:
    object_id = str(_safe_get(sp, "id") or "servicePrincipal")
    display_name = _safe_get(sp, "display_name") or _safe_get(sp, "displayName")
    app_id = _safe_get(sp, "app_id") or _safe_get(sp, "appId")

    tags = _base_tags(sp)
    if app_id:
        tags["appId"] = str(app_id)

    return ResourceNode(
        id=_graph_id("servicePrincipal", object_id),
        name=str(display_name or object_id),
        type="Microsoft.Graph/ServicePrincipal",
        subscription_id="Tenant",
        tags=tags,
    )


def conditional_access_policy_to_node(policy: Any) -> ResourceNode:
    object_id = str(_safe_get(policy, "id") or "conditionalAccessPolicy")
    display_name = _safe_get(policy, "display_name") or _safe_get(policy, "displayName")
    state = _safe_get(policy, "state")

    tags = _base_tags(policy)
    if state is not None:
        tags["state"] = str(state)

    return ResourceNode(
        id=_graph_id("conditionalAccessPolicy", object_id),
        name=str(display_name or object_id),
        type="Microsoft.Graph/ConditionalAccessPolicy",
        subscription_id="Tenant",
        tags=tags,
    )


def risky_user_to_node(risky: Any) -> ResourceNode:
    object_id = str(_safe_get(risky, "id") or "riskyUser")
    display_name = _safe_get(risky, "user_display_name") or _safe_get(risky, "userDisplayName")
    upn = _safe_get(risky, "user_principal_name") or _safe_get(risky, "userPrincipalName")

    tags = _base_tags(risky)
    risk_level = _safe_get(risky, "risk_level") or _safe_get(risky, "riskLevel")
    risk_state = _safe_get(risky, "risk_state") or _safe_get(risky, "riskState")
    if risk_level is not None:
        tags["riskLevel"] = str(risk_level)
    if risk_state is not None:
        tags["riskState"] = str(risk_state)
    if upn:
        tags["userPrincipalName"] = str(upn)

    return ResourceNode(
        id=_graph_id("riskyUser", object_id),
        name=str(display_name or upn or object_id),
        type="Microsoft.Graph/RiskyUser",
        subscription_id="Tenant",
        tags=tags,
    )
