# This file was auto-generated by Fern from our API Definition.

import typing
from json.decoder import JSONDecodeError

from ...core.api_error import ApiError
from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
from ...core.http_response import AsyncHttpResponse, HttpResponse
from ...core.jsonable_encoder import jsonable_encoder
from ...core.pydantic_utilities import parse_obj_as
from ...core.request_options import RequestOptions
from ...core.serialization import convert_and_respect_annotation_metadata
from ...errors.bad_request_error import BadRequestError
from ...errors.forbidden_error import ForbiddenError
from ...errors.internal_server_error import InternalServerError
from ...errors.not_found_error import NotFoundError
from ...errors.unauthorized_error import UnauthorizedError
from ...types.agent_email_entry import AgentEmailEntry
from ...types.agent_phone_entry import AgentPhoneEntry
from ...types.agent_use_case import AgentUseCase
from ...types.agent_website_entry import AgentWebsiteEntry
from ...types.error import Error
from ...types.test_agent_response import TestAgentResponse
from ...types.test_agent_whitelist_response import TestAgentWhitelistResponse

# this is used as the default value for optional parameters
OMIT = typing.cast(typing.Any, ...)


class RawTestClient:
    def __init__(self, *, client_wrapper: SyncClientWrapper):
        self._client_wrapper = client_wrapper

    def create_agent(
        self,
        *,
        display_name: str,
        description: str,
        logo_url: str,
        hero_url: str,
        phone_numbers: typing.Sequence[AgentPhoneEntry],
        emails: typing.Sequence[AgentEmailEntry],
        websites: typing.Sequence[AgentWebsiteEntry],
        privacy_url: str,
        terms_url: str,
        color: str,
        is_conversational: bool,
        agent_use_case: typing.Optional[AgentUseCase] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[TestAgentResponse]:
        """
        Create a new RCS test agent for development and testing.

        ## Overview

        Test agents let you build and test full RCS functionality — rich cards, carousels, buttons,
        quick replies, and media messages — without going through the full carrier review process.
        Messages from test agents can only be sent to [whitelisted phone numbers](/api-reference/rcs-agents/test/whitelist-number).

        ## Limits

        - **Maximum 5 test agents per account.**

        ## Image Requirements

        | Image | Format | Max Size |
        |-------|--------|----------|
        | Logo  | JPEG, PNG | 50 KB |
        | Hero  | JPEG, PNG | 200 KB |

        ## After Creation

        Once your test agent is created, you'll need to:

        1. **Whitelist test phone numbers** using [`POST /rcs/test/agents/{agentId}/whitelist`](/api-reference/rcs-agents/test/whitelist-number).
        2. **Accept the tester invite** on each whitelisted device.
        3. **Send messages** using [`POST /messages/send/rcs`](/api-reference/messages/send-rcs) with the returned agent ID as the `from` field.

        > **2-Minute Cooldown**
        >
        > After creating a test agent, there is a mandatory 2-minute cooldown before you can whitelist phone numbers.
        > This is a requirement imposed by Google's RBM platform.

        Parameters
        ----------
        display_name : str
            Display name of the agent shown to users in RCS conversations.
            Must be between 1 and 40 characters.

        description : str
            Short description of what the agent does. Shown to users in the agent's profile.
            Must be between 1 and 100 characters.

        logo_url : str
            URL to the agent's logo image. Displayed as the agent's avatar in conversations.

            **Requirements:**
            - Format: JPEG or PNG
            - Max file size: 50 KB
            - Recommended: Square aspect ratio

        hero_url : str
            URL to the agent's hero banner image. Displayed at the top of the agent's profile.

            **Requirements:**
            - Format: JPEG or PNG
            - Max file size: 200 KB
            - Recommended: Landscape aspect ratio

        phone_numbers : typing.Sequence[AgentPhoneEntry]
            Contact phone numbers displayed on the agent's profile.
            At least 1 and up to 3 entries.

        emails : typing.Sequence[AgentEmailEntry]
            Contact email addresses displayed on the agent's profile.
            At least 1 and up to 3 entries.

        websites : typing.Sequence[AgentWebsiteEntry]
            Website links displayed on the agent's profile.
            At least 1 and up to 3 entries.

        privacy_url : str
            URL to the agent's privacy policy.

        terms_url : str
            URL to the agent's terms and conditions.

        color : str
            The agent's brand color as a hex color code. Used for UI accents in the RCS conversation.
            Must have sufficient contrast with white for accessibility.

        is_conversational : bool
            Whether the agent supports two-way conversations. Set to `true` if the agent
            will respond to user messages. Set to `false` for send-only agents (e.g., notifications).

        agent_use_case : typing.Optional[AgentUseCase]

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[TestAgentResponse]
            The test agent was created successfully.

            Use the returned `id` as the `from` field when sending RCS messages, and when whitelisting phone numbers.
        """
        _response = self._client_wrapper.httpx_client.request(
            "rcs/test/agents",
            method="POST",
            json={
                "displayName": display_name,
                "description": description,
                "logoUrl": logo_url,
                "heroUrl": hero_url,
                "phoneNumbers": convert_and_respect_annotation_metadata(
                    object_=phone_numbers, annotation=typing.Sequence[AgentPhoneEntry], direction="write"
                ),
                "emails": convert_and_respect_annotation_metadata(
                    object_=emails, annotation=typing.Sequence[AgentEmailEntry], direction="write"
                ),
                "websites": convert_and_respect_annotation_metadata(
                    object_=websites, annotation=typing.Sequence[AgentWebsiteEntry], direction="write"
                ),
                "privacyUrl": privacy_url,
                "termsUrl": terms_url,
                "color": color,
                "isConversational": is_conversational,
                "agentUseCase": agent_use_case,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    TestAgentResponse,
                    parse_obj_as(
                        type_=TestAgentResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 400:
                raise BadRequestError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 401:
                raise UnauthorizedError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 403:
                raise ForbiddenError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 500:
                raise InternalServerError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def update_agent(
        self,
        agent_id: str,
        *,
        display_name: typing.Optional[str] = OMIT,
        description: typing.Optional[str] = OMIT,
        logo_url: typing.Optional[str] = OMIT,
        hero_url: typing.Optional[str] = OMIT,
        phone_numbers: typing.Optional[typing.Sequence[AgentPhoneEntry]] = OMIT,
        emails: typing.Optional[typing.Sequence[AgentEmailEntry]] = OMIT,
        websites: typing.Optional[typing.Sequence[AgentWebsiteEntry]] = OMIT,
        privacy_url: typing.Optional[str] = OMIT,
        terms_url: typing.Optional[str] = OMIT,
        color: typing.Optional[str] = OMIT,
        is_conversational: typing.Optional[bool] = OMIT,
        agent_use_case: typing.Optional[AgentUseCase] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[TestAgentResponse]:
        """
        Update an existing RCS test agent's configuration.

        All fields are optional — only include the fields you want to update.

        ## Image Requirements

        If updating images, the same requirements apply as when creating an agent:

        | Image | Format | Max Size |
        |-------|--------|----------|
        | Logo  | JPEG, PNG | 50 KB |
        | Hero  | JPEG, PNG | 200 KB |

        > **2-Minute Cooldown**
        >
        > After updating a test agent, there is a mandatory 2-minute cooldown before you can whitelist phone numbers.
        > This is a requirement imposed by Google's RBM platform.

        Parameters
        ----------
        agent_id : str
            The RCS agent ID (must be prefixed with `agent_`).

        display_name : typing.Optional[str]
            Display name of the agent shown to users in RCS conversations.
            Must be between 1 and 40 characters.

        description : typing.Optional[str]
            Short description of what the agent does.
            Must be between 1 and 100 characters.

        logo_url : typing.Optional[str]
            URL to the agent's logo image.

            **Requirements:**
            - Format: JPEG or PNG
            - Max file size: 50 KB
            - Recommended: Square aspect ratio

        hero_url : typing.Optional[str]
            URL to the agent's hero banner image.

            **Requirements:**
            - Format: JPEG or PNG
            - Max file size: 200 KB
            - Recommended: Landscape aspect ratio

        phone_numbers : typing.Optional[typing.Sequence[AgentPhoneEntry]]
            Contact phone numbers displayed on the agent's profile. At least 1 and up to 3 entries.

        emails : typing.Optional[typing.Sequence[AgentEmailEntry]]
            Contact email addresses displayed on the agent's profile. At least 1 and up to 3 entries.

        websites : typing.Optional[typing.Sequence[AgentWebsiteEntry]]
            Website links displayed on the agent's profile. At least 1 and up to 3 entries.

        privacy_url : typing.Optional[str]
            URL to the agent's privacy policy.

        terms_url : typing.Optional[str]
            URL to the agent's terms and conditions.

        color : typing.Optional[str]
            The agent's brand color as a hex color code. Must have sufficient contrast with white.

        is_conversational : typing.Optional[bool]
            Whether the agent supports two-way conversations.

        agent_use_case : typing.Optional[AgentUseCase]

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[TestAgentResponse]
            The test agent was updated successfully.
        """
        _response = self._client_wrapper.httpx_client.request(
            f"rcs/test/agents/{jsonable_encoder(agent_id)}",
            method="PATCH",
            json={
                "displayName": display_name,
                "description": description,
                "logoUrl": logo_url,
                "heroUrl": hero_url,
                "phoneNumbers": convert_and_respect_annotation_metadata(
                    object_=phone_numbers, annotation=typing.Sequence[AgentPhoneEntry], direction="write"
                ),
                "emails": convert_and_respect_annotation_metadata(
                    object_=emails, annotation=typing.Sequence[AgentEmailEntry], direction="write"
                ),
                "websites": convert_and_respect_annotation_metadata(
                    object_=websites, annotation=typing.Sequence[AgentWebsiteEntry], direction="write"
                ),
                "privacyUrl": privacy_url,
                "termsUrl": terms_url,
                "color": color,
                "isConversational": is_conversational,
                "agentUseCase": agent_use_case,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    TestAgentResponse,
                    parse_obj_as(
                        type_=TestAgentResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 400:
                raise BadRequestError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 401:
                raise UnauthorizedError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 403:
                raise ForbiddenError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 500:
                raise InternalServerError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def whitelist_number(
        self, agent_id: str, *, phone_number: str, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[TestAgentWhitelistResponse]:
        """
        Whitelist a phone number for testing with a specific test agent.

        During development and testing, RCS agents can only send messages to whitelisted phone numbers.
        Use this endpoint to whitelist specific phone numbers so you can send and receive messages from the test agent.

        ## Verification Process

        After whitelisting, the recipient must accept the tester invite:

        1. The recipient's device will receive a message from "RBM Tester Management".
        2. The recipient must tap "Make me a tester" to accept.
        3. Once accepted, the status transitions from `PENDING` to `ACCEPTED`.

        ## Verification

        <div style="display: flex; gap: 16px;">
          <div style="flex: 1; text-align: center;">
            <strong>Accepting the invite</strong><br/>
            <img src="https://pncl.to/f769cAvCbEx-MmezZjR6dz6KVkr5ZO" alt="Accepting the tester invite" style="max-width: 100%;" />
          </div>
          <div style="flex: 1; text-align: center;">
            <strong>Declining the invite</strong><br/>
            <img src="https://pncl.to/VRere3tEKfx4n0HNaxK-vwl7pbLHTJ" alt="Declining the tester invite" style="max-width: 100%;" />
          </div>
        </div>

        ## Cooldown

        There is a **2-minute cooldown** after creating or updating a test agent before you can whitelist numbers.
        Attempting to whitelist during the cooldown returns a `500` error with the remaining wait time.

        Parameters
        ----------
        agent_id : str
            The RCS agent ID (must be prefixed with `agent_`).

        phone_number : str
            Phone number to whitelist for testing in E.164 format.

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[TestAgentWhitelistResponse]
            Returns the whitelist status of the phone number.
        """
        _response = self._client_wrapper.httpx_client.request(
            f"rcs/test/agents/{jsonable_encoder(agent_id)}/whitelist",
            method="POST",
            json={
                "phoneNumber": phone_number,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    TestAgentWhitelistResponse,
                    parse_obj_as(
                        type_=TestAgentWhitelistResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 400:
                raise BadRequestError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 401:
                raise UnauthorizedError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 403:
                raise ForbiddenError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 500:
                raise InternalServerError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def get_whitelist_status(
        self, agent_id: str, *, phone_number: str, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[TestAgentWhitelistResponse]:
        """
        Check the current whitelist status of a phone number for a specific test agent.

        Use this endpoint to poll the status of a previously whitelisted number and determine
        whether the recipient has accepted or rejected the tester invite.

        ## Status Values

        - **`PENDING`** — The tester invite was sent but the recipient has not yet responded. Ask the recipient to check their messages and accept the invite.
        - **`ACCEPTED`** — The recipient accepted the invite. Messages can be exchanged.
        - **`REJECTED`** — The recipient rejected the invite or the invite could not be delivered since the carrier does not support test agents. If the user rejected the invite, they can accept it again by tapping "Make me a tester" in the same message from "RBM Tester Management".

        <div style="display: flex; gap: 16px;">
          <div style="flex: 1; text-align: center;">
            <strong>Accepting the invite</strong><br/>
            <img src="https://pncl.to/f769cAvCbEx-MmezZjR6dz6KVkr5ZO" alt="Accepting the tester invite" style="max-width: 100%;" />
          </div>
          <div style="flex: 1; text-align: center;">
            <strong>Declining the invite</strong><br/>
            <img src="https://pncl.to/VRere3tEKfx4n0HNaxK-vwl7pbLHTJ" alt="Declining the tester invite" style="max-width: 100%;" />
          </div>
        </div>

        Parameters
        ----------
        agent_id : str
            The RCS agent ID (must be prefixed with `agent_`).

        phone_number : str
            The phone number to check whitelist status for (E.164 format).

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[TestAgentWhitelistResponse]
            The current whitelist status of the phone number.
        """
        _response = self._client_wrapper.httpx_client.request(
            f"rcs/test/agents/{jsonable_encoder(agent_id)}/numbers",
            method="GET",
            params={
                "phoneNumber": phone_number,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    TestAgentWhitelistResponse,
                    parse_obj_as(
                        type_=TestAgentWhitelistResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 400:
                raise BadRequestError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 401:
                raise UnauthorizedError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 403:
                raise ForbiddenError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 500:
                raise InternalServerError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)


class AsyncRawTestClient:
    def __init__(self, *, client_wrapper: AsyncClientWrapper):
        self._client_wrapper = client_wrapper

    async def create_agent(
        self,
        *,
        display_name: str,
        description: str,
        logo_url: str,
        hero_url: str,
        phone_numbers: typing.Sequence[AgentPhoneEntry],
        emails: typing.Sequence[AgentEmailEntry],
        websites: typing.Sequence[AgentWebsiteEntry],
        privacy_url: str,
        terms_url: str,
        color: str,
        is_conversational: bool,
        agent_use_case: typing.Optional[AgentUseCase] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[TestAgentResponse]:
        """
        Create a new RCS test agent for development and testing.

        ## Overview

        Test agents let you build and test full RCS functionality — rich cards, carousels, buttons,
        quick replies, and media messages — without going through the full carrier review process.
        Messages from test agents can only be sent to [whitelisted phone numbers](/api-reference/rcs-agents/test/whitelist-number).

        ## Limits

        - **Maximum 5 test agents per account.**

        ## Image Requirements

        | Image | Format | Max Size |
        |-------|--------|----------|
        | Logo  | JPEG, PNG | 50 KB |
        | Hero  | JPEG, PNG | 200 KB |

        ## After Creation

        Once your test agent is created, you'll need to:

        1. **Whitelist test phone numbers** using [`POST /rcs/test/agents/{agentId}/whitelist`](/api-reference/rcs-agents/test/whitelist-number).
        2. **Accept the tester invite** on each whitelisted device.
        3. **Send messages** using [`POST /messages/send/rcs`](/api-reference/messages/send-rcs) with the returned agent ID as the `from` field.

        > **2-Minute Cooldown**
        >
        > After creating a test agent, there is a mandatory 2-minute cooldown before you can whitelist phone numbers.
        > This is a requirement imposed by Google's RBM platform.

        Parameters
        ----------
        display_name : str
            Display name of the agent shown to users in RCS conversations.
            Must be between 1 and 40 characters.

        description : str
            Short description of what the agent does. Shown to users in the agent's profile.
            Must be between 1 and 100 characters.

        logo_url : str
            URL to the agent's logo image. Displayed as the agent's avatar in conversations.

            **Requirements:**
            - Format: JPEG or PNG
            - Max file size: 50 KB
            - Recommended: Square aspect ratio

        hero_url : str
            URL to the agent's hero banner image. Displayed at the top of the agent's profile.

            **Requirements:**
            - Format: JPEG or PNG
            - Max file size: 200 KB
            - Recommended: Landscape aspect ratio

        phone_numbers : typing.Sequence[AgentPhoneEntry]
            Contact phone numbers displayed on the agent's profile.
            At least 1 and up to 3 entries.

        emails : typing.Sequence[AgentEmailEntry]
            Contact email addresses displayed on the agent's profile.
            At least 1 and up to 3 entries.

        websites : typing.Sequence[AgentWebsiteEntry]
            Website links displayed on the agent's profile.
            At least 1 and up to 3 entries.

        privacy_url : str
            URL to the agent's privacy policy.

        terms_url : str
            URL to the agent's terms and conditions.

        color : str
            The agent's brand color as a hex color code. Used for UI accents in the RCS conversation.
            Must have sufficient contrast with white for accessibility.

        is_conversational : bool
            Whether the agent supports two-way conversations. Set to `true` if the agent
            will respond to user messages. Set to `false` for send-only agents (e.g., notifications).

        agent_use_case : typing.Optional[AgentUseCase]

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[TestAgentResponse]
            The test agent was created successfully.

            Use the returned `id` as the `from` field when sending RCS messages, and when whitelisting phone numbers.
        """
        _response = await self._client_wrapper.httpx_client.request(
            "rcs/test/agents",
            method="POST",
            json={
                "displayName": display_name,
                "description": description,
                "logoUrl": logo_url,
                "heroUrl": hero_url,
                "phoneNumbers": convert_and_respect_annotation_metadata(
                    object_=phone_numbers, annotation=typing.Sequence[AgentPhoneEntry], direction="write"
                ),
                "emails": convert_and_respect_annotation_metadata(
                    object_=emails, annotation=typing.Sequence[AgentEmailEntry], direction="write"
                ),
                "websites": convert_and_respect_annotation_metadata(
                    object_=websites, annotation=typing.Sequence[AgentWebsiteEntry], direction="write"
                ),
                "privacyUrl": privacy_url,
                "termsUrl": terms_url,
                "color": color,
                "isConversational": is_conversational,
                "agentUseCase": agent_use_case,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    TestAgentResponse,
                    parse_obj_as(
                        type_=TestAgentResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 400:
                raise BadRequestError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 401:
                raise UnauthorizedError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 403:
                raise ForbiddenError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 500:
                raise InternalServerError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def update_agent(
        self,
        agent_id: str,
        *,
        display_name: typing.Optional[str] = OMIT,
        description: typing.Optional[str] = OMIT,
        logo_url: typing.Optional[str] = OMIT,
        hero_url: typing.Optional[str] = OMIT,
        phone_numbers: typing.Optional[typing.Sequence[AgentPhoneEntry]] = OMIT,
        emails: typing.Optional[typing.Sequence[AgentEmailEntry]] = OMIT,
        websites: typing.Optional[typing.Sequence[AgentWebsiteEntry]] = OMIT,
        privacy_url: typing.Optional[str] = OMIT,
        terms_url: typing.Optional[str] = OMIT,
        color: typing.Optional[str] = OMIT,
        is_conversational: typing.Optional[bool] = OMIT,
        agent_use_case: typing.Optional[AgentUseCase] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[TestAgentResponse]:
        """
        Update an existing RCS test agent's configuration.

        All fields are optional — only include the fields you want to update.

        ## Image Requirements

        If updating images, the same requirements apply as when creating an agent:

        | Image | Format | Max Size |
        |-------|--------|----------|
        | Logo  | JPEG, PNG | 50 KB |
        | Hero  | JPEG, PNG | 200 KB |

        > **2-Minute Cooldown**
        >
        > After updating a test agent, there is a mandatory 2-minute cooldown before you can whitelist phone numbers.
        > This is a requirement imposed by Google's RBM platform.

        Parameters
        ----------
        agent_id : str
            The RCS agent ID (must be prefixed with `agent_`).

        display_name : typing.Optional[str]
            Display name of the agent shown to users in RCS conversations.
            Must be between 1 and 40 characters.

        description : typing.Optional[str]
            Short description of what the agent does.
            Must be between 1 and 100 characters.

        logo_url : typing.Optional[str]
            URL to the agent's logo image.

            **Requirements:**
            - Format: JPEG or PNG
            - Max file size: 50 KB
            - Recommended: Square aspect ratio

        hero_url : typing.Optional[str]
            URL to the agent's hero banner image.

            **Requirements:**
            - Format: JPEG or PNG
            - Max file size: 200 KB
            - Recommended: Landscape aspect ratio

        phone_numbers : typing.Optional[typing.Sequence[AgentPhoneEntry]]
            Contact phone numbers displayed on the agent's profile. At least 1 and up to 3 entries.

        emails : typing.Optional[typing.Sequence[AgentEmailEntry]]
            Contact email addresses displayed on the agent's profile. At least 1 and up to 3 entries.

        websites : typing.Optional[typing.Sequence[AgentWebsiteEntry]]
            Website links displayed on the agent's profile. At least 1 and up to 3 entries.

        privacy_url : typing.Optional[str]
            URL to the agent's privacy policy.

        terms_url : typing.Optional[str]
            URL to the agent's terms and conditions.

        color : typing.Optional[str]
            The agent's brand color as a hex color code. Must have sufficient contrast with white.

        is_conversational : typing.Optional[bool]
            Whether the agent supports two-way conversations.

        agent_use_case : typing.Optional[AgentUseCase]

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[TestAgentResponse]
            The test agent was updated successfully.
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"rcs/test/agents/{jsonable_encoder(agent_id)}",
            method="PATCH",
            json={
                "displayName": display_name,
                "description": description,
                "logoUrl": logo_url,
                "heroUrl": hero_url,
                "phoneNumbers": convert_and_respect_annotation_metadata(
                    object_=phone_numbers, annotation=typing.Sequence[AgentPhoneEntry], direction="write"
                ),
                "emails": convert_and_respect_annotation_metadata(
                    object_=emails, annotation=typing.Sequence[AgentEmailEntry], direction="write"
                ),
                "websites": convert_and_respect_annotation_metadata(
                    object_=websites, annotation=typing.Sequence[AgentWebsiteEntry], direction="write"
                ),
                "privacyUrl": privacy_url,
                "termsUrl": terms_url,
                "color": color,
                "isConversational": is_conversational,
                "agentUseCase": agent_use_case,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    TestAgentResponse,
                    parse_obj_as(
                        type_=TestAgentResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 400:
                raise BadRequestError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 401:
                raise UnauthorizedError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 403:
                raise ForbiddenError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 500:
                raise InternalServerError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def whitelist_number(
        self, agent_id: str, *, phone_number: str, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[TestAgentWhitelistResponse]:
        """
        Whitelist a phone number for testing with a specific test agent.

        During development and testing, RCS agents can only send messages to whitelisted phone numbers.
        Use this endpoint to whitelist specific phone numbers so you can send and receive messages from the test agent.

        ## Verification Process

        After whitelisting, the recipient must accept the tester invite:

        1. The recipient's device will receive a message from "RBM Tester Management".
        2. The recipient must tap "Make me a tester" to accept.
        3. Once accepted, the status transitions from `PENDING` to `ACCEPTED`.

        ## Verification

        <div style="display: flex; gap: 16px;">
          <div style="flex: 1; text-align: center;">
            <strong>Accepting the invite</strong><br/>
            <img src="https://pncl.to/f769cAvCbEx-MmezZjR6dz6KVkr5ZO" alt="Accepting the tester invite" style="max-width: 100%;" />
          </div>
          <div style="flex: 1; text-align: center;">
            <strong>Declining the invite</strong><br/>
            <img src="https://pncl.to/VRere3tEKfx4n0HNaxK-vwl7pbLHTJ" alt="Declining the tester invite" style="max-width: 100%;" />
          </div>
        </div>

        ## Cooldown

        There is a **2-minute cooldown** after creating or updating a test agent before you can whitelist numbers.
        Attempting to whitelist during the cooldown returns a `500` error with the remaining wait time.

        Parameters
        ----------
        agent_id : str
            The RCS agent ID (must be prefixed with `agent_`).

        phone_number : str
            Phone number to whitelist for testing in E.164 format.

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[TestAgentWhitelistResponse]
            Returns the whitelist status of the phone number.
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"rcs/test/agents/{jsonable_encoder(agent_id)}/whitelist",
            method="POST",
            json={
                "phoneNumber": phone_number,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    TestAgentWhitelistResponse,
                    parse_obj_as(
                        type_=TestAgentWhitelistResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 400:
                raise BadRequestError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 401:
                raise UnauthorizedError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 403:
                raise ForbiddenError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 500:
                raise InternalServerError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def get_whitelist_status(
        self, agent_id: str, *, phone_number: str, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[TestAgentWhitelistResponse]:
        """
        Check the current whitelist status of a phone number for a specific test agent.

        Use this endpoint to poll the status of a previously whitelisted number and determine
        whether the recipient has accepted or rejected the tester invite.

        ## Status Values

        - **`PENDING`** — The tester invite was sent but the recipient has not yet responded. Ask the recipient to check their messages and accept the invite.
        - **`ACCEPTED`** — The recipient accepted the invite. Messages can be exchanged.
        - **`REJECTED`** — The recipient rejected the invite or the invite could not be delivered since the carrier does not support test agents. If the user rejected the invite, they can accept it again by tapping "Make me a tester" in the same message from "RBM Tester Management".

        <div style="display: flex; gap: 16px;">
          <div style="flex: 1; text-align: center;">
            <strong>Accepting the invite</strong><br/>
            <img src="https://pncl.to/f769cAvCbEx-MmezZjR6dz6KVkr5ZO" alt="Accepting the tester invite" style="max-width: 100%;" />
          </div>
          <div style="flex: 1; text-align: center;">
            <strong>Declining the invite</strong><br/>
            <img src="https://pncl.to/VRere3tEKfx4n0HNaxK-vwl7pbLHTJ" alt="Declining the tester invite" style="max-width: 100%;" />
          </div>
        </div>

        Parameters
        ----------
        agent_id : str
            The RCS agent ID (must be prefixed with `agent_`).

        phone_number : str
            The phone number to check whitelist status for (E.164 format).

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[TestAgentWhitelistResponse]
            The current whitelist status of the phone number.
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"rcs/test/agents/{jsonable_encoder(agent_id)}/numbers",
            method="GET",
            params={
                "phoneNumber": phone_number,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    TestAgentWhitelistResponse,
                    parse_obj_as(
                        type_=TestAgentWhitelistResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 400:
                raise BadRequestError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 401:
                raise UnauthorizedError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 403:
                raise ForbiddenError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 500:
                raise InternalServerError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
