from __future__ import annotations

import logging
import time
from typing import Any

import httpx


logger = logging.getLogger("connecthub.integrations")


class BaseClient:
    """
    统一的外部系统访问 SDK 基类。
    业务 Job 禁止直接写 HTTP，只能调用 integrations 下的 Client。
    """

    def __init__(
        self,
        *,
        base_url: str,
        timeout_s: float = 10.0,
        retries: int = 2,
        retry_backoff_s: float = 0.5,
        headers: dict[str, str] | None = None,
    ) -> None:
        self.base_url = base_url.rstrip("/")
        self.timeout_s = timeout_s
        self.retries = retries
        self.retry_backoff_s = retry_backoff_s
        self.headers = headers or {}

        self._client = httpx.Client(
            base_url=self.base_url,
            timeout=httpx.Timeout(self.timeout_s),
            headers=self.headers,
        )

    def close(self) -> None:
        self._client.close()

    def request(self, method: str, path: str, **kwargs: Any) -> httpx.Response:
        url = path if path.startswith("/") else f"/{path}"
        last_exc: Exception | None = None
        for attempt in range(self.retries + 1):
            try:
                start = time.time()
                resp = self._client.request(method=method, url=url, **kwargs)
                elapsed_ms = int((time.time() - start) * 1000)
                logger.info("HTTP %s %s -> %s (%sms)", method, url, resp.status_code, elapsed_ms)
                resp.raise_for_status()
                return resp
            except Exception as e:  # noqa: BLE001 (framework-wide)
                last_exc = e
                logger.warning("HTTP failed (%s %s) attempt=%s err=%r", method, url, attempt + 1, e)
                if attempt < self.retries:
                    time.sleep(self.retry_backoff_s * (2**attempt))
                    continue
                raise
        assert last_exc is not None
        raise last_exc

    def get_json(self, path: str, **kwargs: Any) -> Any:
        return self.request("GET", path, **kwargs).json()

    def post_json(self, path: str, json: Any = None, **kwargs: Any) -> Any:
        return self.request("POST", path, json=json, **kwargs).json()


