"""ARIAPolicyProvider — PolicyProviderInterface backed by ARIA's AuthZEN PDP.

Registers with AGT's ControlPlane plugin registry to supply ARIA-managed
policy definitions.
"""

from __future__ import annotations

import logging
import time
from typing import Any

import httpx
from agent_control_plane.interfaces.plugin_interface import (  # type: ignore[import-untyped]
    PluginCapability,
    PluginMetadata,
    PolicyDefinition,
    PolicyProviderInterface,
)
from authzen_backend.auth import ClientCredentialsAuth, StaticTokenAuth

from aria_agentkit import compat as _compat  # noqa: F401 — version gate

logger = logging.getLogger(__name__)


class ARIAPolicyProvider(PolicyProviderInterface):  # type: ignore[misc]
    """PolicyProviderInterface that fetches policy definitions from ARIA PDP.

    The PDP's policy listing endpoint is queried and results are cached.
    ``refresh()`` invalidates the cache for the next ``get_policies()`` call.

    Args:
        pdp_base_url: Base URL of the ARIA PDP (without path).
        pdp_application: ARIA PDP application identifier.
        token: Static bearer token for PDP authentication.
        client_credentials: ``ClientCredentialsAuth`` instance for
            OAuth 2.0 client-credentials auth.
        http_client: Optional pre-configured httpx.Client.
        timeout: HTTP timeout in seconds.
        cache_ttl: Seconds to cache policy definitions. 0 disables caching.
    """

    def __init__(
        self,
        pdp_base_url: str,
        pdp_application: str,
        *,
        token: str | None = None,
        client_credentials: ClientCredentialsAuth | None = None,
        http_client: httpx.Client | None = None,
        timeout: float = 10.0,
        cache_ttl: float = 300.0,
    ) -> None:
        self._pdp_base_url = pdp_base_url.rstrip("/")
        self._pdp_application = pdp_application
        self._timeout = timeout
        self._cache_ttl = cache_ttl

        self._auth: ClientCredentialsAuth | StaticTokenAuth | None = None
        if token is not None and client_credentials is not None:
            raise ValueError("Provide either 'token' or 'client_credentials', not both.")
        if client_credentials is not None:
            self._auth = client_credentials
        elif token is not None:
            self._auth = StaticTokenAuth(token=token)

        self._owns_client = http_client is None
        self._client = http_client or httpx.Client(timeout=timeout)

        self._cached_policies: list[PolicyDefinition] = []
        self._cache_timestamp: float = 0.0

        self._metadata = PluginMetadata(
            name="aria-pdp",
            version="0.1.0",
            description="ARIA AuthZEN PDP policy provider",
            plugin_type="policy_provider",
            capabilities=[PluginCapability.REMOTE_POLICIES, PluginCapability.DYNAMIC_POLICIES],
        )

    @property
    def metadata(self) -> PluginMetadata:
        return self._metadata

    def get_policies(
        self,
        agent_id: str | None = None,
        action_type: str | None = None,
    ) -> list[PolicyDefinition]:
        """Fetch applicable policies from the ARIA PDP.

        Results are cached for ``cache_ttl`` seconds. Pass filters to
        narrow results, though the PDP may not support all filter types.
        """
        now = time.monotonic()
        if self._cached_policies and (now - self._cache_timestamp) < self._cache_ttl:
            return self._filter(self._cached_policies, agent_id, action_type)

        try:
            headers = self._build_headers()
            url = f"{self._pdp_base_url}/policies"
            params: dict[str, str] = {"application": self._pdp_application}
            if agent_id:
                params["agent_id"] = agent_id

            resp = self._client.get(url, headers=headers, params=params)
            resp.raise_for_status()
            data = resp.json()

            policies = self._parse_policies(data)
            self._cached_policies = policies
            self._cache_timestamp = now

            return self._filter(policies, agent_id, action_type)

        except Exception:
            logger.exception("Failed to fetch policies from ARIA PDP")
            return self._filter(self._cached_policies, agent_id, action_type)

    def refresh(self) -> None:
        """Invalidate the policy cache so the next call re-fetches."""
        self._cache_timestamp = 0.0
        self._cached_policies = []

    def _build_headers(self) -> dict[str, str]:
        headers: dict[str, str] = {"Accept": "application/json"}
        if self._auth is not None:
            headers["Authorization"] = f"Bearer {self._auth.get_token(self._client)}"
        return headers

    @staticmethod
    def _parse_policies(data: Any) -> list[PolicyDefinition]:
        """Parse PDP response into PolicyDefinition objects."""
        policies: list[PolicyDefinition] = []
        items = data if isinstance(data, list) else data.get("policies", [])
        for item in items:
            if not isinstance(item, dict):
                continue
            policies.append(
                PolicyDefinition(
                    policy_id=str(item.get("id", "")),
                    name=str(item.get("name", "")),
                    description=str(item.get("description", "")),
                    rules=item.get("rules", []),
                    priority=int(item.get("priority", 0)),
                    enabled=bool(item.get("enabled", True)),
                    metadata=item.get("metadata", {}),
                )
            )
        return policies

    @staticmethod
    def _filter(
        policies: list[PolicyDefinition],
        agent_id: str | None,
        action_type: str | None,
    ) -> list[PolicyDefinition]:
        """Client-side filter on cached policies."""
        result = policies
        if agent_id:
            result = [
                p
                for p in result
                if p.metadata.get("agent_id") == agent_id or not p.metadata.get("agent_id")
            ]
        if action_type:
            result = [
                p for p in result if any(r.get("action_type") == action_type for r in p.rules)
            ]
        return result

    def close(self) -> None:
        """Release HTTP resources."""
        if self._owns_client:
            self._client.close()

    def __repr__(self) -> str:
        return (
            f"ARIAPolicyProvider(pdp_base_url={self._pdp_base_url!r}, "
            f"pdp_application={self._pdp_application!r})"
        )

    def __enter__(self) -> ARIAPolicyProvider:
        return self

    def __exit__(self, *args: object) -> None:
        self.close()
