"""HTTP client for RealTimeX desktop application backend.

Centralizes all communication with the RTX backend API, including:
- Credential retrieval
- System variable fetching
- Flow configuration fetching
"""

from __future__ import annotations

from typing import Any

from agent_flows.api.error_mapping import FlowApiErrorMapper
from agent_flows.api.http_client import ApiClient
from agent_flows.exceptions import ApiError, CredentialError, SystemVariableError
from agent_flows.models.config import AgentFlowsConfig
from agent_flows.utils.logging import get_logger

log = get_logger(__name__)


class RealTimeXClient:
    """Unified client for RealTimeX backend API communication.

    This client owns all HTTP communication with the RTX desktop app backend.
    Domain-specific processing (decryption, caching, merging) is handled by
    the consuming managers.

    Example:
        async with RealTimeXClient(config) as client:
            credential = await client.fetch_credential("cred-123")
            variables = await client.fetch_system_variables()
            flow = await client.fetch_flow("flow-uuid")
    """

    def __init__(self, config: AgentFlowsConfig) -> None:
        """Initialize RealTimeX client.

        Args:
            config: Application configuration containing base URL, API key, etc.
        """
        self._config = config
        self._api = ApiClient(
            base_url=config.app_base_url,
            api_key=config.api_key,
            timeout=config.timeout,
            max_retries=config.max_retries,
        )
        self._flow_api = ApiClient(
            base_url=config.base_url,
            api_key=config.api_key,
            timeout=config.timeout,
            max_retries=config.max_retries,
            error_mapper=FlowApiErrorMapper(),
        )

    async def close(self) -> None:
        """Release HTTP session resources."""
        await self._api.close()
        await self._flow_api.close()

    async def __aenter__(self) -> RealTimeXClient:
        return self

    async def __aexit__(
        self,
        _exc_type: type[BaseException] | None,
        _exc: BaseException | None,
        _tb: Any,
    ) -> None:
        await self.close()

    # ─── Credentials ─────────────────────────────────────────────────────────

    async def fetch_credential(self, credential_id: str) -> dict[str, Any]:
        """Fetch raw credential record from backend.

        Args:
            credential_id: Unique identifier of the credential.

        Returns:
            Raw credential dict containing encrypted 'data' field and metadata.

        Raises:
            CredentialError: If fetch fails or response is malformed.
        """
        try:
            payload = await self._api.request("GET", f"/api/v1/credentials/{credential_id}")
        except ApiError as exc:
            raise CredentialError(
                f"Failed to fetch credential '{credential_id}' from backend",
                credential_id=credential_id,
            ) from exc

        if not isinstance(payload, dict):
            raise CredentialError(
                "Credential service returned unexpected response type",
                credential_id=credential_id,
            )

        return self._extract_credential_record(payload, credential_id)

    def _extract_credential_record(
        self, payload: dict[str, Any], credential_id: str
    ) -> dict[str, Any]:
        """Extract credential record from API response envelope."""
        if payload.get("status") is False:
            raise CredentialError(
                "Credential service reported failure status",
                credential_id=credential_id,
                details={"status": payload.get("status")},
            )

        credential = payload.get("credential")
        if not isinstance(credential, dict):
            raise CredentialError(
                "Credential service returned malformed payload",
                credential_id=credential_id,
            )
        return credential

    # ─── System Variables ────────────────────────────────────────────────────

    async def fetch_system_variables(self) -> dict[str, Any]:
        """Fetch user-specific system variables from backend.

        System variables include user-specific values like email, name,
        and other contextual data for flow execution.

        Returns:
            Dict mapping variable names to their values.

        Raises:
            SystemVariableError: If fetch fails or response is malformed.
        """
        try:
            payload = await self._api.request("GET", "/api/system/prompt-variables")
        except ApiError as exc:
            raise SystemVariableError("Failed to fetch system variables") from exc

        return self._parse_variables_payload(payload)

    def _parse_variables_payload(self, payload: Any) -> dict[str, Any]:
        """Parse system variables from API response format."""
        if not isinstance(payload, dict):
            raise SystemVariableError("System variable response must be a JSON object")

        variables = payload.get("variables")
        if not isinstance(variables, list):
            raise SystemVariableError("System variable response missing 'variables' list")

        result: dict[str, Any] = {}
        for item in variables:
            if not isinstance(item, dict):
                continue
            key = item.get("key")
            if isinstance(key, str) and key:
                result[key] = item.get("value")
        return result

    # ─── Flows ───────────────────────────────────────────────────────────────

    async def fetch_flow(self, flow_id: str) -> dict[str, Any]:
        """Fetch single flow configuration from backend.

        Args:
            flow_id: UUID of the flow to retrieve.

        Returns:
            Raw flow dict ready for FlowConfig parsing.

        Raises:
            ApiError: If fetch fails.
            FlowNotFoundError: If flow does not exist (404).
        """
        log.debug("Fetching flow from RTX backend", flow_id=flow_id)
        payload = await self._flow_api.request(
            "GET",
            f"agent-flows/{flow_id}",
            params={"ignore_purchased_check": "y"},
        )

        if not isinstance(payload, dict):
            raise ApiError(
                f"Invalid flow response: expected dict, got {type(payload).__name__}",
                status_code=200,
                response_body=str(payload),
            )
        return payload

    async def list_flows(self) -> list[dict[str, Any]]:
        """Fetch all accessible flow configurations from backend.

        Returns:
            List of raw flow dicts for parsing into FlowConfig objects.

        Raises:
            ApiError: If fetch fails or response format is invalid.
        """
        log.debug("Listing flows from RTX backend")
        payload = await self._flow_api.request(
            "GET",
            "agent-flows",
            params={"ignore_purchased_check": "y"},
        )

        if payload is None:
            return []
        if not isinstance(payload, list):
            raise ApiError(
                f"Invalid flows response: expected list, got {type(payload).__name__}",
                status_code=200,
                response_body=str(payload),
            )
        return payload
