"""APDP constraint manifest fetching and formatting.

The constraint manifest tells an agent what it is allowed to do
*before* it starts reasoning. It includes capabilities, constraints,
escalation rules, delegation scope, dynamic limits, and guidance.
"""

from __future__ import annotations

import logging
from typing import Any

import httpx

logger = logging.getLogger("wl_apdp.manifest")


def fetch_agent_manifest(
    base_url: str,
    agent_id: str,
    delegation_chain: dict[str, Any] | None = None,
    ttl_seconds: int = 1800,
    token: str | None = None,
    timeout: float = 10.0,
) -> dict[str, Any] | None:
    """Fetch the WL-APDP constraint manifest for an agent session.

    Calls POST /constraints/manifest to get the agent's capability manifest,
    including capabilities, constraints, escalation rules, guidance, and
    dynamic limits.

    Args:
        base_url: APDP server base URL (e.g., "http://localhost:8081").
        agent_id: The agent slug (e.g., "my-agent-v1").
        delegation_chain: Optional delegation chain from user context.
        ttl_seconds: Manifest time-to-live in seconds (default 30 min).
        token: Optional bearer token for authentication.
        timeout: HTTP request timeout in seconds.

    Returns:
        The manifest dict if successful, None if APDP is unavailable or fails.
        Never raises — fail-open by default.
    """
    request_body: dict[str, Any] = {
        "agent_id": agent_id,
        "ttl_seconds": ttl_seconds,
    }
    if delegation_chain:
        request_body["delegation_chain"] = delegation_chain

    headers: dict[str, str] = {}
    if token:
        headers["Authorization"] = f"Bearer {token}"

    try:
        with httpx.Client(
            base_url=base_url, timeout=timeout, headers=headers
        ) as client:
            resp = client.post("/constraints/manifest", json=request_body)
            resp.raise_for_status()
            data = resp.json()

        manifest = data.get("manifest")
        if manifest:
            logger.info(
                "Fetched constraint manifest for %s: %d capabilities, %d constraints, trust=%s",
                agent_id,
                len(manifest.get("capabilities", [])),
                len(manifest.get("constraints", {}).get("forbidden_actions", [])),
                manifest.get("agent", {}).get("trust_level", "?"),
            )
        return manifest

    except httpx.HTTPStatusError as e:
        logger.warning(
            "APDP manifest fetch failed for %s: HTTP %d — %s",
            agent_id,
            e.response.status_code,
            e.response.text[:200],
        )
        return None
    except Exception as e:
        logger.warning("APDP manifest fetch error for %s: %s", agent_id, e)
        return None


def format_manifest_for_prompt(manifest: dict[str, Any]) -> str:
    """Format a WL-APDP constraint manifest into text for an LLM system prompt.

    Extracts capabilities, constraints, guidance, and dynamic limits into
    a structured text block that the LLM can reason about during its ReAct loop.

    Args:
        manifest: The manifest dict from fetch_agent_manifest().

    Returns:
        A formatted string to append to the system prompt.
    """
    sections: list[str] = []
    sections.append("## Authorization Manifest (from WL-APDP)")

    # Agent identity
    agent_info = manifest.get("agent", {})
    if agent_info:
        sections.append(
            f"\nAgent: {agent_info.get('slug', agent_info.get('agent_id', '?'))}"
        )
        sections.append(f"Trust Level: {agent_info.get('trust_level', '?')}/10")
        sections.append(f"Trust State: {agent_info.get('trust_state', '?')}")
        caps = agent_info.get("capabilities", [])
        if caps:
            sections.append(f"Registered Capabilities: {', '.join(caps)}")

    # Capabilities
    capabilities = manifest.get("capabilities", [])
    if capabilities:
        sections.append("\n### What You Can Do")
        for cap in capabilities:
            action = cap.get("action", "?")
            resources = ", ".join(cap.get("resource_types", []))
            risk = cap.get("risk_level", "?")
            desc = cap.get("description", "")
            preflight = cap.get("recommend_preflight", False)
            line = f"- **{action}** on [{resources}] (risk: {risk})"
            if desc:
                line += f" — {desc}"
            if preflight:
                line += " [preflight recommended]"
            sections.append(line)

    # Constraints
    constraints = manifest.get("constraints", {})
    forbidden = constraints.get("forbidden_actions", [])
    if forbidden:
        sections.append("\n### What You CANNOT Do")
        for f in forbidden:
            action = f.get("action", "?")
            resource_type = f.get("resource_type", "")
            reason = f.get("reason", "")
            line = f"- **{action}**"
            if resource_type:
                line += f" on {resource_type}"
            if reason:
                line += f" — {reason}"
            sections.append(line)

    approval_required = constraints.get("approval_required", [])
    if approval_required:
        sections.append(
            f"\nApproval required for intent categories: {', '.join(approval_required)}"
        )

    max_risk = constraints.get("max_risk_level")
    if max_risk:
        sections.append(f"Maximum risk level: {max_risk}")

    # Escalation rules
    escalation = manifest.get("escalation_rules", [])
    if escalation:
        sections.append("\n### Escalation Rules")
        for rule in escalation:
            condition = rule.get("condition", "?")
            esc_type = rule.get("escalation_type", "?")
            desc = rule.get("description", "")
            sections.append(f"- When: {condition} → {esc_type}")
            if desc:
                sections.append(f"  {desc}")

    # Delegation scope
    delegation_scope = manifest.get("delegation_scope")
    if delegation_scope:
        sections.append("\n### Delegation Scope")
        allowed_actions = delegation_scope.get("allowed_actions", [])
        if allowed_actions:
            sections.append(f"Delegated actions: {', '.join(allowed_actions)}")
        valid_until = delegation_scope.get("valid_until")
        if valid_until:
            sections.append(f"Delegation valid until: {valid_until}")

    # Dynamic limits
    limits = manifest.get("dynamic_limits", {})
    if limits:
        remaining = limits.get("actions_remaining")
        expiry = limits.get("seconds_until_expiry")
        if remaining is not None:
            sections.append(f"\nActions remaining: {remaining}")
        if expiry is not None:
            sections.append(f"Session expires in: {expiry}s")

    # Guidance (natural language from APDP)
    guidance = manifest.get("guidance", {})
    if guidance:
        sections.append("\n### Guidance")
        role = guidance.get("role_summary")
        if role:
            sections.append(f"Role: {role}")
        limitations = guidance.get("limitations", [])
        if limitations:
            sections.append("Limitations:")
            for lim in limitations:
                sections.append(f"  - {lim}")
        practices = guidance.get("best_practices", [])
        if practices:
            sections.append("Best practices:")
            for bp in practices:
                sections.append(f"  - {bp}")
        uncertain = guidance.get("when_uncertain")
        if uncertain:
            sections.append(f"When uncertain: {uncertain}")

    return "\n".join(sections)
