"""Exchanges resource — wraps /exchanges endpoints."""

from __future__ import annotations

from functools import cached_property
from typing import TYPE_CHECKING, List
from urllib.parse import quote

import httpx

from rulebook._resource import AsyncAPIResource, SyncAPIResource
from rulebook._response import APIResponse
from rulebook._types import NOT_GIVEN, Headers, NotGiven, Query, make_request_options
from rulebook.types.exchange import Exchange
from rulebook.types.exchange_detail import ExchangeDetail

__all__ = [
    "Exchanges",
    "AsyncExchanges",
    "ExchangesWithRawResponse",
    "AsyncExchangesWithRawResponse",
]


class Exchanges(SyncAPIResource):
    """Access exchange discovery endpoints.

    Usage::

        exchanges = client.exchanges.list()
        detail = client.exchanges.retrieve("NYSE")
    """

    @cached_property
    def with_raw_response(self) -> ExchangesWithRawResponse:
        """Returns a resource wrapper that provides raw HTTP response access.

        Usage::

            raw = client.exchanges.with_raw_response.retrieve("NYSE")
            print(raw.status_code)
            detail = raw.parse()
        """
        return ExchangesWithRawResponse(self)

    def list(
        self,
        *,
        extra_headers: Headers | None = None,
        extra_query: Query | None = None,
        timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
    ) -> List[Exchange]:
        """List all exchanges accessible to the authenticated user.

        Each exchange includes a summary of available fee types and total record count.

        Args:
            extra_headers: Send extra headers with this request.
            extra_query: Add additional query parameters to this request.
            timeout: Override the client-level default timeout for this request, in seconds.
        """
        return self._get(
            "/exchanges/",
            cast_to=list[Exchange],
            options=make_request_options(
                extra_headers=extra_headers,
                extra_query=extra_query,
                timeout=timeout,
            ),
        )

    def retrieve(
        self,
        exchange_name: str,
        *,
        extra_headers: Headers | None = None,
        extra_query: Query | None = None,
        timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
    ) -> ExchangeDetail:
        """Get full metadata for a specific exchange.

        Returns all available filter values (fee types, categories, actions,
        participants, symbol classifications, symbol types, trade types) and the
        date range of available data. Use this to discover what query parameters
        you can use when fetching fee schedule results.

        Args:
            exchange_name: Exchange identifier (e.g., ``"NYSE"``, ``"CBOE"``, ``"NASDAQ"``).
            extra_headers: Send extra headers with this request.
            extra_query: Add additional query parameters to this request.
            timeout: Override the client-level default timeout for this request, in seconds.

        Raises:
            ValueError: If ``exchange_name`` is empty.
            NotFoundError: If the exchange does not exist (404).
            PermissionDeniedError: If you don't have access to this exchange (403).
        """
        if not exchange_name:
            raise ValueError(
                f"Expected a non-empty value for `exchange_name` but received {exchange_name!r}"
            )
        return self._get(
            f"/exchanges/{quote(exchange_name, safe='')}",
            cast_to=ExchangeDetail,
            options=make_request_options(
                extra_headers=extra_headers,
                extra_query=extra_query,
                timeout=timeout,
            ),
        )


class AsyncExchanges(AsyncAPIResource):
    """Async access to exchange discovery endpoints.

    Usage::

        exchanges = await client.exchanges.list()
        detail = await client.exchanges.retrieve("NYSE")
    """

    @cached_property
    def with_raw_response(self) -> AsyncExchangesWithRawResponse:
        return AsyncExchangesWithRawResponse(self)

    async def list(
        self,
        *,
        extra_headers: Headers | None = None,
        extra_query: Query | None = None,
        timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
    ) -> List[Exchange]:
        """List all exchanges accessible to the authenticated user.

        Args:
            extra_headers: Send extra headers with this request.
            extra_query: Add additional query parameters to this request.
            timeout: Override the client-level default timeout for this request, in seconds.
        """
        return await self._get(
            "/exchanges/",
            cast_to=list[Exchange],
            options=make_request_options(
                extra_headers=extra_headers,
                extra_query=extra_query,
                timeout=timeout,
            ),
        )

    async def retrieve(
        self,
        exchange_name: str,
        *,
        extra_headers: Headers | None = None,
        extra_query: Query | None = None,
        timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
    ) -> ExchangeDetail:
        """Get full metadata for a specific exchange.

        Args:
            exchange_name: Exchange identifier (e.g., ``"NYSE"``, ``"CBOE"``, ``"NASDAQ"``).
            extra_headers: Send extra headers with this request.
            extra_query: Add additional query parameters to this request.
            timeout: Override the client-level default timeout for this request, in seconds.
        """
        if not exchange_name:
            raise ValueError(
                f"Expected a non-empty value for `exchange_name` but received {exchange_name!r}"
            )
        return await self._get(
            f"/exchanges/{quote(exchange_name, safe='')}",
            cast_to=ExchangeDetail,
            options=make_request_options(
                extra_headers=extra_headers,
                extra_query=extra_query,
                timeout=timeout,
            ),
        )


# ---------------------------------------------------------------------------
# Raw response wrappers
# ---------------------------------------------------------------------------


class ExchangesWithRawResponse:
    """Provides raw HTTP response access for exchange endpoints.

    Usage::

        raw = client.exchanges.with_raw_response.list()
        print(raw.status_code)
        exchanges = raw.parse()
    """

    _exchanges: Exchanges

    def __init__(self, exchanges: Exchanges) -> None:
        self._exchanges = exchanges

    def list(
        self,
        *,
        extra_headers: Headers | None = None,
        extra_query: Query | None = None,
        timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
    ) -> APIResponse[List[Exchange]]:
        """List exchanges, returning raw API response."""
        opts = make_request_options(
            extra_headers=extra_headers, extra_query=extra_query, timeout=timeout
        )
        # Make request through the client but capture raw response
        client = self._exchanges._client
        response = client._client.get(
            f"{client._base_url}/exchanges/",
            headers={**client.default_headers, **(opts.get("headers") or {})},
            timeout=opts.get("timeout", client._timeout),
        )
        client._process_response_raw(response)
        return APIResponse(response=response, cast_to=list[Exchange])

    def retrieve(
        self,
        exchange_name: str,
        *,
        extra_headers: Headers | None = None,
        extra_query: Query | None = None,
        timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
    ) -> APIResponse[ExchangeDetail]:
        """Get exchange detail, returning raw API response."""
        if not exchange_name:
            raise ValueError(
                f"Expected a non-empty value for `exchange_name` but received {exchange_name!r}"
            )
        opts = make_request_options(
            extra_headers=extra_headers, extra_query=extra_query, timeout=timeout
        )
        client = self._exchanges._client
        response = client._client.get(
            f"{client._base_url}/exchanges/{quote(exchange_name, safe='')}",
            headers={**client.default_headers, **(opts.get("headers") or {})},
            timeout=opts.get("timeout", client._timeout),
        )
        client._process_response_raw(response)
        return APIResponse(response=response, cast_to=ExchangeDetail)


class AsyncExchangesWithRawResponse:
    """Async raw HTTP response access for exchange endpoints."""

    _exchanges: AsyncExchanges

    def __init__(self, exchanges: AsyncExchanges) -> None:
        self._exchanges = exchanges

    async def list(
        self,
        *,
        extra_headers: Headers | None = None,
        extra_query: Query | None = None,
        timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
    ) -> APIResponse[List[Exchange]]:
        opts = make_request_options(
            extra_headers=extra_headers, extra_query=extra_query, timeout=timeout
        )
        client = self._exchanges._client
        response = await client._client.get(
            f"{client._base_url}/exchanges/",
            headers={**client.default_headers, **(opts.get("headers") or {})},
            timeout=opts.get("timeout", client._timeout),
        )
        client._process_response_raw(response)
        return APIResponse(response=response, cast_to=list[Exchange])

    async def retrieve(
        self,
        exchange_name: str,
        *,
        extra_headers: Headers | None = None,
        extra_query: Query | None = None,
        timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
    ) -> APIResponse[ExchangeDetail]:
        if not exchange_name:
            raise ValueError(
                f"Expected a non-empty value for `exchange_name` but received {exchange_name!r}"
            )
        opts = make_request_options(
            extra_headers=extra_headers, extra_query=extra_query, timeout=timeout
        )
        client = self._exchanges._client
        response = await client._client.get(
            f"{client._base_url}/exchanges/{quote(exchange_name, safe='')}",
            headers={**client.default_headers, **(opts.get("headers") or {})},
            timeout=opts.get("timeout", client._timeout),
        )
        client._process_response_raw(response)
        return APIResponse(response=response, cast_to=ExchangeDetail)
