# Copyright 2026 Guildhouse Dev
# SPDX-License-Identifier: Apache-2.0

"""Entra Agent ID registrar — registers agent identities via Microsoft Graph.

Implements AgentRegistrar for GCAP-SPEC-LLM-PRINCIPAL-BROKER-0001 §4.2.

Uses the shared GraphClient for authenticated Graph API access.
When Entra Agent ID Blueprint APIs reach GA, this driver should be updated
to use the dedicated /agentIdentityBlueprints and /agentIdentities endpoints.
"""

import logging

from gsap_broker.intune.graph_client import GraphClient

from .base import AgentCredentials

logger = logging.getLogger(__name__)


class EntraRegistrar:
    """AgentRegistrar implementation using Microsoft Entra + Graph API."""

    def __init__(self, graph_client: GraphClient, agent_blueprint_id: str = ""):
        self.graph = graph_client
        self.agent_blueprint_id = agent_blueprint_id

    async def register_agent(
        self,
        delegation_id: str,
        agent_type: str,
        delegator_id: str,
        display_name: str,
        expires_at: str,
        metadata: dict | None = None,
    ) -> AgentCredentials:
        tags = [
            f"agent_type:{agent_type}",
            f"delegation_id:{delegation_id}",
            f"delegator:{delegator_id}",
            "governed:true",
            "HideApp",
        ]
        if self.agent_blueprint_id:
            tags.append(f"blueprint:{self.agent_blueprint_id}")

        app_body = {
            "displayName": display_name,
            "signInAudience": "AzureADMyOrg",
            "tags": tags,
            "notes": f"Governed AI agent. Delegator: {delegator_id}. Expires: {expires_at}",
            "passwordCredentials": [],
        }

        resp = await self.graph.post("/applications", body=app_body)
        if resp.status_code == 401:
            # Token expired between construction and call — retry once.
            resp = await self.graph.post("/applications", body=app_body)
        resp.raise_for_status()

        app_data = resp.json()
        app_id = app_data["appId"]
        object_id = app_data["id"]

        secret_resp = await self.graph.post(
            f"/applications/{object_id}/addPassword",
            body={
                "passwordCredential": {
                    "displayName": f"delegation-{delegation_id}",
                    "endDateTime": expires_at,
                }
            },
        )
        secret_resp.raise_for_status()
        client_secret = secret_resp.json().get("secretText", "")

        sp_resp = await self.graph.post(
            "/servicePrincipals",
            body={"appId": app_id, "displayName": display_name, "tags": tags},
        )
        if sp_resp.status_code not in (200, 201, 409):
            sp_resp.raise_for_status()

        logger.info("Entra: registered agent %s (appId=%s)", display_name, app_id)
        return AgentCredentials(
            client_id=app_id,
            client_secret=client_secret,
            agent_display_name=display_name,
            idp_backend="entra",
        )

    async def delete_agent(self, client_id: str) -> bool:
        try:
            data = await self.graph.get(
                "/applications", params={"$filter": f"appId eq '{client_id}'"}
            )
        except Exception:
            return False

        apps = data.get("value", [])
        if not apps:
            return False

        object_id = apps[0]["id"]
        deleted = await self.graph.delete(f"/applications/{object_id}")
        if deleted:
            logger.info("Entra: deleted agent app %s", client_id)
        return deleted

    async def get_agent_token(self, client_id: str) -> str | None:
        return None
