"""
Unified action proxy client for sandbox/runtime automations.
"""

from __future__ import annotations

import os
from typing import Any, Optional

import httpx

ACTION_CONTRACT_VERSION = "2026-02-1"
DEFAULT_TIMEOUT_SECONDS = 10.0


class ActionError(RuntimeError):
    """Raised when action execution fails."""


def _extract_error_message(response: httpx.Response) -> str:
    try:
        payload = response.json()
        if isinstance(payload, dict):
            detail = payload.get("detail")
            if isinstance(detail, str) and detail.strip():
                return detail
            message = payload.get("message")
            if isinstance(message, str) and message.strip():
                return message
    except Exception:
        pass

    text = response.text.strip()
    if text:
        return text
    return f"HTTP {response.status_code}"


class ActionClient:
    """Simple synchronous client for `/api/action/execute`."""

    def __init__(
        self,
        *,
        base_url: str,
        token: str,
        timeout_seconds: float = DEFAULT_TIMEOUT_SECONDS,
    ) -> None:
        if not base_url or not token:
            raise ValueError("base_url and token are required")
        self._base_url = base_url.rstrip("/")
        self._token = token
        self._timeout = timeout_seconds

    @classmethod
    def get_default(cls) -> "ActionClient":
        """Create a client from runtime environment variables."""
        base_url = os.environ.get("TORIVERS_ACTION_PROXY_URL")
        token = os.environ.get("TORIVERS_ACTION_TOKEN")
        if not base_url or not token:
            raise ActionError(
                "Action proxy runtime is not configured. "
                "Set TORIVERS_ACTION_PROXY_URL and TORIVERS_ACTION_TOKEN."
            )
        return cls(base_url=base_url, token=token)

    def execute(
        self,
        action: str,
        payload: dict[str, Any],
        idempotency_key: Optional[str] = None,
        version: str = ACTION_CONTRACT_VERSION,
    ) -> dict[str, Any]:
        """Execute one action and return response data."""
        if not action:
            raise ActionError("Action name is required")

        request_payload: dict[str, Any] = {
            "version": version,
            "action": action,
            "payload": payload or {},
        }
        if idempotency_key:
            request_payload["idempotency_key"] = idempotency_key

        try:
            with httpx.Client(timeout=self._timeout) as client:
                response = client.post(
                    f"{self._base_url}/api/action/execute",
                    headers={"Authorization": f"Bearer {self._token}"},
                    json=request_payload,
                )
        except httpx.TimeoutException as exc:
            raise ActionError(f"Action request timed out: {exc}") from exc
        except httpx.RequestError as exc:
            raise ActionError(f"Action request failed: {exc}") from exc

        if response.status_code == 401:
            raise ActionError("Action authentication failed")
        if response.status_code == 403:
            raise ActionError("Action not authorized")
        if response.status_code >= 400:
            raise ActionError(_extract_error_message(response))

        try:
            body = response.json()
        except ValueError as exc:
            raise ActionError("Invalid action response payload") from exc

        if not isinstance(body, dict):
            raise ActionError("Invalid action response payload")
        if body.get("success") is False:
            raise ActionError(str(body.get("error") or "Action execution failed"))

        data = body.get("data")
        if isinstance(data, dict):
            return data
        return {}
