"""
Microsoft Graph API utilities.

Provides helper functions for interacting with Microsoft Graph API
to resolve identities (users, groups, service principals).

IMPORTANT: Requires the following Microsoft Graph API permissions in your Azure AD app:
- User.Read.All (to resolve user principals)
- Group.Read.All (to resolve group principals)
- Application.Read.All (to resolve service principals)

Without these permissions, principal name resolution will return None.
"""

from typing import Any, Dict, Optional

import requests


class GraphHelper:
    """
    Helper class for Microsoft Graph API operations.

    Used to resolve principal IDs to display names.
    """

    GRAPH_SCOPE = "https://graph.microsoft.com/.default"
    GRAPH_API_URL = "https://graph.microsoft.com/v1.0"

    def __init__(self, auth_provider):
        """
        Initialize Graph helper.

        Args:
            auth_provider: AuthProvider instance for authentication
        """
        self._auth = auth_provider
        self._base_url = self.GRAPH_API_URL

    def _get_token(self) -> str:
        """Get Graph API access token."""
        return self._auth.getToken(self.GRAPH_SCOPE)

    def _make_request(self, endpoint: str) -> Optional[Dict[str, Any]]:
        """
        Make a Graph API request.

        Args:
            endpoint: API endpoint (e.g., '/users/{id}')

        Returns:
            dict: Response data, or None if request fails
        """
        url = f"{self._base_url}{endpoint}"

        try:
            token = self._get_token()
            headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}

            response = requests.get(url, headers=headers, timeout=30)
            response.raise_for_status()
            return response.json()
        except requests.exceptions.HTTPError as e:
            # Silently fail for 403/404 (common for missing permissions or cross-tenant principals)
            if hasattr(e, "response") and e.response is not None:
                if e.response.status_code in [403, 404]:
                    return None
            return None
        except Exception:
            return None

    def getPrincipalName(self, principal_id: str, principal_type: str) -> Optional[str]:
        """
        Get the display name for a principal (user, group, or service principal).

        Args:
            principal_id: Principal's object ID
            principal_type: Type of principal ('User', 'Group', 'ServicePrincipal')

        Returns:
            str: Display name, or None if not found

        Examples:
            >>> graph = GraphHelper(client)
            >>> name = graph.getPrincipalName("user-id", "User")
        """
        if not principal_id or not principal_type:
            return None

        # Map principal type to Graph API endpoint
        endpoint_map = {
            "User": f"/users/{principal_id}",
            "Group": f"/groups/{principal_id}",
            "ServicePrincipal": f"/servicePrincipals/{principal_id}",
        }

        endpoint = endpoint_map.get(principal_type)
        if not endpoint:
            return None

        data = self._make_request(endpoint)
        if not data:
            return None

        return data.get("displayName")
