import json
import requests
from typing import Dict, Any, Optional


class ResolutionResult:
    """Wraps the full API response for easy access to all fields."""

    def __init__(self, data: Dict[str, Any]):
        self._data = data

    @property
    def resolved_state(self) -> Dict[str, Any]:
        return self._data.get("resolved_state", {})

    @property
    def resolution_id(self) -> Optional[str]:
        return self._data.get("resolution_id")

    @property
    def status(self) -> str:
        return self._data.get("status", "unknown")

    @property
    def trial_remaining(self) -> Optional[int]:
        return self._data.get("trial_remaining")

    @property
    def billed(self) -> bool:
        return self._data.get("billed", False)

    def raw(self) -> Dict[str, Any]:
        return self._data

    def __repr__(self):
        return f"ResolutionResult(status={self.status!r}, resolution_id={self.resolution_id!r})"


class SignalCendError(Exception):
    def __init__(self, status_code: int, error_code: str, message: str):
        self.status_code = status_code
        self.error_code = error_code
        self.message = message
        super().__init__(f"[{error_code}] {message} (HTTP {status_code})")


class Client:
    """
    Official Python client for the SignalCend device state resolution API.

    Usage:
        from signalcend import Client

        client = Client(api_key="your-api-key", api_secret="your-api-secret")

        result = client.resolve({
            "sensor_007": [
                {"timestamp": "2026-01-15T14:32:01Z", "value": "offline", "signal_strength": -82},
                {"timestamp": "2026-01-15T14:32:03Z", "value": "online",  "signal_strength": -71}
            ]
        })

        print(result.resolved_state)
        print(result.resolution_id)
    """

    BASE_URL = "https://api.signalcend.com"

    def __init__(
        self,
        api_key: str,
        api_secret: str,
        base_url: Optional[str] = None,
        timeout: int = 30,
    ):
        if not api_key:
            raise ValueError("api_key is required")
        if not api_secret:
            raise ValueError("api_secret is required")

        self.api_key = api_key
        self.api_secret = api_secret
        self.base_url = (base_url or self.BASE_URL).rstrip("/")
        self.timeout = timeout
        self._session = requests.Session()
        self._session.headers.update({"Content-Type": "application/json"})

    def _sign(self, body: str) -> str:
        response = self._session.post(
            f"{self.base_url}/sign",
            data=body,
            timeout=self.timeout,
        )
        response.raise_for_status()
        data = response.json()
        signature = data.get("signature")
        if not signature:
            raise SignalCendError(
                response.status_code,
                "SIGN_FAILED",
                f"Sign endpoint returned no signature. Response: {data}"
            )
        return signature

    def resolve(
        self,
        events: Dict[str, Any],
        session_id: Optional[str] = None,
        reconnect_window_seconds: Optional[int] = None,
    ) -> ResolutionResult:
        payload: Dict[str, Any] = {
            "api_key": self.api_key,
            "events": events,
        }
        if session_id is not None:
            payload["session_id"] = session_id
        if reconnect_window_seconds is not None:
            payload["reconnect_window_seconds"] = reconnect_window_seconds

        body = json.dumps(payload, separators=(",", ":"))
        signature = self._sign(body)

        response = self._session.post(
            f"{self.base_url}/resolve",
            data=body,
            headers={"X-Signature": signature},
            timeout=self.timeout,
        )

        data = response.json()

        if not response.ok:
            raise SignalCendError(
                response.status_code,
                data.get("error_code", "UNKNOWN_ERROR"),
                data.get("message", "An unknown error occurred"),
            )

        return ResolutionResult(data)

    def usage(self) -> Dict[str, Any]:
        response = self._session.get(
            f"{self.base_url}/v1/usage",
            params={"api_key": self.api_key},
            timeout=self.timeout,
        )
        response.raise_for_status()
        return response.json()

    def __repr__(self):
        return f"Client(api_key={self.api_key[:8]}...)"
