from dataclasses import dataclass
from typing import Any
import requests


@dataclass(frozen=True)
class GatewayResult:
    status_code: int
    correlation_id: str | None
    body: dict[str, Any]


class SmsGatewayClient:
    def __init__(self, base_url: str, api_token: str, timeout: float = 15.0) -> None:
        if not base_url or not api_token or timeout <= 0:
            raise ValueError("Client configuration is invalid.")
        self._base_url = base_url.rstrip("/")
        self._api_token = api_token
        self._timeout = timeout

    def send_message(self, message: dict[str, Any], idempotency_key: str, correlation_id: str | None = None) -> GatewayResult:
        return self._request("POST", "/api/v1/messages", message, idempotency_key, correlation_id)

    def send_bulk(self, batch: dict[str, Any], idempotency_key: str, correlation_id: str | None = None) -> GatewayResult:
        return self._request("POST", "/api/v1/messages/bulk", batch, idempotency_key, correlation_id)

    def get_status(self, message_id: str, correlation_id: str | None = None) -> GatewayResult:
        return self._request("GET", f"/api/v1/messages/{message_id}/status", None, None, correlation_id)

    def configure_webhook(self, configuration: dict[str, Any], correlation_id: str | None = None) -> GatewayResult:
        return self._request("PUT", "/api/v1/webhook", configuration, None, correlation_id)

    def _request(self, method: str, path: str, body: dict[str, Any] | None, idempotency_key: str | None, correlation_id: str | None) -> GatewayResult:
        headers = {"Accept": "application/json", "Authorization": f"Bearer {self._api_token}"}
        if idempotency_key:
            headers["Idempotency-Key"] = idempotency_key
        if correlation_id:
            headers["X-Correlation-ID"] = correlation_id
        try:
            response = requests.request(method, self._base_url + path, headers=headers, json=body, timeout=self._timeout)
        except requests.RequestException as error:
            raise RuntimeError("Gateway request failed safely.") from error
        try:
            decoded = response.json()
        except requests.JSONDecodeError as error:
            raise RuntimeError("Gateway returned invalid JSON.") from error
        if not isinstance(decoded, dict):
            raise RuntimeError("Gateway returned an unexpected JSON value.")
        return GatewayResult(response.status_code, response.headers.get("X-Correlation-ID"), decoded)

