import logging from typing import Any import aiohttp from config import settings logger = logging.getLogger(__name__) class APIClient: """HTTP client for backend API communication.""" def __init__(self): self.base_url = settings.API_URL self._session: aiohttp.ClientSession | None = None logger.info(f"[APIClient] Initialized with base_url: {self.base_url}") async def _get_session(self) -> aiohttp.ClientSession: if self._session is None or self._session.closed: logger.info("[APIClient] Creating new aiohttp session") self._session = aiohttp.ClientSession() return self._session async def _request( self, method: str, endpoint: str, **kwargs ) -> dict[str, Any] | None: """Make HTTP request to backend API.""" session = await self._get_session() url = f"{self.base_url}/api/v1{endpoint}" logger.info(f"[APIClient] {method} {url}") if 'json' in kwargs: logger.info(f"[APIClient] Request body: {kwargs['json']}") if 'params' in kwargs: logger.info(f"[APIClient] Request params: {kwargs['params']}") try: async with session.request(method, url, **kwargs) as response: logger.info(f"[APIClient] Response status: {response.status}") response_text = await response.text() logger.info(f"[APIClient] Response body: {response_text[:500]}") if response.status == 200: import json return json.loads(response_text) elif response.status == 404: logger.warning(f"[APIClient] 404 Not Found") return None else: logger.error(f"[APIClient] API error {response.status}: {response_text}") return {"error": response_text} except aiohttp.ClientError as e: logger.error(f"[APIClient] Request failed: {e}") return {"error": str(e)} except Exception as e: logger.error(f"[APIClient] Unexpected error: {e}") return {"error": str(e)} async def confirm_telegram_link( self, token: str, telegram_id: int, telegram_username: str | None ) -> dict[str, Any]: """Confirm Telegram account linking.""" result = await self._request( "POST", "/telegram/confirm-link", json={ "token": token, "telegram_id": telegram_id, "telegram_username": telegram_username } ) return result or {"error": "Не удалось связаться с сервером"} async def get_user_by_telegram_id(self, telegram_id: int) -> dict[str, Any] | None: """Get user by Telegram ID.""" return await self._request("GET", f"/telegram/user/{telegram_id}") async def unlink_telegram(self, telegram_id: int) -> dict[str, Any]: """Unlink Telegram account.""" result = await self._request( "POST", f"/telegram/unlink/{telegram_id}" ) return result or {"error": "Не удалось связаться с сервером"} async def get_user_marathons(self, telegram_id: int) -> list[dict[str, Any]]: """Get user's marathons.""" result = await self._request("GET", f"/telegram/marathons/{telegram_id}") if isinstance(result, list): return result return result.get("marathons", []) if result else [] async def get_marathon_details( self, marathon_id: int, telegram_id: int ) -> dict[str, Any] | None: """Get marathon details for user.""" return await self._request( "GET", f"/telegram/marathon/{marathon_id}", params={"telegram_id": telegram_id} ) async def get_user_stats(self, telegram_id: int) -> dict[str, Any] | None: """Get user's overall statistics.""" return await self._request("GET", f"/telegram/stats/{telegram_id}") async def close(self): """Close the HTTP session.""" if self._session and not self._session.closed: await self._session.close() # Global API client instance api_client = APIClient()