"""Client for tab-api-ingest service.

Provides async HTTP client for consuming racing data from the tab-api-ingest
REST API instead of calling the TAB Affiliates API directly.
"""

from __future__ import annotations

from typing import Any

import httpx

from packages.core.common.logging import get_logger
from packages.core.common.settings import get_settings

logger = get_logger(__name__)


class IngestServiceClient:
    """Async HTTP client for tab-api-ingest REST API.

    Fetches meetings, races, and runner data from the tab-api-ingest service
    which sources data from the TAB/HRNZ APIs and stores it in its own DB.

    Example:
        >>> async with IngestServiceClient() as client:
        >>>     meetings = await client.get_meetings(date="2024-01-01")
        >>>     for meeting in meetings:
        >>>         races = await client.get_races(meeting_id=meeting["meeting"])
    """

    def __init__(self, base_url: str | None = None):
        """Initialize the ingest service client.

        Args:
            base_url: tab-api-ingest service base URL. Defaults to the
                      INGEST_SERVICE_URL setting or http://localhost:9090.
        """
        settings = get_settings()
        self.base_url = (
            base_url
            or getattr(settings, "ingest_service", None)
            and settings.ingest_service.url
            or "http://localhost:9090"
        )
        self._client: httpx.AsyncClient | None = None

    async def __aenter__(self):
        """Async context manager entry — creates the HTTP client."""
        self._client = httpx.AsyncClient(timeout=30.0)
        return self

    async def __aexit__(self, exc_type, exc_val, exc_tb):
        """Async context manager exit — closes the HTTP client."""
        if self._client is not None:
            await self._client.aclose()
            self._client = None

    async def _request(
        self,
        method: str,
        url: str,
        params: dict[str, Any] | None = None,
    ) -> Any:
        """Make an HTTP request with the internal client.

        Args:
            method: HTTP method
            url: Full URL
            params: Query parameters

        Returns:
            Response JSON data

        Raises:
            httpx.HTTPError: On request failure
        """
        if self._client is None:
            self._client = httpx.AsyncClient(timeout=30.0)
        response = await self._client.request(method, url, params=params)
        response.raise_for_status()
        return response.json()

    async def get_meetings(
        self,
        date: str | None = None,
        country: str | None = None,
        category: str | None = None,
    ) -> list[dict[str, Any]]:
        """Fetch meetings from the ingest service.

        Args:
            date: Optional ISO date filter (YYYY-MM-DD)
            country: Optional country code filter (e.g. NZ, AUS)
            category: Optional racing category (T, H, G)

        Returns:
            List of meeting data dictionaries
        """
        params: dict[str, Any] = {}
        if date:
            params["date"] = date
        if country:
            params["country"] = country
        if category:
            params["category"] = category

        logger.info(
            "Fetching meetings from ingest service",
            extra={"params": params},
        )

        data = await self._request("GET", f"{self.base_url}/api/meetings", params)
        logger.info(f"Found {len(data)} meetings from ingest service")
        return data

    async def get_race(self, race_id: str) -> dict[str, Any]:
        """Fetch a single race from the ingest service.

        Args:
            race_id: Race/event ID (string)

        Returns:
            Race data dictionary
        """
        logger.info(f"Fetching race {race_id} from ingest service")
        return await self._request("GET", f"{self.base_url}/api/races/{race_id}")

    async def get_runners(self, race_id: str) -> list[dict[str, Any]]:
        """Fetch runners for a race from the ingest service.

        Args:
            race_id: Race/event ID (string)

        Returns:
            List of runner data dictionaries
        """
        logger.info(f"Fetching runners for race {race_id} from ingest service")
        return await self._request(
            "GET",
            f"{self.base_url}/api/races/{race_id}/runners",
        )

    async def get_races(
        self,
        date: str | None = None,
        meeting_id: str | None = None,
        status: str | None = None,
        limit: int = 100,
        offset: int = 0,
    ) -> list[dict[str, Any]]:
        """Fetch races from the ingest service.

        Args:
            date: Optional ISO date filter (YYYY-MM-DD)
            meeting_id: Optional meeting ID filter
            status: Optional status filter
            limit: Maximum results (default 100)
            offset: Pagination offset (default 0)

        Returns:
            List of race data dictionaries
        """
        params: dict[str, Any] = {"limit": limit, "offset": offset}
        if date:
            params["date"] = date
        if meeting_id:
            params["meetingId"] = meeting_id
        if status:
            params["status"] = status

        logger.info(
            "Fetching races from ingest service",
            extra={"params": params},
        )

        return await self._request("GET", f"{self.base_url}/api/races", params)
