"""Beacon v2 API client."""

from __future__ import annotations

from typing import Any

from ai4gh.core.client import GA4GHClient
from ai4gh.specs.beacon.models import (
    BeaconInfo,
    BeaconQuery,
    BeaconRequest,
    BeaconRequestMeta,
    BeaconResponse,
    Granularity,
)


class BeaconClient(GA4GHClient):
    """Client for the GA4GH Beacon v2 API.

    Beacon v2 provides a discovery protocol for genomic and clinical data.
    It supports queries across multiple entry types including individuals,
    biosamples, genomic variants, cohorts, runs, and analyses.
    """

    spec_name = "beacon"
    spec_version = "2.0.0"
    base_path = ""

    async def get_info(self) -> BeaconInfo:
        """Get Beacon info (/ endpoint).

        Returns:
            Beacon informational response.
        """
        data = await self._request("GET", "/")
        response = data.get("response", data)
        return BeaconInfo.model_validate(response)

    async def get_configuration(self) -> dict[str, Any]:
        """Get Beacon configuration.

        Returns:
            Beacon configuration object.
        """
        data: dict[str, Any] = await self._request("GET", "/configuration")
        return data

    async def get_map(self) -> dict[str, Any]:
        """Get Beacon map (available entry types and endpoints).

        Returns:
            Beacon map object.
        """
        data: dict[str, Any] = await self._request("GET", "/map")
        return data

    async def get_entry_types(self) -> dict[str, Any]:
        """Get available entry types.

        Returns:
            Entry types configuration.
        """
        data: dict[str, Any] = await self._request("GET", "/entry_types")
        return data

    async def get_filtering_terms(
        self,
        *,
        skip: int | None = None,
        limit: int | None = None,
    ) -> dict[str, Any]:
        """Get available filtering terms.

        Args:
            skip: Number of results to skip.
            limit: Maximum number of results.

        Returns:
            Filtering terms response.
        """
        params: dict[str, Any] = {}
        if skip is not None:
            params["skip"] = skip
        if limit is not None:
            params["limit"] = limit
        result: dict[str, Any] = await self._request(
            "GET", "/filtering_terms", params=params or None
        )
        return result

    async def query(
        self,
        entry_type: str,
        *,
        filters: list[dict[str, Any]] | None = None,
        request_parameters: dict[str, Any] | None = None,
        granularity: Granularity | None = None,
        skip: int | None = None,
        limit: int | None = None,
        test_mode: bool = False,
    ) -> BeaconResponse:
        """Query a Beacon entry type (POST).

        Args:
            entry_type: Entry type to query (e.g., "individuals", "biosamples",
                "g_variants", "cohorts", "runs", "analyses").
            filters: List of filter objects.
            request_parameters: Additional request parameters.
            granularity: Response granularity (boolean, count, record).
            skip: Number of results to skip.
            limit: Maximum number of results.
            test_mode: If true, return test data.

        Returns:
            Beacon response.
        """
        query = BeaconQuery(
            filters=None,
            request_parameters=request_parameters,
            test_mode=test_mode,
        )
        if skip is not None or limit is not None:
            query.pagination = {}
            if skip is not None:
                query.pagination["skip"] = skip
            if limit is not None:
                query.pagination["limit"] = limit

        meta = BeaconRequestMeta()
        if granularity:
            meta.requested_schemas = []

        request = BeaconRequest(meta=meta, query=query)
        body = request.model_dump(exclude_none=True)

        if filters:
            body.setdefault("query", {})["filters"] = filters

        if granularity:
            body.setdefault("query", {})["requestedGranularity"] = granularity.value

        data = await self._request("POST", f"/{entry_type}", json=body)
        return BeaconResponse.model_validate(data)

    async def query_get(
        self,
        entry_type: str,
        *,
        skip: int | None = None,
        limit: int | None = None,
    ) -> BeaconResponse:
        """Query a Beacon entry type (GET).

        Args:
            entry_type: Entry type to query.
            skip: Number of results to skip.
            limit: Maximum number of results.

        Returns:
            Beacon response.
        """
        params: dict[str, Any] = {}
        if skip is not None:
            params["skip"] = skip
        if limit is not None:
            params["limit"] = limit
        data = await self._request("GET", f"/{entry_type}", params=params or None)
        return BeaconResponse.model_validate(data)
