# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

from __future__ import annotations

import os
from typing import TYPE_CHECKING, Any, Mapping
from typing_extensions import Self, override

import httpx

from . import _exceptions
from ._qs import Querystring
from ._types import (
    Omit,
    Timeout,
    NotGiven,
    Transport,
    ProxiesTypes,
    RequestOptions,
    not_given,
)
from ._utils import is_given, get_async_library
from ._compat import cached_property
from ._models import SecurityOptions
from ._version import __version__
from ._streaming import Stream as Stream, AsyncStream as AsyncStream
from ._exceptions import RevoxError, APIStatusError
from ._base_client import (
    DEFAULT_MAX_RETRIES,
    SyncAPIClient,
    AsyncAPIClient,
)

if TYPE_CHECKING:
    from .resources import call, users, voices, campaigns, assistants
    from .resources.call import CallResource, AsyncCallResource
    from .resources.voices import VoicesResource, AsyncVoicesResource
    from .resources.campaigns import CampaignsResource, AsyncCampaignsResource
    from .resources.assistants import AssistantsResource, AsyncAssistantsResource
    from .resources.users.users import UsersResource, AsyncUsersResource

__all__ = ["Timeout", "Transport", "ProxiesTypes", "RequestOptions", "Revox", "AsyncRevox", "Client", "AsyncClient"]


class Revox(SyncAPIClient):
    # client options
    api_key: str

    def __init__(
        self,
        *,
        api_key: str | None = None,
        base_url: str | httpx.URL | None = None,
        timeout: float | Timeout | None | NotGiven = not_given,
        max_retries: int = DEFAULT_MAX_RETRIES,
        default_headers: Mapping[str, str] | None = None,
        default_query: Mapping[str, object] | None = None,
        # Configure a custom httpx client.
        # We provide a `DefaultHttpxClient` class that you can pass to retain the default values we use for `limits`, `timeout` & `follow_redirects`.
        # See the [httpx documentation](https://www.python-httpx.org/api/#client) for more details.
        http_client: httpx.Client | None = None,
        # Enable or disable schema validation for data returned by the API.
        # When enabled an error APIResponseValidationError is raised
        # if the API responds with invalid data for the expected schema.
        #
        # This parameter may be removed or changed in the future.
        # If you rely on this feature, please open a GitHub issue
        # outlining your use-case to help us decide if it should be
        # part of our public interface in the future.
        _strict_response_validation: bool = False,
    ) -> None:
        """Construct a new synchronous Revox client instance.

        This automatically infers the `api_key` argument from the `REVOX_API_KEY` environment variable if it is not provided.
        """
        if api_key is None:
            api_key = os.environ.get("REVOX_API_KEY")
        if api_key is None:
            raise RevoxError(
                "The api_key client option must be set either by passing api_key to the client or by setting the REVOX_API_KEY environment variable"
            )
        self.api_key = api_key

        if base_url is None:
            base_url = os.environ.get("REVOX_BASE_URL")
        if base_url is None:
            base_url = f"https://www.getrevox.com/api"

        super().__init__(
            version=__version__,
            base_url=base_url,
            max_retries=max_retries,
            timeout=timeout,
            http_client=http_client,
            custom_headers=default_headers,
            custom_query=default_query,
            _strict_response_validation=_strict_response_validation,
        )

    @cached_property
    def campaigns(self) -> CampaignsResource:
        from .resources.campaigns import CampaignsResource

        return CampaignsResource(self)

    @cached_property
    def assistants(self) -> AssistantsResource:
        from .resources.assistants import AssistantsResource

        return AssistantsResource(self)

    @cached_property
    def call(self) -> CallResource:
        from .resources.call import CallResource

        return CallResource(self)

    @cached_property
    def voices(self) -> VoicesResource:
        from .resources.voices import VoicesResource

        return VoicesResource(self)

    @cached_property
    def users(self) -> UsersResource:
        from .resources.users import UsersResource

        return UsersResource(self)

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

    @cached_property
    def with_streaming_response(self) -> RevoxWithStreamedResponse:
        return RevoxWithStreamedResponse(self)

    @property
    @override
    def qs(self) -> Querystring:
        return Querystring(array_format="comma")

    @override
    def _auth_headers(self, security: SecurityOptions) -> dict[str, str]:
        return {
            **(self._bearer_auth if security.get("bearer_auth", False) else {}),
        }

    @property
    def _bearer_auth(self) -> dict[str, str]:
        api_key = self.api_key
        return {"Authorization": f"Bearer {api_key}"}

    @property
    @override
    def default_headers(self) -> dict[str, str | Omit]:
        return {
            **super().default_headers,
            "X-Stainless-Async": "false",
            **self._custom_headers,
        }

    def copy(
        self,
        *,
        api_key: str | None = None,
        base_url: str | httpx.URL | None = None,
        timeout: float | Timeout | None | NotGiven = not_given,
        http_client: httpx.Client | None = None,
        max_retries: int | NotGiven = not_given,
        default_headers: Mapping[str, str] | None = None,
        set_default_headers: Mapping[str, str] | None = None,
        default_query: Mapping[str, object] | None = None,
        set_default_query: Mapping[str, object] | None = None,
        _extra_kwargs: Mapping[str, Any] = {},
    ) -> Self:
        """
        Create a new client instance re-using the same options given to the current client with optional overriding.
        """
        if default_headers is not None and set_default_headers is not None:
            raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive")

        if default_query is not None and set_default_query is not None:
            raise ValueError("The `default_query` and `set_default_query` arguments are mutually exclusive")

        headers = self._custom_headers
        if default_headers is not None:
            headers = {**headers, **default_headers}
        elif set_default_headers is not None:
            headers = set_default_headers

        params = self._custom_query
        if default_query is not None:
            params = {**params, **default_query}
        elif set_default_query is not None:
            params = set_default_query

        http_client = http_client or self._client
        return self.__class__(
            api_key=api_key or self.api_key,
            base_url=base_url or self.base_url,
            timeout=self.timeout if isinstance(timeout, NotGiven) else timeout,
            http_client=http_client,
            max_retries=max_retries if is_given(max_retries) else self.max_retries,
            default_headers=headers,
            default_query=params,
            **_extra_kwargs,
        )

    # Alias for `copy` for nicer inline usage, e.g.
    # client.with_options(timeout=10).foo.create(...)
    with_options = copy

    @override
    def _make_status_error(
        self,
        err_msg: str,
        *,
        body: object,
        response: httpx.Response,
    ) -> APIStatusError:
        if response.status_code == 400:
            return _exceptions.BadRequestError(err_msg, response=response, body=body)

        if response.status_code == 401:
            return _exceptions.AuthenticationError(err_msg, response=response, body=body)

        if response.status_code == 403:
            return _exceptions.PermissionDeniedError(err_msg, response=response, body=body)

        if response.status_code == 404:
            return _exceptions.NotFoundError(err_msg, response=response, body=body)

        if response.status_code == 409:
            return _exceptions.ConflictError(err_msg, response=response, body=body)

        if response.status_code == 422:
            return _exceptions.UnprocessableEntityError(err_msg, response=response, body=body)

        if response.status_code == 429:
            return _exceptions.RateLimitError(err_msg, response=response, body=body)

        if response.status_code >= 500:
            return _exceptions.InternalServerError(err_msg, response=response, body=body)
        return APIStatusError(err_msg, response=response, body=body)


class AsyncRevox(AsyncAPIClient):
    # client options
    api_key: str

    def __init__(
        self,
        *,
        api_key: str | None = None,
        base_url: str | httpx.URL | None = None,
        timeout: float | Timeout | None | NotGiven = not_given,
        max_retries: int = DEFAULT_MAX_RETRIES,
        default_headers: Mapping[str, str] | None = None,
        default_query: Mapping[str, object] | None = None,
        # Configure a custom httpx client.
        # We provide a `DefaultAsyncHttpxClient` class that you can pass to retain the default values we use for `limits`, `timeout` & `follow_redirects`.
        # See the [httpx documentation](https://www.python-httpx.org/api/#asyncclient) for more details.
        http_client: httpx.AsyncClient | None = None,
        # Enable or disable schema validation for data returned by the API.
        # When enabled an error APIResponseValidationError is raised
        # if the API responds with invalid data for the expected schema.
        #
        # This parameter may be removed or changed in the future.
        # If you rely on this feature, please open a GitHub issue
        # outlining your use-case to help us decide if it should be
        # part of our public interface in the future.
        _strict_response_validation: bool = False,
    ) -> None:
        """Construct a new async AsyncRevox client instance.

        This automatically infers the `api_key` argument from the `REVOX_API_KEY` environment variable if it is not provided.
        """
        if api_key is None:
            api_key = os.environ.get("REVOX_API_KEY")
        if api_key is None:
            raise RevoxError(
                "The api_key client option must be set either by passing api_key to the client or by setting the REVOX_API_KEY environment variable"
            )
        self.api_key = api_key

        if base_url is None:
            base_url = os.environ.get("REVOX_BASE_URL")
        if base_url is None:
            base_url = f"https://www.getrevox.com/api"

        super().__init__(
            version=__version__,
            base_url=base_url,
            max_retries=max_retries,
            timeout=timeout,
            http_client=http_client,
            custom_headers=default_headers,
            custom_query=default_query,
            _strict_response_validation=_strict_response_validation,
        )

    @cached_property
    def campaigns(self) -> AsyncCampaignsResource:
        from .resources.campaigns import AsyncCampaignsResource

        return AsyncCampaignsResource(self)

    @cached_property
    def assistants(self) -> AsyncAssistantsResource:
        from .resources.assistants import AsyncAssistantsResource

        return AsyncAssistantsResource(self)

    @cached_property
    def call(self) -> AsyncCallResource:
        from .resources.call import AsyncCallResource

        return AsyncCallResource(self)

    @cached_property
    def voices(self) -> AsyncVoicesResource:
        from .resources.voices import AsyncVoicesResource

        return AsyncVoicesResource(self)

    @cached_property
    def users(self) -> AsyncUsersResource:
        from .resources.users import AsyncUsersResource

        return AsyncUsersResource(self)

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

    @cached_property
    def with_streaming_response(self) -> AsyncRevoxWithStreamedResponse:
        return AsyncRevoxWithStreamedResponse(self)

    @property
    @override
    def qs(self) -> Querystring:
        return Querystring(array_format="comma")

    @override
    def _auth_headers(self, security: SecurityOptions) -> dict[str, str]:
        return {
            **(self._bearer_auth if security.get("bearer_auth", False) else {}),
        }

    @property
    def _bearer_auth(self) -> dict[str, str]:
        api_key = self.api_key
        return {"Authorization": f"Bearer {api_key}"}

    @property
    @override
    def default_headers(self) -> dict[str, str | Omit]:
        return {
            **super().default_headers,
            "X-Stainless-Async": f"async:{get_async_library()}",
            **self._custom_headers,
        }

    def copy(
        self,
        *,
        api_key: str | None = None,
        base_url: str | httpx.URL | None = None,
        timeout: float | Timeout | None | NotGiven = not_given,
        http_client: httpx.AsyncClient | None = None,
        max_retries: int | NotGiven = not_given,
        default_headers: Mapping[str, str] | None = None,
        set_default_headers: Mapping[str, str] | None = None,
        default_query: Mapping[str, object] | None = None,
        set_default_query: Mapping[str, object] | None = None,
        _extra_kwargs: Mapping[str, Any] = {},
    ) -> Self:
        """
        Create a new client instance re-using the same options given to the current client with optional overriding.
        """
        if default_headers is not None and set_default_headers is not None:
            raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive")

        if default_query is not None and set_default_query is not None:
            raise ValueError("The `default_query` and `set_default_query` arguments are mutually exclusive")

        headers = self._custom_headers
        if default_headers is not None:
            headers = {**headers, **default_headers}
        elif set_default_headers is not None:
            headers = set_default_headers

        params = self._custom_query
        if default_query is not None:
            params = {**params, **default_query}
        elif set_default_query is not None:
            params = set_default_query

        http_client = http_client or self._client
        return self.__class__(
            api_key=api_key or self.api_key,
            base_url=base_url or self.base_url,
            timeout=self.timeout if isinstance(timeout, NotGiven) else timeout,
            http_client=http_client,
            max_retries=max_retries if is_given(max_retries) else self.max_retries,
            default_headers=headers,
            default_query=params,
            **_extra_kwargs,
        )

    # Alias for `copy` for nicer inline usage, e.g.
    # client.with_options(timeout=10).foo.create(...)
    with_options = copy

    @override
    def _make_status_error(
        self,
        err_msg: str,
        *,
        body: object,
        response: httpx.Response,
    ) -> APIStatusError:
        if response.status_code == 400:
            return _exceptions.BadRequestError(err_msg, response=response, body=body)

        if response.status_code == 401:
            return _exceptions.AuthenticationError(err_msg, response=response, body=body)

        if response.status_code == 403:
            return _exceptions.PermissionDeniedError(err_msg, response=response, body=body)

        if response.status_code == 404:
            return _exceptions.NotFoundError(err_msg, response=response, body=body)

        if response.status_code == 409:
            return _exceptions.ConflictError(err_msg, response=response, body=body)

        if response.status_code == 422:
            return _exceptions.UnprocessableEntityError(err_msg, response=response, body=body)

        if response.status_code == 429:
            return _exceptions.RateLimitError(err_msg, response=response, body=body)

        if response.status_code >= 500:
            return _exceptions.InternalServerError(err_msg, response=response, body=body)
        return APIStatusError(err_msg, response=response, body=body)


class RevoxWithRawResponse:
    _client: Revox

    def __init__(self, client: Revox) -> None:
        self._client = client

    @cached_property
    def campaigns(self) -> campaigns.CampaignsResourceWithRawResponse:
        from .resources.campaigns import CampaignsResourceWithRawResponse

        return CampaignsResourceWithRawResponse(self._client.campaigns)

    @cached_property
    def assistants(self) -> assistants.AssistantsResourceWithRawResponse:
        from .resources.assistants import AssistantsResourceWithRawResponse

        return AssistantsResourceWithRawResponse(self._client.assistants)

    @cached_property
    def call(self) -> call.CallResourceWithRawResponse:
        from .resources.call import CallResourceWithRawResponse

        return CallResourceWithRawResponse(self._client.call)

    @cached_property
    def voices(self) -> voices.VoicesResourceWithRawResponse:
        from .resources.voices import VoicesResourceWithRawResponse

        return VoicesResourceWithRawResponse(self._client.voices)

    @cached_property
    def users(self) -> users.UsersResourceWithRawResponse:
        from .resources.users import UsersResourceWithRawResponse

        return UsersResourceWithRawResponse(self._client.users)


class AsyncRevoxWithRawResponse:
    _client: AsyncRevox

    def __init__(self, client: AsyncRevox) -> None:
        self._client = client

    @cached_property
    def campaigns(self) -> campaigns.AsyncCampaignsResourceWithRawResponse:
        from .resources.campaigns import AsyncCampaignsResourceWithRawResponse

        return AsyncCampaignsResourceWithRawResponse(self._client.campaigns)

    @cached_property
    def assistants(self) -> assistants.AsyncAssistantsResourceWithRawResponse:
        from .resources.assistants import AsyncAssistantsResourceWithRawResponse

        return AsyncAssistantsResourceWithRawResponse(self._client.assistants)

    @cached_property
    def call(self) -> call.AsyncCallResourceWithRawResponse:
        from .resources.call import AsyncCallResourceWithRawResponse

        return AsyncCallResourceWithRawResponse(self._client.call)

    @cached_property
    def voices(self) -> voices.AsyncVoicesResourceWithRawResponse:
        from .resources.voices import AsyncVoicesResourceWithRawResponse

        return AsyncVoicesResourceWithRawResponse(self._client.voices)

    @cached_property
    def users(self) -> users.AsyncUsersResourceWithRawResponse:
        from .resources.users import AsyncUsersResourceWithRawResponse

        return AsyncUsersResourceWithRawResponse(self._client.users)


class RevoxWithStreamedResponse:
    _client: Revox

    def __init__(self, client: Revox) -> None:
        self._client = client

    @cached_property
    def campaigns(self) -> campaigns.CampaignsResourceWithStreamingResponse:
        from .resources.campaigns import CampaignsResourceWithStreamingResponse

        return CampaignsResourceWithStreamingResponse(self._client.campaigns)

    @cached_property
    def assistants(self) -> assistants.AssistantsResourceWithStreamingResponse:
        from .resources.assistants import AssistantsResourceWithStreamingResponse

        return AssistantsResourceWithStreamingResponse(self._client.assistants)

    @cached_property
    def call(self) -> call.CallResourceWithStreamingResponse:
        from .resources.call import CallResourceWithStreamingResponse

        return CallResourceWithStreamingResponse(self._client.call)

    @cached_property
    def voices(self) -> voices.VoicesResourceWithStreamingResponse:
        from .resources.voices import VoicesResourceWithStreamingResponse

        return VoicesResourceWithStreamingResponse(self._client.voices)

    @cached_property
    def users(self) -> users.UsersResourceWithStreamingResponse:
        from .resources.users import UsersResourceWithStreamingResponse

        return UsersResourceWithStreamingResponse(self._client.users)


class AsyncRevoxWithStreamedResponse:
    _client: AsyncRevox

    def __init__(self, client: AsyncRevox) -> None:
        self._client = client

    @cached_property
    def campaigns(self) -> campaigns.AsyncCampaignsResourceWithStreamingResponse:
        from .resources.campaigns import AsyncCampaignsResourceWithStreamingResponse

        return AsyncCampaignsResourceWithStreamingResponse(self._client.campaigns)

    @cached_property
    def assistants(self) -> assistants.AsyncAssistantsResourceWithStreamingResponse:
        from .resources.assistants import AsyncAssistantsResourceWithStreamingResponse

        return AsyncAssistantsResourceWithStreamingResponse(self._client.assistants)

    @cached_property
    def call(self) -> call.AsyncCallResourceWithStreamingResponse:
        from .resources.call import AsyncCallResourceWithStreamingResponse

        return AsyncCallResourceWithStreamingResponse(self._client.call)

    @cached_property
    def voices(self) -> voices.AsyncVoicesResourceWithStreamingResponse:
        from .resources.voices import AsyncVoicesResourceWithStreamingResponse

        return AsyncVoicesResourceWithStreamingResponse(self._client.voices)

    @cached_property
    def users(self) -> users.AsyncUsersResourceWithStreamingResponse:
        from .resources.users import AsyncUsersResourceWithStreamingResponse

        return AsyncUsersResourceWithStreamingResponse(self._client.users)


Client = Revox

AsyncClient = AsyncRevox
