# auto-generated. DO NOT EDIT
import json
import logging
import mimetypes
import os
from collections.abc import AsyncGenerator
from dataclasses import dataclass
from datetime import datetime, date, timedelta
import pytz
from dateutil import tz
from dateutil.parser import isoparse
from enum import Enum
from io import BufferedReader
from typing import Union, Optional, Literal, List

from pydantic import TypeAdapter

from wxc_sdk.all_types import *
from wxc_sdk.as_mpe import MultipartEncoder
from wxc_sdk.as_rest import AsRestSession
from wxc_sdk.base import to_camel, StrOrDict, dt_iso_str, enum_str
from wxc_sdk.base import SafeEnum as Enum

log = logging.getLogger(__name__)


# there seems to be a problem with getting too many users with calling data at the same time
# this is the maximum number the SDK enforces
MAX_USERS_WITH_CALLING_DATA = 10
CALLING_DATA_TIMEOUT_PROTECTION = False


__all__ = ['AsAccessCodesApi', 'AsAdminAuditEventsApi', 'AsAgentCallerIdApi', 'AsAnnouncementApi',
           'AsAnnouncementsRepositoryApi', 'AsAnonCallsApi', 'AsApiChild', 'AsAppServicesApi', 'AsAppSharedLineApi',
           'AsApplyLineKeyTemplatesJobsApi', 'AsAttachmentActionsApi', 'AsAuthorizationsApi', 'AsAutoAttendantApi',
           'AsAvailableNumbersApi', 'AsBargeApi', 'AsCQPolicyApi', 'AsCallBridgeApi', 'AsCallInterceptApi',
           'AsCallParkApi', 'AsCallPickupApi', 'AsCallPolicyApi', 'AsCallQueueAgentsApi', 'AsCallQueueApi',
           'AsCallRecordingApi', 'AsCallRecordingJobsApi', 'AsCallRecordingSettingsApi', 'AsCallRoutingApi',
           'AsCallWaitingApi', 'AsCallerIdApi', 'AsCallingBehaviorApi', 'AsCallparkExtensionApi', 'AsCallsApi',
           'AsConferenceControlsApi', 'AsConvergedRecordingsApi', 'AsCustomerExperienceEssentialsApi',
           'AsDECTDevicesApi', 'AsDetailedCDRApi', 'AsDeviceConfigurationsApi', 'AsDeviceSettingsJobsApi',
           'AsDevicesApi', 'AsDevicesDynamicSettingsApi', 'AsDialPlanApi', 'AsDigitPatternsApi',
           'AsDisableCallingLocationJobsApi', 'AsDndApi', 'AsECBNApi', 'AsEmergencyAddressApi', 'AsEventsApi',
           'AsExecAssistantApi', 'AsExecutiveSettingsApi', 'AsFeatureAccessApi', 'AsFeatureSelector',
           'AsForwardingApi', 'AsGoOverrideApi', 'AsGroupsApi', 'AsGuestCallingApi', 'AsGuestManagementApi',
           'AsHotDeskApi', 'AsHotDeskingSigninViaVoicePortalApi', 'AsHotelingApi', 'AsHuntGroupApi',
           'AsIncomingPermissionsApi', 'AsInternalDialingApi', 'AsJobsApi', 'AsLicensesApi',
           'AsLocationAccessCodesApi', 'AsLocationEmergencyServicesApi', 'AsLocationInterceptApi', 'AsLocationMoHApi',
           'AsLocationNumbersApi', 'AsLocationVoicemailSettingsApi', 'AsLocationsApi', 'AsMSTeamsSettingApi',
           'AsManageNumbersJobsApi', 'AsMeBargeApi', 'AsMeCallBlockApi', 'AsMeCallCenterApi', 'AsMeCallParkApi',
           'AsMeCallPickupApi', 'AsMeCallPoliciesApi', 'AsMeCallerIdApi', 'AsMeDNDApi', 'AsMeEndpointsApi',
           'AsMeExecutiveApi', 'AsMeForwardingApi', 'AsMePersonalAssistantApi', 'AsMeRecordingApi', 'AsMeSNRApi',
           'AsMeSettingsApi', 'AsMeVoicemailApi', 'AsMeetingChatsApi', 'AsMeetingClosedCaptionsApi',
           'AsMeetingInviteesApi', 'AsMeetingParticipantsApi', 'AsMeetingPreferencesApi', 'AsMeetingQandAApi',
           'AsMeetingQualitiesApi', 'AsMeetingTranscriptsApi', 'AsMeetingsApi', 'AsMembershipApi', 'AsMessagesApi',
           'AsModeManagementApi', 'AsMonitoringApi', 'AsMoveUsersJobsApi', 'AsMusicOnHoldApi', 'AsNumbersApi',
           'AsOperatingModesApi', 'AsOrgEmergencyServicesApi', 'AsOrgMSTeamsSettingApi',
           'AsOrganisationAccessCodesApi', 'AsOrganisationVoicemailSettingsAPI', 'AsOrganizationApi',
           'AsOrganizationContactsApi', 'AsOutgoingPermissionsApi', 'AsPSTNApi', 'AsPagingApi', 'AsPeopleApi',
           'AsPersonForwardingApi', 'AsPersonSettingsApi', 'AsPersonSettingsApiChild', 'AsPersonalAssistantApi',
           'AsPlayListApi', 'AsPreferredAnswerApi', 'AsPremisePstnApi', 'AsPriorityAlertApi', 'AsPrivacyApi',
           'AsPrivateNetworkConnectApi', 'AsPushToTalkApi', 'AsQueueCallRecordingSettingsApi',
           'AsRebuildPhonesJobsApi', 'AsReceptionistApi', 'AsReceptionistContactsDirectoryApi', 'AsRecordingsApi',
           'AsReportsApi', 'AsRestSession', 'AsRolesApi', 'AsRoomTabsApi', 'AsRoomsApi', 'AsRouteGroupApi',
           'AsRouteListApi', 'AsSCIM2BulkApi', 'AsSCIM2GroupsApi', 'AsSCIM2UsersApi', 'AsScheduleApi',
           'AsScimApiChild', 'AsScimV2Api', 'AsSelectiveAcceptApi', 'AsSelectiveForwardApi', 'AsSelectiveRejectApi',
           'AsSendActivationEmailApi', 'AsSequentialRingApi', 'AsSimRingApi', 'AsSingleNumberReachApi', 'AsStatusAPI',
           'AsSupervisorApi', 'AsTeamMembershipsApi', 'AsTeamsApi', 'AsTelephonyApi', 'AsTelephonyDevicesApi',
           'AsTelephonyLocationApi', 'AsTransferNumbersApi', 'AsTranslationPatternsApi', 'AsTrunkApi',
           'AsUpdateDynamicDeviceSettingsJobsApi', 'AsUpdateRoutingPrefixJobsApi', 'AsVirtualExtensionsApi',
           'AsVirtualLinesApi', 'AsVoiceMessagingApi', 'AsVoicePortalApi', 'AsVoicemailApi', 'AsVoicemailGroupsApi',
           'AsVoicemailRulesApi', 'AsWebexSimpleApi', 'AsWebhookApi', 'AsWorkspaceDevicesApi',
           'AsWorkspaceLocationApi', 'AsWorkspaceLocationFloorApi', 'AsWorkspaceNumbersApi',
           'AsWorkspacePersonalizationApi', 'AsWorkspaceSettingsApi', 'AsWorkspacesApi', 'AsWrapupReasonApi',
           'AsXApi']


@dataclass(init=False, repr=False)
class AsApiChild:
    """
    Base class for child APIs of :class:`WebexSimpleApi`
    """
    session: AsRestSession

    def __init__(self, *, session: AsRestSession, base: str = None):
        #: REST session
        self.session = session
        if base:
            self.base = base

    # noinspection PyMethodOverriding

    def __init_subclass__(cls, base: str):
        """
        Subclass registration hook. Each APIChild has a specific endpoint prefix which we gather at subclass
        registration time-

        :param base: APIChild specific URL path
        """
        super().__init_subclass__()
        # save endpoint prefix
        cls.base = base

    def ep(self, path: str = None):
        """
        endpoint URL for given path

        :param path: path after APIChild subclass specific endpoint URI prefix
        :type path: str
        :return: endpoint URL
        :rtype: str
        """
        path = path or ''
        if self.base and path:
            path = f'/{path}'
        return self.session.ep(f'{self.base}{path}')

    async def get(self, *args, **kwargs) -> StrOrDict:
        """
        GET request

        :param args:
        :param kwargs:
        :return:
        """
        return await self.session.rest_get(*args, **kwargs)

    async def post(self, *args, **kwargs) -> StrOrDict:
        """
        POST request

        :param args:
        :param kwargs:
        :return:
        """
        return await self.session.rest_post(*args, **kwargs)

    async def put(self, *args, **kwargs) -> StrOrDict:
        """
        PUT request

        :param args:
        :param kwargs:
        :return:
        """
        return await self.session.rest_put(*args, **kwargs)

    async def delete(self, *args, **kwargs) -> StrOrDict:
        """
        DELETE request

        :param args:
        :param kwargs:
        """
        return await self.session.rest_delete(*args, **kwargs)

    async def patch(self, *args, **kwargs) -> StrOrDict:
        """
        PATCH request

        :param args:
        :param kwargs:
        """
        return await self.session.rest_patch(*args, **kwargs)


class AsAdminAuditEventsApi(AsApiChild, base='adminAudit'):
    """
    Admin Audit Events

    Admin Audit Events are available to full administrators for `certain events
    <https://help.webex.com/en-us/article/nqzomav/Control-Hub-audit-events-reference>`_ performed in Webex Control Hub.

    Administrators with accounts created before 2019 who have never logged into `Webex Control Hub
    <https://admin.webex.com>`_ will need to log into
    Webex Control Hub at least once to enable access to this API.

    An administrator account with the `audit:events_read` scope is required to use this API.
    """

    def list_events_gen(self, org_id: str, from_: Union[str, datetime], to_: Union[str, datetime],
                    actor_id: str = None, event_categories: list[str] = None,
                    **params) -> AsyncGenerator[AuditEvent, None, None]:
        """
        List Admin Audit Events

        List admin audit events in your organization. Several query parameters are available to filter the response.

        Long result sets will be split into `pages
        <https://developer.webex.com/docs/basics#pagination>`_.

        **NOTE**: A maximum of one year of audit events can be returned per request.

        :param org_id: List events in this organization, by ID.
        :type org_id: str
        :param from_: List events which occurred after a specific date and time.
        :type from_: Union[str, datetime]
        :param to_: List events which occurred before a specific date and time.
        :type to_: Union[str, datetime]
        :param actor_id: List events performed by this person, by ID.
        :type actor_id: str
        :param event_categories: List events, by event categories.
        :type event_categories: list[str]
        :return: Generator yielding :class:`AuditEvent` instances
        """
        params['orgId'] = org_id
        if isinstance(from_, str):
            from_ = isoparse(from_)
        from_ = dt_iso_str(from_)
        params['from'] = from_
        if isinstance(to_, str):
            to_ = isoparse(to_)
        to_ = dt_iso_str(to_)
        params['to'] = to_
        if actor_id is not None:
            params['actorId'] = actor_id
        if event_categories is not None:
            params['eventCategories'] = ','.join(event_categories)
        url = self.ep('events')
        return self.session.follow_pagination(url=url, model=AuditEvent, item_key='items', params=params)

    async def list_events(self, org_id: str, from_: Union[str, datetime], to_: Union[str, datetime],
                    actor_id: str = None, event_categories: list[str] = None,
                    **params) -> List[AuditEvent]:
        """
        List Admin Audit Events

        List admin audit events in your organization. Several query parameters are available to filter the response.

        Long result sets will be split into `pages
        <https://developer.webex.com/docs/basics#pagination>`_.

        **NOTE**: A maximum of one year of audit events can be returned per request.

        :param org_id: List events in this organization, by ID.
        :type org_id: str
        :param from_: List events which occurred after a specific date and time.
        :type from_: Union[str, datetime]
        :param to_: List events which occurred before a specific date and time.
        :type to_: Union[str, datetime]
        :param actor_id: List events performed by this person, by ID.
        :type actor_id: str
        :param event_categories: List events, by event categories.
        :type event_categories: list[str]
        :return: Generator yielding :class:`AuditEvent` instances
        """
        params['orgId'] = org_id
        if isinstance(from_, str):
            from_ = isoparse(from_)
        from_ = dt_iso_str(from_)
        params['from'] = from_
        if isinstance(to_, str):
            to_ = isoparse(to_)
        to_ = dt_iso_str(to_)
        params['to'] = to_
        if actor_id is not None:
            params['actorId'] = actor_id
        if event_categories is not None:
            params['eventCategories'] = ','.join(event_categories)
        url = self.ep('events')
        return [o async for o in self.session.follow_pagination(url=url, model=AuditEvent, item_key='items', params=params)]

    async def list_event_categories(self) -> list[str]:
        """
        List Admin Audit Event Categories

        Get the list of all admin event categories.

        :rtype: list[str]
        """
        url = self.ep('eventCategories')
        data = await super().get(url)
        r = data['eventCategories']
        return r


class AsAttachmentActionsApi(AsApiChild, base='attachment/actions'):
    """
    Users create attachment actions by interacting with message attachments such as clicking on a submit button in a
    card.
    """

    async def details(self, action_id: str) -> AttachmentAction:
        """
        Shows details for a attachment action, by ID.
        Specify the attachment action ID in the id URI parameter.

        :param action_id: A unique identifier for the attachment action.
        :type action_id: str
        """
        url = self.ep(f'{action_id}')
        data = await super().get(url=url)
        return AttachmentAction.model_validate(data)


class AsAuthorizationsApi(AsApiChild, base='authorizations'):
    """
    Authorizations

    This is an admin API. As a partner admin revoking tokens for your customer
    users, you must be a `Full Admin.` As an admin in your own org revoking tokens
    for your home org users, you must have either of the following roles: `Full
    Admin,` `Device Admin,` `Or user Admin.` Read-only admins are not supported.

    Regular users can manage their tokens via the following
    `GUI
    <https://idbroker.webex.com/idb/profile#/tokens>`_

    Authorizations are user grants to applications to act on the user's behalf. Authorizations are how `Integrations
    <https://developer.webex.com/docs/integrations>`_ get
    authorized with specific `access scopes
    <https://developer.webex.com/docs/integrations#scopes>`_ in the oAuth client life-cycle. Integrations and some of the Webex service
    portals, like `developer.webex.com
    <https://developer.webex.com/>`_, are all oAuth clients, each with their unique `clientId.`

    Your application receives an API `access token
    <https://developer.webex.com/docs/integrations#getting-an-access-token>`_ and a `refresh token
    used to call Webex APIs for which the user authorized the scopes. Access tokens expire reasonably frequently,
    while refresh tokens (when being regularly used) will be refreshed to last forever (see `Using the Refresh Token
    <https://developer.webex.com/docs/integrations#using-the-refresh-token>`_
    for details).

    In this API an authorization is synonymous with an `API access token
    <https://developer.webex.com/docs/integrations#getting-an-access-token>`_.

    To provide admins with fine-grained token management control, you use the `/authorizations
    <https://developer.webex.com/docs/api/v1/authorizations>`_ API with the `DELETE`
    HTTP method to revoke access and refresh tokens.

    Deleting a refresh token will revoke all associated access tokens as well. Deleting an access token will revoke the
    developer's ability to call the APIs with it.
    Webex subsystems may cache the token's validity briefly after the authorization is deleted.

    Admins can revoke user authorizations for users in their organization. When an admin deletes their own token, the
    `clientId` used to authorize the request must match the `clientId` used to generate the token.

    To use the authorizations API in an Integration the scopes must include: `identity:tokens_write,`
    `identity:tokens_read.`
    """

    async def list(self, person_id: str = None, person_email: str = None) -> list[Authorization]:
        """
        Lists all authorizations for a user. Either personId or personEmail must be provided. This API does not support
        pagination.

        :param person_id: List authorizations for this user id.
        :param person_email: List authorizations for this user email.
        :return: List of Authorizations
        """
        params = {to_camel(k): v
                  for k, v in locals().items()
                  if k not in {'self'} and v is not None}
        if frozenset(params) not in {frozenset({'personId'}),
                                     frozenset({'personEmail'})}:
            raise ValueError(
                'Invalid parameter combination: exactly one of person_id or person_email has to be present.')
        url = self.ep()
        data = await self.get(url, params=params)
        return TypeAdapter(list[Authorization]).validate_python(data['items'])

    async def get_token_expiration_status(self) -> int:
        """
        Get expiration status for a token

        Epoch-based expiration time for the token.

        :rtype: int
        """
        url = self.ep('tokenExpiry')
        data = await super().get(url)
        r = data['exp']
        return r

    async def delete(self, authorization_id: str = None, client_id: str = None, org_id: str = None):
        """
        Deletes an authorization, by authorization ID or client ID and org ID.

        Specify the authorization Id in the authorizationId parameter in the URI which was listed in the list
        resource. The client_id parameter can be combined with org_id.

        :param authorization_id: The unique identifier for the authorization
        :type authorization_id: str
        :param client_id: The unique oAuth client id.
        :type client_id: str
        :param org_id: The ID of the organization. If no orgId is specified, use orgId from the OAuth token.
        :type org_id: str
        """
        if authorization_id:
            if client_id or org_id:
                raise ValueError(
                    'Invalid parameter combination: authorization_id cannot be combined with client_id or org_id.')
            url = self.ep(authorization_id)
            params = None
        else:
            if not client_id:
                raise ValueError(
                    'Invalid parameter combination: client_id is required when authorization_id is not specified.')
            url = self.ep()
            params = client_id and {'clientId': client_id} or dict()
            if org_id:
                params['orgId'] = org_id
        await super().delete(url, params=params)


class AsConvergedRecordingsApi(AsApiChild, base=''):
    """
    Converged Recordings

    Webex Meetings and Webex Calling (with Webex as the Call Recording provider) leverage the same recording
    infrastructure. That ensures that users can use the same recording API to fetch call recordings and/or meeting
    recordings. This convergence allows the sharing of functionality (summaries, transcripts, etc.) across the Webex
    Suite and provides a consistent user experience.

    This API is currently limited to Webex Calling i.e., providing details for call recordings but will later be
    extended to include Webex Meeting recordings.

    The access token needs the following scopes:

    User: `spark:recordings_read` `spark:recordings_write`

    Admin: `spark-admin:recordings_read` `spark-admin:recordings_write`

    Compliance officer: `spark-compliance:recordings_read` `spark-compliance:recordings_write`

    When the recording is paused in a call, the recording does not contain the pause. If the recording is stopped and
    restarted in a call, several recordings are created. Those recordings will be consolidated and available all at
    once.

    For information on the call recording feature, refer to `Manage call recording for Webex Calling
    <https://help.webex.com/en-us/article/ilga4/Manage-call-recording-for-Webex-Calling#wbxch_t_manage-call
    -recording_selecting-call-recording-provider>`_.
    """

    def list_for_admin_or_compliance_officer_gen(self, from_: Union[str, datetime] = None,
                                             to_: Union[str, datetime] = None, status: RecordingStatus = None,
                                             service_type: RecordingServiceType = None,
                                             format_: str = None, owner_id: str = None,
                                             owner_email: str = None,
                                             owner_type: RecordingOwnerType = None,
                                             storage_region: RecordingStorageRegion = None,
                                             location_id: str = None, topic: str = None,
                                             timezone: str = None,
                                             **params) -> AsyncGenerator[ConvergedRecording, None, None]:
        """
        List Recordings for Admin or Compliance officer

        List recordings for an admin or compliance officer. You can specify a date range, and the maximum number of
        recordings to return.

        The list returned is sorted in descending order by the date and time that the recordings were created.

        Long result sets are split into `pages
        <https://developer.webex.com/docs/basics#pagination>`_.

        List recordings requires the `spark-compliance:recordings_read` scope for compliance officer and
        `spark-admin:recordings_read` scope for admin.

        :param from_: Starting date and time (inclusive) for recordings to return, in any `ISO 8601
            <https://en.wikipedia.org/wiki/ISO_8601>`_ compliant format.
            `from` cannot be after `to`. The interval between `from` and `to` must be within 30 days.
        :type from_: Union[str, datetime]
        :param to_: Ending date and time (exclusive) for List recordings to return, in any `ISO 8601
            <https://en.wikipedia.org/wiki/ISO_8601>`_ compliant format.
            `to` cannot be before `from`. The interval between `from` and `to` must be within 30 days.
        :type to_: Union[str, datetime]
        :param status: Recording's status. If not specified or `available`, retrieves recordings that are available.
            Otherwise, if specified as `deleted`, retrieves recordings that have been moved into the recycle bin.
        :type status: RecordingStatus
        :param service_type: Recording's service-type. If specified, the API filters recordings by service-type. Valid
            values are `calling` and `customerAssist`.
        :type service_type: RecordingServiceType
        :param format_: Recording's file format. If specified, the API filters recordings by format. Valid values are
            `MP3`.
        :type format_: str
        :param owner_id: Webex user Id to fetch recordings for a particular user.
        :type owner_id: str
        :param owner_email: Webex email address to fetch recordings for a particular user.
        :type owner_email: str
        :param owner_type: Recording based on type of user.
        :type owner_type: RecordingOwnerType
        :param storage_region: Recording stored in certain Webex locations.
        :type storage_region: RecordingStorageRegion
        :param location_id: Fetch recordings for users in a particular Webex Calling location (as configured in Control
            Hub).
        :type location_id: str
        :param topic: Recording's topic. If specified, the API filters recordings by topic in a case-insensitive
            manner.
        :type topic: str
        :param timezone: e.g. UTC
        :type timezone: str
        :return: Generator yielding :class:`ConvergedRecording` instances
        """
        if from_ is not None:
            if isinstance(from_, str):
                from_ = isoparse(from_)
            from_ = dt_iso_str(from_)
            params['from'] = from_
        if to_ is not None:
            if isinstance(to_, str):
                to_ = isoparse(to_)
            to_ = dt_iso_str(to_)
            params['to'] = to_
        if status is not None:
            params['status'] = enum_str(status)
        if service_type is not None:
            params['serviceType'] = enum_str(service_type)
        if format_ is not None:
            params['format'] = enum_str(format_)
        if owner_id is not None:
            params['ownerId'] = owner_id
        if owner_email is not None:
            params['ownerEmail'] = owner_email
        if owner_type is not None:
            params['ownerType'] = enum_str(owner_type)
        if storage_region is not None:
            params['storageRegion'] = enum_str(storage_region)
        if location_id is not None:
            params['locationId'] = location_id
        if topic is not None:
            params['topic'] = topic
        if timezone is not None:
            params['timezone'] = timezone
        url = self.ep('admin/convergedRecordings')
        return self.session.follow_pagination(url=url, model=ConvergedRecording, item_key='items', params=params)

    async def list_for_admin_or_compliance_officer(self, from_: Union[str, datetime] = None,
                                             to_: Union[str, datetime] = None, status: RecordingStatus = None,
                                             service_type: RecordingServiceType = None,
                                             format_: str = None, owner_id: str = None,
                                             owner_email: str = None,
                                             owner_type: RecordingOwnerType = None,
                                             storage_region: RecordingStorageRegion = None,
                                             location_id: str = None, topic: str = None,
                                             timezone: str = None,
                                             **params) -> List[ConvergedRecording]:
        """
        List Recordings for Admin or Compliance officer

        List recordings for an admin or compliance officer. You can specify a date range, and the maximum number of
        recordings to return.

        The list returned is sorted in descending order by the date and time that the recordings were created.

        Long result sets are split into `pages
        <https://developer.webex.com/docs/basics#pagination>`_.

        List recordings requires the `spark-compliance:recordings_read` scope for compliance officer and
        `spark-admin:recordings_read` scope for admin.

        :param from_: Starting date and time (inclusive) for recordings to return, in any `ISO 8601
            <https://en.wikipedia.org/wiki/ISO_8601>`_ compliant format.
            `from` cannot be after `to`. The interval between `from` and `to` must be within 30 days.
        :type from_: Union[str, datetime]
        :param to_: Ending date and time (exclusive) for List recordings to return, in any `ISO 8601
            <https://en.wikipedia.org/wiki/ISO_8601>`_ compliant format.
            `to` cannot be before `from`. The interval between `from` and `to` must be within 30 days.
        :type to_: Union[str, datetime]
        :param status: Recording's status. If not specified or `available`, retrieves recordings that are available.
            Otherwise, if specified as `deleted`, retrieves recordings that have been moved into the recycle bin.
        :type status: RecordingStatus
        :param service_type: Recording's service-type. If specified, the API filters recordings by service-type. Valid
            values are `calling` and `customerAssist`.
        :type service_type: RecordingServiceType
        :param format_: Recording's file format. If specified, the API filters recordings by format. Valid values are
            `MP3`.
        :type format_: str
        :param owner_id: Webex user Id to fetch recordings for a particular user.
        :type owner_id: str
        :param owner_email: Webex email address to fetch recordings for a particular user.
        :type owner_email: str
        :param owner_type: Recording based on type of user.
        :type owner_type: RecordingOwnerType
        :param storage_region: Recording stored in certain Webex locations.
        :type storage_region: RecordingStorageRegion
        :param location_id: Fetch recordings for users in a particular Webex Calling location (as configured in Control
            Hub).
        :type location_id: str
        :param topic: Recording's topic. If specified, the API filters recordings by topic in a case-insensitive
            manner.
        :type topic: str
        :param timezone: e.g. UTC
        :type timezone: str
        :return: Generator yielding :class:`ConvergedRecording` instances
        """
        if from_ is not None:
            if isinstance(from_, str):
                from_ = isoparse(from_)
            from_ = dt_iso_str(from_)
            params['from'] = from_
        if to_ is not None:
            if isinstance(to_, str):
                to_ = isoparse(to_)
            to_ = dt_iso_str(to_)
            params['to'] = to_
        if status is not None:
            params['status'] = enum_str(status)
        if service_type is not None:
            params['serviceType'] = enum_str(service_type)
        if format_ is not None:
            params['format'] = enum_str(format_)
        if owner_id is not None:
            params['ownerId'] = owner_id
        if owner_email is not None:
            params['ownerEmail'] = owner_email
        if owner_type is not None:
            params['ownerType'] = enum_str(owner_type)
        if storage_region is not None:
            params['storageRegion'] = enum_str(storage_region)
        if location_id is not None:
            params['locationId'] = location_id
        if topic is not None:
            params['topic'] = topic
        if timezone is not None:
            params['timezone'] = timezone
        url = self.ep('admin/convergedRecordings')
        return [o async for o in self.session.follow_pagination(url=url, model=ConvergedRecording, item_key='items', params=params)]

    def list_gen(self, from_: Union[str, datetime] = None,
             to_: Union[str, datetime] = None, status: RecordingStatus = None,
             service_type: RecordingServiceType = None,
             format_: str = None, owner_id: str = None,
             owner_email: str = None, owner_type: RecordingOwnerType = None,
             storage_region: RecordingStorageRegion = None,
             location_id: str = None, topic: str = None, timezone: str = None,
             **params) -> AsyncGenerator[ConvergedRecording, None, None]:
        """
        List Recordings

        List recordings. You can specify a date range, and the maximum number of recordings to return.

        The list returned is sorted in descending order by the date and time that the recordings were created.

        Long result sets are split into `pages
        <https://developer.webex.com/docs/basics#pagination>`_.

        List recordings requires the `spark:recordings_read` scope.

        :param from_: Starting date and time (inclusive) for recordings to return, in any `ISO 8601
            <https://en.wikipedia.org/wiki/ISO_8601>`_ compliant format.
            `from` cannot be after `to`. The interval between `from` and `to` must be within 30 days.
        :type from_: Union[str, datetime]
        :param to_: Ending date and time (exclusive) for List recordings to return, in any `ISO 8601
            <https://en.wikipedia.org/wiki/ISO_8601>`_ compliant format.
            `to` cannot be before `from`. The interval between `from` and `to` must be within 30 days.
        :type to_: Union[str, datetime]
        :param status: Recording's status. If not specified or `available`, retrieves recordings that are available.
            Otherwise, if specified as `deleted`, retrieves recordings that have been moved into the recycle bin.
        :type status: RecordingStatus
        :param service_type: Recording's service-type. If specified, the API filters recordings by service-type. Valid
            values are `calling` and `customerAssist`.
        :type service_type: RecordingServiceType
        :param format_: Recording's file format. If specified, the API filters recordings by format. Valid values are
            `MP3`.
        :type format_: RecordingObjectFormat
        :param owner_id: Webex user Id to fetch recordings for a particular user.
        :type owner_id: str
        :param owner_email: Webex email address to fetch recordings for a particular user.
        :type owner_email: str
        :param owner_type: Recording based on type of user.
        :type owner_type: RecordingOwnerType
        :param storage_region: Recording stored in certain Webex locations.
        :type storage_region: RecordingStorageRegion
        :param location_id: Fetch recordings for users in a particular Webex Calling location (as configured in Control
            Hub).
        :type location_id: str
        :param topic: Recording's topic. If specified, the API filters recordings by topic in a case-insensitive
            manner.
        :type topic: str
        :param timezone: e.g. UTC
        :type timezone: str
        :return: Generator yielding :class:`ConvergedRecording` instances
        """
        if from_ is not None:
            if isinstance(from_, str):
                from_ = isoparse(from_)
            from_ = dt_iso_str(from_)
            params['from'] = from_
        if to_ is not None:
            if isinstance(to_, str):
                to_ = isoparse(to_)
            to_ = dt_iso_str(to_)
            params['to'] = to_
        if status is not None:
            params['status'] = enum_str(status)
        if service_type is not None:
            params['serviceType'] = enum_str(service_type)
        if format_ is not None:
            params['format'] = enum_str(format_)
        if owner_id is not None:
            params['ownerId'] = owner_id
        if owner_email is not None:
            params['ownerEmail'] = owner_email
        if owner_type is not None:
            params['ownerType'] = enum_str(owner_type)
        if storage_region is not None:
            params['storageRegion'] = enum_str(storage_region)
        if location_id is not None:
            params['locationId'] = location_id
        if topic is not None:
            params['topic'] = topic
        if timezone is not None:
            params['timezone'] = timezone
        url = self.ep('convergedRecordings')
        return self.session.follow_pagination(url=url, model=ConvergedRecording, item_key='items', params=params)

    async def list(self, from_: Union[str, datetime] = None,
             to_: Union[str, datetime] = None, status: RecordingStatus = None,
             service_type: RecordingServiceType = None,
             format_: str = None, owner_id: str = None,
             owner_email: str = None, owner_type: RecordingOwnerType = None,
             storage_region: RecordingStorageRegion = None,
             location_id: str = None, topic: str = None, timezone: str = None,
             **params) -> List[ConvergedRecording]:
        """
        List Recordings

        List recordings. You can specify a date range, and the maximum number of recordings to return.

        The list returned is sorted in descending order by the date and time that the recordings were created.

        Long result sets are split into `pages
        <https://developer.webex.com/docs/basics#pagination>`_.

        List recordings requires the `spark:recordings_read` scope.

        :param from_: Starting date and time (inclusive) for recordings to return, in any `ISO 8601
            <https://en.wikipedia.org/wiki/ISO_8601>`_ compliant format.
            `from` cannot be after `to`. The interval between `from` and `to` must be within 30 days.
        :type from_: Union[str, datetime]
        :param to_: Ending date and time (exclusive) for List recordings to return, in any `ISO 8601
            <https://en.wikipedia.org/wiki/ISO_8601>`_ compliant format.
            `to` cannot be before `from`. The interval between `from` and `to` must be within 30 days.
        :type to_: Union[str, datetime]
        :param status: Recording's status. If not specified or `available`, retrieves recordings that are available.
            Otherwise, if specified as `deleted`, retrieves recordings that have been moved into the recycle bin.
        :type status: RecordingStatus
        :param service_type: Recording's service-type. If specified, the API filters recordings by service-type. Valid
            values are `calling` and `customerAssist`.
        :type service_type: RecordingServiceType
        :param format_: Recording's file format. If specified, the API filters recordings by format. Valid values are
            `MP3`.
        :type format_: RecordingObjectFormat
        :param owner_id: Webex user Id to fetch recordings for a particular user.
        :type owner_id: str
        :param owner_email: Webex email address to fetch recordings for a particular user.
        :type owner_email: str
        :param owner_type: Recording based on type of user.
        :type owner_type: RecordingOwnerType
        :param storage_region: Recording stored in certain Webex locations.
        :type storage_region: RecordingStorageRegion
        :param location_id: Fetch recordings for users in a particular Webex Calling location (as configured in Control
            Hub).
        :type location_id: str
        :param topic: Recording's topic. If specified, the API filters recordings by topic in a case-insensitive
            manner.
        :type topic: str
        :param timezone: e.g. UTC
        :type timezone: str
        :return: Generator yielding :class:`ConvergedRecording` instances
        """
        if from_ is not None:
            if isinstance(from_, str):
                from_ = isoparse(from_)
            from_ = dt_iso_str(from_)
            params['from'] = from_
        if to_ is not None:
            if isinstance(to_, str):
                to_ = isoparse(to_)
            to_ = dt_iso_str(to_)
            params['to'] = to_
        if status is not None:
            params['status'] = enum_str(status)
        if service_type is not None:
            params['serviceType'] = enum_str(service_type)
        if format_ is not None:
            params['format'] = enum_str(format_)
        if owner_id is not None:
            params['ownerId'] = owner_id
        if owner_email is not None:
            params['ownerEmail'] = owner_email
        if owner_type is not None:
            params['ownerType'] = enum_str(owner_type)
        if storage_region is not None:
            params['storageRegion'] = enum_str(storage_region)
        if location_id is not None:
            params['locationId'] = location_id
        if topic is not None:
            params['topic'] = topic
        if timezone is not None:
            params['timezone'] = timezone
        url = self.ep('convergedRecordings')
        return [o async for o in self.session.follow_pagination(url=url, model=ConvergedRecording, item_key='items', params=params)]

    async def purge_recordings_from_recycle_bin(self, purge_all: bool = None, owner_email: str = None,
                                          recording_ids: List[str] = None):
        """
        Purge Recordings from Recycle Bin

        Purge recordings from the recycle bin with recording IDs or purge all the recordings that are in the recycle
        bin. A recording once purged cannot be restored.

        Only the following two entities can use this API

        * Administrator: A user or an application with the scope `spark-admin:recordings_write`.

        * User: An authenticated user who does not have the scope `spark-admin:recordings_write` but has
          `spark:recordings_write`.

        As an `administrator`, you can purge a list of recordings or all recordings of a particular user within the org
        you manage from the recycle bin.

        As a `user`, you can purge a list of your own recordings or all your recordings from the recycle bin.

        * If `purgeAll` is `true`:

        * `recordingIds` should be empty.

        * If the caller of this API is an `administrator`, `ownerEmail` should not be empty and all recordings owned
          the `ownerEmail` will be purged from the recycle bin.

        * If the caller of this API is a `user`, `ownerEmail` should be empty and all recordings owned by the caller
          will be purged from the recycle bin.

        * If `purgeAll` is `false`:

        * `ownerEmail` should be empty.

        * `recordingIds` should not be empty and its maximum size is `100`.

        :param purge_all: If not specified or `false`, purges the recordings specified by `recordingIds` from the
            recycle bin. If `true`, purges all recordings owned by the caller in case of `user`, and all recordings
            owned by `ownerEmail` in case of `administrator` from the recycle bin.
        :type purge_all: bool
        :param owner_email: Email address for the recording owner. This parameter is only used if `purgeAll` is set to
            `true` and the user or application calling the API has the required administrator scope
            `spark-admin:recordings_write`. The administrator may specify the email of a user from an org they manage
            and the API will purge all the recordings of that user from the recycle bin.
        :type owner_email: str
        :param recording_ids: Recording IDs for purging recordings from the recycle bin in batch.
        :type recording_ids: list[str]
        :rtype: None
        """
        body = dict()
        if purge_all is not None:
            body['purgeAll'] = purge_all
        if owner_email is not None:
            body['ownerEmail'] = owner_email
        if recording_ids is not None:
            body['recordingIds'] = recording_ids
        url = self.ep('convergedRecordings/purge')
        await super().post(url, json=body)

    async def reassign(self, reassign_owner_email: str, owner_email: str = None, recording_ids: List[str] = None):
        """
        Reassign Recordings

        Reassigns recordings to a new user. As an administrator, you can reassign a list of recordings or all
        recordings of a particular user to a new user.

        The recordings can belong to an org user, a virtual line, or a workspace, but the destination user should only
        be a valid org user.

        * If only the 'ownerEmail' is provided, it indicates that all recordings owned by the "ownerEmail" will be
          reassigned.

        * If only the 'recordingIds' are provided, it indicates that these 'recordingIds' will be reassigned.

        * If both the 'ownerEmail' and 'recordingIds' are provided, only the recordingIds belonging to the provided
          'ownerEmail' will be reassigned.

        The `spark-admin:recordings_write` scope is required to reassign recordings.

        :param reassign_owner_email: New owner of the recordings.
        :type reassign_owner_email: str
        :param owner_email: Recording owner email. Can be a user, a virtual line, or a workspace.
        :type owner_email: str
        :param recording_ids: List of recording identifiers to be reassigned.
        :type recording_ids: list[str]
        :rtype: None
        """
        body = dict()
        if owner_email is not None:
            body['ownerEmail'] = owner_email
        if recording_ids is not None:
            body['recordingIds'] = recording_ids
        body['reassignOwnerEmail'] = reassign_owner_email
        url = self.ep('convergedRecordings/reassign')
        await super().post(url, json=body)

    async def restore_recordings_from_recycle_bin(self, restore_all: bool = None, owner_email: str = None,
                                            recording_ids: List[str] = None):
        """
        Restore Recordings from Recycle Bin

        Restore recordings from the recycle bin with recording IDs or restore all the recordings that are in the
        recycle bin.

        Only the following two entities can use this API

        * Administrator: A user or an application with the scope `spark-admin:recordings_write`.

        * User: An authenticated user who does not have the scope `spark-admin:recordings_write` but has
          `spark:recordings_write`.

        As an `administrator`, you can restore a list of recordings or all recordings of a particular user within the
        org you manage from the recycle bin.

        As a `user`, you can restore a list of your own recordings or all your recordings from the recycle bin.

        * If `restoreAll` is `true`:

        * `recordingIds` should be empty.

        * If the caller of this API is an `administrator`, `ownerEmail` should not be empty and all recordings owned by
          the `ownerEmail` will be restored from the recycle bin.

        * If the caller of this API is a `user`, `ownerEmail` should be empty and all recordings owned by the caller
          will be restored from the recycle bin.

        * If `restoreAll` is `false`:

        * `ownerEmail` should be empty.

        * `recordingIds` should not be empty and its maximum size is `100`.

        :param restore_all: If not specified or `false`, restores the recordings specified by `recordingIds` from the
            recycle bin. If `true`, restores all recordings owned by the caller in case of `user`, and all recordings
            owned by `ownerEmail` in case of `administrator` from the recycle bin.
        :type restore_all: bool
        :param owner_email: Email address for the recording owner. This parameter is only used if `restoreAll` is set
            to `true` and the user or application calling the API has the required administrator scope
            `spark-admin:recordings_write`. The administrator may specify the email of a user from an org they manage
            and the API will restore all the recordings of that user from the recycle bin.
        :type owner_email: str
        :param recording_ids: Recording IDs for restoring recordings from the recycle bin in batch.
        :type recording_ids: list[str]
        :rtype: None
        """
        body = dict()
        if restore_all is not None:
            body['restoreAll'] = restore_all
        if owner_email is not None:
            body['ownerEmail'] = owner_email
        if recording_ids is not None:
            body['recordingIds'] = recording_ids
        url = self.ep('convergedRecordings/restore')
        await super().post(url, json=body)

    async def move_recordings_into_the_recycle_bin(self, trash_all: bool = None, owner_email: str = None,
                                             recording_ids: List[str] = None):
        """
        Move Recordings into the Recycle Bin

        Move recordings into the recycle bin with recording IDs or move all the recordings to the recycle bin.

        Only the following two entities can use this API

        * Administrator: A user or an application with the scope `spark-admin:recordings_write`.

        * User: An authenticated user who does not have the scope `spark-admin:recordings_write` but has
          `spark:recordings_write`.

        As an `administrator`, you can move a list of recordings or all recordings of a particular user within the org
        you manage to the recycle bin.

        As a `user`, you can move a list of your own recordings or all your recordings to the recycle bin.

        Recordings in the recycle bin can be recovered by `Restore Recordings from Recycle Bin
        <https://developer.webex.com/docs/api/v1/converged-recordings/restore-recordings-from-recycle-bin>`_ API. If
        you'd like to
        empty recordings from the recycle bin, you can use `Purge Recordings from Recycle Bin
        <https://developer.webex.com/docs/api/v1/converged-recordings/purge-recordings-from-recycle-bin>`_ API to
        purge all or some
        of them.

        * If `trashAll` is `true`:

        * `recordingIds` should be empty.

        * If the caller of this API is an `administrator`, `ownerEmail` should not be empty and all recordings owned by
          the `ownerEmail` will be moved to the recycle bin.

        * If the caller of this API is a `user`, `ownerEmail` should be empty and all recordings owned by the caller
          will be moved to the recycle bin.

        * If `trashAll` is `false`:

        * `ownerEmail` should be empty.

        * `recordingIds` should not be empty and its maximum size is `100`.

        :param trash_all: If not specified or `false`, moves the recordings specified by `recordingIds` to the recycle
            bin. If `true`, moves all recordings owned by the caller in case of `user`, and all recordings owned by
            `ownerEmail` in case of `administrator` to the recycle bin.
        :type trash_all: bool
        :param owner_email: Email address for the recording owner. This parameter is only used if `trashAll` is set to
            `true` and the user or application calling the API has the required administrator scope
            `spark-admin:recordings_write`. The administrator may specify the email of a user from an org they manage
            and the API will move all the recordings of that user into the recycle bin.
        :type owner_email: str
        :param recording_ids: Recording IDs for moving recordings to the recycle bin in batch.
        :type recording_ids: list[str]
        :rtype: None
        """
        body = dict()
        if trash_all is not None:
            body['trashAll'] = trash_all
        if owner_email is not None:
            body['ownerEmail'] = owner_email
        if recording_ids is not None:
            body['recordingIds'] = recording_ids
        url = self.ep('convergedRecordings/softDelete')
        await super().post(url, json=body)

    async def delete(self, recording_id: str, reason: str = None, comment: str = None):
        """
        Delete a Recording

        Removes a recording with a specified recording ID. The deleted recording cannot be recovered.

        If a Compliance Officer deletes another user's recording, the recording will be inaccessible to regular users
        (host, attendees and shared), and to Compliance officer also. This action purges the recordings from Webex.

        Delete a Recording requires the `spark-compliance:recordings_write` scope.

        :param recording_id: A unique identifier for the recording.
        :type recording_id: str
        :param reason: Reason for deleting a recording. Only required when a Compliance Officer is operating on another
            user's recording.
        :type reason: str
        :param comment: Compliance Officer's explanation for deleting a recording. The comment can be a maximum of 255
            characters long.
        :type comment: str
        :rtype: None
        """
        body = dict()
        if reason is not None:
            body['reason'] = reason
        if comment is not None:
            body['comment'] = comment
        url = self.ep(f'convergedRecordings/{recording_id}')
        await super().delete(url, json=body)

    async def details(self, recording_id: str) -> ConvergedRecordingWithDirectDownloadLinks:
        """
        Get Recording Details

        Retrieves details for a recording with a specified recording ID.

        Only recordings of owner with the authenticated user may be retrieved.

        Get Recording Details requires the `spark-compliance:recordings_read` scope for compliance officer,
        `spark-admin:recordings_read` scope for admin and `spark:recordings_read` scope for user.

        :param recording_id: A unique identifier for the recording.
        :type recording_id: str
        :rtype: :class:`ConvergedRecordingWithDirectDownloadLinks`
        """
        url = self.ep(f'convergedRecordings/{recording_id}')
        data = await super().get(url)
        r = ConvergedRecordingWithDirectDownloadLinks.model_validate(data)
        return r

    async def metadata(self, recording_id: str, show_all_types: bool = None) -> ConvergedRecordingMeta:
        """
        Get Recording metadata

        Retrieves metadata details for a recording with a specified recording ID. The recording must be owned by the
        authenticated user.

        For information on the metadata fields, refer to `Metadata Guide
        <https://developer.webex.com/docs/api/guides/consolidated-metadata-documentation-and-samples-guide>`_

        Get Recording metadata requires the `spark-compliance:recordings_read` scope for compliance officer,
        `spark-admin:recordings_read` for admin and `spark:recordings_read` for user.

        :param recording_id: A unique identifier for the recording.
        :type recording_id: str
        :param show_all_types: If `showAllTypes` is `true`, all attributes will be shown. If it's `false` or not
            specified, the following attributes of the metadata will be hidden.

            * serviceData.callActivity.mediaStreams

            * serviceData.callActivity.participants

            * serviceData.callActivity.redirectInfo

            * serviceData.callActivity.redirectedCall
        :type show_all_types: bool
        :rtype: None
        """
        params = {}
        if show_all_types is not None:
            params['showAllTypes'] = str(show_all_types).lower()
        url = self.ep(f'convergedRecordings/{recording_id}/metadata')
        data = await super().get(url, params=params)
        return ConvergedRecordingMeta.model_validate(data)


class AsDetailedCDRApi(AsApiChild, base=''):
    """
    Reports: Detailed Call History

    The base URL for these APIs is **analytics-calling.webexapis.com** (or
    **analytics-calling-gov.webexapis.com** for Government). These endpoints are
    not compatible with the API reference's **Try It** feature. For questions or
    assistance, please contact the Webex Developer Support team at
    devsupport@webex.com.

    The CDR Feed API is recommended for users who need to pull CDR records for a specific time period. For more
    up-to-date records, use the cdr_stream endpoint API instead.

    To retrieve Detailed Call History information, your request must include a token with the
    `spark-admin:calling_cdr_read` `scope
    <https://developer.webex.com/docs/integrations#scopes>`_. Additionally, the authenticating user must have the
    administrator role "Webex Calling Detailed Call History API access" enabled.

    The CDR Feed API can query any 12-hour period between 5 minutes ago and 30 days prior to the current UTC time. Only
    12 hours of records can be retrieved per request (i.e., the time between the selected start and end times in a
    single API call). For example: If a call ends at 9:46 AM, the record is available for collection starting at 9:51
    AM and remains available until 9:46 AM 30 days later. The maximum query duration starting at 9:51 AM would end at
    9:51 PM the same day.

    This API is rate-limited to 1 initial request per minute per user token, with up to 10 additional pagination
    requests per minute per user token.

    Details on the fields returned from this API and their potential values are available at
    <https://help.webex.com/en-us/article/nmug598/Reports-for-Your-Cloud-Collaboration-Portfolio>. Select the **Report
    templates** tab, and under the **Webex Calling reports**, see **Calling Detailed Call History Report**.

    By default, the calls to analytics-calling.webexapis.com are routed to the closest region's servers. If the
    region's servers host the organization's data, then the data is returned. Otherwise, an HTTP 451 error code is
    returned. In such cases, the response body contains endpoint information indicating where the organization’s data
    can be retrieved.
    """

    def get_cdr_history_gen(self, start_time: Union[str, datetime] = None, end_time: Union[datetime, str] = None,
                        locations: list[str] = None, host:str = 'analytics-calling.webexapis.com', stream: bool = False,
                        **params) -> AsyncGenerator[CDR, None, None]:
        """
        Provides Webex Calling Detailed Call History data for your organization.

        Results can be filtered with the startTime, endTime and locations request parameters. The startTime and endTime
        parameters specify the start and end of the time period for the Detailed Call History reports you wish to
        collect.
        The API will return all reports that were created between startTime and endTime.

        :param start_time: Time of the first report you wish to collect. (Report time is the time the call finished).

            Note: The specified time must be between 5 minutes ago and 48 hours ago, and Can be a datetime object or
            an ISO-8601 datetime string to be parsed by :meth:`dateutil.parser.isoparse`

        :type start_time: Union[str, datetime]
        :param end_time: Time of the last report you wish to collect. (Report time is the time the call finished).

            Note: The specified time should be earlier than startTime and no earlier than 48 hours ago. Can be a
            datetime object or an ISO-8601 datetime string to be parsed by :meth:`dateutil.parser.isoparse`.
        :type end_time: Union[str, datetime]
        :param locations: Names of the location (as shown in Control Hub). Up to 10 comma-separated locations can be
            provided. Allows you to query reports by location.
        :type locations: list[str]
        :param host: analytics host to access. Defaults to 'analytics.webexapis.com'.
        :type host: str
        :param stream: If true, collect data from cdr_stream, else from cdr_feed. Defaults to False (cdr_feed).
        :type stream: bool
        :param params: additional arguments
        :return:
        """
        endpoint = 'cdr_stream' if stream else 'cdr_feed'
        url = f'https://{host}/v1/{endpoint}'
        if locations:
            params['locations'] = ','.join(locations)
        if not start_time:
            start_time = datetime.now(tz=tz.tzutc()) - timedelta(hours=47, minutes=58)
        if not end_time:
            end_time = datetime.now(tz=tz.tzutc()) - timedelta(minutes=5, seconds=30)

        def guess_datetime(dt: Union[datetime, str]) -> str:
            if isinstance(dt, str):
                dt = isoparse(dt)
            r = dt_iso_str(dt)
            return r

        params['startTime'] = guess_datetime(start_time)
        params['endTime'] = guess_datetime(end_time)
        # noinspection PyTypeChecker
        return self.session.follow_pagination(url=url, model=CDR, params=params, item_key='items')

    async def get_cdr_history(self, start_time: Union[str, datetime] = None, end_time: Union[datetime, str] = None,
                        locations: list[str] = None, host:str = 'analytics-calling.webexapis.com', stream: bool = False,
                        **params) -> List[CDR]:
        """
        Provides Webex Calling Detailed Call History data for your organization.

        Results can be filtered with the startTime, endTime and locations request parameters. The startTime and endTime
        parameters specify the start and end of the time period for the Detailed Call History reports you wish to
        collect.
        The API will return all reports that were created between startTime and endTime.

        :param start_time: Time of the first report you wish to collect. (Report time is the time the call finished).

            Note: The specified time must be between 5 minutes ago and 48 hours ago, and Can be a datetime object or
            an ISO-8601 datetime string to be parsed by :meth:`dateutil.parser.isoparse`

        :type start_time: Union[str, datetime]
        :param end_time: Time of the last report you wish to collect. (Report time is the time the call finished).

            Note: The specified time should be earlier than startTime and no earlier than 48 hours ago. Can be a
            datetime object or an ISO-8601 datetime string to be parsed by :meth:`dateutil.parser.isoparse`.
        :type end_time: Union[str, datetime]
        :param locations: Names of the location (as shown in Control Hub). Up to 10 comma-separated locations can be
            provided. Allows you to query reports by location.
        :type locations: list[str]
        :param host: analytics host to access. Defaults to 'analytics.webexapis.com'.
        :type host: str
        :param stream: If true, collect data from cdr_stream, else from cdr_feed. Defaults to False (cdr_feed).
        :type stream: bool
        :param params: additional arguments
        :return:
        """
        endpoint = 'cdr_stream' if stream else 'cdr_feed'
        url = f'https://{host}/v1/{endpoint}'
        if locations:
            params['locations'] = ','.join(locations)
        if not start_time:
            start_time = datetime.now(tz=tz.tzutc()) - timedelta(hours=47, minutes=58)
        if not end_time:
            end_time = datetime.now(tz=tz.tzutc()) - timedelta(minutes=5, seconds=30)

        def guess_datetime(dt: Union[datetime, str]) -> str:
            if isinstance(dt, str):
                dt = isoparse(dt)
            r = dt_iso_str(dt)
            return r

        params['startTime'] = guess_datetime(start_time)
        params['endTime'] = guess_datetime(end_time)
        # noinspection PyTypeChecker
        return [o async for o in self.session.follow_pagination(url=url, model=CDR, params=params, item_key='items')]


class AsDeviceConfigurationsApi(AsApiChild, base='deviceConfigurations'):
    """
    The Device Configurations API allows developers to view and modify configurations on Webex Rooms devices, as well as
    other devices that use the configuration service.

    Viewing the list of all device configurations in an organization requires an administrator auth token with
    the spark-admin:devices_read scope. Adding, updating, or deleting configurations for devices in an organization
    requires an administrator auth token with both the spark-admin:devices_write and the spark-admin:devices_read scope.
    """

    async def list(self, device_id: str, key: str = None) -> DeviceConfigurationResponse:
        """
        Lists all device configurations associated with the given device ID. Administrators can list configurations
        for all devices within an organization.

        :param device_id: List device configurations by device ID.
        :type device_id: str
        :param key: This can optionally be used to filter configurations. Keys are composed of segments. It's
            possible to use absolute paths, wildcards or ranges.

            Absolute gives only one configuration as a result. Conference.MaxReceiveCallRate for example gives the
            ConferenceMaxReceiveCallRate configuration.

            Wildcards (*) can specify multiple configurations with shared segments. Audio.Ultrasound.* for example
            will filter on all Audio Ultrasound configurations.

            Range ([number]) can be used to filter numbered segments. FacilityService.Service[1].Name for instance
            only shows the first FacilityService Service Name configuration, FacilityService.Service[*].Name shows all,
            FacilityService.Service[1..3].Name shows the first three and FacilityService.Service[2..n].Name shows all
            starting at 2.
        :type key: str

        :return: device configurations
        """
        params = key and {'key': key} or dict()
        params['deviceId'] = device_id
        data = await self.get(self.ep(), params=params)
        return DeviceConfigurationResponse.model_validate(data)

    async def update(self, device_id: str, operations: List[DeviceConfigurationOperation]) -> DeviceConfigurationResponse:
        """
        Update Device Configurations

        :param device_id: Update device configurations by device ID.
        :param operations: list if operations to apply
        """
        body = [op.for_update() for op in operations]
        data = await self.patch(self.ep(), json=body, content_type='application/json-patch+json',
                          params={'deviceId': device_id})
        return DeviceConfigurationResponse.model_validate(data)


class AsDeviceSettingsJobsApi(AsApiChild, base='telephony/config/jobs/devices/callDeviceSettings'):
    """
    API for jobs to update device settings at the location and organization level

    """

    async def change(self, location_id: Optional[str], customization: DeviceCustomization,
               org_id: str = None) -> StartJobResponse:
        """
        Change device settings across organization or locations jobs.

        Performs bulk and asynchronous processing for all types of device settings initiated by organization and system
        admins in a stateful persistent manner. This job will modify the requested device settings across all
        non-customized devices. Whenever a location ID is specified in the request, it will modify the requested
        device settings only for non-customized devices that are part of the provided location within an organization.

        Returns a unique job ID which can then be utilized further to retrieve status and errors for the same.

        Only one job per customer can be running at any given time within the same organization. An attempt to run
        multiple jobs at the same time will result in a 409 error response.

        Running a job requires a full or read-only administrator auth token with a scope
        of spark-admin:telephony_config_write.

        :param location_id: Location within an organization where changes of device settings will be applied to all the
            devices within it.
        :type location_id: str
        :param customization: customization. Atttribute custom_enabled Indicates if all the devices within this
            location will be customized with new requested customizations(if set to true) or will be overridden with
            the one at organization level (if set to false or any other value). This field has no effect when the job
            is being triggered at organization level.
        :type customization: DeviceCustomization
        :param org_id: Apply change device settings for all the devices under this organization.
        :type org_id: str
        :return: information about the created job
        :rtype: StartJobResponse
        """
        url = self.ep()
        params = org_id and {'prgId': org_id} or None
        body = {}
        if location_id:
            body['locationId'] = location_id
            body['locationCustomizationsEnabled'] = customization.custom_enabled
        if customization.custom_enabled or not location_id:
            body['customizations'] = json.loads(customization.customizations.model_dump_json())
        data = await self.post(url=url, params=params, json=body)
        return StartJobResponse.model_validate(data)

    def list_gen(self, org_id: str = None, **params) -> AsyncGenerator[StartJobResponse, None, None]:
        """
        List change device settings jobs.

        Lists all the jobs for jobType calldevicesettings for the given organization in order of most recent one to
        oldest one irrespective of its status.

        This API requires a full or read-only administrator auth token with a scope of
        spark-admin:telephony_config_read.

        :param org_id: Retrieve list of 'calldevicesettings' jobs for this organization.
        :type org_id: str
        :param params: optional parameters
        :return: Generator of :class:`StartJobResponse` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep()
        # noinspection PyTypeChecker
        return self.session.follow_pagination(url=url, model=StartJobResponse, params=params)

    async def list(self, org_id: str = None, **params) -> List[StartJobResponse]:
        """
        List change device settings jobs.

        Lists all the jobs for jobType calldevicesettings for the given organization in order of most recent one to
        oldest one irrespective of its status.

        This API requires a full or read-only administrator auth token with a scope of
        spark-admin:telephony_config_read.

        :param org_id: Retrieve list of 'calldevicesettings' jobs for this organization.
        :type org_id: str
        :param params: optional parameters
        :return: Generator of :class:`StartJobResponse` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep()
        # noinspection PyTypeChecker
        return [o async for o in self.session.follow_pagination(url=url, model=StartJobResponse, params=params)]

    async def status(self, job_id: str, org_id: str = None) -> StartJobResponse:
        """
        Get change device settings job status.

        Provides details of the job with jobId of jobType calldevicesettings.

        This API requires a full or read-only administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param job_id: Retrieve job details for this jobId.
        :type job_id: str
        :param org_id: Retrieve job details for this org
        :type org_id: str
        :return: job details
        :rtype: StartJobResponse
        """
        url = self.ep(job_id)
        params = org_id and {'orgId': org_id} or None
        data = await self.get(url=url, params=params)
        return StartJobResponse.model_validate(data)

    def errors_gen(self, job_id: str, org_id: str = None) -> AsyncGenerator[JobErrorItem, None, None]:
        """
        List change device settings job errors.

        Lists all error details of the job with jobId of jobType calldevicesettings.

        This API requires a full or read-only administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param job_id: Retrieve job details for this jobId.
        :param org_id: Retrieve list of jobs for this organization.
        :return:
        """
        params = org_id and {'orgId': org_id} or None
        url = self.ep(f'{job_id}/errors')
        # noinspection PyTypeChecker
        return self.session.follow_pagination(url=url, model=JobErrorItem, params=params)

    async def errors(self, job_id: str, org_id: str = None) -> List[JobErrorItem]:
        """
        List change device settings job errors.

        Lists all error details of the job with jobId of jobType calldevicesettings.

        This API requires a full or read-only administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param job_id: Retrieve job details for this jobId.
        :param org_id: Retrieve list of jobs for this organization.
        :return:
        """
        params = org_id and {'orgId': org_id} or None
        url = self.ep(f'{job_id}/errors')
        # noinspection PyTypeChecker
        return [o async for o in self.session.follow_pagination(url=url, model=JobErrorItem, params=params)]


class AsDevicesApi(AsApiChild, base='devices'):
    """
    Devices

    Devices represent cloud-registered Webex RoomOS devices and Webex Calling phones. Devices may be associated with
    `Workspaces
    <https://developer.webex.com/docs/api/v1/workspaces>`_.

    The following scopes are required for performing the specified actions:

    * Searching and viewing details for devices requires an auth token with the `spark:devices_read` scope.
    * Updating or deleting your devices requires an auth token with the `spark:devices_write` scope.
    * Viewing the list of all devices in an organization requires an administrator auth token with the
      `spark-admin:devices_read` scope.
    * Adding, updating, or deleting all devices in an organization requires an administrator auth token with the
      `spark-admin:devices_write` scope.
    * Generating an activation code requires an auth token with the `spark-admin:devices_write` scope, and one of the
      `identity:placeonetimepassword_create` or `identity:one_time_password` scopes.

    These APIs cannot be used with Cisco 98xx devices that are not yet Webex
    Aware. Use Webex Control Hub to manage these devices.
    """

    #: device jobs Api
    settings_jobs: AsDeviceSettingsJobsApi

    def __init__(self, *, session: AsRestSession):
        super().__init__(session=session)
        self.settings_jobs = AsDeviceSettingsJobsApi(session=session)

    def list_gen(self, person_id: str = None, workspace_id: str = None, location_id: str = None,
             workspace_location_id: str = None, display_name: str = None, product: str = None,
             product_type: ProductType = None, tag: str = None, connection_status: ConnectionStatus = None,
             serial: str = None, software: str = None, upgrade_channel: str = None, error_code: str = None,
             capability: str = None, permission: str = None, mac: str = None, device_platform: DevicePlatform = None,
             planned_maintenance: MaintenanceMode = None, org_id: str = None, **params) -> AsyncGenerator[Device, None, None]:
        """
        List Devices

        Lists all active Webex devices associated with the authenticated user, such as devices activated in personal
        mode. Administrators can list all devices within an organization.

        :param person_id: List devices by person ID.
        :type person_id: str
        :param workspace_id: List devices by workspace ID.
        :type workspace_id: str
        :param location_id: List devices by location ID.
        :type location_id: str
        :param workspace_location_id: List devices by workspace location ID. Deprecated, prefer `location_id`.
        :type workspace_location_id: str
        :param display_name: List devices with this display name.
        :type display_name: str
        :param product: List devices with this product name.
        :type product: str
        :param product_type: List devices with this type. Possible values: roomdesk, phone, accessory, webexgo, unknown
        :type product_type: str
        :param tag: List devices which have a tag. Searching for multiple tags (logical AND) can be done by comma
        :type tag: str
            separating the tag values or adding several tag parameters.
        :param connection_status: List devices with this connection statu
        :type connection_status: str
        :param serial: List devices with this serial number.
        :type serial: str
        :param software: List devices with this software version.
        :type software: str
        :param upgrade_channel: List devices with this upgrade channel.
        :type upgrade_channel: str
        :param error_code: List devices with this error code.
        :type error_code: str
        :param capability: List devices with this capability. For example: xapi
        :type capability: str
        :param permission: List devices with this permission.
        :type permission: str
        :param mac: List devices with this MAC address.
        :type mac: str
        :param device_platform: List devices with this device platform.
        :type device_platform: DevicePlatform
        :param org_id: List devices in this organization. Only admin users of another organization (such as partners)
            may use this parameter.
        :param planned_maintenance:
        List devices with this planned maintenance.
        :type planned_maintenance: MaintenanceMode
        :type org_id: str
        :return: Generator yielding :class:`Device` instances
        """
        if display_name is not None:
            params['displayName'] = display_name
        if person_id is not None:
            params['personId'] = person_id
        if workspace_id is not None:
            params['workspaceId'] = workspace_id
        if org_id is not None:
            params['orgId'] = org_id
        if connection_status is not None:
            params['connectionStatus'] = connection_status
        if product is not None:
            params['product'] = enum_str(product)
        if product_type is not None:
            params['type'] = enum_str(product_type)
        if serial is not None:
            params['serial'] = serial
        if tag is not None:
            params['tag'] = tag
        if software is not None:
            params['software'] = software
        if upgrade_channel is not None:
            params['upgradeChannel'] = upgrade_channel
        if error_code is not None:
            params['errorCode'] = error_code
        if capability is not None:
            params['capability'] = enum_str(capability)
        if permission is not None:
            params['permission'] = permission
        if location_id is not None:
            params['locationId'] = location_id
        if workspace_location_id is not None:
            params['workspaceLocationId'] = workspace_location_id
        if mac is not None:
            params['mac'] = mac
        if device_platform is not None:
            params['devicePlatform'] = enum_str(device_platform)
        if planned_maintenance is not None:
            params['plannedMaintenance'] = enum_str(planned_maintenance)
        url = self.ep()
        return self.session.follow_pagination(url=url, model=Device, params=params, item_key='items')

    async def list(self, person_id: str = None, workspace_id: str = None, location_id: str = None,
             workspace_location_id: str = None, display_name: str = None, product: str = None,
             product_type: ProductType = None, tag: str = None, connection_status: ConnectionStatus = None,
             serial: str = None, software: str = None, upgrade_channel: str = None, error_code: str = None,
             capability: str = None, permission: str = None, mac: str = None, device_platform: DevicePlatform = None,
             planned_maintenance: MaintenanceMode = None, org_id: str = None, **params) -> List[Device]:
        """
        List Devices

        Lists all active Webex devices associated with the authenticated user, such as devices activated in personal
        mode. Administrators can list all devices within an organization.

        :param person_id: List devices by person ID.
        :type person_id: str
        :param workspace_id: List devices by workspace ID.
        :type workspace_id: str
        :param location_id: List devices by location ID.
        :type location_id: str
        :param workspace_location_id: List devices by workspace location ID. Deprecated, prefer `location_id`.
        :type workspace_location_id: str
        :param display_name: List devices with this display name.
        :type display_name: str
        :param product: List devices with this product name.
        :type product: str
        :param product_type: List devices with this type. Possible values: roomdesk, phone, accessory, webexgo, unknown
        :type product_type: str
        :param tag: List devices which have a tag. Searching for multiple tags (logical AND) can be done by comma
        :type tag: str
            separating the tag values or adding several tag parameters.
        :param connection_status: List devices with this connection statu
        :type connection_status: str
        :param serial: List devices with this serial number.
        :type serial: str
        :param software: List devices with this software version.
        :type software: str
        :param upgrade_channel: List devices with this upgrade channel.
        :type upgrade_channel: str
        :param error_code: List devices with this error code.
        :type error_code: str
        :param capability: List devices with this capability. For example: xapi
        :type capability: str
        :param permission: List devices with this permission.
        :type permission: str
        :param mac: List devices with this MAC address.
        :type mac: str
        :param device_platform: List devices with this device platform.
        :type device_platform: DevicePlatform
        :param org_id: List devices in this organization. Only admin users of another organization (such as partners)
            may use this parameter.
        :param planned_maintenance:
        List devices with this planned maintenance.
        :type planned_maintenance: MaintenanceMode
        :type org_id: str
        :return: Generator yielding :class:`Device` instances
        """
        if display_name is not None:
            params['displayName'] = display_name
        if person_id is not None:
            params['personId'] = person_id
        if workspace_id is not None:
            params['workspaceId'] = workspace_id
        if org_id is not None:
            params['orgId'] = org_id
        if connection_status is not None:
            params['connectionStatus'] = connection_status
        if product is not None:
            params['product'] = enum_str(product)
        if product_type is not None:
            params['type'] = enum_str(product_type)
        if serial is not None:
            params['serial'] = serial
        if tag is not None:
            params['tag'] = tag
        if software is not None:
            params['software'] = software
        if upgrade_channel is not None:
            params['upgradeChannel'] = upgrade_channel
        if error_code is not None:
            params['errorCode'] = error_code
        if capability is not None:
            params['capability'] = enum_str(capability)
        if permission is not None:
            params['permission'] = permission
        if location_id is not None:
            params['locationId'] = location_id
        if workspace_location_id is not None:
            params['workspaceLocationId'] = workspace_location_id
        if mac is not None:
            params['mac'] = mac
        if device_platform is not None:
            params['devicePlatform'] = enum_str(device_platform)
        if planned_maintenance is not None:
            params['plannedMaintenance'] = enum_str(planned_maintenance)
        url = self.ep()
        return [o async for o in self.session.follow_pagination(url=url, model=Device, params=params, item_key='items')]

    async def details(self, device_id: str, org_id: str = None) -> Device:
        """
        Get Device Details
        Shows details for a device, by ID.

        Specify the device ID in the deviceId parameter in the URI.

        :param device_id: A unique identifier for the device.
        :type device_id: str
        :param org_id:
        :type org_id: str
        :return: Device details
        :rtype: Device
        """
        url = self.ep(device_id)
        params = org_id and {'orgId': org_id} or None
        data = await self.get(url=url, params=params)
        return Device.model_validate(data)

    async def delete(self, device_id: str, org_id: str = None):
        """
        Delete a Device

        Deletes a device, by ID.

        Specify the device ID in the deviceId parameter in the URI.

        :param device_id: A unique identifier for the device.
        :type device_id: str
        :param org_id:
        :type org_id: str
        """
        url = self.ep(device_id)
        params = org_id and {'orgId': org_id} or None
        await super().delete(url=url, params=params)

    async def modify_device_tags(self, device_id: str, op: TagOp, value: List[str] = None, org_id: str = None) -> Device:
        """
        Modify Device Tags

        Update requests use JSON Patch syntax.

        :param device_id: A unique identifier for the device.
        :type device_id: str
        :param op: tag operation
        :type op: TagOp
        :param value: list of tags
        :type value: list[str]
        :param org_id:
        :type org_id: str
        :return: device details
        :rtype: Device
        """
        body = {'op': op.value if isinstance(op, TagOp) else op,
                'path': 'tags'}
        if value is not None:
            body['value'] = value
        url = self.ep(device_id)
        params = org_id and {'orgId': org_id} or None
        data = await self.patch(url=url, json=body, params=params, content_type='application/json-patch+json')
        return Device.model_validate(data)

    async def activation_code(self, workspace_id: str = None, person_id: str = None, model: str = None,
                        org_id: str = None) -> ActivationCodeResponse:
        """
        Create a Device Activation Code

        Generate an activation code for a device in a specific workspace by `workspaceId` or for a person by
        `personId`. This requires an auth token with the `identity:placeonetimepassword_create` and
        `spark-admin:devices_write` scopes.

        * Adding a device to a workspace with calling type `none` or `thirdPartySipCalling` will reset the workspace
          calling type to `freeCalling`.

        * Either `workspaceId` or `personId` should be provided. If both are supplied, the request will be invalid.

        * If no `model` is supplied, the `code` returned will only be accepted on RoomOS devices.

        * If your device is a phone, you must provide the `model` as a field. You can get the `model` from the
          `supported devices
          <https://developer.webex.com/docs/api/v1/device-call-settings/read-the-list-of-supported-devices>`_ API.


        :param workspace_id: The ID of the workspace where the device will be activated.
        :type workspace_id: str
        :param person_id: The ID of the person who will own the device once activated.
        :type person_id: str
        :param model: The model of the device being created.
        :type model: str
        :param org_id: The organization associated with the activation code generated.
        :type org_id: str
        :rtype: ActivationCodeResponse
        """
        params = org_id and {'orgId': org_id} or None
        body = {}
        if workspace_id is not None:
            body['workspaceId'] = workspace_id
        if person_id is not None:
            body['personId'] = person_id
        if model is not None:
            body['model'] = model
        url = self.ep('activationCode')
        data = await self.post(url=url, json=body, params=params)
        return ActivationCodeResponse.model_validate(data)

    async def create_by_mac_address(self, mac: str, workspace_id: str = None, person_id: str = None,
                              model: str = None, password: str = None, org_id: str = None) -> Optional[Device]:
        """
        Create a phone by it's MAC address in a specific workspace or for a person.
        Specify the mac, model and either workspaceId or personId.

        :param mac: The MAC address of the device being created.
        :type mac: str
        :param workspace_id: The ID of the workspace where the device will be activated.
        :type workspace_id: str
        :param person_id: The ID of the person who will own the device once activated.
        :type person_id: str
        :param model: The model of the device being created.
        :type model: str
        :param password: SIP password to be configured for the phone, only required with third party devices.
        :type password: str
        :param org_id: The organization associated with the device.
        :type org_id: str
        :return: created device information
        :rtype: Device
        """
        params = org_id and {'orgId': org_id} or None
        body = {'mac': mac}
        if workspace_id is not None:
            body['workspaceId'] = workspace_id
        if person_id is not None:
            body['personId'] = person_id
        if model is not None:
            body['model'] = model
        if password is not None:
            body['password'] = password
        url = self.ep()
        data = await super().post(url=url, json=body, params=params)
        if not data:
            return None
        return Device.model_validate(data)


class AsEventsApi(AsApiChild, base='events'):
    """
    Events are generated when actions take place within Webex, such as when someone creates or deletes a message.
    The Events API can only be used by a Compliance Officer with an API access token that contains the
    spark-compliance:events_read scope. See the Compliance Guide for more information.
    """

    def list_gen(self, resource: EventResource = None, type_: EventType = None, actor_id: str = None,
             from_: Union[str, datetime] = None, to_: Union[str, datetime] = None,
             **params) -> AsyncGenerator[ComplianceEvent, None, None]:
        """
        List events in your organization. Several query parameters are available to filter the response.

        Long result sets will be split into `pages
        <https://developer.webex.com/docs/basics#pagination>`_.

        :param resource: List events with a specific resource type.
        :type resource: EventResource
        :param type_: List events with a specific event type.
        :type type_: EventType
        :param actor_id: List events performed by this person, by person ID.
        :type actor_id: str
        :param from_: List events which occurred after a specific date and time.
        :type from_: str
        :param to_: List events that occurred before a specific date and time. If not specified, events up to the
            present time will be listed. Cannot be set to a future date relative to the current time.
        :type to_: str
        """
        if resource is not None:
            params['resource'] = enum_str(resource)
        if type_ is not None:
            params['type'] = enum_str(type_)
        if actor_id is not None:
            params['actorId'] = actor_id
        if from_ is not None:
            if isinstance(from_, str):
                from_ = isoparse(from_)
            from_ = dt_iso_str(from_)
            params['from'] = from_
        if to_ is not None:
            if isinstance(to_, str):
                to_ = isoparse(to_)
            to_ = dt_iso_str(to_)
            params['to'] = to_
        url = self.ep()
        return self.session.follow_pagination(url=url, model=ComplianceEvent, params=params)

    async def list(self, resource: EventResource = None, type_: EventType = None, actor_id: str = None,
             from_: Union[str, datetime] = None, to_: Union[str, datetime] = None,
             **params) -> List[ComplianceEvent]:
        """
        List events in your organization. Several query parameters are available to filter the response.

        Long result sets will be split into `pages
        <https://developer.webex.com/docs/basics#pagination>`_.

        :param resource: List events with a specific resource type.
        :type resource: EventResource
        :param type_: List events with a specific event type.
        :type type_: EventType
        :param actor_id: List events performed by this person, by person ID.
        :type actor_id: str
        :param from_: List events which occurred after a specific date and time.
        :type from_: str
        :param to_: List events that occurred before a specific date and time. If not specified, events up to the
            present time will be listed. Cannot be set to a future date relative to the current time.
        :type to_: str
        """
        if resource is not None:
            params['resource'] = enum_str(resource)
        if type_ is not None:
            params['type'] = enum_str(type_)
        if actor_id is not None:
            params['actorId'] = actor_id
        if from_ is not None:
            if isinstance(from_, str):
                from_ = isoparse(from_)
            from_ = dt_iso_str(from_)
            params['from'] = from_
        if to_ is not None:
            if isinstance(to_, str):
                to_ = isoparse(to_)
            to_ = dt_iso_str(to_)
            params['to'] = to_
        url = self.ep()
        return [o async for o in self.session.follow_pagination(url=url, model=ComplianceEvent, params=params)]

    async def details(self, event_id: str) -> ComplianceEvent:
        """
        Shows details for an event, by event ID.
        Specify the event ID in the eventId parameter in the URI.

        :param event_id: The unique identifier for the event.
        :type event_id: str
        """
        url = self.ep(f'{event_id}')
        data = await super().get(url=url)
        return ComplianceEvent.model_validate(data)


class AsGroupsApi(AsApiChild, base='groups'):
    """
    Groups contain a collection of members in Webex. A member represents a Webex user. A group is used to assign
    templates and settings to the set of members contained in a group. To create and manage a group, including adding
    and removing members from a group, an auth token containing the identity:groups_rw is required. Searching and
    viewing members of a group requires an auth token with a scope of identity:groups_read.
    To learn more about managing people to use as members in the /groups API please refer to the People API.
    """

    def list_gen(self, include_members: bool = None, attributes: str = None, sort_by: str = None,
             sort_order: str = None, list_filter: str = None, org_id: str = None,
             **params) -> AsyncGenerator[Group, None, None]:
        """
        List groups in your organization.

        :param include_members: Include members in list response
        :type include_members: bool
        :param attributes: comma separated list of attributes to return
        :type attributes: str
        :param sort_by: attribute to sort by
        :type sort_by: str
        :param sort_order: sort order, ascending or descending
        :type sort_order: str
        :param org_id: List groups in this organization. Only admin users of another organization (such as partners)
            may use this parameter.
        :type org_id: str
        :param list_filter: Searches the group by displayName with an operator and a value. The available operators
            are eq (equal) and sw (starts with). Only displayName can be used to filter results.
        :type list_filter: str
        :param params:
        :return: generator of :class:`Group` objects
        """
        params.update((to_camel(k), v) for i, (k, v) in enumerate(locals().items())
                      if i and k != 'params' and v is not None)
        for k, v in params.items():
            if isinstance(v, bool):
                params[k] = 'true' if v else 'false'
        if lf := params.pop('listFilter', None):
            params['filter'] = lf
        url = self.ep()
        return self.session.follow_pagination(url=url, model=Group, item_key='groups', params=params)

    async def list(self, include_members: bool = None, attributes: str = None, sort_by: str = None,
             sort_order: str = None, list_filter: str = None, org_id: str = None,
             **params) -> List[Group]:
        """
        List groups in your organization.

        :param include_members: Include members in list response
        :type include_members: bool
        :param attributes: comma separated list of attributes to return
        :type attributes: str
        :param sort_by: attribute to sort by
        :type sort_by: str
        :param sort_order: sort order, ascending or descending
        :type sort_order: str
        :param org_id: List groups in this organization. Only admin users of another organization (such as partners)
            may use this parameter.
        :type org_id: str
        :param list_filter: Searches the group by displayName with an operator and a value. The available operators
            are eq (equal) and sw (starts with). Only displayName can be used to filter results.
        :type list_filter: str
        :param params:
        :return: generator of :class:`Group` objects
        """
        params.update((to_camel(k), v) for i, (k, v) in enumerate(locals().items())
                      if i and k != 'params' and v is not None)
        for k, v in params.items():
            if isinstance(v, bool):
                params[k] = 'true' if v else 'false'
        if lf := params.pop('listFilter', None):
            params['filter'] = lf
        url = self.ep()
        return [o async for o in self.session.follow_pagination(url=url, model=Group, item_key='groups', params=params)]

    async def create(self, settings: Group) -> Group:
        """
        Create a new group using the provided settings. Only display_name is mandatory

        :param settings: settings for new group
        :type settings: Group
        :return: new group
        :rtype: :class:`Group`
        """
        url = self.ep()
        body = settings.model_dump_json(exclude={'group_id': True,
                                                 'members': {'__all__': {'member_type': True,
                                                                         'display_name': True,
                                                                         'operation': True}},
                                                 'created': True,
                                                 'last_modified': True})
        data = await self.post(url, data=body)
        return Group.model_validate(data)

    async def details(self, group_id: str, include_members: bool = None) -> Group:
        """
        Get group details

        :param group_id: group id
        :type group_id: str
        :param include_members: return members in response
        :type include_members: bool
        :return: group details
        :rtype: Group
        """
        url = self.ep(group_id)
        params = dict()
        if include_members is not None:
            params['includeMembers'] = 'true' if include_members else 'false'
        data = await self.get(url, params=params)
        return Group.model_validate(data)

    def members_gen(self, group_id: str, **params) -> AsyncGenerator[GroupMember, None, None]:
        """
        Query members of a group

        :param group_id: group id
        :type group_id: str
        :param params:
        :return: generator of :class:`GroupMember` instances
        """
        url = self.ep(f'{group_id}/Members')
        return self.session.follow_pagination(url=url, model=GroupMember, params=params, item_key='members')

    async def members(self, group_id: str, **params) -> List[GroupMember]:
        """
        Query members of a group

        :param group_id: group id
        :type group_id: str
        :param params:
        :return: generator of :class:`GroupMember` instances
        """
        url = self.ep(f'{group_id}/Members')
        return [o async for o in self.session.follow_pagination(url=url, model=GroupMember, params=params, item_key='members')]

    async def update(self, group_id: str, settings: Group = None, remove_all: bool = None) -> Group:
        """
        update group information.

        Options: change displayName, add new members, remove some or all members, replace all members

        :param group_id:
        :param settings:
        :param remove_all:
        :return:
        """
        if not any((settings, remove_all)):
            raise ValueError('settings or remove_all have to be present')
        url = self.ep(group_id)
        if settings:
            body = settings.model_dump_json(exclude={'group_id': True,
                                                     'members': {'__all__': {'member_type': True,
                                                                             'display_name': True}},
                                                     'created': True,
                                                     'last_modified': True})
        else:
            body = 'purgeAllValues:{"attributes":["members"]}'
        data = await self.patch(url, data=body)
        return Group.model_validate(data)

    async def delete_group(self, group_id: str):
        """
        Delete a group

        :param group_id: group id
        :type group_id: str
        """
        url = self.ep(group_id)
        await self.delete(url)


class AsGuestManagementApi(AsApiChild, base='guests'):
    """
    Guest Management

    Guests in Webex are users with only a temporary identity and are often used for single-transaction collaborations.
    Examples include click-to-call and click-to-chat applications where the guest interacts with the agent only for
    the duration of the call or chat session.
    Guests in Webex are created and managed via a Service App with the scope guest-issuer:write and guest-issuer:read
    and are represented by a token with a fixed and predefined set of scopes.

    Since the Service App manages its own pool of guests, you need to insert the Service App token into the developer
    portal's Try-It mode rather than your default personal token.

    The `guests/token` endpoint is used to create and retrieve guest tokens, and the `guests/count` is used to assess
    the number of current guests.

    Creating guests via the guest-issuer application type is deprecated and will
    be removed in the future.

    """

    async def create(self, subject: str, display_name: str) -> Guest:
        """
        Create a Guest

        Create a new token for a single guest user. The Service App that creates the guest must have the scope
        `guest-issuer:write`.

        Guests are implicitly created by retrieving the guest access token.

        Repeated calls to this API with the same `subject` will create additional tokens without invalidating previous
        ones. Tokens are valid until the `expiresIn`.

        Guests can be renamed by supplying the same `subject` and changing the `displayName.`

        To retrieve a new token for an existing guest, please provide the existing guest's `subject`. Tokens are valid
        until `expiresIn`.

        :param subject: The unique and external identifier of the guest.
        :type subject: str
        :param display_name: The display name shown in the Webex application.
        :type display_name: str
        :rtype: :class:`Guest`
        """
        body = dict()
        body['subject'] = subject
        body['displayName'] = display_name
        url = self.ep('token')
        data = await super().post(url, json=body)
        guest = Guest.model_validate(data)
        now = datetime.now(pytz.UTC)
        if not guest.expires_at and guest.expires_in:
            delta = timedelta(seconds=guest.expires_in)
            guest.expires_at = now + delta

        return guest

    async def guest_count(self) -> int:
        """
        Get Guest Count

        To retrieve the number of guests, the scopes `guest-issuer:read` or `guest-issuer:write` are needed.

        :rtype: int
        """
        url = self.ep('count')
        data = await super().get(url)
        return data


class AsApplyLineKeyTemplatesJobsApi(AsApiChild, base='telephony/config/jobs/devices/applyLineKeyTemplate'):
    async def apply(self, action: ApplyLineKeyTemplateAction, template_id: str = None,
              location_ids: list[str] = None, exclude_devices_with_custom_layout: bool = None,
              include_device_tags: list[str] = None, exclude_device_tags: list[str] = None,
              advisory_types: LineKeyTemplateAdvisoryTypes = None,
              org_id: str = None) -> ApplyLineKeyTemplateJobDetails:
        """
        Apply a Line key Template

        Apply a Line Key Template or reset devices to their factory Line Key settings.

        Line Keys, also known as Programmable Line Keys (PLK) are the keys found on either sides of a typical desk phone
        display.

        A Line Key Template is a definition of actions that will be performed by each of the Line Keys for a particular
        device model.

        This API allows users to apply a line key template or apply factory default Line Key settings to devices in a
        set of locations or across all locations in the organization.

        Applying a Line Key Template or resetting devices to their default Line Key configuration requires a full
        administrator auth token with a scope of `spark-admin:telephony_config_write`.

        :param action: Line key Template action to perform.
        :type action: PostApplyLineKeyTemplateRequestAction
        :param template_id: `templateId` is required for `APPLY_TEMPLATE` action.
        :type template_id: str
        :param location_ids: Used to search for devices only in the given locations.
        :type location_ids: list[str]
        :param exclude_devices_with_custom_layout: Indicates whether to exclude devices with custom layout.
        :type exclude_devices_with_custom_layout: bool
        :param include_device_tags: Include devices only with these tags.
        :type include_device_tags: list[str]
        :param exclude_device_tags: Exclude devices with these tags.
        :type exclude_device_tags: list[str]
        :param advisory_types: Refine search with advisories.
        :type advisory_types: LineKeyTemplateAdvisoryTypes
        :param org_id: Apply Line Key Template for this organization.
        :type org_id: str
        :rtype: :class:`ApplyLineKeyTemplateJobDetails`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        body['action'] = enum_str(action)
        if template_id is not None:
            body['templateId'] = template_id
        if location_ids is not None:
            body['locationIds'] = location_ids
        if exclude_devices_with_custom_layout is not None:
            body['excludeDevicesWithCustomLayout'] = exclude_devices_with_custom_layout
        if include_device_tags is not None:
            body['includeDeviceTags'] = include_device_tags
        if exclude_device_tags is not None:
            body['excludeDeviceTags'] = exclude_device_tags
        if advisory_types is not None:
            body['advisoryTypes'] = advisory_types.model_dump(mode='json', by_alias=True, exclude_none=True)
        url = self.ep()
        data = await super().post(url, params=params, json=body)
        r = ApplyLineKeyTemplateJobDetails.model_validate(data)
        return r

    async def list(self, org_id: str = None) -> list[ApplyLineKeyTemplateJobDetails]:
        """
        Get List of Apply Line Key Template jobs

        Get the list of all apply line key templates jobs in an organization.

        Line Keys, also known as Programmable Line Keys (PLK) are the keys found on either sides of a typical desk phone
        display.
        A Line Key Template is a definition of actions that will be performed by each of the Line Keys for a particular
        device model.
        This API allows users to retrieve all the apply line key templates jobs in an organization.

        Retrieving the list of apply line key templates jobs in an organization requires a full, user or read-only
        administrator auth token with a scope of `spark-admin:telephony_config_read`.

        :param org_id: Retrieve list of line key templates jobs in this organization.
        :type org_id: str
        :rtype: list[ApplyLineKeyTemplateJobDetails]
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep()
        data = await super().get(url, params=params)
        r = TypeAdapter(list[ApplyLineKeyTemplateJobDetails]).validate_python(data['items'])
        return r

    async def status(self, job_id: str, org_id: str = None) -> ApplyLineKeyTemplateJobDetails:
        """
        Get the job status of an Apply Line Key Template job

        Get the status of an apply line key template job by its job ID.

        Line Keys, also known as Programmable Line Keys (PLK) are the keys found on either sides of a typical desk phone
        display.
        A Line Key Template is a definition of actions that will be performed by each of the Line Keys for a particular
        device model.
        This API allows users to check the status of an apply line key templates job by job ID in an organization.

        Checking the the status of an apply line key templates job in an organization requires a full, user or
        read-only administrator auth token with a scope of `spark-admin:telephony_config_read`.

        :param job_id: Retrieve job status for this `jobId`.
        :type job_id: str
        :param org_id: Check a line key template job status in this organization.
        :type org_id: str
        :rtype: :class:`ApplyLineKeyTemplateJobDetails`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(job_id)
        data = await super().get(url, params=params)
        r = ApplyLineKeyTemplateJobDetails.model_validate(data)
        return r

    def errors_gen(self, job_id: str, org_id: str = None) -> AsyncGenerator[JobErrorItem, None, None]:
        """
        Get job errors for an Apply Line Key Template job

        GET job errors for an apply Line Key Template job in an organization.

        Line Keys, also known as Programmable Line Keys (PLK) are the keys found on either sides of a typical desk phone
        display.
        A Line Key Template is a definition of actions that will be performed by each of the Line Keys for a particular
        device model.
        This API allows users to retrieve all the errors of an apply line key templates job by job ID in an
        organization.

        Retrieving all the errors of an apply line key templates job in an organization requires a full, user or
        read-only administrator auth token with a scope of `spark-admin:telephony_config_read`.

        :param job_id: Retrieve job errors for this `jobId`.
        :type job_id: str
        :param org_id: Retrieve list of errors for an apply line key template job in this organization.
        :type org_id: str
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'{job_id}/errors')
        return self.session.follow_pagination(url=url, model=JobErrorItem, params=params)

    async def errors(self, job_id: str, org_id: str = None) -> List[JobErrorItem]:
        """
        Get job errors for an Apply Line Key Template job

        GET job errors for an apply Line Key Template job in an organization.

        Line Keys, also known as Programmable Line Keys (PLK) are the keys found on either sides of a typical desk phone
        display.
        A Line Key Template is a definition of actions that will be performed by each of the Line Keys for a particular
        device model.
        This API allows users to retrieve all the errors of an apply line key templates job by job ID in an
        organization.

        Retrieving all the errors of an apply line key templates job in an organization requires a full, user or
        read-only administrator auth token with a scope of `spark-admin:telephony_config_read`.

        :param job_id: Retrieve job errors for this `jobId`.
        :type job_id: str
        :param org_id: Retrieve list of errors for an apply line key template job in this organization.
        :type org_id: str
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'{job_id}/errors')
        return [o async for o in self.session.follow_pagination(url=url, model=JobErrorItem, params=params)]


class AsCallRecordingJobsApi(AsApiChild, base='telephony/config/jobs/callRecording'):
    def list_gen(self, org_id: str = None, **params) -> AsyncGenerator[CallRecordingJobStatus, None, None]:
        """
        List Call Recording Jobs

        Get the list of all call recording jobs in an organization.

        The Call Recording feature supports multiple third-party call recording providers, or vendors, to capture and
        manage call recordings. An organization is configured with an overall provider, but locations can be
        configured to use a different vendor than the overall organization default.

        Requires a full or read-only administrator auth token with a scope of `spark-admin:telephony_config_read`.

        :param org_id: List call recording jobs in this organization.
        :type org_id: str
        :return: Generator yielding :class:`CallRecordingJobStatus` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep('jobs/callRecording')
        return self.session.follow_pagination(url=url, model=CallRecordingJobStatus, item_key='items', params=params)

    async def list(self, org_id: str = None, **params) -> List[CallRecordingJobStatus]:
        """
        List Call Recording Jobs

        Get the list of all call recording jobs in an organization.

        The Call Recording feature supports multiple third-party call recording providers, or vendors, to capture and
        manage call recordings. An organization is configured with an overall provider, but locations can be
        configured to use a different vendor than the overall organization default.

        Requires a full or read-only administrator auth token with a scope of `spark-admin:telephony_config_read`.

        :param org_id: List call recording jobs in this organization.
        :type org_id: str
        :return: Generator yielding :class:`CallRecordingJobStatus` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep('jobs/callRecording')
        return [o async for o in self.session.follow_pagination(url=url, model=CallRecordingJobStatus, item_key='items', params=params)]

    async def status(self, job_id: str, org_id: str = None) -> CallRecordingJobStatus:
        """
        Get the Job Status of a Call Recording Job

        Get the details of a call recording job by its job ID.

        The Call Recording feature supports multiple third-party call recording providers, or vendors, to capture and
        manage call recordings. An organization is configured with an overall provider, but locations can be
        configured to use a different vendor than the overall organization default.

        Requires a full or read-only administrator auth token with a scope of `spark-admin:telephony_config_read`.

        :param job_id: Retrieve job status for this `jobId`.
        :type job_id: str
        :param org_id: Retrieve job status in this organization.
        :type org_id: str
        :rtype: :class:`CallRecordingJobStatus`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(job_id)
        data = await super().get(url, params=params)
        r = CallRecordingJobStatus.model_validate(data)
        return r

    def errors_gen(self, job_id: str, org_id: str = None,
               **params) -> AsyncGenerator[JobErrorItem, None, None]:
        """
        Get Job Errors for a Call Recording Job

        Get errors for a call recording job in an organization.

        The Call Recording feature supports multiple third-party call recording providers, or vendors, to capture and
        manage call recordings. An organization is configured with an overall provider, but locations can be
        configured to use a different vendor than the overall organization default.

        Requires a full or read-only administrator auth token with a scope of `spark-admin:telephony_config_read`.

        :param job_id: Retrieve job errors for this job.
        :type job_id: str
        :param org_id: Retrieve job errors for a call recording job in this organization.
        :type org_id: str
        :return: Generator yielding :class:`ItemObject` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'{job_id}/errors')
        return self.session.follow_pagination(url=url, model=JobErrorItem, item_key='items', params=params)

    async def errors(self, job_id: str, org_id: str = None,
               **params) -> List[JobErrorItem]:
        """
        Get Job Errors for a Call Recording Job

        Get errors for a call recording job in an organization.

        The Call Recording feature supports multiple third-party call recording providers, or vendors, to capture and
        manage call recordings. An organization is configured with an overall provider, but locations can be
        configured to use a different vendor than the overall organization default.

        Requires a full or read-only administrator auth token with a scope of `spark-admin:telephony_config_read`.

        :param job_id: Retrieve job errors for this job.
        :type job_id: str
        :param org_id: Retrieve job errors for a call recording job in this organization.
        :type org_id: str
        :return: Generator yielding :class:`ItemObject` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'{job_id}/errors')
        return [o async for o in self.session.follow_pagination(url=url, model=JobErrorItem, item_key='items', params=params)]


class AsDisableCallingLocationJobsApi(AsApiChild, base='telephony/config'):

    def list_gen(self, org_id: str = None,
             **params) -> AsyncGenerator[DisableCallingLocationJobStatus, None, None]:
        """
        Get a List of Disable Calling Location Jobs

        Get a List of Disable Calling Location Jobs for the organization.

        Retrieving the list of disable calling location jobs requires a full administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param org_id: List disable calling location jobs for this organization.
        :type org_id: str
        :return: Generator yielding :class:`DisableCallingLocationJobStatus` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep('jobs/locations/deleteCallingLocation')
        return self.session.follow_pagination(url=url, model=DisableCallingLocationJobStatus, item_key='items',
                                              params=params)

    async def list(self, org_id: str = None,
             **params) -> List[DisableCallingLocationJobStatus]:
        """
        Get a List of Disable Calling Location Jobs

        Get a List of Disable Calling Location Jobs for the organization.

        Retrieving the list of disable calling location jobs requires a full administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param org_id: List disable calling location jobs for this organization.
        :type org_id: str
        :return: Generator yielding :class:`DisableCallingLocationJobStatus` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep('jobs/locations/deleteCallingLocation')
        return [o async for o in self.session.follow_pagination(url=url, model=DisableCallingLocationJobStatus, item_key='items',
                                              params=params)]

    async def initiate(self, location_id: str, location_name: str = None,
                 force_delete: bool = None,
                 org_id: str = None) -> DisableCallingLocationJobStatus:
        """
        Disable a Location for Webex Calling.

        Initiating a disable calling location job requires a full administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        The API returns a jobId that can be used with other job-related APIs to track the status and progress of the
        disable operation.

        :param location_id: Unique identifier for the calling location to disable.
        :type location_id: str
        :param location_name: Name of the calling location to disable.
        :type location_name: str
        :param force_delete: Force delete is only applicable when calling features like call queues, hunt groups,
            virtual lines, etc  or a trunk that is not in use exists in the calling location and customer still wants
            to disable the calling location.
        :type force_delete: bool
        :param org_id: Organization ID for disabling the location for Webex Calling.
        :type org_id: str
        :rtype: :class:`DisableCallingLocationJobStatus`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        body['locationId'] = location_id
        if location_name is not None:
            body['locationName'] = location_name
        if force_delete is not None:
            body['forceDelete'] = force_delete
        url = self.ep('jobs/locations/deleteCallingLocation')
        data = await super().post(url, params=params, json=body)
        r = DisableCallingLocationJobStatus.model_validate(data)
        return r

    async def status(self, job_id: str,
               org_id: str = None) -> DisableCallingLocationJobStatus:
        """
        Get Disable Calling Location Job Status

        Get the status and details of a specific disable calling location job.

        Retrieving job status requires a full administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param job_id: Unique identifier for the job.
        :type job_id: str
        :param org_id: Organization ID for which to retrieve the job status.
        :type org_id: str
        :rtype: :class:`DisableCallingLocationJobStatus`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'jobs/locations/deleteCallingLocation/{job_id}')
        data = await super().get(url, params=params)
        r = DisableCallingLocationJobStatus.model_validate(data)
        return r

    async def pause(self, job_id: str, org_id: str = None):
        """
        Pause a Disable Calling Location Job

        Pause an in-progress disable calling location job. The job must be in the PROCESSING state to be paused.

        Pausing a job requires a full administrator auth token with a scope of `spark-admin:telephony_config_write`.

        :param job_id: Unique identifier for the job to pause.
        :type job_id: str
        :param org_id: Organization ID for which to pause the job.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'jobs/locations/deleteCallingLocation/{job_id}/actions/pause/invoke')
        await super().post(url, params=params)

    async def resume(self, job_id: str, org_id: str = None):
        """
        Resume a Paused Disable Calling Location Job

        Resume a previously paused disable calling location job. The job must be in the PAUSED state to be resumed.

        Resuming a job requires a full administrator auth token with a scope of `spark-admin:telephony_config_write`.

        :param job_id: Unique identifier for the job to resume.
        :type job_id: str
        :param org_id: Organization ID for which to resume the job.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'jobs/locations/deleteCallingLocation/{job_id}/actions/resume/invoke')
        await super().post(url, params=params)

    async def errors(self, job_id: str, org_id: str = None) -> List[JobErrorItem]:
        """
        Retrieve Errors for a Disable Calling Location Job

        Retrieve detailed error information for a disable calling location job. This is particularly useful for jobs
        that have failed or encountered errors during processing.

        Retrieving job errors requires a full administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        Possible error codes include:

        * `BATCH-1012002` - Unable to delete calling location from Broadworks.
        * `BATCH-1012004` - Safe delete checks failed.
        * `BATCH-1012005` - Failed to perform safe delete checks.
        * `BATCH-1012006` - Trunks in use in the location. Count: {0}
        * `BATCH-1012007` - Users associated with the location. Count: {0}
        * `BATCH-1012008` - Workspaces associated with the location. Count: {0}
        * `BATCH-1012009` - Virtual lines associated with the location. Count: {0}
        * `BATCH-1012010` - Number order is pending.
        * `BATCH-1012011` - Features associated with the location. This is a blocking error, use forceDelete to disable
          the calling location.
        * `BATCH-1012012` - Not allowed to delete the last calling location. Calling requires at least one active
          location in the organization, This is a blocking error.
        * `BATCH-1012013` - Local gateway's associated with the location. Count: {0}. This is a blocking error, use
          forceDelete to disable the calling location.
        * `BATCH-1012014` - Location not found.

        :param job_id: Unique identifier for the job to get errors for.
        :type job_id: str
        :param org_id: Organization ID for disable calling location job.
        :type org_id: str
        :rtype: list[JobErrorItem]
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'jobs/locations/deleteCallingLocation/{job_id}/errors')
        data = await super().get(url, params=params)
        r = TypeAdapter(list[JobErrorItem]).validate_python(data['items'])
        return r


class AsManageNumbersJobsApi(AsApiChild, base='telephony/config/jobs/numbers'):
    """
    API for jobs to manage numbers
    """

    def list_gen(self, org_id: str = None, **params) -> AsyncGenerator[NumberJob, None, None]:
        """
        List Manage Numbers Jobs

        Lists all Manage Numbers jobs for the given organization in order of most recent one to oldest one irrespective
        of its status.

        The public API only supports initiating jobs which move numbers between locations.

        Via Control Hub they can initiate both the move and delete, so this listing can show both.

        This API requires a full or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param org_id: Retrieve list of Manage Number jobs for this organization.
        :type org_id: str
        :return: Generator yielding :class:`NumberJob` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep('manageNumbers')
        return self.session.follow_pagination(url=url, model=NumberJob, params=params)

    async def list(self, org_id: str = None, **params) -> List[NumberJob]:
        """
        List Manage Numbers Jobs

        Lists all Manage Numbers jobs for the given organization in order of most recent one to oldest one irrespective
        of its status.

        The public API only supports initiating jobs which move numbers between locations.

        Via Control Hub they can initiate both the move and delete, so this listing can show both.

        This API requires a full or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param org_id: Retrieve list of Manage Number jobs for this organization.
        :type org_id: str
        :return: Generator yielding :class:`NumberJob` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep('manageNumbers')
        return [o async for o in self.session.follow_pagination(url=url, model=NumberJob, params=params)]

    async def initiate_job(self, operation: str, number_list: List[NumberItem], target_location_id: str = None,
                     number_usage_type: str = None) -> NumberJob:
        """
        Initiate Number Jobs

        Starts the execution of an operation on a set of numbers. Supported operations are: `MOVE`,
        `NUMBER_USAGE_CHANGE`.

        Up to 1000 numbers can be given in `MOVE` operation type and `NUMBER_USAGE_CHANGE` operation type per request.
        If another move number job request is initiated while a move job is in progress, the API call will receive a
        `409` HTTP status code.

        In order to move a number the following is required:

            * The number must be unassigned.
            * Both locations must have the same PSTN Connection Type.
            * Both locations must have the same PSTN Provider.
            * Both locations have to be in the same country.

        For example, you can move from Cisco Calling Plan to Cisco Calling Plan, but you cannot move from Cisco Calling
        Plan to a location with Cloud Connected PSTN.

        In order to change the number usage the following is required:

            * The number must be unassigned.
            * Number Usage Type can be set to `NONE` if carrier has the PSTN service `GEOGRAPHIC_NUMBERS`.
            * Number Usage Type can be set to `SERVICE` if carrier has the PSTN service `SERVICE_NUMBERS`.

        For example, you can initiate a `NUMBER_USAGE_CHANGE` job to change the number type from Standard number to
        Service number, or the other way around.

        This API requires a full administrator auth token with a scope of `spark-admin:telephony_config_write`.

        :param operation: The kind of operation to be carried out.
        :type operation: str
        :param number_list: Numbers on which to execute the operation.
        :type number_list: list[NumberItem]
        :param target_location_id: Mandatory for a `MOVE` operation. The target location within organization where the
            unassigned numbers will be moved from the source location.
        :type target_location_id: str
        :param number_usage_type: Mandatory for `NUMBER_USAGE_CHANGE` operation. Indicates the number usage type.
        :type number_usage_type: str
        :rtype: :class:`NumberJob`
        """
        body = dict()
        body['operation'] = operation
        if target_location_id is not None:
            body['targetLocationId'] = target_location_id
        if number_usage_type is not None:
            body['numberUsageType'] = number_usage_type
        body['numberList'] = TypeAdapter(list[NumberItem]).dump_python(number_list, mode='json', by_alias=True,
                                                                       exclude_none=True)
        url = self.ep('manageNumbers')
        data = await super().post(url=url, json=body)
        return NumberJob.model_validate(data)

    async def status(self, job_id: str = None) -> NumberJob:
        """
        Get Manage Numbers Job Status

        Returns the status and other details of the job.

        This API requires a full or read-only administrator auth token with a scope of
        spark-admin:telephony_config_read.

        :param job_id: Retrieve job details for this jobId.
        :type job_id: str
        """
        url = self.ep(f'manageNumbers/{job_id}')
        data = await super().get(url=url)
        return NumberJob.model_validate(data)

    async def pause(self, job_id: str = None, org_id: str = None):
        """
        Pause the Manage Numbers Job

        Pause the running Manage Numbers Job. A paused job can be resumed or abandoned.

        This API requires a full administrator auth token with a scope of spark-admin:telephony_config_write.

        :param job_id: Pause the Manage Numbers job for this jobId.
        :type job_id: str
        :param org_id: Pause the Manage Numbers job for this organization.
        :type org_id: str
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'manageNumbers/{job_id}/actions/pause/invoke')
        await super().post(url=url, params=params)
        return

    async def resume(self, job_id: str = None, org_id: str = None):
        """
        Resume the Manage Numbers Job

        Resume the paused Manage Numbers Job. A paused job can be resumed or abandoned.

        This API requires a full administrator auth token with a scope of spark-admin:telephony_config_write.

        :param job_id: Resume the Manage Numbers job for this jobId.
        :type job_id: str
        :param org_id: Resume the Manage Numbers job for this organization.
        :type org_id: str
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'manageNumbers/{job_id}/actions/resume/invoke')
        await super().post(url=url, params=params)
        return

    async def abandon(self, job_id: str = None, org_id: str = None):
        """
        Abandon the Manage Numbers Job.
        This API requires a full administrator auth token with a scope of spark-admin:telephony_config_write.

        :param job_id: Abandon the Manage Numbers job for this jobId.
        :type job_id: str
        :param org_id: Abandon the Manage Numbers job for this organization.
        :type org_id: str
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'manageNumbers/{job_id}/actions/abandon/invoke')
        await super().post(url=url, params=params)
        return

    def errors_gen(self, job_id: str = None, org_id: str = None,
               **params) -> AsyncGenerator[JobErrorItem, None, None]:
        """
        List Manage Numbers Job errors

        Lists all error details of Manage Numbers job. This will not list any errors if exitCode is COMPLETED. If the
        status is COMPLETED_WITH_ERRORS then this lists the cause of failures.

        List of possible Errors:

            * BATCH-1017021 - Failed to move because it is an inactive number.

            * BATCH-1017022 - Failed to move because the source location and target location have different CCP
              providers.

            * BATCH-1017023 - Failed because it is not an unassigned number.

            * BATCH-1017024 - Failed because it is a main number.

            * BATCH-1017027 - Manage Numbers Move Operation is not supported.

            * BATCH-1017031 - Hydra request is supported only for single number move job.

        This API requires a full or read-only administrator auth token with a scope of
        spark-admin:telephony_config_read.

        :param job_id: Retrieve the error details for this jobId.
        :type job_id: str
        :param org_id: Retrieve list of jobs for this organization.
        :type org_id: str
        """
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'manageNumbers/{job_id}/errors')
        return self.session.follow_pagination(url=url, model=JobErrorItem, params=params)

    async def errors(self, job_id: str = None, org_id: str = None,
               **params) -> List[JobErrorItem]:
        """
        List Manage Numbers Job errors

        Lists all error details of Manage Numbers job. This will not list any errors if exitCode is COMPLETED. If the
        status is COMPLETED_WITH_ERRORS then this lists the cause of failures.

        List of possible Errors:

            * BATCH-1017021 - Failed to move because it is an inactive number.

            * BATCH-1017022 - Failed to move because the source location and target location have different CCP
              providers.

            * BATCH-1017023 - Failed because it is not an unassigned number.

            * BATCH-1017024 - Failed because it is a main number.

            * BATCH-1017027 - Manage Numbers Move Operation is not supported.

            * BATCH-1017031 - Hydra request is supported only for single number move job.

        This API requires a full or read-only administrator auth token with a scope of
        spark-admin:telephony_config_read.

        :param job_id: Retrieve the error details for this jobId.
        :type job_id: str
        :param org_id: Retrieve list of jobs for this organization.
        :type org_id: str
        """
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'manageNumbers/{job_id}/errors')
        return [o async for o in self.session.follow_pagination(url=url, model=JobErrorItem, params=params)]


class AsMoveUsersJobsApi(AsApiChild, base='telephony/config/jobs/person/moveLocation'):
    async def validate_or_initiate(self, users_list: list[MoveUsersList],
                             org_id: str = None) -> StartMoveUsersJobResponse:
        """
        Validate or Initiate Move Users Job

        This API allows the user to perform any one of the following operations:

            * When the `validate` attribute is true, this validates the user move from one location to another location.

            * When the `validate` attribute is false, this performs the user move from one location to another location.

        In order to validate or move a user,

            * Maximum of one calling user can be moved at a time.

            * The target location must be a calling location.

            * Only one new extension can be moved to the target location, which is optional. An empty value will
            remove the
              already configured extension. If not provided, the existing extension will be retained to the user.

            * Only one new phone number belonging to the target location can be assigned to the user, which is optional.
              Phone number must follow E.164 format. An empty value will remove the already configured phone number.
              If not
              provided, the existing phone number of the user will be moved to the target location.

        Any errors that occur during initial API request validation will be captured directly in error response with
        appropriate HTTP status code.

        List of possible Errors:

            + 1026005 - Request is supported only for single user.

            + 1026006 - Attribute 'Location ID' is required.

            + 1026006 - Attribute 'User ID' is required.

            + 1026006 - Attribute 'Validate' is required.

            + 1026010 - User is not a valid Calling User.

            + 1026011 - Users list should not be empty.

            + 1026012 - Users should not be empty.

            + 1026013 - The source and the target location cannot be the same.

            + 1026014 - Error occurred while processing the move users request.

            + 1026015 - Error occurred while moving user number to target location.

            + 1026016 - User should have either phone number or extension.

            + 1026017 - Phone number is not in e164 format.

        When the `validate` is set to be true, the errors and impacts associated with the user move will be identified
        and returned in the response.

        List of possible Errors:

            + 4003 - `User Not Found`

            + 4007 - `User Not Found`

            + 4152 - `Location Not Found`

            + 5620 - `Location Not Found`

            + 4202 - `The extension is not available. It is already assigned to a user : {0}`

            + 8264 - `Routing profile is different with new group: {0}`

            + 19600 - `User has to be within an enterprise to be moved.`

            + 19601 - `User can only be moved to a different group within the same enterprise.`

            + 19602 - `Only regular end user can be moved. Service instance virtual user cannot be moved.`

            + 19603 - `New group already reaches maximum number of user limits.`

            + 19604 - `The {0} number of the user is the same as the calling line ID of the group.`

            + 19605 - `User is assigned services not authorized to the new group: {0}.`

            + 19606 - `User is in an active hoteling/flexible seating association.`

            + 19607 - `User is pilot user of a trunk group.`

            + 19608 - `User is using group level device profiles which is used by other users in current group.
            Following
              are the device profiles shared with other users: {0}.`

            + 19609 - `Following device profiles cannot be moved to the new group because there are already devices with
              the same name defined in the new group: {0}.`

            + 19610 - `The extension of the user is used as transfer to operator number for following Auto Attendent :
              {0}.`

            + 19611 - `Fail to move announcement file from {0} to {1}.`

            + 19612 - `Fail to move device management file from {0} to {1}.`

            + 19613 - `User is assigned service packs not authorized to the new group: {0}.`

            + 25008 - `Missing Mandatory field name: {0}`

            + 25110 - `{fieldName} cannot be less than {0} or greater than {1} characters.`

            + 25378 - `Target location is same as user's current location.`

            + 25379 - `Error Occurred while Fetching User's Current Location Id.`

            + 25381 - `Error Occurred while rolling back to Old Location Call recording Settings`

            + 25382 - `Error Occurred while Disabling Call Recording for user which is required Before User can be
            Moved`

            + 25383 - `OCI Error while moving user`

            + 25384 - `Error Occurred while checking for Possible Call Recording Impact.`

            + 25385 - `Error Occurred while getting Call Recording Settings`

            + 27559 - `The groupExternalId search criteria contains groups with different calling zone.`

            + 27960 - `Parameter isWebexCalling, newPhoneNumber, or newExtension can only be set in Webex Calling
              deployment mode.`

            + 27961 - `Parameter isWebexCalling shall be set if newPhoneNumber or newExtension is set.`

            + 27962 - `Work space cannot be moved.`

            + 27963 - `Virtual profile user cannot be moved.`

            + 27965 - `The user's phone number: {0}, is same as the current group charge number.`

            + 27966 - `The phone number, {0}, is not available in the new group.`

            + 27967 - `User is configured as the ECBN user for another user in the current group.`

            + 27968 - `User is configured as the ECBN user for the current group.`

            + 27969 - `User is associated with DECT handset(s): {0}`

            + 27970 - `User is using a customer managed device: {0}`

            + 27971 - `User is using an ATA device: {0}`

            + 27972 - `User is in an active hotdesking association.`

            + 27975 - `Need to unassign CLID number from group before moving the number to the new group. Phone number:
              {0}`

            + 27976 - `Local Gateway configuration is different with new group. Phone number: {0}`

            + 1026015 - `Error occurred while moving user number to target location`

            + 10010000 - `Total numbers exceeded maximum limit allowed`

            + 10010001 - `to-location and from-location cannot be same`

            + 10010002 - `to-location and from-location should belong to same customer`

            + 10010003 - `to-location must have a carrier`

            + 10010004 - `from-location must have a carrier`

            + 10010005 - `Different Carrier move is not supported for non-Cisco PSTN carriers.`

            + 10010006 - `Number move not supported for WEBEX_DIRECT carriers.`

            + 10010007 - `Numbers out of sync, missing on CPAPI`

            + 10010008 - `from-location not found or pstn connection missing in CPAPI`

            + 10010010 - `from-location is in transition`

            + 10010009 - `to-location not found or pstn connection missing in CPAPI`

            + 10010011 - `to-location is in transition`

            + 10010012 - `Numbers don't have a carrier Id`

            + 10010013 - `Location less numbers don't have a carrier Id`

            + 10010014 - `Different Carrier move is not supported for numbers with different country or region.`

            + 10010015 - `Numbers contain mobile and non-mobile types.`

            + 10010016 - `To/From location carriers must be same for mobile numbers.`

            + 10010017 - `Move request for location less number not supported`

            + 10010200 - `Move request for more than one block number is not supported`

            + 10010201 - `Cannot move block number as few numbers not from the block starting %s to %s`

            + 10010202 - `Cannot move block number as few numbers failed VERIFICATION from the block %s to %s`

            + 10010203 - `Cannot move block number as few numbers missing from the block %s to %s`

            + 10010204 - `Cannot move number as it is NOT a part of the block %s to %s`

            + 10010205 - `Move request for Cisco PSTN block order not supported.`

            + 10010299 - `Move order couldn't be created as no valid number to move`

            + 10030000 - `Number not found`

            + 10030001 - `Number does not belong to from-location`

            + 10030002 - `Number is not present in CPAPI`

            + 10030003 - `Number assigned to an user or device`

            + 10030004 - `Number not in Active status`

            + 10030005 - `Number is set as main number of the location`

            + 10030006 - `Number has pending order associated with it`

            + 10030007 - `Number belongs to a location but a from-location was not set`

            + 10030008 - `Numbers from multiple carrier ids are not supported`

            + 10030009 - `Location less number belongs to a location. from-location value is set to null or no
            location id`

            + 10030010 - `One or more numbers are not portable.`

            + 10030011 - `Mobile number carrier was not set`

            + 10030012 - `Number must be assigned for assigned move`

            + 10050000 - `Failed to update customer reference for phone numbers on carrier`

            + 10050001 - `Failed to update customer reference`

            + 10050002 - `Order is not of operation type MOVE`

            + 10050003 - `CPAPI delete call failed`

            + 10050004 - `Not found in database`

            + 10050005 - `Error sending notification to WxcBillingService`

            + 10050006 - `CPAPI provision number as active call failed with status %s ,reason %s`

            + 10050007 - `Failed to update E911 Service`

            + 10050008 - `Target location does not have Inbound Toll Free license`

            + 10050009 - `Source location or Target location subscription found cancelled or suspended`

            + 10050010 - `Moving On Premises or Non Integrated CCP numbers from one location to another is not
            supported.`

            + 10099999 - `{Error Code} - {Error Message}`

        List of possible Impacts:

            + 19701 - `The identity/device profile the user is using is moved to the new group: {0}.`

            + 19702 - `The user level customized incoming digit string setting is removed from the user. User is set
            to use
              the new group setting.`

            + 19703 - `The user level customized outgoing digit plan setting is removed from the user. User is set to
            use
              the new group setting.`

            + 19704 - `The user level customized enhanced outgoing calling plan setting is removed from the user.
            User is
              set to use the new group setting.`

            + 19705 - `User is removed from following group services: {0}.`

            + 19706 - `The current group schedule used in any criteria is removed from the service settings.`

            + 19707 - `User is removed from the department of the old group.`

            + 19708 - `User is changed to use the default communication barring profile of the new group.`

            + 19709 - `The communication barring profile of the user is assigned to the new group: {0}.`

            + 19710 - `The charge number for the user is removed.`

            + 19711 - `The disabled FACs for the user are removed because they are not available in the new group.`

            + 19712 - `User is removed from trunk group.`

            + 19713 - `The extension of the user is reset to empty due to either the length is out of bounds of the new
              group, or the extension is already taken in new group.`

            + 19714 - `The extension of the following alternate number is reset to empty due to either the length out of
              bounds of the new group or the extension is already taken in new group: {0}.`

            + 19715 - `The collaborate room using current group default collaborate bridge is moved to the default
              collaborate bridge of the new group.`

            + 19716 - `Previously stored voice messages of the user are no longer available. The new voice message
              will be stored on the mail server of the new group.`

            + 19717 - `The primary number, alternate numbers or fax messaging number of the user are assigned to the new
              group: {0}.`

            + 19718 - `Following domains are assigned to the new group: {0}.`

            + 19719 - `The NCOS of the user is assigned to the new group: {0}.`

            + 19720 - `The office zone of the user is assigned to the new group: {0}.`

            + 19721 - `The announcement media files are relocated to the new group directory.`

            + 19722 - `User CLID number is set to use the new group CLID number: {0}.`

            + 19723 - `New group CLID number is not configured.`

            + 19724 - `The group level announcement file(s) are removed from the user's music on hold settings.`

            + 25388 - `Target Location Does not Have Vendor Configured. Call Recording for user will be disabled`

            + 25389 - `Call Recording Vendor for user will be changed from:{0} to:{1}`

            + 25390 - `Dub point of user is moved to new external group`

            + 25391 - `Error Occurred while moving Call recording Settings to new location`

            + 25392 - `Error Occurred while checking for Possible Call Recording Impact.`

            + 25393 - `Sending Billing Notification Failed`

        This API requires a full administrator auth token with a scope
        of `spark-admin:telephony_config_write`, `spark-admin:people_write` and `identity:groups_rw`.

        :param users_list: The user to be moved from the source location.
        :type users_list: list[MoveUsersList]
        :param org_id: Create Move Users job for this organization.
        :type org_id: str
        :rtype: StartJobResponse
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        body['usersList'] = TypeAdapter(list[MoveUsersList]).dump_python(users_list, mode='json', by_alias=True,
                                                                         exclude_none=True)
        url = self.ep()
        data = await super().post(url, params=params, json=body)
        r = StartMoveUsersJobResponse.model_validate(data['response'])
        return r

    def list_gen(self, org_id: str = None, **params) -> AsyncGenerator[MoveUserJobDetails, None, None]:
        """
        List Move Users Jobs

        Lists all the Move Users jobs for the given organization in order of most recent job to oldest job irrespective
        of its status.

        This API requires a full or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param org_id: Retrieve list of Move Users jobs for this organization.
        :type org_id: str
        :return: Generator yielding :class:`JobDetailsResponse` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep('')
        return self.session.follow_pagination(url=url, model=MoveUserJobDetails, item_key='items', params=params)

    async def list(self, org_id: str = None, **params) -> List[MoveUserJobDetails]:
        """
        List Move Users Jobs

        Lists all the Move Users jobs for the given organization in order of most recent job to oldest job irrespective
        of its status.

        This API requires a full or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param org_id: Retrieve list of Move Users jobs for this organization.
        :type org_id: str
        :return: Generator yielding :class:`JobDetailsResponse` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep('')
        return [o async for o in self.session.follow_pagination(url=url, model=MoveUserJobDetails, item_key='items', params=params)]

    async def status(self, job_id: str, org_id: str = None) -> MoveUserJobDetails:
        """
        Get Move Users Job Status

        Returns the status and other details of the job.

        This API requires a full or read-only administrator auth token with a scope
        of `spark-admin:telephony_config_read`.

        :param job_id: Retrieve job details for this `jobId`.
        :type job_id: str
        :param org_id: Retrieve job details for this organization.
        :type org_id: str
        :rtype: :class:`MoveUserJobDetails`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(job_id)
        data = await super().get(url, params=params)
        r = MoveUserJobDetails.model_validate(data)
        return r

    async def abandon(self, job_id: str, org_id: str = None):
        """
        Abandon the Move Users Job.

        This API requires a full administrator auth token with a scope of `spark-admin:telephony_config_write`.

        :param job_id: Abandon the Move Users job for this `jobId`.
        :type job_id: str
        :param org_id: Abandon the Move Users job for this organization.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'{job_id}/actions/abandon/invoke')
        await super().post(url, params=params)

    async def pause(self, job_id: str, org_id: str = None):
        """
        Pause the Move Users Job

        Pause the running Move Users Job. A paused job can be resumed or abandoned.

        This API requires a full administrator auth token with a scope of `spark-admin:telephony_config_write`.

        :param job_id: Pause the Move Users job for this `jobId`.
        :type job_id: str
        :param org_id: Pause the Move Users job for this organization.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'{job_id}/actions/pause/invoke')
        await super().post(url, params=params)

    async def resume(self, job_id: str, org_id: str = None):
        """
        Resume the Move Users Job

        Resume the paused Move Users Job that is in paused status.

        This API requires a full administrator auth token with a scope of `spark-admin:telephony_config_write`.

        :param job_id: Resume the Move Users job for this `jobId`.
        :type job_id: str
        :param org_id: Resume the Move Users job for this organization.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'{job_id}/actions/resume/invoke')
        await super().post(url, params=params)

    def errors_gen(self, job_id: str, org_id: str = None,
               **params) -> AsyncGenerator[JobErrorItem, None, None]:
        """
        List Move Users Job errors

        Lists all error details of Move Users job. This will not list any errors if `exitCode` is `COMPLETED`. If the
        status is `COMPLETED_WITH_ERRORS` then this lists the cause of failures.

        This API requires a full or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param job_id: Retrieve the error details for this `jobId`.
        :type job_id: str
        :param org_id: Retrieve list of jobs for this organization.
        :type org_id: str
        :return: Generator yielding :class:`JobErrorItem` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'{job_id}/errors')
        return self.session.follow_pagination(url=url, model=JobErrorItem, item_key='items', params=params)


# noinspection DuplicatedCode

    async def errors(self, job_id: str, org_id: str = None,
               **params) -> List[JobErrorItem]:
        """
        List Move Users Job errors

        Lists all error details of Move Users job. This will not list any errors if `exitCode` is `COMPLETED`. If the
        status is `COMPLETED_WITH_ERRORS` then this lists the cause of failures.

        This API requires a full or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param job_id: Retrieve the error details for this `jobId`.
        :type job_id: str
        :param org_id: Retrieve list of jobs for this organization.
        :type org_id: str
        :return: Generator yielding :class:`JobErrorItem` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'{job_id}/errors')
        return [o async for o in self.session.follow_pagination(url=url, model=JobErrorItem, item_key='items', params=params)]


# noinspection DuplicatedCode


class AsRebuildPhonesJobsApi(AsApiChild, base='telephony/config/jobs/devices/rebuildPhones'):
    async def rebuild_phones_configuration(self, location_id: str, org_id: str = None) -> StartJobResponse:
        """
        Rebuild Phones Configuration

        Not supported for Webex for Government (FedRAMP)

        Rebuild all phone configurations for the specified location.

        Rebuild phones jobs are used when there is a change in the network configuration of phones in a location, i.e.
        a change in the network configuration of devices in a location from public to private and vice-versa.

        This API requires a full administrator auth token with a scope of `spark-admin:telephony_config_write`.

        :param location_id: Unique identifier of the location.
        :type location_id: str
        :param org_id: Rebuild phones for this organization.
        :type org_id: str
        :rtype: :class:`RebuildPhonesJob`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        body['locationId'] = location_id
        url = self.ep()
        data = await super().post(url, params=params, json=body)
        r = StartJobResponse.model_validate(data)
        return r

    async def list(self, org_id: str = None) -> list[StartJobResponse]:
        """
        List Rebuild Phones Jobs

        Not supported for Webex for Government (FedRAMP)

        Get the list of all Rebuild Phones jobs in an organization.

        Rebuild phones jobs are used when there is a change in the network configuration of phones in a location, i.e.
        a change in the network configuration of devices in a location from public to private and vice-versa.

        This API requires a full administrator auth token with a scope of `spark-admin:telephony_config_read`.

        :param org_id: List of rebuild phones jobs in this organization.
        :type org_id: str
        :rtype: list[RebuildPhonesJob]
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep()
        data = await super().get(url, params=params)
        r = TypeAdapter(list[StartJobResponse]).validate_python(data['items'])
        return r

    async def status(self, job_id: str, org_id: str = None) -> StartJobResponse:
        """
        Get the Job Status of a Rebuild Phones Job

        Not supported for Webex for Government (FedRAMP)

        Get the details of a rebuild phones job by its job ID.

        Rebuild phones jobs are used when there is a change in the network configuration of phones in a location, i.e.
        a change in the network configuration of devices in a location from public to private and vice-versa.

        This API requires a full administrator auth token with a scope of `spark-admin:telephony_config_read`.

        :param job_id: Retrieve job status for this `jobId`.
        :type job_id: str
        :param org_id: Check a rebuild phones job status in this organization.
        :type org_id: str
        :rtype: :class:`RebuildPhonesJob`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'{job_id}')
        data = await super().get(url, params=params)
        r = StartJobResponse.model_validate(data)
        return r

    def errors_gen(self, job_id: str, org_id: str = None) -> AsyncGenerator[JobErrorItem, None, None]:
        """
        Get Job Errors for a Rebuild Phones Job

        Not supported for Webex for Government (FedRAMP)

        Get errors for a rebuild phones job in an organization.

        Rebuild phones jobs are used when there is a change in the network configuration of phones in a location, i.e.
        a change in the network configuration of devices in a location from public to private and vice-versa.

        This API requires a full administrator auth token with a scope of `spark-admin:telephony_config_read`.

        :param job_id: Retrieve job errors for this `jobId`.
        :type job_id: str
        :param org_id: Retrieve list of errors for a rebuild phones job in this organization.
        :type org_id: str
        :rtype: list[ItemObject]
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'{job_id}/errors')
        return self.session.follow_pagination(url=url, model=JobErrorItem, params=params)

    async def errors(self, job_id: str, org_id: str = None) -> List[JobErrorItem]:
        """
        Get Job Errors for a Rebuild Phones Job

        Not supported for Webex for Government (FedRAMP)

        Get errors for a rebuild phones job in an organization.

        Rebuild phones jobs are used when there is a change in the network configuration of phones in a location, i.e.
        a change in the network configuration of devices in a location from public to private and vice-versa.

        This API requires a full administrator auth token with a scope of `spark-admin:telephony_config_read`.

        :param job_id: Retrieve job errors for this `jobId`.
        :type job_id: str
        :param org_id: Retrieve list of errors for a rebuild phones job in this organization.
        :type org_id: str
        :rtype: list[ItemObject]
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'{job_id}/errors')
        return [o async for o in self.session.follow_pagination(url=url, model=JobErrorItem, params=params)]


class AsSendActivationEmailApi(AsApiChild, base='identity/organizations'):
    """
    Send Activation Email

    APIs allowing an admin to send activation emails to users.
    """

    async def start(self, org_id: str) -> ActivationEmailJobDetail:
        """
        Initiate Bulk Activation Email Resend Job

        Initiate a bulk activation email resend job that sends an activation email to all eligible users in an
        organization. Only a single instance of the job can be running for an organization.

        Requires a full or user administrator auth token with a scope of `spark-admin:people_write`.

        :param org_id: Initiate job for this organization.
        :type org_id: str
        :rtype: :class:`ActivationEmailJobDetail`
        """
        url = self.ep(f'{org_id}/jobs/sendActivationEmails')
        data = await super().post(url)
        r = ActivationEmailJobDetail.model_validate(data)
        return r

    def errors_gen(self, org_id: str, job_id: str,
               **params) -> AsyncGenerator[JobErrorItem, None, None]:
        """
        Get Bulk Activation Email Resend Job Errors

        Get errors of an activation email resend job by its job ID.

        Requires a full or user administrator auth token with a scope of `spark-admin:people_write` or read-only
        administrator auth token with a scope of `spark-admin:people_read`.

        :param org_id: Check job status for this organization.
        :type org_id: str
        :param job_id: Retrieve job status for this `jobId`.
        :type job_id: str
        :return: Generator yielding :class:`JobErrorItem` instances
        """
        url = self.ep(f'{org_id}/jobs/sendActivationEmails/{job_id}/errors')
        return self.session.follow_pagination(url=url, model=JobErrorItem, item_key='items', params=params)

    async def errors(self, org_id: str, job_id: str,
               **params) -> List[JobErrorItem]:
        """
        Get Bulk Activation Email Resend Job Errors

        Get errors of an activation email resend job by its job ID.

        Requires a full or user administrator auth token with a scope of `spark-admin:people_write` or read-only
        administrator auth token with a scope of `spark-admin:people_read`.

        :param org_id: Check job status for this organization.
        :type org_id: str
        :param job_id: Retrieve job status for this `jobId`.
        :type job_id: str
        :return: Generator yielding :class:`JobErrorItem` instances
        """
        url = self.ep(f'{org_id}/jobs/sendActivationEmails/{job_id}/errors')
        return [o async for o in self.session.follow_pagination(url=url, model=JobErrorItem, item_key='items', params=params)]

    async def status(self, org_id: str,
               job_id: str) -> ActivationEmailJobDetail:
        """
        Get Bulk Activation Email Resend Job Status

        Get the details of an activation email resend job by its job ID.

        Requires a full or user administrator auth token with a scope of `spark-admin:people_write` or read-only
        administrator auth token with a scope of `spark-admin:people_read`.

        :param org_id: Check job status for this organization.
        :type org_id: str
        :param job_id: Retrieve job status for this `jobId`.
        :type job_id: str
        :rtype: :class:`ActivationEmailJobDetail`
        """
        url = self.ep(f'{org_id}/jobs/sendActivationEmails/{job_id}/status')
        data = await super().get(url)
        r = ActivationEmailJobDetail.model_validate(data)
        return r


class AsUpdateDynamicDeviceSettingsJobsApi(AsApiChild, base='telephony/config'):
    def list_gen(self, org_id: str = None,
             **params) -> AsyncGenerator[StartJobResponse, None, None]:
        """
        List dynamic device settings jobs.

        Lists all the jobs for job type `dynamicdevicesettings` for the given organization in order of most recent one
        to oldest one irrespective of its status.

        This API requires a full or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param org_id: Retrieve list of dynamic device settings jobs for this organization.
        :type org_id: str
        :return: Generator yielding :class:`StartJobResponse` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep('jobs/devices/dynamicDeviceSettings')
        return self.session.follow_pagination(url=url, model=StartJobResponse, item_key='items',
                                              params=params)

    async def list(self, org_id: str = None,
             **params) -> List[StartJobResponse]:
        """
        List dynamic device settings jobs.

        Lists all the jobs for job type `dynamicdevicesettings` for the given organization in order of most recent one
        to oldest one irrespective of its status.

        This API requires a full or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param org_id: Retrieve list of dynamic device settings jobs for this organization.
        :type org_id: str
        :return: Generator yielding :class:`StartJobResponse` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep('jobs/devices/dynamicDeviceSettings')
        return [o async for o in self.session.follow_pagination(url=url, model=StartJobResponse, item_key='items',
                                              params=params)]

    async def update_across_org_or_location(self,
                                      tags: List[DynamicSettingsUpdateJobItem],
                                      location_id: str = None,
                                      org_id: str = None) -> StartJobResponse:
        """
        Updates dynamic Device Settings Across Organization Or Location

        Creates a job to update device settings at location or organization level.

        The job runs asynchronously and persistently, applying the requested settings in bulk to all relevant devices,
        which may belong to multiple families as specified in the request. If a `locationId` is provided, only devices
        in that location are affected.

        A unique job ID is returned to track status and errors.

        Only one job can run per customer per organization at a time. Additionally, this job cannot run in parallel
        with other device jobs such as `Call device settings
        <https://developer.webex.com/docs/api/v1/device-call-settings/change-device-settings-across-organization-or
        -location-job>`_ and `Rebuild Phones

        Running a job requires a full administrator auth token with a scope of `spark-admin:telephony_config_write`.

        :param tags: Array of tag identifiers for settings to be updated. Each setting is identified by a
            `familyOrModelDisplayName` and `tag`. Supports updating multiple settings across different device families
            in a single request.
        :type tags: list[DynamicSettingsUpdateJobItem]
        :param location_id: If present, the requested settings will be updated to devices under this location.
        :type location_id: str
        :param org_id: Apply update dynamic device settings for all the devices under this organization.
        :type org_id: str
        :rtype: :class:`StartJobResponse`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        if location_id is not None:
            body['locationId'] = location_id
        body['tags'] = TypeAdapter(list[DynamicSettingsUpdateJobItem]).dump_python(tags, mode='json',
                                                                                   by_alias=True,
                                                                                   exclude_none=True)
        url = self.ep('jobs/devices/dynamicDeviceSettings')
        data = await super().post(url, params=params, json=body)
        r = StartJobResponse.model_validate(data)
        return r

    async def status(self, job_id: str) -> StartJobResponse:
        """
        Get Device Dynamic Settings Job Status

        Get dynamic device settings job status.

        Provides details of the job with `jobId` of `jobType` `dynamicdevicesettings`.

        This API requires a full or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param job_id: Retrieve job details for this `jobId`.
        :type job_id: str
        :rtype: :class:`StartJobResponse`
        """
        url = self.ep(f'jobs/devices/dynamicDeviceSettings/{job_id}')
        data = await super().get(url)
        r = StartJobResponse.model_validate(data)
        return r

    def errors_gen(self, job_id: str, org_id: str = None,
               **params) -> AsyncGenerator[JobErrorItem, None, None]:
        """
        List Dynamic Device Settings Job Errors

        List Update dynamic device settings job errors.

        Lists all error details of the job with `jobId` of `jobType` `dynamicdevicesettings`.

        This API requires a full or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param job_id: Retrieve job details for this `jobId`.
        :type job_id: str
        :param org_id: Retrieve the status of job for this organization.
        :type org_id: str
        :return: Generator yielding :class:`JobErrorItem` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'jobs/devices/dynamicDeviceSettings/{job_id}/errors')
        return self.session.follow_pagination(url=url, model=JobErrorItem, item_key='items', params=params)


# noinspection DuplicatedCode

    async def errors(self, job_id: str, org_id: str = None,
               **params) -> List[JobErrorItem]:
        """
        List Dynamic Device Settings Job Errors

        List Update dynamic device settings job errors.

        Lists all error details of the job with `jobId` of `jobType` `dynamicdevicesettings`.

        This API requires a full or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param job_id: Retrieve job details for this `jobId`.
        :type job_id: str
        :param org_id: Retrieve the status of job for this organization.
        :type org_id: str
        :return: Generator yielding :class:`JobErrorItem` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'jobs/devices/dynamicDeviceSettings/{job_id}/errors')
        return [o async for o in self.session.follow_pagination(url=url, model=JobErrorItem, item_key='items', params=params)]


# noinspection DuplicatedCode


class AsUpdateRoutingPrefixJobsApi(AsApiChild, base='telephony/config/jobs/updateRoutingPrefix'):
    def list_gen(self, org_id: str = None) -> AsyncGenerator[StartJobResponse, None, None]:
        """
        Get a List of Update Routing Prefix jobs

        Get the list of all update routing prefix jobs in an organization.

        The routing prefix is associated with a location and is used to route calls belonging to that location.
        This API allows users to retrieve all the update routing prefix jobs in an organization.

        Retrieving the list of update routing prefix jobs in an organization requires a full, user, or read-only
        administrator auth token with a scope of `spark-admin:telephony_config_read`.

        :param org_id: Retrieve list of update routing prefix jobs in this organization.
        :type org_id: str
        :rtype: list[StartJobResponse]
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep()
        return self.session.follow_pagination(url=url, model=StartJobResponse, params=params, item_key='items')

    async def list(self, org_id: str = None) -> List[StartJobResponse]:
        """
        Get a List of Update Routing Prefix jobs

        Get the list of all update routing prefix jobs in an organization.

        The routing prefix is associated with a location and is used to route calls belonging to that location.
        This API allows users to retrieve all the update routing prefix jobs in an organization.

        Retrieving the list of update routing prefix jobs in an organization requires a full, user, or read-only
        administrator auth token with a scope of `spark-admin:telephony_config_read`.

        :param org_id: Retrieve list of update routing prefix jobs in this organization.
        :type org_id: str
        :rtype: list[StartJobResponse]
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep()
        return [o async for o in self.session.follow_pagination(url=url, model=StartJobResponse, params=params, item_key='items')]

    async def status(self, job_id: str, org_id: str = None) -> StartJobResponse:
        """
        Get the job status of Update Routing Prefix job

        Get the status of the update routing prefix job by its job ID.

        The routing prefix is associated with a location and is used to route calls belonging to that location.
        This API allows users to check the status of update routing prefix job by job ID in an organization.

        Checking the status of the update routing prefix job in an organization requires a full, user, or read-only
        administrator auth token with a scope of `spark-admin:telephony_config_read`.

        :param job_id: Retrieve job status for this `jobId`.
        :type job_id: str
        :param org_id: Check update routing prefix job status in this organization.
        :type org_id: str
        :rtype: :class:`StartJobResponse`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(job_id)
        data = await super().get(url, params=params)
        r = StartJobResponse.model_validate(data)
        return r

    def errors_gen(self, job_id: str, org_id: str = None) -> AsyncGenerator[JobErrorItem, None, None]:
        """
        Get job errors for update routing prefix job

        GET job errors for the update routing prefix job in an organization.

        The routing prefix is associated with a location and is used to route calls belonging to that location.
        This API allows users to retrieve all the errors of the update routing prefix job by job ID in an organization.

        Retrieving all the errors of the update routing prefix job in an organization requires a full, user, or
        read-only administrator auth token with a scope of `spark-admin:telephony_config_read`.

        :param job_id: Retrieve job errors for this `jobId`.
        :type job_id: str
        :param org_id: Retrieve list of errors for update routing prefix job in this organization.
        :type org_id: str
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'{job_id}/errors')
        return self.session.follow_pagination(url=url, model=JobErrorItem, params=params)

    async def errors(self, job_id: str, org_id: str = None) -> List[JobErrorItem]:
        """
        Get job errors for update routing prefix job

        GET job errors for the update routing prefix job in an organization.

        The routing prefix is associated with a location and is used to route calls belonging to that location.
        This API allows users to retrieve all the errors of the update routing prefix job by job ID in an organization.

        Retrieving all the errors of the update routing prefix job in an organization requires a full, user, or
        read-only administrator auth token with a scope of `spark-admin:telephony_config_read`.

        :param job_id: Retrieve job errors for this `jobId`.
        :type job_id: str
        :param org_id: Retrieve list of errors for update routing prefix job in this organization.
        :type org_id: str
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'{job_id}/errors')
        return [o async for o in self.session.follow_pagination(url=url, model=JobErrorItem, params=params)]


class AsJobsApi(AsApiChild, base='telephony/config/jobs'):
    """
    Jobs API
    """
    #: API to send activation emails
    activation_emails: AsSendActivationEmailApi
    #: API for apply line key template jobs
    apply_line_key_templates: AsApplyLineKeyTemplatesJobsApi
    #: call recording jobs
    call_recording: AsCallRecordingJobsApi
    #: API for device settings jobs
    device_settings: AsDeviceSettingsJobsApi
    #: API for dynamic device settings jobs
    dynamic_device_settings: AsUpdateDynamicDeviceSettingsJobsApi
    #: API for manage numbers jobs
    manage_numbers: AsManageNumbersJobsApi
    # ; API for move users jobs
    move_users: AsMoveUsersJobsApi
    #: API for rebuild phone jobs
    rebuild_phones: AsRebuildPhonesJobsApi
    #: API for update routing prefix jobs
    update_routing_prefix: AsUpdateRoutingPrefixJobsApi
    #: API for disable calling location jobs
    disable_calling_location: AsDisableCallingLocationJobsApi

    def __init__(self, *, session: AsRestSession):
        super().__init__(session=session)
        self.activation_emails = AsSendActivationEmailApi(session=session)
        self.apply_line_key_templates = AsApplyLineKeyTemplatesJobsApi(session=session)
        self.call_recording = AsCallRecordingJobsApi(session=session)
        self.device_settings = AsDeviceSettingsJobsApi(session=session)
        self.dynamic_device_settings = AsUpdateDynamicDeviceSettingsJobsApi(session=session)
        self.manage_numbers = AsManageNumbersJobsApi(session=session)
        self.move_users = AsMoveUsersJobsApi(session=session)
        self.rebuild_phones = AsRebuildPhonesJobsApi(session=session)
        self.update_routing_prefix = AsUpdateRoutingPrefixJobsApi(session=session)
        self.disable_calling_location = AsDisableCallingLocationJobsApi(session=session)


class AsLicensesApi(AsApiChild, base='licenses'):
    """
    Licenses

    An allowance for features and services that are provided to users on a Webex services subscription. Cisco and its
    partners manage the amount of licenses provided to administrators and users. License can be assigned only by
    admins.

    Viewing the list of all licenses in your organization and viewing license details requires an administrator auth
    token with a `scope
    <https://developer.webex.com/docs/integrations#scopes>`_ of `spark-admin:licenses_read`.

    Updating the licenses of users requires an administrator auth token with a `scope
    <https://developer.webex.com/docs/integrations#scopes>`_ of `spark-admin:people_write`.

    To learn about how to allocate Hybrid Services licenses, see the `Managing Hybrid Services
    <https://developer.webex.com/docs/api/guides/managing-hybrid-services-licenses>`_ guide.
    """

    async def list(self, org_id: str = None) -> list[License]:
        """
        List all licenses for a given organization. If no org_id is specified, the default is the organization of
        the authenticated user.

        Response properties that are not applicable to the license will not be present in the response.

        :param org_id: List licenses for this organization.
        :type org_id: str
        :return: yields :class:`License` instances
        """
        params = org_id and {'orgId': org_id} or None
        url = self.ep()
        data = await super().get(url, params=params)
        r = TypeAdapter(list[License]).validate_python(data['items'])
        return r

    async def details(self, license_id) -> License:
        """
        Shows details for a license, by ID.

        Response properties that are not applicable to the license will not be present in the response.

        :param license_id: The unique identifier for the license.
        :type license_id: str
        :return: license details
        :rtype: License
        """
        ep = self.ep(license_id)
        return License.model_validate(await self.get(ep))

    def assigned_users_gen(self, license_id: str, **params) -> AsyncGenerator[LicenseUser, None, None]:
        """
        Get users license is assigned to, by license ID.

        Specify the license ID in the `licenseId` parameter in the URI.
        Long result sets will be
        split into `pages
        <https://developer.webex.com/docs/basics#pagination>`_.

        :param license_id: The unique identifier for the license.
        :type license_id: str
        """
        ep = self.ep(license_id)
        params['includeAssignedTo'] = 'user'
        return self.session.follow_pagination(url=ep, model=LicenseUser, item_key='users',
                                              params=params)

    async def assigned_users(self, license_id: str, **params) -> List[LicenseUser]:
        """
        Get users license is assigned to, by license ID.

        Specify the license ID in the `licenseId` parameter in the URI.
        Long result sets will be
        split into `pages
        <https://developer.webex.com/docs/basics#pagination>`_.

        :param license_id: The unique identifier for the license.
        :type license_id: str
        """
        ep = self.ep(license_id)
        params['includeAssignedTo'] = 'user'
        return [o async for o in self.session.follow_pagination(url=ep, model=LicenseUser, item_key='users',
                                              params=params)]

    async def assign_licenses_to_users(self, email: str = None, person_id: str = None,
                                 licenses: List[LicenseRequest] = None, site_urls: List[SiteUrlsRequest] = None,
                                 org_id: str = None) -> UserLicensesResponse:
        """
        Assign Licenses to Users

        Assign licenses and attendee `siteUrls` to existing users. Only an admin can assign licenses. Only existing
        users can be assigned a license. Assign meeting licenses to users outside your organization (Status will be
        pending until the user accepts the invite)

        At least one of the following body parameters is required to assign license to the user: `email`, `personId`.
        For Calling license assignment, properties `phoneNumber` or `extension` are required. If `phoneNumber` is not
        provided then `locationId` is mandatory.

        When assigning licenses and attendee siteUrls to a user who does not belong to the organization, the licenses
        and siteUrls remain in pending state until the user accepts them. The `pendingLicenses` and `pendingSiteUrls`
        are part of the response.

        :param email: Email address of the user.
        :type email: str
        :param person_id: A unique identifier for the user.
        :type person_id: str
        :param licenses: An array of licenses to be assigned to the user.
        :type licenses: list[LicenseRequest]
        :param site_urls: An array of siteUrls to be assigned to the user.
        :type site_urls: list[SiteUrlsRequest]
        :param org_id: The ID of the organization to which the licenses and siteUrls belong. If not specified, the
            organization ID from the OAuth token is used.
        :type org_id: str
        :rtype: :class:`UserLicensesResponse`

        Example:

            .. code-block:: python

                self.api.licenses.assign_licenses_to_users(
                    person_id=new_user.person_id,
                    licenses=[LicenseRequest(id=calling_license_id,
                                             properties=LicenseProperties(location_id=target_location.location_id,
                                                                          extension=extension))])

        """
        body = dict()
        if email is not None:
            body['email'] = email
        if person_id is not None:
            body['personId'] = person_id
        if org_id is not None:
            body['orgId'] = org_id
        if licenses is not None:
            body['licenses'] = TypeAdapter(list[LicenseRequest]).dump_python(licenses, mode='json', by_alias=True,
                                                                             exclude_none=True)
        if site_urls is not None:
            body['siteUrls'] = TypeAdapter(list[SiteUrlsRequest]).dump_python(site_urls, mode='json', by_alias=True,
                                                                              exclude_none=True)
        url = self.ep('users')
        data = await super().patch(url, json=body)
        r = UserLicensesResponse.model_validate(data)
        return r


class AsLocationsApi(AsApiChild, base='locations'):
    """
    Locations

    Locations allow you to organize users and workspaces based on a physical location. You can configure both calling
    and workspace management functions into the same location. To enable a location for Webex Calling, use the
    `Enable a Location for Webex Calling
    <https://developer.webex.com/docs/api/v1/location-call-settings/enable-a-location-for-webex-calling>`_ API.
    You can also create and inspect locations in Webex Control Hub. See `Locations on Control Hub
    <https://help.webex.com/en-us/article/ajh6iy/Locations-in-Control-Hub>`_ for more information.
    """

    def list_gen(self, name: str = None, location_id: str = None, org_id: str = None,
             **params) -> AsyncGenerator[Location, None, None]:
        """
        List Locations

        List locations for an organization.

            * Use query parameters to filter the result set by location name, ID, or organization.
            * Long result sets will be split into `pages <https://developer.webex.com/docs/basics#pagination>`_.
            * Searching and viewing locations in your organization requires an administrator or location administrator auth
              token with any of the following scopes: `spark-admin:locations_read`, `spark-admin:people_read` or
              `spark-admin:device_read`.

        :param name: List locations whose name contains this string (case-insensitive).
        :type name: str
        :param location_id: List locations by ID.
        :type location_id: str
        :param org_id: List locations in this organization. Only admin users of another organization
            (such as partners) may use this parameter.
        :type org_id: str
        :return: generator of :class:`Location` instances
        """
        params.update((to_camel(k), v)
                      for i, (k, v) in enumerate(locals().items())
                      if i and k != 'params' and v is not None)
        if location_id is not None:
            params.pop('locationId')
            params['id'] = location_id
        ep = self.ep()
        # noinspection PyTypeChecker
        return self.session.follow_pagination(url=ep, model=Location, params=params)

    async def list(self, name: str = None, location_id: str = None, org_id: str = None,
             **params) -> List[Location]:
        """
        List Locations

        List locations for an organization.

            * Use query parameters to filter the result set by location name, ID, or organization.
            * Long result sets will be split into `pages <https://developer.webex.com/docs/basics#pagination>`_.
            * Searching and viewing locations in your organization requires an administrator or location administrator auth
              token with any of the following scopes: `spark-admin:locations_read`, `spark-admin:people_read` or
              `spark-admin:device_read`.

        :param name: List locations whose name contains this string (case-insensitive).
        :type name: str
        :param location_id: List locations by ID.
        :type location_id: str
        :param org_id: List locations in this organization. Only admin users of another organization
            (such as partners) may use this parameter.
        :type org_id: str
        :return: generator of :class:`Location` instances
        """
        params.update((to_camel(k), v)
                      for i, (k, v) in enumerate(locals().items())
                      if i and k != 'params' and v is not None)
        if location_id is not None:
            params.pop('locationId')
            params['id'] = location_id
        ep = self.ep()
        # noinspection PyTypeChecker
        return [o async for o in self.session.follow_pagination(url=ep, model=Location, params=params)]

    async def by_name(self, name: str, org_id: str = None) -> Optional[Location]:
        """
        Get a location by name

        :param name: name of the location to search
        :type name: str
        :param org_id: search in list of locations  in this organization. Only admin users of another organization
            (such as partners) may use this parameter.
        :type org_id: str
        :return: locations
        :rtype: Location
        """
        return next((location for location in await self.list(name=name, org_id=org_id)
                     if location.name == name), None)

    async def details(self, location_id: str, org_id: str = None) -> Location:
        """
        Get Location Details

        Shows details for a location, by ID.

        Specify the location ID in the `locationId` parameter in the URI.

        Use query parameter `orgId` to filter the result set by organization(optional).

        Searching and viewing location in your organization requires an administrator or location administrator auth
        token with any of the following scopes:

            * `spark-admin:locations_read`
            * `spark-admin:people_read`
            * `spark-admin:device_read`

        :param location_id: A unique identifier for the location.
        :type location_id: str
        :param org_id: Get location common attributes for this organization.
        :type org_id: str

        :return: location details
        :rtype: :class:`Location`
        """
        params = org_id and {'orgId': org_id} or None
        ep = self.ep(location_id)
        return Location.model_validate(await self.get(ep, params=params))

    async def create(self, name: str, time_zone: str, preferred_language: str, announcement_language: str, address1: str,
               city: str, state: str, postal_code: str, country: str, address2: str = None, latitude: str = None,
               longitude: str = None, notes: str = None, org_id: str = None) -> str:
        """
        Create a Location

        Create a new Location for a given organization. Only an admin in the organization can create a new Location.

        Creating a location in your organization requires a full administrator auth token with a scope of
        `spark-admin:locations_write`.

        Partners may specify `orgId` query parameter to create location in managed organization.

        The following body parameters are required to create a new location:

            * `name`
            * `timeZone`
            * `preferredLanguage`
            * `address`
            * `announcementLanguage`.

        `latitude`, `longitude` and `notes` are optional parameters to create a new location.

        Important: While the `name` field supports up to 256 characters, locations that will be enabled for Webex
        Calling must have names with a maximum of 80 characters. If you plan to enable calling for this location,
        ensure the name does not exceed 80 characters to maintain compatibility with Control Hub and calling features.

        :param name: The name of the location. Supports up to 256 characters, but locations enabled for Webex Calling
            are limited to 80 characters maximum.
        :type name: str
        :param time_zone: Time zone associated with this location
        :type time_zone: str
        :param preferred_language: Default email language.
        :type preferred_language: str
        :param announcement_language: Location's phone announcement language.
        :type announcement_language: str
        :param address1: Address 1
        :type address1: str
        :param address2: Address 2
        :type address2: str
        :param city: City
        :type city: str
        :param state: State Code
        :type state: str
        :param postal_code: Postal Code
        :type postal_code: str
        :param country: ISO-3166 2-Letter Country Code.
        :type country: str
        :param org_id: Create a location common attribute for this organization.
        :param latitude: Latitude
        :type latitude: str
        :param longitude: Longitude
        :type longitude: str
        :param notes: Notes
        :type notes: str
        :type org_id: str
        :return: ID of new location
        :rtype: str
        """
        body = {}
        address = {}
        for p, v in list(locals().items()):
            if p in {'address', 'body', 'self'} or v is None:
                continue
            p = to_camel(p)
            if p in {'address1', 'address2', 'city', 'state', 'postalCode', 'country'}:
                # these are actually address parameters
                address[p] = v
            else:
                body[p] = v
        body['address'] = address
        params = org_id and {'orgId': org_id} or None
        url = self.ep()
        data = await self.post(url=url, json=body, params=params)
        # TODO: doc issue, looks like this endpoint returns location details, but the doc only mentions "id"
        return data['id']

    async def delete(self, location_id: str, org_id: str = None):
        """
        Delete Location

        Delete a location, by ID.

        * Specify the location ID in the `locationId` parameter in the URI.

        * Deleting a location in your organization requires a full administrator auth token with a scope of
        `spark-admin:locations_write`.

        * NOTE: Disabling Webex Calling for a Webex Calling enabled location is required prior to deleting a location.

        :param location_id: A unique identifier for the location.
        :type location_id: str
        :param org_id: Specify the organization for the location to be deleted.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'{location_id}')
        await super().delete(url, params=params)

    async def update(self, location_id: str, settings: Location, org_id: str = None):
        """
        Update details for a location, by ID.

        Specify the location ID in the locationId parameter in the URI. Only an admin can update a location details.

        Updating a location in your organization requires an administrator auth token with
        the spark-admin:locations_write.

        Partners may specify `orgId` query parameter to update location in managed organization.

        Important: While the `name` field supports up to 256 characters, locations that are enabled for Webex
        Calling must have names with a maximum of 80 characters. If the location is enabled for calling, ensure the
        name does not exceed 80 characters to maintain compatibility with Control Hub and calling features.

        :param location_id: Update location common attributes for this location.
        :type location_id: str
        :param settings: new settings for the org:
        :type settings: :class:`Location`
        :param org_id: Update location common attributes for this organization
        :type org_id: str
        """
        settings_copy = settings.model_copy(deep=True)
        if settings_copy.address and not settings_copy.address.address2:
            settings_copy.address.address2 = None

        params = org_id and {'orgId': org_id} or None
        url = self.ep(location_id)

        data = settings.update()
        await self.put(url=url, json=data, params=params)

    async def list_floors(self, location_id: str) -> List[Floor]:
        """
        List Location Floors

        Requires an administrator auth token with the `spark-admin:locations_read` scope.

        :param location_id: A unique identifier for the location.
        :type location_id: str
        :rtype: list[Floor]
        """
        url = self.ep(f'{location_id}/floors')
        data = await super().get(url)
        r = TypeAdapter(list[Floor]).validate_python(data['items'])
        return r

    async def create_floor(self, location_id: str, floor_number: int, display_name: str = None) -> Floor:
        """
        Create a Location Floor

        Create a new floor in the given location. The `displayName` parameter is optional, and omitting it will result
        in the creation of a floor without that value set.
        Requires an administrator auth token with the `spark-admin:locations_write` scope.

        :param location_id: A unique identifier for the location.
        :type location_id: str
        :param floor_number: The floor number.
        :type floor_number: int
        :param display_name: The floor display name.
        :type display_name: str
        :rtype: :class:`Floor`
        """
        body = dict()
        body['floorNumber'] = floor_number
        if display_name is not None:
            body['displayName'] = display_name
        url = self.ep(f'{location_id}/floors')
        data = await super().post(url, json=body)
        r = Floor.model_validate(data)
        return r

    async def floor_details(self, location_id: str, floor_id: str) -> Floor:
        """
        Get Location Floor Details

        Shows details for a floor, by ID. Specify the floor ID in the `floorId` parameter in the URI.
        Requires an administrator auth token with the `spark-admin:locations_read` scope.

        :param location_id: A unique identifier for the location.
        :type location_id: str
        :param floor_id: A unique identifier for the floor.
        :type floor_id: str
        :rtype: :class:`Floor`
        """
        url = self.ep(f'{location_id}/floors/{floor_id}')
        data = await super().get(url)
        r = Floor.model_validate(data)
        return r

    async def update_floor(self, floor: Floor) -> Floor:
        """
        Update a Location Floor

        Updates details for a floor, by ID. Specify the floor ID in the `floorId` parameter in the URI. Include all
        details for the floor returned by a previous call to `Get Location Floor Details
        <https://developer.webex.com/docs/api/v1/locations/get-location-floor-details>`_. Omitting the optional
        `displayName` field will result in that field no longer being defined for the floor.
        Requires an administrator auth token with the `spark-admin:locations_write` scope.

        :param floor: new floor settings
        :type floor: Floor
        :rtype: :class:`Floor`
        """
        url = self.ep(f'{floor.location_id}/floors/{floor.id}')
        data = await super().put(url, json=floor.update())
        r = Floor.model_validate(data)
        return r

    async def delete_floor(self, location_id: str, floor_id: str):
        """
        Delete a Location Floor

        Deletes a floor, by ID.
        Requires an administrator auth token with the `spark-admin:locations_write` scope.

        :param location_id: A unique identifier for the location.
        :type location_id: str
        :param floor_id: A unique identifier for the floor.
        :type floor_id: str
        :rtype: None
        """
        url = self.ep(f'{location_id}/floors/{floor_id}')
        await super().delete(url)


class AsGoOverrideApi(AsApiChild, base='telephony/config/people/me/settings/webexGoOverride'):

    async def get(self) -> bool:
        """
        Get My WebexGoOverride Settings

        Retrieve "Mobile User Aware" override setting for Do Not Disturb feature.

        When enabled, a mobile device will still ring even if Do Not Disturb, Quiet Hours, or Presenting Status are
        enabled.

        When disabled, a mobile device will return busy for all incoming calls if Do Not Disturb, Quiet Hours, or
        Presenting Status are enabled.

        It requires a user auth token with `spark:telephony_config_read` scope.

        :rtype: bool
        """
        url = self.ep()
        data = await super().get(url)
        r = data['enabled']
        return r

    async def update(self, enabled: bool = None):
        """
        Modify My WebexGoOverride Settings

        Update "Mobile User Aware" override setting for Do Not Disturb feature.

        When enabled, a mobile device will still ring even if Do Not Disturb, Quiet Hours, or Presenting Status are
        enabled.

        When disabled, a mobile device will return busy for all incoming calls if Do Not Disturb, Quiet Hours, or
        Presenting Status are enabled.

        It requires a user auth token with the `spark:telephony_config_write` scope.

        :param enabled: True if the "Mobile User Aware" override setting for Do Not Disturb feature is enabled.
        :type enabled: bool
        :rtype: None
        """
        body = dict()
        if enabled is not None:
            body['enabled'] = enabled
        url = self.ep()
        await super().put(url, json=body)


class AsMeBargeApi(AsApiChild, base='telephony/config/people/me'):
    async def get(self) -> BargeSettings:
        """
        Retrieve Barge-In Settings

        Retrieve Barge-In settings of the user.

        The Barge-In feature enables you to use a Feature Access Code (FAC) to answer a call that was directed to
        another subscriber, or barge-in on the call if it was already answered. Barge-In can be used across locations.

        This API requires a user auth token with a scope of `spark:telephony_config_read`.

        :rtype: :class:`BargeInInfo`
        """
        url = self.ep('settings/bargeIn')
        data = await super().get(url)
        r = BargeSettings.model_validate(data)
        return r

    async def configure(self, settings: BargeSettings):
        """
        Configure Barge-In Settings

        Configure person's Barge-In settings.

        The Barge-In feature enables you to use a Feature Access Code (FAC) to answer a call that was directed to
        another subscriber, or barge-in on the call if it was already answered. Barge-In can be used across locations.

        This API requires a user auth token with a scope of `spark:telephony_config_write`.

        :param settings: Barge-In settings
        :type settings: :class:`BargeSettings`
        """
        body = settings.model_dump(mode='json', by_alias=True, exclude_unset=True)
        url = self.ep('settings/bargeIn')
        await super().put(url, json=body)


class AsMeCallBlockApi(AsApiChild, base='telephony/config/people/me'):
    async def settings(self) -> list[CallBlockNumber]:
        """
        Get My Call Block Settings

        Get details of call block settings associated with the authenticated user.

        Call block settings allow you to get the User Call Block Number List.

        This API requires a user auth token with a scope of `spark:telephony_config_read`.

        :rtype: list[CallBlockNumber]
        """
        url = self.ep('settings/callBlock')
        data = await super().get(url)
        r = TypeAdapter(list[CallBlockNumber]).validate_python(data['numbers'])
        return r

    async def add_number(self, phone_number: str) -> str:
        """
        Add a phone number to user's Call Block List

        Add a phone number to the call block list for the authenticated user.

        Call block settings allow you to get the User Call Block Number List.

        This API requires a user auth token with a scope of `spark:telephony_config_write`.

        :param phone_number: Phone number which is blocked by user.
        :type phone_number: str
        :rtype: str
        """
        body = dict()
        body['phoneNumber'] = phone_number
        url = self.ep('settings/callBlock/numbers')
        data = await super().post(url, json=body)
        r = data['id']
        return r

    async def delete_number(self, phone_number_id: str):
        """
        Delete User Call Block Number

        Delete call block number settings associated with the authenticated user.

        Call block settings allow you to delete a number from the User Call Block Number List.

        This API requires a user auth token with a scope of `spark-admin:people_write`.

        :param phone_number_id: Unique identifier of the phone number.
        :type phone_number_id: str
        :rtype: None
        """
        url = self.ep(f'settings/callBlock/numbers/{phone_number_id}')
        await super().delete(url)

    async def state_for_number(self, phone_number_id: str) -> bool:
        """
        Get My Call Block State For Specific Number

        Get call block state details for a specific number associated with the authenticated user.

        Call block settings allow you to get the User Call Block Number List.

        This API requires a user auth token with a scope of `spark:telephony_config_read`.

        :param phone_number_id: Unique identifier of the phone number.
        :type phone_number_id: str
        :rtype: bool
        """
        url = self.ep(f'settings/callBlock/numbers/{urllib.parse.quote(phone_number_id)}')
        data = await super().get(url)
        r = data['blockCallsEnabled']
        return r


class AsMeCallCenterApi(AsApiChild, base='telephony/config/people/me'):
    async def settings(self) -> MeCallCenterSettings:
        """
        Get My Call Center Settings

        Retrieves the call center settings and list of all call centers the logged in user belongs to.

        Calls from the Call Centers are routed to agents based on configuration. An agent can be assigned to one or
        more call queues and can be managed by supervisors.
        The user must have the call center service assigned.

        This API requires a user auth token with a scope of `spark:telephony_config_read`.

        :rtype: :class:`MeCallCenterSettings`
        """
        url = self.ep('settings/queues')
        data = await super().get(url)
        r = MeCallCenterSettings.model_validate(data)
        return r

    async def modify(self, settings: MeCallCenterSettings):
        """
        Modify My Call Center Settings

        Modify the call center settings and availability for an agent in one or more call centers to which the logged
        in user belongs.

        Calls from the Call Centers are routed to agents based on configuration. An agent can be assigned to one or
        more call queues and can be managed by supervisors.
        Contains a list specifying the desired availability status of one or more call centers.

        This API requires a user auth token with a scope of `spark:telephony_config_write`.

        :param settings: settings
        :type settings: :class:`MeCallCenterSettings`
        """
        body = settings.update()
        url = self.ep('settings/queues')
        await super().put(url, json=body)


class AsMeCallParkApi(AsApiChild, base='telephony/config/people/me'):
    async def settings(self) -> MeGroupSettings:
        """
        Get My Call Park Settings

        Get details of call park settings associated with the authenticated user.

        Call Park allows call recipients to place a call on hold so that it can be retrieved from another device.

        This API requires a user auth token with a scope of `spark:telephony_config_read`.

        :rtype: :class:`MeGroupSettings`
        """
        url = self.ep('settings/callPark')
        data = await super().get(url)
        r = MeGroupSettings.model_validate(data)
        return r


class AsMeCallPickupApi(AsApiChild, base='telephony/config/people/me'):
    async def settings(self) -> MeGroupSettings:
        """
        Get My Call Pickup Group Settings

        Get Call Pickup Group Settings for the authenticated user.

        Call pickup group enables a user to answer any ringing line within their pickup group. A call pickup group is
        an administrator-defined set of users within a location, to which the call pickup feature applies.

        This API requires a user auth token with a scope of `spark:telephony_config_read`.

        :rtype: :class:`MeGroupSettings`
        """
        url = self.ep('settings/callPickupGroup')
        data = await super().get(url)
        r = MeGroupSettings.model_validate(data)
        return r


class AsMeCallPoliciesApi(AsApiChild, base='telephony/config/people/me'):

    async def settings(self) -> PrivacyOnRedirectedCalls:
        """
        Get Call Policies Settings for User

        Get call policies settings for the authenticated user.

        Call Policies in Webex allow you to manage how your call information is displayed and handled. You can view
        privacy settings for your connected line ID on redirected calls and review other call-related preferences.

        This API requires a user auth token with a scope of `spark:telephony_config_read`.

        :rtype: UserCallPoliciesGetConnectedLineIdPrivacyOnRedirectedCalls
        """
        url = self.ep('settings/callPolicies')
        data = await super().get(url)
        r = TypeAdapter(PrivacyOnRedirectedCalls).validate_python(data['connectedLineIdPrivacyOnRedirectedCalls'])
        return r

    async def update(self, connected_line_id_privacy_on_redirected_calls: PrivacyOnRedirectedCalls = None):
        """
        Modify Call Policies Settings for User

        Update call policies settings for the authenticated user.

        Call Policies in Webex allow you to manage how your call information is displayed and handled. You can
        configure privacy settings for your connected line ID on redirected calls and control other call-related
        preferences.

        This API requires a user auth token with a scope of `spark:telephony_config_write`.

        :param connected_line_id_privacy_on_redirected_calls:

            * `NO_PRIVACY` - Caller sees the final destination's identity when call is redirected.
            * `PRIVACY_FOR_EXTERNAL_CALLS` - Internal callers see the final destination's identity; external callers
              see the original recipient's identity.
            * `PRIVACY_FOR_ALL_CALLS` - All callers see the original recipient's identity when call is redirected
        :type connected_line_id_privacy_on_redirected_calls: UserCallPoliciesGetConnectedLineIdPrivacyOnRedirectedCalls
        :rtype: None
        """
        body = dict()
        if connected_line_id_privacy_on_redirected_calls is not None:
            body['connectedLineIdPrivacyOnRedirectedCalls'] = enum_str(connected_line_id_privacy_on_redirected_calls)
        url = self.ep('settings/callPolicies')
        await super().put(url, json=body)


class AsMeCallerIdApi(AsApiChild, base='telephony/config/people/me'):

    async def settings(self) -> MeCallerIdSettings:
        """
        Get My Caller ID Settings

        Get Caller ID Settings for the authenticated user.

        Calling Line ID Delivery Blocking in Webex prevents your name and phone number from being shown to people you
        call.
        Connected Line Identification Restriction allows you to block your name and phone number from being shown when
        receiving a call.

        This API requires a user auth token with a scope of `spark:telephony_config_read`.

        :rtype: :class:`MeCallerIdSettings`
        """
        url = self.ep('settings/callerId')
        data = await super().get(url)
        r = MeCallerIdSettings.model_validate(data)
        return r

    async def update(self, settings: MeCallerIdSettings):
        """
        Modify My Caller ID Settings

        Update Caller ID Settings for the authenticated user.

        Calling Line ID Delivery Blocking in Webex prevents your name and phone number from being shown to people you
        call.
        Connected Line Identification Restriction allows you to block your name and phone number from being shown when
        receiving a call.

        This API requires a user auth token with a scope of `spark:telephony_config_write`.

        :param settings: new settings
        :type settings: :class:`MeCallerIdSettings`
        """
        body = settings.model_dump(mode='json', by_alias=True, exclude_unset=True)
        url = self.ep('settings/callerId')
        await super().put(url, json=body)

    async def available_caller_id_list(self) -> list[MeSelectedCallerId]:
        """
        Get My Available Caller ID List

        Get details of available caller IDs of the authenticated user.

        Caller ID settings control how a person's information is displayed when making outgoing calls.
        The available caller ID list shows the caller IDs that the user can choose from.

        This API requires a user auth token with a scope of `spark:telephony_config_read`.

        :rtype: list[MeSelectedCallerId]
        """
        url = self.ep('settings/availableCallerIds')
        data = await super().get(url)
        r = TypeAdapter(list[MeSelectedCallerId]).validate_python(data['availableCallerIds'])
        return r

    async def get_selected_caller_id_settings(self) -> MeSelectedCallerId:
        """
        Read My Selected Caller ID Settings

        Read selected caller ID settings associated with the authenticated user.

        Caller ID settings control how a person's information is displayed when making outgoing calls.
        Selected Caller ID settings allow users to choose which configuration among available caller IDs is selected
        currently.

        This API requires a user auth token with a scope of `spark:telephony_config_read`.

        :rtype: MeSelectedCallerId
        """
        url = self.ep('settings/selectedCallerId')
        data = await super().get(url)
        r = MeSelectedCallerId.model_validate(data['selected'])
        return r

    async def modify_selected_caller_id_settings(self, settings: MeSelectedCallerId):
        """
        Configure My Selected Caller ID Settings

        Update selected caller ID settings associated with the authenticated user.

        Caller ID settings control how a person's information is displayed when making outgoing calls.
        Selected Caller ID settings allow users to choose which configuration among available caller IDs is selected
        currently.

        This API requires a user auth token with a scope of `spark:telephony_config_write`.

        :param settings: new settings
        :type settings: :class:`MeSelectedCallerId`
        """
        body = settings.update()
        url = self.ep('settings/selectedCallerId')
        await super().put(url, json=body)


class AsMeDNDApi(AsApiChild, base='telephony/config/people/me'):
    async def settings(self) -> DND:
        """
        Get Do Not Disturb Settings for User

        Get Do Not Disturb settings for the authenticated user.

        Do Not Disturb (DND) enables users to block or silence incoming calls on their phone. When activated, the phone
        either stops ringing or rejects calls depending on the configured option, but users can still see call
        information and answer calls if desired.

        This API requires a user auth token with a scope of `spark:telephony_config_read`.

        :rtype: :class:`DND`
        """
        url = self.ep('settings/doNotDisturb')
        data = await super().get(url)
        r = DND.model_validate(data)
        return r

    async def configure(self, dnd_settings: DND):
        """
        Update Do Not Disturb Settings for User

        Update Do Not Disturb settings for the authenticated user.

        Do Not Disturb (DND) enables users to block or silence incoming calls on their phone. When activated, the phone
        either stops ringing or rejects calls depending on the configured option, but users can still see call
        information and answer calls if desired.

        This API requires a user auth token with a scope of `spark:telephony_config_write`.

        :param dnd_settings: new setting to be applied
        :type dnd_settings: DND
        :rtype: None
        """
        body = dnd_settings.model_dump(mode='json', by_alias=True, exclude_unset=True)
        url = self.ep('settings/doNotDisturb')
        await super().put(url, json=body)


class AsMeEndpointsApi(AsApiChild, base='telephony/config/people/me'):
    async def list(self) -> list[MeEndpoint]:
        """
        Read the List of My Endpoints

        Retrieve the list of endpoints associated with the authenticated user.

        Endpoints are devices, applications, or hotdesking guest profiles. Endpoints can be owned by an authenticated
        user or have the user as a secondary line.

        This API requires a user auth token with a scope of `spark:telephony_config_read`.

        :rtype: list[MeEndpoint]
        """
        url = self.ep('endpoints')
        data = await super().get(url)
        r = TypeAdapter(list[MeEndpoint]).validate_python(data['endpoints'])
        return r

    async def details(self, endpoint_id: str) -> MeEndpoint:
        """
        Get My Endpoints Details

        Get details of an endpoint associated with the authenticated user.

        Endpoints are devices, applications, or hotdesking guest profiles. Endpoints can be owned by an authenticated
        user or have the user as a secondary line.

        This API requires a user auth token with a scope of `spark:telephony_config_read`.

        :param endpoint_id: Unique identifier of the endpoint.
        :type endpoint_id: str
        :rtype: :class:`MeEndpoint`
        """
        url = self.ep(f'endpoints/{endpoint_id}')
        data = await super().get(url)
        r = MeEndpoint.model_validate(data)
        return r

    async def update(self, endpoint_id: str, mobility_alerting_enabled: bool):
        """
        Modify My Endpoints Details

        Update alerting settings of the mobility endpoint associated with the authenticated user.

        Endpoints are devices, applications, or hotdesking guest profiles. Endpoints can be owned by an authenticated
        user or have the user as a secondary line.

        This API requires a user auth token with a scope of `spark:telephony_config_write`.

        :param endpoint_id: Unique identifier of the endpoint.
        :type endpoint_id: str
        :param mobility_alerting_enabled: If `true`, alerting is enabled for the endpoint.
        :type mobility_alerting_enabled: bool
        :rtype: None
        """
        body = {'mobilitySettings': {'alertingEnabled': mobility_alerting_enabled}}
        url = self.ep(f'endpoints/{endpoint_id}')
        await super().put(url, json=body)

    async def available_preferred_answer_endpoints(self, org_id: str = None) -> List[MeEndpoint]:
        """
        Get List Available Preferred Answer Endpoints

        Get the person's preferred answer endpoint and the list of endpoints available for selection. The list of
        endpoints is empty if the person has no endpoints assigned which support the preferred answer endpoint
        functionality.

        A Webex Calling user may be associated with multiple endpoints such as Webex App (desktop or mobile), Cisco
        desk IP phone, Webex Calling-supported analog devices or third-party endpoints. Preferred answering endpoints
        allow users to specify which of these devices should be prioritized for answering calls, particularly when a
        person's extension (or a virtual line assigned to them) rings on multiple devices. This helps ensure that
        calls are answered on the most convenient or appropriate device for the person.

        This API requires a user auth token with a scope of `spark:telephony_config_read`.

        :param org_id: ID of the organization in which the person resides. Only admin users of another organization
            (such as partners) may use this parameter as the default is the same organization as the token used to
            access API.
        :type org_id: str
        :rtype: list[Endpoints]
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep('settings/availablePreferredAnswerEndpoints')
        data = await super().get(url, params=params)
        r = TypeAdapter(list[MeEndpoint]).validate_python(data['endpoints'])
        return r

    async def get_preferred_answer_endpoint(self) -> MeEndpoint:
        """
        Get Preferred Answer Endpoint

        Retrieve the selected preferred answering endpoint for the user. If a preferred endpoint is not set for the
        person, API returns empty

        A Webex Calling user may be associated with multiple endpoints such as Webex App (desktop or mobile), Cisco
        desk IP phone, Webex Calling-supported analog devices or third-party endpoints. Preferred answering endpoints
        allow users to specify which of these devices should be prioritized for answering calls, particularly when a
        person's extension (or a virtual line assigned to them) rings on multiple devices. This helps ensure that
        calls are answered on the most convenient or appropriate device for the person.

        This API requires a user auth token with a scope of `spark:telephony_config_read`.

        :rtype: :class:`MeEndpoint`
        """

        url = self.ep('settings/preferredAnswerEndpoint')
        data = await super().get(url)
        if not data:
            return None
        r = MeEndpoint.model_validate(data)
        return r

    async def modify_preferred_answer_endpoint(self, id: str):
        """
        Modify Preferred Answer Endpoint

        Sets or clears the person’s preferred answer endpoint. To clear the preferred answer endpoint the `id`
        attribute must be set to null.

        A Webex Calling user may be associated with multiple endpoints such as Webex App (desktop or mobile), Cisco
        desk IP phone, Webex Calling-supported analog devices or third-party endpoints. Preferred answering endpoints
        allow users to specify which of these devices should be prioritized for answering calls, particularly when a
        person's extension (or a virtual line assigned to them) rings on multiple devices. This helps ensure that
        calls are answered on the most convenient or appropriate device for the person.

        This API requires a user auth token with a scope of `spark:telephony_config_write`.

        :param id: Person’s preferred answer endpoint.
        :type id: str
        :type org_id: str
        :rtype: None
        """
        body = dict()
        body['id'] = id
        url = self.ep('settings/preferredAnswerEndpoint')
        await super().put(url, json=body)


class AsMeExecutiveApi(AsApiChild, base='telephony/config/people/me'):
    async def alert_settings(self) -> ExecAlert:
        """
        Get User Executive Alert Settings

        Get executive alert settings for the authenticated user.

        Executive Alert settings in Webex allow you to control how calls are routed to executive assistants, including
        alerting mode, rollover options, and caller ID presentation. You can configure settings such as sequential or
        simultaneous alerting, and specify what happens when calls aren't answered.

        This API requires a user auth token with a scope of `spark:telephony_config_read`.

        :rtype: :class:`ExecAlert`
        """
        url = self.ep('settings/executive/alert')
        data = await super().get(url)
        r = ExecAlert.model_validate(data)
        return r

    async def update_alert_settings(self, settings: ExecAlert):
        """
        Modify User Executive Alert Settings

        Update executive alert settings for the authenticated user.

        Executive Alert settings in Webex allow you to control how calls are routed to executive assistants, including
        alerting mode, rollover options, and caller ID presentation. You can configure settings such as sequential or
        simultaneous alerting, and specify what happens when calls aren't answered.

        This API requires a user auth token with a scope of `spark:telephony_config_write`.

        :param settings: Alert settings
        :type settings: ExecAlert
        """
        body = settings.update()
        url = self.ep('settings/executive/alert')
        await super().put(url, json=body)

    async def assigned_assistants(self) -> AssignedAssistants:
        """
        Get My Executive Assigned Assistants

        Get list of assigned executive assistants for an authenticated user.

        As an executive, you can add assistants to your executive pool to manage calls for you. You can set when and
        which types of calls they can handle. Assistants can opt in when needed or opt out when not required.

        This API requires a user auth token with a scope of `spark:telephony_config_read`.

        :rtype: :class:`AssignedAssistants`
        """
        url = self.ep('settings/executive/assignedAssistants')
        data = await super().get(url)
        r = AssignedAssistants.model_validate(data)
        return r

    async def update_assigned_assistants(self, assigned_assistants: AssignedAssistants):
        """
        Modify My Executive Assigned Assistants

        Update assigned executive assistants for the authenticated user.

        As an executive, you can add assistants to your executive pool to manage calls for you. You can set when and
        which types of calls they can handle. Assistants can opt in when needed or opt out when not required.

        This API requires a user auth token with a scope of `spark:telephony_config_write`.

        :param assigned_assistants: Assigned Assistants
        :type assigned_assistants: AssignedAssistants
        :rtype: None
        """
        body = assigned_assistants.update()
        if assigned_assistants.assistants:
            body['assistantIds'] = [a.id for a in assigned_assistants.assistants]
            body.pop('assistants')
        url = self.ep('settings/executive/assignedAssistants')
        await super().put(url, json=body)

    async def executive_assistant_settings(self) -> AssistantSettings:
        """
        Get My Executive Assistant Settings

        Get settings for an executive assistant.

        Executive assistants can make, answer, intercept, and route calls appropriately on behalf of their executive.
        Assistants can also set the call forwarding destination, and join or leave an executive’s pool.

        This API requires a user auth token with a scope of `spark:telephony_config_read`.

        :rtype: :class:`AssistantSettings`
        """
        url = self.ep('settings/executive/assistant')
        data = await super().get(url)
        r = AssistantSettings.model_validate(data)
        return r

    async def update_executive_assistant_settings(self, assistant_settings: AssistantSettings):
        """
        Modify My Executive Assistant Settings

        Update Settings for an executive assistant.

        Executive assistants can make, answer, intercept, and route calls appropriately on behalf of their executive.
        Assistants can also set the call forwarding destination, and join or leave an executive’s pool.

        This API requires a user auth token with a scope of `spark:telephony_config_write`.

        :param assistant_settings: My Executive Assistant Settings
        :type assistant_settings: AssistantSettings
        """
        body = assistant_settings.update()
        url = self.ep('settings/executive/assistant')
        await super().put(url, json=body)

    async def executive_available_assistants(self) -> list[ExecOrAssistant]:
        """
        Get My Executive Available Assistants

        Get a list of available executive assistants for the authenticated user.

        As an executive, you can add assistants to your executive pool to manage calls for you. You can set when and
        which types of calls they can handle. Assistants can opt in when needed or opt out when not required.

        This API requires a user auth token with a scope of `spark:telephony_config_read`.

        :rtype: list[ExecOrAssistant]
        """
        url = self.ep('settings/executive/availableAssistants')
        data = await super().get(url)
        r = TypeAdapter(list[ExecOrAssistant]).validate_python(data['assistants'])
        return r

    async def executive_call_filtering_settings(self) -> ExecCallFiltering:
        """
        Get User Executive Call Filtering Settings

        Get executive call filtering settings for the authenticated user.

        Executive Call Filtering in Webex allows you to control which calls are allowed to reach the executive
        assistant based on custom criteria, such as specific phone numbers or call types. You can enable or disable
        call filtering and configure filter rules to manage incoming calls.

        This API requires a user auth token with a scope of `spark:telephony_config_read`.

        :rtype: :class:`ExecCallFiltering`
        """
        url = self.ep('settings/executive/callFiltering')
        data = await super().get(url)
        r = ExecCallFiltering.model_validate(data)
        return r

    async def update_executive_call_filtering_settings(self, settings: ExecCallFiltering):
        """
        Update User Executive Call Filtering Settings

        Update executive call filtering settings for the authenticated user.

        Executive Call Filtering in Webex allows you to control which calls are allowed to reach the executive
        assistant based on custom criteria, such as specific phone numbers or call types. You can enable or disable
        call filtering and configure filter rules to manage incoming calls.

        This API requires a user auth token with a scope of `spark:telephony_config_write`.

        :param settings: Call Filtering Settings
        :type settings: ExecCallFiltering
        """
        body = settings.update()
        url = self.ep('settings/executive/callFiltering')
        await super().put(url, json=body)

    async def create_call_filtering_criteria(self, settings: ExecCallFilteringCriteria) -> str:
        """
        Add User Executive Call Filtering Criteria

        Create a new executive call filtering criteria for the authenticated user.

        Executive Call Filtering Criteria in Webex allows you to define detailed filter rules for incoming calls. This
        API creates a new filter rule with the specified configuration, including schedule, phone numbers, and call
        routing preferences.

        This API requires a user auth token with a scope of `spark:telephony_config_write`.

        :param settings: Call Filtering Settings
        :type settings: ExecCallFilteringCriteria
        """
        body = settings.create()
        url = self.ep('settings/executive/callFiltering/criteria')
        data = await super().post(url, json=body)
        r = data['id']
        return r

    async def delete_call_filtering_criteria(self, id: str):
        """
        Delete User Executive Call Filtering Criteria

        Delete a specific executive call filtering criteria for the authenticated user.

        Executive Call Filtering Criteria in Webex allows you to manage detailed filter rules for incoming calls. This
        API removes a specific filter rule by its unique identifier.

        This API requires a user auth token with a scope of `spark:telephony_config_write`.

        :param id: The `id` parameter specifies the unique identifier for the executive call filtering criteria.
            Example: `Y2lzY29zcGFyazovL3VzL0NSSVRFUklBL2RHVnpkRjltYVd4MFpYST0`.
        :type id: str
        :rtype: None
        """
        url = self.ep(f'settings/executive/callFiltering/criteria/{id}')
        await super().delete(url)

    async def call_filtering_criteria(self, id: str) -> ExecCallFilteringCriteria:
        """
        Get User Executive Call Filtering Criteria Settings

        Get executive call filtering criteria settings for the authenticated user.

        Executive Call Filtering Criteria in Webex allows you to retrieve detailed configuration for a specific filter
        rule. This includes schedule settings, phone number filters, and call routing preferences for executive call
        filtering.

        This API requires a user auth token with a scope of `spark:telephony_config_read`.

        :param id: The `id` parameter specifies the unique identifier for the executive call filtering criteria.
            Example: `Y2lzY29zcGFyazovL3VzL0NSSVRFUklBL2RHVnpkRjltYVd4MFpYST0`.
        :type id: str
        :rtype: :class:`ExecCallFilteringCriteria`
        """
        url = self.ep(f'settings/executive/callFiltering/criteria/{id}')
        data = await super().get(url)
        r = ExecCallFilteringCriteria.model_validate(data)
        return r

    async def update_call_filtering_criteria(self, id: str, settings: ExecCallFilteringCriteria) -> str:
        """
        Update User Executive Call Filtering Criteria Settings

        Update executive call filtering criteria settings for the authenticated user.

        Executive Call Filtering Criteria in Webex allows you to modify detailed configuration for a specific filter
        rule. This includes updating schedule settings, phone number filters, and call routing preferences for
        executive call filtering.

        This API requires a user auth token with a scope of `spark:telephony_config_write`.

        :param id: The `id` parameter specifies the unique identifier for the executive call filtering criteria.
            Example: `Y2lzY29zcGFyazovL3VzL0NSSVRFUklBL2RHVnpkRjltYVd4MFpYST0`.
        :type id: str
        :param settings: Call Filtering Settings
        :type settings: ExecCallFilteringCriteria
        :rtype: str
        """
        body = settings.update()
        url = self.ep(f'settings/executive/callFiltering/criteria/{id}')
        data = await super().put(url, json=body)
        r = data['id']
        return r

    async def screening_settings(self) -> ExecScreening:
        """
        Get User Executive Screening Settings

        Get executive screening settings for the authenticated user.

        Executive Screening in Webex allows you to manage how incoming calls are screened and alerted based on your
        preferences. You can enable or disable executive screening and configure alert types and locations for
        notifications.

        This API requires a user auth token with a scope of `spark:telephony_config_read`.

        :rtype: :class:`ExecScreening`
        """
        url = self.ep('settings/executive/screening')
        data = await super().get(url)
        r = ExecScreening.model_validate(data)
        return r

    async def update_screening_settings(self, settings: ExecScreening):
        """
        Modify User Executive Screening Settings

        Update executive screening settings for the authenticated user.

        Executive Screening in Webex allows you to manage how incoming calls are screened and alerted based on your
        preferences. You can enable or disable executive screening and configure alert types and locations for
        notifications.

        This API requires a user auth token with a scope of `spark:telephony_config_write`.

        :param settings: Screening Settings
        :type settings: ExecScreening

        """
        body = settings.model_dump(mode='json', by_alias=True, exclude_unset=True)
        url = self.ep('settings/executive/screening')
        await super().put(url, json=body)


class AsMeForwardingApi(AsApiChild, base='telephony/config/people/me'):
    async def settings(self) -> PersonForwardingSetting:
        """
        Read My Call Forwarding Settings

        Read call forwarding settings associated with the authenticated user.

        Three types of call forwarding are supported:

        + Always - forwards all incoming calls to the destination you choose.

        + When busy - forwards all incoming calls to the destination you chose while the phone is in use or the person
        is busy.

        + When no answer - forwarding only occurs when you are away or not answering your phone.

        In addition, the Business Continuity feature will send calls to a destination of your choice if your phone is
        not connected to the network for any reason, such as a power outage, failed Internet connection, or wiring
        problem.

        This API requires a user auth token with a scope of `spark:telephony_config_read`.

        :rtype: :class:`PersonForwardingSetting`
        """
        url = self.ep('settings/callForwarding')
        data = await super().get(url)
        r = PersonForwardingSetting.model_validate(data)
        return r

    async def configure(self, forwarding: PersonForwardingSetting):
        """
        Configure My Call Forwarding Settings

        Update call forwarding settings associated with the authenticated user.

        Three types of call forwarding are supported:

        + Always - forwards all incoming calls to the destination you choose.

        + When busy - forwards all incoming calls to the destination you chose while the phone is in use or the person
        is busy.

        + When no answer - forwarding only occurs when you are away or not answering your phone.

        In addition, the Business Continuity feature will send calls to a destination of your choice if your phone is
        not connected to the network for any reason, such as a power outage, failed Internet connection, or wiring
        problem.

        This API requires a user auth token with a scope of `spark:telephony_config_write`.

        :param forwarding: new forwarding settings
        :type forwarding: PersonForwardingSetting

        Example:

            .. code-block:: python

                api = self.api.me.forwarding

                forwarding = api.read()
                always = CallForwardingAlways(
                    enabled=True,
                    destination='9999',
                    destination_voicemail_enabled=True,
                    ring_reminder_enabled=True)
                forwarding.call_forwarding.always = always
                api.configure(forwarding=forwarding)
        """
        body = forwarding.update()
        url = self.ep('settings/callForwarding')
        await super().put(url, json=body)


class AsMePersonalAssistantApi(AsApiChild, base='telephony/config/people/me/settings/personalAssistant'):
    """
    Personal Assistant Settings For Me

    Call settings for me APIs allow a person to read or modify their settings.

    Viewing settings requires a user auth token with a scope of `spark:telephony_config_read`.

    Configuring settings requires a user auth token with a scope of `spark:telephony_config_write`.
    """

    async def get(self) -> PersonalAssistant:
        """
        Get My Personal Assistant

        Retrieve user's own Personal Assistant details.

        Personal Assistant is used to manage a user's incoming calls when they are away.

        Retrieving Personal Assistant details requires a user auth token with `spark:telephony_config_read`.

        :rtype: :class:`PersonalAssistant`
        """
        url = self.ep()
        data = await super().get(url)
        r = PersonalAssistant.model_validate(data)
        return r

    async def update(self, settings: PersonalAssistant):
        """
        Modify My Personal Assistant

        Update user's own Personal Assistant details.

        Personal Assistant is used to manage a user's incoming calls when they are away.

        Updating Personal Assistant details requires a auth token with the `spark:telephony_config_write`.

        :param settings: Personal Assistant settings.
        :type settings: PersonalAssistant
        :rtype: None
        """
        body = settings.model_dump(mode='json', exclude_unset=True, by_alias=True)
        url = self.ep()
        await super().put(url, json=body)


class AsMeRecordingApi(AsApiChild, base='telephony/config/people/me'):
    async def settings(self) -> MeRecordingSettings:
        """
        Get My Call Recording Settings

        Get details of call recording settings associated with the authenticated user.

        Call recording settings allow you to access and customize options that determine when and how your calls are
        recorded, providing control over recording modes and notifications.

        This API requires a user auth token with a scope of `spark:telephony_config_read`.

        :rtype: :class:`MeRecordingSettings`
        """
        url = self.ep('settings/callRecording')
        data = await super().get(url)
        r = MeRecordingSettings.model_validate(data)
        return r


class AsMeSNRApi(AsApiChild, base='telephony/config/people/me'):

    async def settings(self) -> MeSNRSettings:
        """
        Get User's Single Number Reach Settings

        Retrieves all single number reach settings configured for the authenticated user.

        The "Single Number Reach" feature in Webex allows users to access their business phone capabilities from any
        device, making it easy to make and receive calls as if at their office. This is especially useful for remote
        or mobile workers needing flexibility.

        This API requires a user auth token with a scope of `spark:telephony_config_read`.

        :rtype: :class:`MeSNRSettings`
        """
        url = self.ep('settings/singleNumberReach')
        data = await super().get(url)
        r = MeSNRSettings.model_validate(data)
        return r

    async def update(self, alert_all_locations_for_click_to_dial_calls_enabled: bool = None):
        """
        Update User's Single Number Reach Settings

        Updates single number reach settings associated with the authenticated user.

        The "Single Number Reach" feature in Webex allows users to access their business phone capabilities from any
        device, making it easy to make and receive calls as if at their office. This is especially useful for remote
        or mobile workers needing flexibility.

        This API requires a user auth token with a scope of `spark:telephony_config_write`.

        :param alert_all_locations_for_click_to_dial_calls_enabled: If `true`, all locations will be alerted for
            click-to-dial calls.
        :type alert_all_locations_for_click_to_dial_calls_enabled: bool
        :rtype: None
        """
        body = dict()
        if alert_all_locations_for_click_to_dial_calls_enabled is not None:
            body['alertAllLocationsForClickToDialCallsEnabled'] = alert_all_locations_for_click_to_dial_calls_enabled
        url = self.ep('settings/singleNumberReach')
        await super().put(url, json=body)

    async def create_snr(self, settings: SingleNumberReachNumber) -> str:
        """
        Add phone number as User's Single Number Reach

        Add a phone number as a single number reach for the authenticated user.

        The "Single Number Reach" feature in Webex allows users to access their business phone capabilities from any
        device, making it easy to make and receive calls as if at their office. This is especially useful for remote
        or mobile workers needing flexibility.

        This API requires a user auth token with a scope of `spark:telephony_config_write`.

        :param settings: User's Single Number Reach Settings
        :type settings: :class:`SingleNumberReachNumber`
        :return: Unique identifier of the phone number.
        :rtype: str
        """
        body = settings.create_update()
        url = self.ep('settings/singleNumberReach/numbers')
        data = await super().post(url, json=body)
        r = data['id']
        return r

    async def delete_snr(self, phone_number_id: str):
        """
        Delete User's Single Number Reach Contact Settings

        Delete contact settings associated with the authenticated user.

        The "Single Number Reach" feature in Webex allows users to access their business phone capabilities from any
        device, making it easy to make and receive calls as if at their office. This is especially useful for remote
        or mobile workers needing flexibility.

        This API requires a user auth token with a scope of `spark:telephony_config_write`.

        :param phone_number_id: Unique identifier of the phone number.
        :type phone_number_id: str
        :rtype: None
        """
        url = self.ep(f'settings/singleNumberReach/numbers/{phone_number_id}')
        await super().delete(url)

    async def update_snr(self, phone_number_id: str, settings: SingleNumberReachNumber):
        """
        Modify User's Single Number Reach Contact Settings

        Update the contact settings of single number reach for the authenticated user.

        The "Single Number Reach" feature in Webex allows users to access their business phone capabilities from any
        device, making it easy to make and receive calls as if at their office. This is especially useful for remote
        or mobile workers needing flexibility.

        This API requires a user auth token with a scope of `spark:telephony_config_write`.

        :param phone_number_id: Unique identifier of the phone number.
        :type phone_number_id: str
        :param settings: User's Single Number Reach Settings
        :type settings: :class:`SingleNumberReachNumber`
        """
        body = settings.create_update()
        url = self.ep(f'settings/singleNumberReach/numbers/{phone_number_id}')
        await super().put(url, json=body)


class AsMeVoicemailApi(AsApiChild, base='telephony/config/people/me'):
    async def settings(self) -> VoicemailSettings:
        """
        Read Voicemail Settings for a Person

        Retrieve a person's voicemail settings.

        The voicemail feature transfers callers to voicemail based on your settings. You can then retrieve voice
        messages via voicemail.

        Optionally, notifications can be sent to a mobile phone via text or email. These notifications will not include
        the voicemail files.

        This API requires a user auth token with a scope of `spark-admin:people_read`.

        :rtype: :class:`VoicemailSettings`
        """
        url = self.ep('settings/voicemail')
        data = await super().get(url)
        r = VoicemailSettings.model_validate(data)
        return r

    async def configure(self, settings: VoicemailSettings):
        """
        Configure Voicemail Settings for a Person

        Configure a person's voicemail settings.

        The voicemail feature transfers callers to voicemail based on your settings. You can then retrieve voice
        messages via voicemail.

        Optionally, notifications can be sent to a mobile phone via text or email. These notifications will not include
        the voicemail files.

        This API requires a user auth token with a scope of `spark-admin:people_write`.

        :param settings: Voicemail settings
        :type settings: VoicemailSettings
        :rtype: None
        """
        body = settings.update()
        url = self.ep('settings/voicemail')
        await super().put(url, json=body)


class AsMeSettingsApi(AsApiChild, base='telephony/config/people/me'):
    """
    Call Settings For Me

    Call settings for me APIs allow a person to read or modify their settings.

    Viewing settings requires a user auth token with a scope of `spark:telephony_config_read`.

    Configuring settings requires a user auth token with a scope of `spark:telephony_config_write`.
    """
    barge: AsMeBargeApi
    call_block: AsMeCallBlockApi
    call_center: AsMeCallCenterApi
    call_park: AsMeCallParkApi
    call_pickup: AsMeCallPickupApi
    call_policies: AsMeCallPoliciesApi
    caller_id: AsMeCallerIdApi
    dnd: AsMeDNDApi
    endpoints: AsMeEndpointsApi
    executive: AsMeExecutiveApi
    forwarding: AsMeForwardingApi
    go_override: AsGoOverrideApi
    personal_assistant: AsMePersonalAssistantApi
    recording: AsMeRecordingApi
    snr: AsMeSNRApi
    voicemail: AsMeVoicemailApi

    def __init__(self, session: AsRestSession):
        """

        :meta private:
        """
        super().__init__(session=session)
        self.barge = AsMeBargeApi(session=session)
        self.call_block = AsMeCallBlockApi(session=session)
        self.call_center = AsMeCallCenterApi(session=session)
        self.call_park = AsMeCallParkApi(session=session)
        self.call_pickup = AsMeCallPickupApi(session=session)
        self.call_policies = AsMeCallPoliciesApi(session=session)
        self.caller_id = AsMeCallerIdApi(session=session)
        self.dnd: AsMeDNDApi = AsMeDNDApi(session=session)
        self.endpoints = AsMeEndpointsApi(session=session)
        self.executive = AsMeExecutiveApi(session=session)
        self.forwarding = AsMeForwardingApi(session=session)
        self.go_override = AsGoOverrideApi(session=session)
        self.personal_assistant = AsMePersonalAssistantApi(session=session)
        self.recording = AsMeRecordingApi(session=session)
        self.snr = AsMeSNRApi(session=session)
        self.voicemail = AsMeVoicemailApi(session=session)

    async def details(self) -> MeProfile:
        """
        Get My Own Details

        Get profile details for the authenticated user.

        Profile details include the user's name, email, location and calling details.

        This API requires a user auth token with a scope of `spark:telephony_config_read`.

        :rtype: :class:`MeProfile`
        """
        url = self.ep()
        data = await super().get(url)
        r = MeProfile.model_validate(data)
        return r

    async def announcement_languages(self) -> list[AnnouncementLanguage]:
        """
        Retrieve announcement languages for the authenticated user

        Retrieve the list of available announcement languages for the authenticated user's telephony configuration.

        Announcement languages determine the language used for system prompts and announcements during calls.

        This API requires a user auth token with a scope of `spark:telephony_config_read`.

        :rtype: list[AnnouncementLanguage]
        """
        url = self.ep('announcementLanguages')
        data = await super().get(url)
        r = TypeAdapter(list[AnnouncementLanguage]).validate_python(data['languages'])
        return r

    async def country_telephony_config_requirements(self,
                                              country_code: str) -> CountryTelephonyConfigRequirements:
        """
        Retrieve country-specific telephony configuration requirements

        Retrieve country-specific telephony configuration requirements for the authenticated user.

        Webex Calling supports multiple regions and time zones to validate and present the information using the local
        date and time, as well as localized dialing rules.

        This API requires a user auth token with a scope of `spark:telephony_config_read`.

        :param country_code: The ISO country code for which configuration requirements are requested.
        :type country_code: str
        :rtype: :class:`CountryTelephonyConfigRequirements`
        """
        url = self.ep(f'countries/{country_code}')
        data = await super().get(url)
        r = CountryTelephonyConfigRequirements.model_validate(data)
        return r

    async def feature_access_codes(self) -> list[FeatureAccessCode]:
        """
        Get My Feature Access Codes

        Retrieve all Feature Access Codes configured for services that are assigned to the authenticated user. For each
        feature access code, the name and code are returned. If an alternate code is defined, it is also returned.

        Feature access codes (FACs), also known as star codes, give users access to advanced calling features.

        This API requires a user auth token with a scope of `spark:telephony_config_read`.

        :rtype: list[FeatureAccessCode]
        """
        url = self.ep('settings/featureAccessCode')
        data = await super().get(url)
        r = TypeAdapter(list[FeatureAccessCode]).validate_python(data['featureAccessCodeList'])
        return r

    async def monitoring_settings(self) -> MeMonitoringSettings:
        """
        Get My Monitoring Settings

        Retrieves the monitoring settings of the logged in person, which shows specified people, places, virtual lines
        or call park extensions that are being monitored.

        Monitors the line status which indicates if a person, place or virtual line is on a call and if a call has been
        parked on that extension.

        This API requires a user auth token with a scope of `spark:telephony_config_read`.

        :rtype: :class:`MeMonitoringSettings`
        """
        url = self.ep('settings/monitoring')
        data = await super().get(url)
        r = MeMonitoringSettings.model_validate(data)
        return r

    async def calling_services_list(self) -> list[ServicesEnum]:
        """
        Get My Calling Services List

        Retrieves the list of enabled calling services for the authenticated user.

        These services are designed to improve call handling and ensure that users can manage their communications
        effectively. They are commonly found in both personal and business telephony systems.

        This API requires a user auth token with a scope of `spark:telephony_config_read`.

        :rtype: list[ServicesEnum]
        """
        url = self.ep('settings/services')
        data = await super().get(url)
        r = TypeAdapter(list[ServicesEnum]).validate_python(data['services'])
        return r

    async def call_captions_settings(self) -> UserCallCaptions:
        """
        Get my call captions settings

        Retrieve the effective call captions settings of the authenticated user.

        **NOTE**: The call captions feature is not supported for Webex Calling Standard users or users assigned to
        locations in India.

        The call caption feature allows the customer to enable and manage closed captions and transcript functionality
        (rolling caption panel) in Webex Calling, without requiring the user to escalate the call to a meeting.

        This API requires a user auth token with a scope of `spark:telephony_config_read`.

        :rtype: :class:`UserCallCaptions`
        """
        url = self.ep('settings/callCaptions')
        data = await super().get(url)
        r = UserCallCaptions.model_validate(data)
        return r


class AsMeetingChatsApi(AsApiChild, base='meetings/postMeetingChats'):
    """
    Chats are content captured in a meeting when chat messages are sent between the participants within a meeting.
    This feature allows a Compliance Officer to access the in-meeting chat content.

    The Compliance Officer can use the Meeting Chats API to retrieve the chats of a meeting and to delete all chats
    associated with a meeting. private chats are text messages between two people. group chats are for larger
    breakout spaces. Meeting chats are different from room messages in that there is no catch-up propagation. For
    example, if a user joins a meeting late only, chat messages that are created from then on, will be propagated to
    this user. To understand which user saw which message if they joined late, you have to query the
    meetingParticipants REST resource for the joined/left times and compare to the meetingsChat chatTime field.

    The Webex meetings chat functionality and API endpoint described here is "upon-request" and not enabled
    by default. If you need it enabled for your org, or if you need help, please contact the Webex Developer Support
    team at devsupport@webex.com.
    """

    def list_gen(self, meeting_id: str, offset: int = None, **params) -> AsyncGenerator[ChatObject, None, None]:
        """
        Lists the meeting chats of a finished meeting instance specified by meetingId. You can set a maximum number
        of chats to return.

        Use this operation to list the chats of a finished meeting instance when they are ready. Please note that
        only meeting instances in state ended are supported for meetingId. Meeting series, scheduled meetings and
        in-progress meeting instances are not supported.

        :param meeting_id: A unique identifier for the meeting instance to which the chats belong. The meeting ID of
            a scheduled personal room meeting is not supported.
        :type meeting_id: str
        :param offset: Offset from the first result that you want to fetch.
        :type offset: int
        """
        params['meetingId'] = meeting_id
        if offset is not None:
            params['offset'] = offset
        url = self.ep()
        return self.session.follow_pagination(url=url, model=ChatObject, params=params)

    async def list(self, meeting_id: str, offset: int = None, **params) -> List[ChatObject]:
        """
        Lists the meeting chats of a finished meeting instance specified by meetingId. You can set a maximum number
        of chats to return.

        Use this operation to list the chats of a finished meeting instance when they are ready. Please note that
        only meeting instances in state ended are supported for meetingId. Meeting series, scheduled meetings and
        in-progress meeting instances are not supported.

        :param meeting_id: A unique identifier for the meeting instance to which the chats belong. The meeting ID of
            a scheduled personal room meeting is not supported.
        :type meeting_id: str
        :param offset: Offset from the first result that you want to fetch.
        :type offset: int
        """
        params['meetingId'] = meeting_id
        if offset is not None:
            params['offset'] = offset
        url = self.ep()
        return [o async for o in self.session.follow_pagination(url=url, model=ChatObject, params=params)]

    async def delete(self, meeting_id: str):
        """
        Deletes the meeting chats of a finished meeting instance specified by meetingId.

        Use this operation to delete the chats of a finished meeting instance when they are ready. Please note that
        only meeting instances in state ended are supported for meetingId. Meeting series, scheduled meetings and
        in-progress meeting instances are not supported.

        :param meeting_id: A unique identifier for the meeting instance to which the chats belong. Meeting IDs of a
            scheduled personal room meeting are not supported.
        :type meeting_id: str
        """
        params = dict()
        params['meetingId'] = meeting_id
        url = self.ep()
        await super().delete(url=url, params=params)
        return


class AsMeetingClosedCaptionsApi(AsApiChild, base='meetingClosedCaptions'):
    """
    Meeting Closed Captions APIs are enabled upon request, and are not available by default. Please contact the Webex
    Developer Support team at devsupport@webex.com if you would like to enable this feature for your organization.
    Meeting closed captions are the automatic transcriptions of what is being said during a meeting in real-time.
    Closed captions appear after being enabled during a meeting and can be translated to a participant's language.
    A closed caption snippet is a short text snippet from a meeting closed caption which was spoken by a particular
    participant in the meeting. A meeting's closed captions consists of many snippets.
    The Closed Captions API manages meeting closed captions and snippets. You can list meeting closed captions, as well
    as list and download snippets. Closed captions can be retrieved in either Web Video Text Tracks (VTT) or plain text
    (TXT) format via the download links provided by the vttDownloadLink and txtDownloadlink response properties,
    respectively.
    Refer to the Meetings API Scopes section of Meetings Overview guide for the scopes required for each API.
    Notes:
    Currently, closed caption APIs are only supported for the Compliance Officer role.
    Closed captions will be available 15 minutes after the meeting is finished.
    """

    def list_gen(self, meeting_id: str, **params) -> AsyncGenerator[ClosedCaption, None, None]:
        """
        Lists closed captions of a finished meeting instance specified by meetingId.

        :param meeting_id: Unique identifier for the meeting instance which the closed captions belong to. This
            parameter only applies to ended meeting instnaces. It does not apply to meeting series, scheduled meetings
            or scheduled personal room meetings.
        :type meeting_id: str
        """
        params['meetingId'] = meeting_id
        url = self.ep()
        return self.session.follow_pagination(url=url, model=ClosedCaption, params=params)

    async def list(self, meeting_id: str, **params) -> List[ClosedCaption]:
        """
        Lists closed captions of a finished meeting instance specified by meetingId.

        :param meeting_id: Unique identifier for the meeting instance which the closed captions belong to. This
            parameter only applies to ended meeting instnaces. It does not apply to meeting series, scheduled meetings
            or scheduled personal room meetings.
        :type meeting_id: str
        """
        params['meetingId'] = meeting_id
        url = self.ep()
        return [o async for o in self.session.follow_pagination(url=url, model=ClosedCaption, params=params)]

    def list_snippets_gen(self, closed_caption_id: str, meeting_id: str, **params) -> AsyncGenerator[CCSnippet, None, None]:
        """
        Lists snippets of a meeting closed caption specified by closedCaptionId.

        :param closed_caption_id: Unique identifier for the meeting closed caption which the snippets belong to.
        :type closed_caption_id: str
        :param meeting_id: Unique identifier for the meeting instance which the closed caption snippets belong to. This
            parameter only applies to ended meeting instances. It does not apply to meeting series, scheduled meetings
            or scheduled personal room meetings.
        :type meeting_id: str
        """
        params['meetingId'] = meeting_id
        url = self.ep(f'{closed_caption_id}/snippets')
        return self.session.follow_pagination(url=url, model=CCSnippet, params=params)

    async def list_snippets(self, closed_caption_id: str, meeting_id: str, **params) -> List[CCSnippet]:
        """
        Lists snippets of a meeting closed caption specified by closedCaptionId.

        :param closed_caption_id: Unique identifier for the meeting closed caption which the snippets belong to.
        :type closed_caption_id: str
        :param meeting_id: Unique identifier for the meeting instance which the closed caption snippets belong to. This
            parameter only applies to ended meeting instances. It does not apply to meeting series, scheduled meetings
            or scheduled personal room meetings.
        :type meeting_id: str
        """
        params['meetingId'] = meeting_id
        url = self.ep(f'{closed_caption_id}/snippets')
        return [o async for o in self.session.follow_pagination(url=url, model=CCSnippet, params=params)]

    async def download_snippets(self, closed_caption_id: str, meeting_id: str, format: str = None):
        """
        Download meeting closed caption snippets from the meeting closed caption specified by closedCaptionId formatted
        either as a Video Text Track (.vtt) file or plain text (.txt) file.

        :param closed_caption_id: Unique identifier for the meeting closed caption.
        :type closed_caption_id: str
        :param meeting_id: Unique identifier for the meeting instance which the closed caption snippets belong to. This
            parameter only applies to meeting instances in the ended state. It does not apply to meeting series,
            scheduled meetings or scheduled personal room meetings.
        :type meeting_id: str
        :param format: Format for the downloaded meeting closed caption snippets. Possible values: vtt, txt
        :type format: str
        """
        # TODO: verify return and adapt
        params = dict()
        params['meetingId'] = meeting_id
        if format is not None:
            params['format'] = format
        url = self.ep(f'{closed_caption_id}/download')
        await super().get(url=url, params=params)
        return


class AsMeetingInviteesApi(AsApiChild, base='meetingInvitees'):
    """
    This API manages invitees' relationships to a meeting.
    You can use the Meeting Invitees API to list, create, update, and delete invitees.
    Refer to the Meetings API Scopes section of Meetings Overview for scopes required for each API.
    """

    def list_gen(self, meeting_id: str, host_email: str = None, panelist: bool = None,
             **params) -> AsyncGenerator[Invitee, None, None]:
        """
        Lists meeting invitees for a meeting with a specified meetingId. You can set a maximum number of invitees to
        return. This operation can be used for meeting series, scheduled meetings, and ended or ongoing meeting
        instance objects. If the specified meetingId is for a meeting series, the invitees for the series will be
        listed; if the meetingId is for a scheduled meeting, the invitees for the particular scheduled meeting will
        be listed; if the meetingId is for an ended or ongoing meeting instance, the invitees for the particular
        meeting instance will be listed. See the Webex Meetings guide for more information about the types of
        meetings. The list returned is sorted in ascending order by email address. Long result sets are split into
        pages.

        :param meeting_id: Unique identifier for the meeting for which invitees are being requested. The meeting
            can be a meeting series, a scheduled meeting, or a meeting instance which has ended or is ongoing. The
            meeting ID of a scheduled personal room meeting is not supported for this API.
        :type meeting_id: str
        :param host_email: Email address for the meeting host. This parameter is only used if the user or
            application calling the API has the admin on-behalf-of scopes. If set, the admin may specify the email of
            a user in a site they manage and the API will return meeting invitees that are hosted by that user.
        :type host_email: str
        :param panelist: Filter invitees or attendees for webinars only. If true,
            returns invitees. If false, returns attendees. If null, returns both invitees and attendees.
        :type panelist: bool
        """
        params['meetingId'] = meeting_id
        if host_email is not None:
            params['hostEmail'] = host_email
        if panelist is not None:
            params['panelist'] = str(panelist).lower()
        url = self.ep()
        return self.session.follow_pagination(url=url, model=Invitee, params=params)

    async def list(self, meeting_id: str, host_email: str = None, panelist: bool = None,
             **params) -> List[Invitee]:
        """
        Lists meeting invitees for a meeting with a specified meetingId. You can set a maximum number of invitees to
        return. This operation can be used for meeting series, scheduled meetings, and ended or ongoing meeting
        instance objects. If the specified meetingId is for a meeting series, the invitees for the series will be
        listed; if the meetingId is for a scheduled meeting, the invitees for the particular scheduled meeting will
        be listed; if the meetingId is for an ended or ongoing meeting instance, the invitees for the particular
        meeting instance will be listed. See the Webex Meetings guide for more information about the types of
        meetings. The list returned is sorted in ascending order by email address. Long result sets are split into
        pages.

        :param meeting_id: Unique identifier for the meeting for which invitees are being requested. The meeting
            can be a meeting series, a scheduled meeting, or a meeting instance which has ended or is ongoing. The
            meeting ID of a scheduled personal room meeting is not supported for this API.
        :type meeting_id: str
        :param host_email: Email address for the meeting host. This parameter is only used if the user or
            application calling the API has the admin on-behalf-of scopes. If set, the admin may specify the email of
            a user in a site they manage and the API will return meeting invitees that are hosted by that user.
        :type host_email: str
        :param panelist: Filter invitees or attendees for webinars only. If true,
            returns invitees. If false, returns attendees. If null, returns both invitees and attendees.
        :type panelist: bool
        """
        params['meetingId'] = meeting_id
        if host_email is not None:
            params['hostEmail'] = host_email
        if panelist is not None:
            params['panelist'] = str(panelist).lower()
        url = self.ep()
        return [o async for o in self.session.follow_pagination(url=url, model=Invitee, params=params)]

    async def create_invitee(self, email: str, meeting_id: str, display_name: str = None, co_host: bool = None,
                       send_email: bool = None, panelist: bool = None, host_email: str = None) -> Invitee:
        """
        Invite a person to attend a meeting.
        Identify the invitee in the request body, by email address.

        :param email: Email address for meeting invitee.
        :type email: str
        :param meeting_id: Unique identifier for the meeting to which a person is being invited. This attribute only
            applies to meeting series and scheduled meeting. If it's a meeting series, the meeting invitee is invited
            to the entire meeting series; if it's a scheduled meeting, the meeting invitee is invited to this individual
            scheduled meeting. It doesn't apply to an ended or ongoing meeting instance. The meeting ID of a scheduled
            personal room meeting is not supported for this API.
        :type meeting_id: str

        :param display_name: Display name for meeting invitee. The maximum length of displayName is 128
            characters. In Webex App, if the email has been associated with an existing Webex account, the display
            name associated with the Webex account will be used; otherwise, the email will be used as displayName. In
            Webex site, if displayName is specified, it will show displayName. If displayName is not specified,
            and the email has been associated with an existing Webex account, the display name associated with the
            Webex account will be used; otherwise, the email will be used as displayName. Please note that if the
            invitee has an existing Webex account, the displayName shown in the meeting will be the displayName
            associated with the Webex account; otherwise, displayName shown in the meeting will be the displayName
            which is specified by the invitee who does not have a Webex account.

        :type display_name: str
        :param co_host: Whether or not invitee is a designated alternate host for the meeting. See Add Alternate
            Hosts for Cisco Webex Meetings for more details.
        :type co_host: bool
        :param send_email: If true, send an email to the invitee.
        :type send_email: bool
        :param panelist: If true, the invitee is a designated panelist for the event meeting.
        :type panelist: bool
        :param host_email: Email address for the meeting host. This attribute should only be set if the user or
            application calling the API has the admin on-behalf-of scopes. When used, the admin may specify the email of
            a user in a site they manage to be the meeting host.
        :type host_email: str
        """
        body = CreateMeetingInviteeBody()
        if email is not None:
            body.email = email
        if meeting_id is not None:
            body.meeting_id = meeting_id
        if display_name is not None:
            body.display_name = display_name
        if co_host is not None:
            body.co_host = co_host
        if send_email is not None:
            body.send_email = send_email
        if panelist is not None:
            body.panelist = panelist
        if host_email is not None:
            body.host_email = host_email
        url = self.ep()
        data = await super().post(url=url, data=body.model_dump_json())
        return Invitee.model_validate(data)

    async def create_invitees(self, meeting_id: str, items: List[CreateInviteesItem],
                        host_email: str = None) -> List[Invitee]:
        """
        Invite people to attend a meeting in bulk.
        Identify each invitee by the email address of each item in the items of the request body.
        Each invitee should have a unique email.
        This API limits the maximum size of items in the request body to 100.

        :param meeting_id: Unique identifier for the meeting to which the people are being invited. This attribute
            only applies to meeting series and scheduled meetings. If it's a meeting series, the meeting invitees are
            invited to the entire meeting series; if it's a scheduled meeting, the meeting invitees are invited to this
            individual scheduled meeting. It doesn't apply to an ended or ongoing meeting instance. The meeting ID of a
            scheduled personal room meeting is not supported for this API.
        :type meeting_id: str
        :param host_email: Email address for the meeting host. This attribute should only be set if the user or
            application calling the API has the admin on-behalf-of scopes. When used, the admin may specify the email of
            a user in a site they manage to be the meeting host.
        :type host_email: str
        :param items: Meeting invitees to be inserted.
        :type items: CreateInviteesItem
        """
        body = CreateMeetingInviteesBody()
        if meeting_id is not None:
            body.meeting_id = meeting_id
        if host_email is not None:
            body.host_email = host_email
        if items is not None:
            body.items = items
        url = self.ep('bulkInsert')
        data = await super().post(url=url, data=body.model_dump_json())
        return data["items"]

    async def invitee_details(self, meeting_invitee_id: str, host_email: str = None) -> Invitee:
        """
        Retrieve details for a meeting invitee identified by a meetingInviteeId in the URI.

        :param meeting_invitee_id: Unique identifier for the invitee whose details are being requested.
        :type meeting_invitee_id: str
        :param host_email: Email address for the meeting host. This parameter is only used if the user or application
            calling the API has the admin on-behalf-of scopes. If set, the admin may specify the email of a user in a
            site they manage and the API will return details for a meeting invitee that is hosted by that user.
        :type host_email: str
        """
        params = {}
        if host_email is not None:
            params['hostEmail'] = host_email
        url = self.ep(f'{meeting_invitee_id}')
        data = await super().get(url=url, params=params)
        return Invitee.model_validate(data)

    async def update(self, meeting_invitee_id: str, email: str, display_name: str = None, co_host: bool = None,
               send_email: bool = None, panelist: bool = None, host_email: str = None) -> Invitee:
        """
        Update details for a meeting invitee identified by a meetingInviteeId in the URI.

        :param meeting_invitee_id: Unique identifier for the invitee to be updated. This parameter only applies to an
            invitee to a meeting series or a scheduled meeting. It doesn't apply to an invitee to an ended or ongoing
            meeting instance.
        :type meeting_invitee_id: str
        :param email: Email address for meeting invitee.
        :type email: str
        :param display_name: Display name for meeting invitee. The maximum length of displayName is 128 characters.
            In Webex App, if the email has been associated with an existing Webex account, the display name associated
            with the Webex account will be used; otherwise, the email will be used as displayName. In Webex site,
            if displayName is specified, it will show displayName. If displayName is not specified, and the email has
            been associated with an existing Webex account, the display name associated with the Webex account will be
            used; otherwise, the email will be used as displayName.
            Please note that if the invitee has an existing Webex account, the displayName shown in the meeting will
            be the displayName associated with the Webex account; otherwise, displayName shown in the meeting will be
            the displayName which is specified by the invitee who does not have a Webex account.
        :type display_name: str
        :param co_host: Whether or not invitee is a designated alternate host for the meeting. See Add Alternate
            Hosts for Cisco Webex Meetings for more details.
        :type co_host: bool
        :param send_email: If true, send an email to the invitee.
        :type send_email: bool
        :param panelist: If true, the invitee is a designated panelist for the event meeting.
        :type panelist: bool
        :param host_email: Email address for the meeting host. This attribute should only be set if the user or
            application calling the API has the admin on-behalf-of scopes. When used, the admin may specify the email of
            a user in a site they manage to be the meeting host.
        :type host_email: str
        """
        body = UpdateMeetingInviteeBody()
        if email is not None:
            body.email = email
        if display_name is not None:
            body.display_name = display_name
        if co_host is not None:
            body.co_host = co_host
        if send_email is not None:
            body.send_email = send_email
        if panelist is not None:
            body.panelist = panelist
        if host_email is not None:
            body.host_email = host_email
        url = self.ep(f'{meeting_invitee_id}')
        data = await super().put(url=url, data=body.model_dump_json())
        return Invitee.model_validate(data)

    async def delete(self, meeting_invitee_id: str, host_email: str = None, send_email: bool = None):
        """
        Removes a meeting invitee identified by a meetingInviteeId specified in the URI. The deleted meeting invitee
        cannot be recovered.
        If the meeting invitee is associated with a meeting series, the invitee will be removed from the entire
        meeting series. If the invitee is associated with a scheduled meeting, the invitee will be removed from only
        that scheduled meeting.

        :param meeting_invitee_id: Unique identifier for the invitee to be removed. This parameter only applies to an
            invitee to a meeting series or a scheduled meeting. It doesn't apply to an invitee to an ended or ongoing
            meeting instance.
        :type meeting_invitee_id: str
        :param host_email: Email address for the meeting host. This parameter is only used if the user or application
            calling the API has the admin on-behalf-of scopes. If set, the admin may specify the email of a user in a
            site they manage and the API will delete a meeting invitee that is hosted by that user.
        :type host_email: str
        :param send_email: If true, send an email to the invitee.
        :type send_email: bool
        """
        params = {}
        if host_email is not None:
            params['hostEmail'] = host_email
        if send_email is not None:
            params['sendEmail'] = str(send_email).lower()
        url = self.ep(f'{meeting_invitee_id}')
        await super().delete(url=url, params=params)
        return


class AsMeetingParticipantsApi(AsApiChild, base='meetingParticipants'):
    """
    This API manages meeting participants.
    Refer to the Meetings API Scopes section of Meetings Overview for scopes required for each API.
    """

    def list_participants_gen(self, meeting_id: str, host_email: str = None, join_time_from: str = None,
                          join_time_to: str = None, **params) -> AsyncGenerator[Participant, None, None]:
        """
        List all participants in a live or post meeting. The meetingId parameter is required, which is the unique
        identifier for the meeting.
        The authenticated user calling this API must either have an Administrator role with the
        meeting:admin_participants_read scope, or be the meeting host.

        :param meeting_id: The unique identifier for the meeting. Please note that currently meeting ID of a scheduled
            personal room meeting is not supported for this API.
        :type meeting_id: str
        :param host_email: Email address for the meeting host. This parameter is only used if the user or application
            calling the API has the admin-level scopes, the admin may specify the email of a user in a site they manage
            and the API will return meeting participants of the meetings that are hosted by that user.
        :type host_email: str
        :param join_time_from: The time participants join a meeting starts from the specified date and time (inclusive)
            in any ISO 8601 compliant format. If joinTimeFrom is not specified, it equals joinTimeTo minus 7 days.
        :type join_time_from: str
        :param join_time_to: The time participants join a meeting before the specified date and time (exclusive) in any
            ISO 8601 compliant format. If joinTimeTo is not specified, it equals joinTimeFrom plus 7 days. The interval
            between joinTimeFrom and joinTimeTo must be within 90 days.
        :type join_time_to: str
        """
        params['meetingId'] = meeting_id
        if host_email is not None:
            params['hostEmail'] = host_email
        if join_time_from is not None:
            params['joinTimeFrom'] = join_time_from
        if join_time_to is not None:
            params['joinTimeTo'] = join_time_to
        url = self.ep()
        return self.session.follow_pagination(url=url, model=Participant, params=params)

    async def list_participants(self, meeting_id: str, host_email: str = None, join_time_from: str = None,
                          join_time_to: str = None, **params) -> List[Participant]:
        """
        List all participants in a live or post meeting. The meetingId parameter is required, which is the unique
        identifier for the meeting.
        The authenticated user calling this API must either have an Administrator role with the
        meeting:admin_participants_read scope, or be the meeting host.

        :param meeting_id: The unique identifier for the meeting. Please note that currently meeting ID of a scheduled
            personal room meeting is not supported for this API.
        :type meeting_id: str
        :param host_email: Email address for the meeting host. This parameter is only used if the user or application
            calling the API has the admin-level scopes, the admin may specify the email of a user in a site they manage
            and the API will return meeting participants of the meetings that are hosted by that user.
        :type host_email: str
        :param join_time_from: The time participants join a meeting starts from the specified date and time (inclusive)
            in any ISO 8601 compliant format. If joinTimeFrom is not specified, it equals joinTimeTo minus 7 days.
        :type join_time_from: str
        :param join_time_to: The time participants join a meeting before the specified date and time (exclusive) in any
            ISO 8601 compliant format. If joinTimeTo is not specified, it equals joinTimeFrom plus 7 days. The interval
            between joinTimeFrom and joinTimeTo must be within 90 days.
        :type join_time_to: str
        """
        params['meetingId'] = meeting_id
        if host_email is not None:
            params['hostEmail'] = host_email
        if join_time_from is not None:
            params['joinTimeFrom'] = join_time_from
        if join_time_to is not None:
            params['joinTimeTo'] = join_time_to
        url = self.ep()
        return [o async for o in self.session.follow_pagination(url=url, model=Participant, params=params)]

    async def query_participants_with_email(self, meeting_id: str, max: int = None, host_email: str = None,
                                      join_time_from: str = None, join_time_to: str = None,
                                      emails: list[str] = None) -> list[Participant]:
        """
        Query participants in a live meeting, or after the meeting, using participant's email. The meetingId parameter
        is the unique identifier for the meeting and is required.
        The authenticated user calling this API must either have an Administrator role with the
        meeting:admin_participants_read scope, or be the meeting host.

        :param meeting_id: The unique identifier for the meeting.
        :type meeting_id: str
        :param max: Limit the maximum number of participants in the response, up to 1000.
        :type max: int
        :param host_email: Email address for the meeting host. This parameter is only used if the user or application
            calling the API has the admin-level scopes, the admin may specify the email of a user in a site they manage
            and the API will return meeting participants of the meetings that are hosted by that user.
        :type host_email: str
        :param join_time_from: The time participants join a meeting starts from the specified date and time (inclusive)
            in any ISO 8601 compliant format. If joinTimeFrom is not specified, it equals joinTimeTo minus 7 days.
        :type join_time_from: str
        :param join_time_to: The time participants join a meeting before the specified date and time (exclusive) in any
            ISO 8601 compliant format. If joinTimeTo is not specified, it equals joinTimeFrom plus 7 days. The interval
            between joinTimeFrom and joinTimeTo must be within 90 days.
        :type join_time_to: str
        :param emails: Participants email list Possible values: a@example.com
        :type emails: List[str]
        """
        params = dict()
        params['meetingId'] = meeting_id
        if max is not None:
            params['max'] = max
        if host_email is not None:
            params['hostEmail'] = host_email
        if join_time_from is not None:
            params['joinTimeFrom'] = join_time_from
        if join_time_to is not None:
            params['joinTimeTo'] = join_time_to
        body = QueryMeetingParticipantsWithEmailBody()
        if emails is not None:
            body.emails = emails
        url = self.ep('query')
        data = await super().post(url=url, params=params, data=body.model_dump_json())
        # TODO: this is wrong -> fix code generation
        return data["items"]

    async def participant_details(self, participant_id: str, host_email: str = None) -> Participant:
        """
        Get a meeting participant details of a live or post meeting. The participantId is required to identify the
        meeting and the participant.
        The authenticated user calling this API must either have an Administrator role with the
        meeting:admin_participants_read scope, or be the meeting host.

        :param participant_id: The unique identifier for the meeting and the participant.
        :type participant_id: str
        :param host_email: Email address for the meeting host. This parameter is only used if the user or application
            calling the API has the admin-level scopes, the admin may specify the email of a user in a site they manage
            and the API will return meeting participants of the meetings that are hosted by that user.
        :type host_email: str
        """
        params = {}
        if host_email is not None:
            params['hostEmail'] = host_email
        url = self.ep(f'{participant_id}')
        data = await super().get(url=url, params=params)
        return Participant.model_validate(data)

    async def update_participant(self, participant_id: str, muted: bool = None, admit: bool = None,
                           expel: bool = None) -> UpdateParticipantResponse:
        """
        To mute, un-mute, expel, or admit a participant in a live meeting. The participantId is required to identify
        the meeting and the participant.
        Notes:

        :param participant_id: The unique identifier for the meeting and the participant.
        :type participant_id: str
        :param muted: The value is true or false, and means to mute or unmute the audio of a participant.
        :type muted: bool
        :param admit: The value can be true or false. The value of true is to admit a participant to the meeting if the
            participant is in the lobby, No-Op if the participant is not in the lobby or when the value is set to
            false.
        :type admit: bool
        :param expel: The attribute is exclusive and its value can be true or false. The value of true means that the
            participant will be expelled from the meeting, the value of false means No-Op.
        :type expel: bool
        """
        body = UpdateParticipantBody()
        if muted is not None:
            body.muted = muted
        if admit is not None:
            body.admit = admit
        if expel is not None:
            body.expel = expel
        url = self.ep(f'{participant_id}')
        data = await super().put(url=url, data=body.model_dump_json())
        return UpdateParticipantResponse.model_validate(data)

    async def admit_participants(self, participant_ids: List[str] = None):
        """
        To admit participants into a live meeting in bulk.
        This API limits the maximum size of items in the request body to 100.
        Each participantId of items in the request body should have the same prefix of meetingId.

        :param participant_ids: The ID that identifies the meeting participant.
        :type participant_ids: List[str]
        """
        body = AdmitParticipantsBody()
        if participant_ids is not None:
            body.items = participant_ids
        url = self.ep('admit')
        await super().post(url=url, data=body.model_dump_json())
        return


class AsMeetingPreferencesApi(AsApiChild, base='meetingPreferences'):
    """
    This API manages a user's meeting preferences, including Personal Meeting Room settings, video and audio settings,
    meeting scheduling options, and site settings.
    Refer to the Meetings API Scopes section of Meetings Overview for scopes required for each API.
    """

    async def details(self, user_email: str = None, site_url: str = None) -> MeetingPreferenceDetails:
        """
        Retrieves meeting preferences for the authenticated user.

        :param user_email: Email address for the user. This parameter is only used if the user or application calling
            the API has the required admin-level scopes. If set, the admin may specify the email of a user in a site
            they manage and the API will return details of the meeting preferences for that user.
        :type user_email: str
        :param site_url: URL of the Webex site to query. For individual use, if siteUrl is not specified, the query
            will use the default site of the user. For admin use, if siteUrl is not specified, the query will use the
            default site for the admin's authorization token used to make the call. In the case where the user belongs
            to a site different than the admin’s default site, the admin can set the site to query using the siteUrl
            parameter. All available Webex sites and default site of a user can be retrieved from
            /meetingPreferences/sites.
        :type site_url: str
        """
        params = {}
        if user_email is not None:
            params['userEmail'] = user_email
        if site_url is not None:
            params['siteUrl'] = site_url
        url = self.ep()
        data = await super().get(url=url, params=params)
        return MeetingPreferenceDetails.model_validate(data)

    async def personal_meeting_room_options(self, user_email: str = None, site_url: str = None) -> PersonalMeetingRoomOptions:
        """
        Retrieves the Personal Meeting Room options for the authenticated user.

        :param user_email: Email address for the user. This parameter is only used if the user or application calling
            the API has the admin-level scopes. If set, the admin may specify the email of a user in a site they manage
            and the API will return details of the Personal Meeting Room options for that user.
        :type user_email: str
        :param site_url: URL of the Webex site to query. For individual use, if siteUrl is not specified, the query
            will use the default site of the user. For admin use, if siteUrl is not specified, the query will use the
            default site for the admin's authorization token used to make the call. In the case where the user belongs
            to a site different than the admin’s default site, the admin can set the site to query using the siteUrl
            parameter. All available Webex sites and default site of a user can be retrieved from
            /meetingPreferences/sites.
        :type site_url: str
        """
        params = {}
        if user_email is not None:
            params['userEmail'] = user_email
        if site_url is not None:
            params['siteUrl'] = site_url
        url = self.ep('personalMeetingRoom')
        data = await super().get(url=url, params=params)
        return PersonalMeetingRoomOptions.model_validate(data)

    async def update_personal_meeting_room_options(self, topic: str, host_pin: str, enabled_auto_lock: bool,
                                             auto_lock_minutes: int, enabled_notify_host: bool, support_co_host: bool,
                                             co_hosts: CoHost, user_email: str = None, site_url: str = None,
                                             support_anyone_as_co_host: bool = None,
                                             allow_first_user_to_be_co_host: bool = None,
                                             allow_authenticated_devices: bool = None) -> PersonalMeetingRoomOptions:
        """
        Update a single meeting

        :param topic: Personal Meeting Room topic to be updated.
        :type topic: str
        :param host_pin: Updated PIN for joining the room as host. The host PIN must be digits of a predefined length,
            e.g. 4 digits. It cannot contain sequential digits, such as 1234 or 4321, or repeated digits of the
            predefined length, such as 1111. The predefined length for host PIN can be viewed in user's My Personal
            Room page and it can only be changed by site administrator.
        :type host_pin: str
        :param enabled_auto_lock: Update for option to automatically lock the Personal Room a number of minutes after a
            meeting starts. When a room is locked, invitees cannot enter until the owner admits them. The period after
            which the meeting is locked is defined by autoLockMinutes.
        :type enabled_auto_lock: bool
        :param auto_lock_minutes: Updated number of minutes after which the Personal Room is locked if enabledAutoLock
            is enabled. Valid options are 0, 5, 10, 15 and 20.
        :type auto_lock_minutes: int
        :param enabled_notify_host: Update for flag to enable notifying the owner of a Personal Room when someone
            enters the Personal Room lobby while the owner is not in the room.
        :type enabled_notify_host: bool
        :param support_co_host: Update for flag allowing other invitees to host a meetingCoHost in the Personal Room
            without the owner.
        :type support_co_host: bool
        :param co_hosts: Updated array defining cohosts for the room if both supportAnyoneAsCoHost and
            allowFirstUserToBeCoHost are false
        :type co_hosts: CoHost
        :param user_email: Email address for the user. This parameter is only used if the user or application calling
            the API has the admin-level scopes. If set, the admin may specify the email of a user in a site they manage
            and the API will update Personal Meeting Room options for that user.
        :type user_email: str
        :param site_url: URL of the Webex site to query. For individual use, if siteUrl is not specified, the query
            will use the default site of the user. For admin use, if siteUrl is not specified, the query will use the
            default site for the admin's authorization token used to make the call. In the case where the user belongs
            to a site different than the admin’s default site, the admin can set the site to query using the siteUrl
            parameter. All available Webex sites and default site of a user can be retrieved from
            /meetingPreferences/sites.
        :type site_url: str
        :param support_anyone_as_co_host: Whether or not to allow any attendee with a host account on the target site
            to become a cohost when joining the Personal Room. The target site is user's preferred site.
        :type support_anyone_as_co_host: bool
        :param allow_first_user_to_be_co_host: Whether or not to allow the first attendee with a host account on the
            target site to become a cohost when joining the Personal Room. The target site is user's preferred site.
        :type allow_first_user_to_be_co_host: bool
        :param allow_authenticated_devices: Whether or not to allow authenticated video devices in the user's
            organization to start or join the meeting without a prompt.
        :type allow_authenticated_devices: bool
        """
        params = {}
        if user_email is not None:
            params['userEmail'] = user_email
        if site_url is not None:
            params['siteUrl'] = site_url
        body = UpdatePersonalMeetingRoomOptionsBody()
        if topic is not None:
            body.topic = topic
        if host_pin is not None:
            body.host_pin = host_pin
        if enabled_auto_lock is not None:
            body.enabled_auto_lock = enabled_auto_lock
        if auto_lock_minutes is not None:
            body.auto_lock_minutes = auto_lock_minutes
        if enabled_notify_host is not None:
            body.enabled_notify_host = enabled_notify_host
        if support_co_host is not None:
            body.support_co_host = support_co_host
        if co_hosts is not None:
            body.co_hosts = co_hosts
        if support_anyone_as_co_host is not None:
            body.support_anyone_as_co_host = support_anyone_as_co_host
        if allow_first_user_to_be_co_host is not None:
            body.allow_first_user_to_be_co_host = allow_first_user_to_be_co_host
        if allow_authenticated_devices is not None:
            body.allow_authenticated_devices = allow_authenticated_devices
        url = self.ep('personalMeetingRoom')
        data = await super().put(url=url, params=params, data=body.model_dump_json())
        return PersonalMeetingRoomOptions.model_validate(data)

    async def audio_options(self, user_email: str = None, site_url: str = None) -> Audio:
        """
        Retrieves audio options for the authenticated user.

        :param user_email: Email address for the user. This parameter is only used if the user or application calling
            the API has the admin-level scopes. If set, the admin may specify the email of a user in a site they manage
            and the API will return details of the audio options for that user.
        :type user_email: str
        :param site_url: URL of the Webex site to query. For individual use, if siteUrl is not specified, the query
            will use the default site of the user. For admin use, if siteUrl is not specified, the query will use the
            default site for the admin's authorization token used to make the call. In the case where the user belongs
            to a site different than the admin’s default site, the admin can set the site to query using the siteUrl
            parameter. All available Webex sites and default site of a user can be retrieved from
            /meetingPreferences/sites.
        :type site_url: str
        """
        params = {}
        if user_email is not None:
            params['userEmail'] = user_email
        if site_url is not None:
            params['siteUrl'] = site_url
        url = self.ep('audio')
        data = await super().get(url=url, params=params)
        return Audio.model_validate(data)

    async def update_audio_options(self, user_email: str = None, site_url: str = None,
                             default_audio_type: DefaultAudioType = None, other_teleconference_description: str = None,
                             enabled_global_call_in: bool = None, enabled_toll_free: bool = None,
                             enabled_auto_connection: bool = None, audio_pin: str = None,
                             office_number: OfficeNumber = None, mobile_number: OfficeNumber = None) -> Audio:
        """
        Updates audio options for the authenticated user.

        :param user_email: Email address for the user. This parameter is only used if the user or application calling
            the API has the admin-level scopes. If set, the admin may specify the email of a user in a site they manage
            and the API will update audio options for that user.
        :type user_email: str
        :param site_url: URL of the Webex site to query. For individual use, if siteUrl is not specified, the query
            will use the default site of the user. For admin use, if siteUrl is not specified, the query will use the
            default site for the admin's authorization token used to make the call. In the case where the user belongs
            to a site different than the admin’s default site, the admin can set the site to query using the siteUrl
            parameter. All available Webex sites and default site of a user can be retrieved
            from /meetingPreferences/sites.
        :type site_url: str
        :param default_audio_type: Default audio type. This attribute can be modified with the with the Update Audio
            Options API.
        :type default_audio_type: DefaultAudioType
        :param other_teleconference_description: Phone number and other information for the teleconference provider to
            be used, along with instructions for invitees. This attribute can be modified with the with the Update
            Audio Options API.
        :type other_teleconference_description: str
        :param enabled_global_call_in: Flag to enable/disable global call ins. Note: If the site does not support
            global call-ins, you cannot set this option. This attribute can be modified with the with the Update Audio
            Options API.
        :type enabled_global_call_in: bool
        :param enabled_toll_free: Flag to enable/disable call-ins from toll-free numbers. Note: If the site does not
            support calls from toll-free numbers, you cannot set this option. This attribute can be modified with the
            with the Update Audio Options API.
        :type enabled_toll_free: bool
        :param enabled_auto_connection: Flag to enable/disable automatically connecting to audio using a computer. The
            meeting host can enable/disable this option. When this option is set to true, the user is automatically
            connected to audio via a computer when they start or join a Webex Meetings meeting on a desktop. This
            attribute can be modified with the Update Audio Options API.
        :type enabled_auto_connection: bool
        :param audio_pin: PIN to provide a secondary level of authentication for calls where the host is using the
            phone and may need to invite additional invitees. It must be exactly 4 digits. It cannot contain sequential
            digits, such as 1234 or 4321, or repeat a digit 4 times, such as 1111. This attribute can be modified with
            the with the Update Audio Options API.
        :type audio_pin: str
        :param office_number: Office phone number. We recommend that phone numbers be specified to facilitate
            connecting via audio. This attribute can be modified with the with the Update Audio Options API.
        :type office_number: OfficeNumber
        :param mobile_number: Mobile phone number. We recommend that phone numbers be specified to facilitate
            connecting via audio. This attribute can be modified with the with the Update Audio Options API.
        :type mobile_number: OfficeNumber
        """
        params = {}
        if user_email is not None:
            params['userEmail'] = user_email
        if site_url is not None:
            params['siteUrl'] = site_url
        body = Audio()
        if default_audio_type is not None:
            body.default_audio_type = default_audio_type
        if other_teleconference_description is not None:
            body.other_teleconference_description = other_teleconference_description
        if enabled_global_call_in is not None:
            body.enabled_global_call_in = enabled_global_call_in
        if enabled_toll_free is not None:
            body.enabled_toll_free = enabled_toll_free
        if enabled_auto_connection is not None:
            body.enabled_auto_connection = enabled_auto_connection
        if audio_pin is not None:
            body.audio_pin = audio_pin
        if office_number is not None:
            body.office_number = office_number
        if mobile_number is not None:
            body.mobile_number = mobile_number
        url = self.ep('audio')
        data = await super().put(url=url, params=params, data=body.model_dump_json())
        return Audio.model_validate(data)

    async def video_options(self, user_email: str = None, site_url: str = None) -> list[VideoDevice]:
        """
        Retrieves video options for the authenticated user.

        :param user_email: Email address for the user. This parameter is only used if the user or application calling
            the API has the admin-level scopes. If set, the admin may specify the email of a user in a site they manage
            and the API will return details of the video options for that user.
        :type user_email: str
        :param site_url: URL of the Webex site to query. For individual use, if siteUrl is not specified, the query
            will use the default site of the user. For admin use, if siteUrl is not specified, the query will use the
            default site for the admin's authorization token used to make the call. In the case where the user belongs
            to a site different than the admin’s default site, the admin can set the site to query using the siteUrl
            parameter. All available Webex sites and default site of a user can be retrieved using Get Site List.
        :type site_url: str
        """
        params = {}
        if user_email is not None:
            params['userEmail'] = user_email
        if site_url is not None:
            params['siteUrl'] = site_url
        url = self.ep('video')
        data = await super().get(url=url, params=params)
        return TypeAdapter(list[VideoDevice]).validate_python(data["videoDevices"])

    async def update_video_options(self, video_devices: VideoDevice, user_email: str = None,
                             site_url: str = None) -> list[VideoDevice]:
        """
        Updates video options for the authenticated user.

        :param video_devices: Array of video devices. If the array is not empty, one device and no more than one
            devices must be set as default device.
        :type video_devices: VideoDevice
        :param user_email: Email address for the user. This parameter is only used if the user or application calling
            the API has the admin-level scopes. If set, the admin may specify the email of a user in a site they manage
            and the API will update video options for that user.
        :type user_email: str
        :param site_url: URL of the Webex site to query. For individual use, if siteUrl is not specified, the query
            will use the default site of the user. For admin use, if siteUrl is not specified, the query will use the
            default site for the admin's authorization token used to make the call. In the case where the user belongs
            to a site different than the admin’s default site, the admin can set the site to query using the siteUrl
            parameter. All available Webex sites and default site of a user can be retrieved from
            /meetingPreferences/sites.
        :type site_url: str
        """
        params = {}
        if user_email is not None:
            params['userEmail'] = user_email
        if site_url is not None:
            params['siteUrl'] = site_url
        body = VideoOptions()
        if video_devices is not None:
            body.video_devices = video_devices
        url = self.ep('video')
        data = await super().put(url=url, params=params, data=body.model_dump_json())
        return TypeAdapter(list[VideoDevice]).validate_python(data["videoDevices"])

    async def scheduling_options(self, user_email: str = None, site_url: str = None) -> SchedulingOptions:
        """
        Retrieves scheduling options for the authenticated user.

        :param user_email: Email address for the user. This parameter is only used if the user or application calling
            the API has the admin-level scopes. If set, the admin may specify the email of a user in a site they manage
            and the API will return details of the scheduling options for that user.
        :type user_email: str
        :param site_url: URL of the Webex site to query. For individual use, if siteUrl is not specified, the query
            will use the default site of the user. For admin use, if siteUrl is not specified, the query will use the
            default site for the admin's authorization token used to make the call. In the case where the user belongs
            to a site different than the admin’s default site, the admin can set the site to query using the siteUrl
            parameter. All available Webex sites and default site of a user can be retrieved from
            /meetingPreferences/sites.
        :type site_url: str
        """
        params = {}
        if user_email is not None:
            params['userEmail'] = user_email
        if site_url is not None:
            params['siteUrl'] = site_url
        url = self.ep('schedulingOptions')
        data = await super().get(url=url, params=params)
        return SchedulingOptions.model_validate(data)

    async def update_scheduling_options(self, user_email: str = None, site_url: str = None,
                                  enabled_join_before_host: bool = None, join_before_host_minutes: int = None,
                                  enabled_auto_share_recording: bool = None,
                                  enabled_webex_assistant_by_default: bool = None) -> SchedulingOptions:
        """
        Updates scheduling options for the authenticated user.

        :param user_email: Email address for the user. This parameter is only used if the user or application calling
            the API has the admin-level scopes. If set, the admin may specify the email of a user in a site they manage
            and the API will update scheduling options for that user.
        :type user_email: str
        :param site_url: URL of the Webex site to query. For individual use, if siteUrl is not specified, the query
            will use the default site of the user. For admin use, if siteUrl is not specified, the query will use the
            default site for the admin's authorization token used to make the call. In the case where the user belongs
            to a site different than the admin’s default site, the admin can set the site to query using the siteUrl
            parameter. All available Webex sites and default site of a user can be retrieved from
            /meetingPreferences/sites.
        :type site_url: str
        :param enabled_join_before_host: Flag to enable/disable Join Before Host. The period during which invitees can
            join before the start time is defined by autoLockMinutes. This attribute can be modified with the Update
            Scheduling Options API. Note: This feature is only effective if the site supports the Join Before Host
            feature. This attribute can be modified with the Update Scheduling Options API.
        :type enabled_join_before_host: bool
        :param join_before_host_minutes: Number of minutes before the start time that an invitee can join a meeting if
            enabledJoinBeforeHost is true. Valid options are 0, 5, 10 and 15. This attribute can be modified with the
            Update Scheduling Options API.
        :type join_before_host_minutes: int
        :param enabled_auto_share_recording: Flag to enable/disable the automatic sharing of the meeting recording with
            invitees when it is available. This attribute can be modified with the Update Scheduling Options API.
        :type enabled_auto_share_recording: bool
        :param enabled_webex_assistant_by_default: Flag to automatically enable Webex Assistant whenever you start a
            meeting. This attribute can be modified with the Update Scheduling Options API.
        :type enabled_webex_assistant_by_default: bool
        """
        params = {}
        if user_email is not None:
            params['userEmail'] = user_email
        if site_url is not None:
            params['siteUrl'] = site_url
        body = SchedulingOptions()
        if enabled_join_before_host is not None:
            body.enabled_join_before_host = enabled_join_before_host
        if join_before_host_minutes is not None:
            body.join_before_host_minutes = join_before_host_minutes
        if enabled_auto_share_recording is not None:
            body.enabled_auto_share_recording = enabled_auto_share_recording
        if enabled_webex_assistant_by_default is not None:
            body.enabled_webex_assistant_by_default = enabled_webex_assistant_by_default
        url = self.ep('schedulingOptions')
        data = await super().put(url=url, params=params, data=body.model_dump_json())
        return SchedulingOptions.model_validate(data)

    async def site_list(self, user_email: str = None) -> list[MeetingsSite]:
        """
        Retrieves the list of Webex sites that the authenticated user is set up to use.

        :param user_email: Email address for the user. This parameter is only used if the user or application calling
            the API has the admin-level scopes. If set, the admin may specify the email of a user and the API will
            return the list of Webex sites for that user.
        :type user_email: str
        """
        params = {}
        if user_email is not None:
            params['userEmail'] = user_email
        url = self.ep('sites')
        data = await super().get(url=url, params=params)
        return TypeAdapter(list[MeetingsSite]).validate_python(data["sites"])

    async def update_default_site(self, default_site: bool, site_url: str, user_email: str = None) -> MeetingsSite:
        """
        Updates the default site for the authenticated user.

        :param default_site: Whether or not to change user's default site. Note: defaultSite should be set to true for
            the user's single default site
        :type default_site: bool
        :param site_url: Access URL for the site.
        :type site_url: str
        :param user_email: Email address for the user. This parameter is only used if the user or application calling
            the API has the admin-level scopes. If set, the admin may specify the email of a user in a site they manage
            and the API will update default site for that user.
        :type user_email: str
        """
        params = dict()
        params['defaultSite'] = str(default_site).lower()
        if user_email is not None:
            params['userEmail'] = user_email
        body = UpdateDefaultSiteBody()
        if site_url is not None:
            body.site_url = site_url
        url = self.ep('sites')
        data = await super().put(url=url, params=params, data=body.model_dump_json())
        return MeetingsSite.model_validate(data)


class AsMeetingQandAApi(AsApiChild, base='meetings/q_and_a'):
    """
    During a Question and Answer (Q&A) session, attendees can pose questions to hosts, co-hosts, and presenters, who
    can answer and moderate those questions. You use the Meeting Q&A API to retrieve the questions and the answers in a
    meeting.
    Currently, these APIs are available to users with one of the meeting host, admin or Compliance Officer roles.
    The features and APIs described here are available upon-request and is not enabled by default. If would like this
    feature enabled for your organization please contact the Webex Developer Support team at devsupport@webex.com.
    """

    def list_gen(self, meeting_id: str, **params) -> AsyncGenerator[QAObject, None, None]:
        """
        Lists questions and answers from a meeting, when ready.
        Notes:

        :param meeting_id: A unique identifier for the meeting instance which the Q&A belongs to.
        :type meeting_id: str

        documentation: https://developer.webex.com/docs/api/v1/meeting-q-and-a/list-meeting-q-and-a
        """
        params['meetingId'] = meeting_id
        url = self.ep()
        return self.session.follow_pagination(url=url, model=QAObject, params=params)

    async def list(self, meeting_id: str, **params) -> List[QAObject]:
        """
        Lists questions and answers from a meeting, when ready.
        Notes:

        :param meeting_id: A unique identifier for the meeting instance which the Q&A belongs to.
        :type meeting_id: str

        documentation: https://developer.webex.com/docs/api/v1/meeting-q-and-a/list-meeting-q-and-a
        """
        params['meetingId'] = meeting_id
        url = self.ep()
        return [o async for o in self.session.follow_pagination(url=url, model=QAObject, params=params)]

    def list_answers_gen(self, question_id: str, meeting_id: str,
                     **params) -> AsyncGenerator[AnswerObject, None, None]:
        """
        Lists the answers to a specific question asked in a meeting.

        :param question_id: The ID of a question.
        :type question_id: str
        :param meeting_id: A unique identifier for the meeting instance which the Q&A belongs to.
        :type meeting_id: str

        documentation: https://developer.webex.com/docs/api/v1/meeting-q-and-a/list-answers-of-a-question
        """
        params['meetingId'] = meeting_id
        url = self.ep(f'{question_id}/answers')
        return self.session.follow_pagination(url=url, model=AnswerObject, params=params)

    async def list_answers(self, question_id: str, meeting_id: str,
                     **params) -> List[AnswerObject]:
        """
        Lists the answers to a specific question asked in a meeting.

        :param question_id: The ID of a question.
        :type question_id: str
        :param meeting_id: A unique identifier for the meeting instance which the Q&A belongs to.
        :type meeting_id: str

        documentation: https://developer.webex.com/docs/api/v1/meeting-q-and-a/list-answers-of-a-question
        """
        params['meetingId'] = meeting_id
        url = self.ep(f'{question_id}/answers')
        return [o async for o in self.session.follow_pagination(url=url, model=AnswerObject, params=params)]


class AsMeetingQualitiesApi(AsApiChild, base=''):
    """
    To retrieve quality information, you must use an administrator token with the analytics:read_all scope. The
    authenticated user must be a read-only or full administrator of the organization to which the meeting belongs and
    must not be an external administrator.
    To use this endpoint, the org needs to be licensed for the Webex Pro Pack.
    For CI-Native site, no additional settings are required.
    For CI-linked site, the admin must also be set as the Full/ReadOnly Site Admin of the site.
    A minimum Webex and Teams client version is required. For details, see Troubleshooting Help Doc.
    Quality information is available 10 minutes after a meeting has started and may be retrieved for up to 7 days.
    A rate limit of 1 API call every 5 minutes for the same meeting instance ID applies.
    """

    def meeting_qualities_gen(self, meeting_id: str, offset: int = None,
                          **params) -> AsyncGenerator[MediaSessionQuality, None, None]:
        """
        Get quality data for a meeting, by meetingId. Only organization administrators can retrieve meeting quality
        data.

        :param meeting_id: Unique identifier for the specific meeting instance. Note: The meetingId can be obtained via
            the Meeting List API when meetingType=meeting. The id attribute in the Meeting List Response is what is
            needed, for example, e5dba9613a9d455aa49f6ffdafb6e7db_I_191395283063545470.
        :type meeting_id: str
        :param offset: Offset from the first result that you want to fetch.
        :type offset: int

        documentation: https://developer.webex.com/docs/api/v1/meeting-qualities/get-meeting-qualities
        """
        params['meetingId'] = meeting_id
        if offset is not None:
            params['offset'] = offset
        url = self.ep('https://analytics.webexapis.com/v1/meeting/qualities')
        return self.session.follow_pagination(url=url, model=MediaSessionQuality, params=params)

    async def meeting_qualities(self, meeting_id: str, offset: int = None,
                          **params) -> List[MediaSessionQuality]:
        """
        Get quality data for a meeting, by meetingId. Only organization administrators can retrieve meeting quality
        data.

        :param meeting_id: Unique identifier for the specific meeting instance. Note: The meetingId can be obtained via
            the Meeting List API when meetingType=meeting. The id attribute in the Meeting List Response is what is
            needed, for example, e5dba9613a9d455aa49f6ffdafb6e7db_I_191395283063545470.
        :type meeting_id: str
        :param offset: Offset from the first result that you want to fetch.
        :type offset: int

        documentation: https://developer.webex.com/docs/api/v1/meeting-qualities/get-meeting-qualities
        """
        params['meetingId'] = meeting_id
        if offset is not None:
            params['offset'] = offset
        url = self.ep('https://analytics.webexapis.com/v1/meeting/qualities')
        return [o async for o in self.session.follow_pagination(url=url, model=MediaSessionQuality, params=params)]


class AsMeetingTranscriptsApi(AsApiChild, base=''):
    """
    Not supported for Webex for Government (FedRAMP)
    A meeting transcript is the automatic transcription of a meeting's recordings by our industry-leading
    speech-to-text engine to capture of what was discussed and decided during the meeting, in text form.
    A transcript snippet is a short text snippet from a meeting transcript which was spoken by a particular participant
    in the meeting. A meeting transcript consists of many snippets.
    This API manages meeting transcripts and snippets. You can use the Transcript API to list meeting transcripts,
    list, get and update transcript snippets. Transcripts may be retrieved via download link defined by vttDownloadLink
    or txtDownloadlink in the response body.
    Refer to the Meetings API Scopes section of Meetings Overview for scopes required for each API.
    NOTE:
    """

    def list_gen(self, meeting_id: str = None, host_email: str = None, site_url: str = None, from_: str = None,
             to_: str = None, **params) -> AsyncGenerator[Transcript, None, None]:
        """
        Lists available transcripts of an ended meeting instance.
        Use this operation to list transcripts of an ended meeting instance when they are ready. Please note that only
        meeting instances in state ended are supported for meetingId. Meeting series, scheduled meetings and
        in-progress meeting instances are not supported.

        :param meeting_id: Unique identifier for the meeting instance to which the transcript belongs. Please note that
            currently the meeting ID of a scheduled personal room meeting is not supported for this API. If meetingId
            is not specified, the operation returns an array of transcripts for all meetings of the current user.
        :type meeting_id: str
        :param host_email: Email address for the meeting host. This parameter is only used if the user or application
            calling the API has the admin-level scopes. If set, the admin may specify the email of a user in a site
            they manage and the API will return details for a meeting that is hosted by that user. If meetingId is not
            specified, it can not support hostEmail.
        :type host_email: str
        :param site_url: URL of the Webex site from which the API lists transcripts. If not specified, the API lists
            transcripts from user's preferred site. All available Webex sites and the preferred site of the user can be
            retrieved by the Get Site List API.
        :type site_url: str
        :param from_: Starting date and time (inclusive) for transcripts to return, in any ISO 8601 compliant format.
            from cannot be after to.
        :type from_: str
        :param to_: Ending date and time (exclusive) for List transcripts to return, in any ISO 8601 compliant format.
            to cannot be before from.
        :type to_: str

        documentation: https://developer.webex.com/docs/api/v1/meeting-transcripts/list-meeting-transcripts
        """
        if meeting_id is not None:
            params['meetingId'] = meeting_id
        if host_email is not None:
            params['hostEmail'] = host_email
        if site_url is not None:
            params['siteUrl'] = site_url
        if from_ is not None:
            params['from'] = from_
        if to_ is not None:
            params['to'] = to_
        url = self.ep('meetingTranscripts')
        return self.session.follow_pagination(url=url, model=Transcript, params=params)

    async def list(self, meeting_id: str = None, host_email: str = None, site_url: str = None, from_: str = None,
             to_: str = None, **params) -> List[Transcript]:
        """
        Lists available transcripts of an ended meeting instance.
        Use this operation to list transcripts of an ended meeting instance when they are ready. Please note that only
        meeting instances in state ended are supported for meetingId. Meeting series, scheduled meetings and
        in-progress meeting instances are not supported.

        :param meeting_id: Unique identifier for the meeting instance to which the transcript belongs. Please note that
            currently the meeting ID of a scheduled personal room meeting is not supported for this API. If meetingId
            is not specified, the operation returns an array of transcripts for all meetings of the current user.
        :type meeting_id: str
        :param host_email: Email address for the meeting host. This parameter is only used if the user or application
            calling the API has the admin-level scopes. If set, the admin may specify the email of a user in a site
            they manage and the API will return details for a meeting that is hosted by that user. If meetingId is not
            specified, it can not support hostEmail.
        :type host_email: str
        :param site_url: URL of the Webex site from which the API lists transcripts. If not specified, the API lists
            transcripts from user's preferred site. All available Webex sites and the preferred site of the user can be
            retrieved by the Get Site List API.
        :type site_url: str
        :param from_: Starting date and time (inclusive) for transcripts to return, in any ISO 8601 compliant format.
            from cannot be after to.
        :type from_: str
        :param to_: Ending date and time (exclusive) for List transcripts to return, in any ISO 8601 compliant format.
            to cannot be before from.
        :type to_: str

        documentation: https://developer.webex.com/docs/api/v1/meeting-transcripts/list-meeting-transcripts
        """
        if meeting_id is not None:
            params['meetingId'] = meeting_id
        if host_email is not None:
            params['hostEmail'] = host_email
        if site_url is not None:
            params['siteUrl'] = site_url
        if from_ is not None:
            params['from'] = from_
        if to_ is not None:
            params['to'] = to_
        url = self.ep('meetingTranscripts')
        return [o async for o in self.session.follow_pagination(url=url, model=Transcript, params=params)]

    def list_compliance_officer_gen(self, site_url: str, from_: str = None, to_: str = None,
                                **params) -> AsyncGenerator[Transcript, None, None]:
        """
        Lists available or deleted transcripts of an ended meeting instance for a specific site.
        The returned list is sorted in descending order by the date and time that the transcript was created.

        :param site_url: URL of the Webex site from which the API lists transcripts.
        :type site_url: str
        :param from_: Starting date and time (inclusive) for transcripts to return, in any ISO 8601 compliant format.
            from cannot be after to.
        :type from_: str
        :param to_: Ending date and time (exclusive) for List transcripts to return, in any ISO 8601 compliant format.
            to cannot be before from.
        :type to_: str

        documentation: https://developer.webex.com/docs/api/v1/meeting-transcripts/list-meeting-transcripts-for
        -compliance-officer
        """
        params['siteUrl'] = site_url
        if from_ is not None:
            params['from'] = from_
        if to_ is not None:
            params['to'] = to_
        url = self.ep('admin/meetingTranscripts')
        return self.session.follow_pagination(url=url, model=Transcript, params=params)

    async def list_compliance_officer(self, site_url: str, from_: str = None, to_: str = None,
                                **params) -> List[Transcript]:
        """
        Lists available or deleted transcripts of an ended meeting instance for a specific site.
        The returned list is sorted in descending order by the date and time that the transcript was created.

        :param site_url: URL of the Webex site from which the API lists transcripts.
        :type site_url: str
        :param from_: Starting date and time (inclusive) for transcripts to return, in any ISO 8601 compliant format.
            from cannot be after to.
        :type from_: str
        :param to_: Ending date and time (exclusive) for List transcripts to return, in any ISO 8601 compliant format.
            to cannot be before from.
        :type to_: str

        documentation: https://developer.webex.com/docs/api/v1/meeting-transcripts/list-meeting-transcripts-for
        -compliance-officer
        """
        params['siteUrl'] = site_url
        if from_ is not None:
            params['from'] = from_
        if to_ is not None:
            params['to'] = to_
        url = self.ep('admin/meetingTranscripts')
        return [o async for o in self.session.follow_pagination(url=url, model=Transcript, params=params)]

    async def download(self, transcript_id: str, format: str = None, host_email: str = None):
        """
        Download a meeting transcript from the meeting transcript specified by transcriptId.

        :param transcript_id: Unique identifier for the meeting transcript.
        :type transcript_id: str
        :param format: Format for the downloaded meeting transcript. Possible values: vtt, txt
        :type format: str
        :param host_email: Email address for the meeting host. This parameter is only used if the user or application
            calling the API has the admin-level scopes. If set, the admin may specify the email of a user in a site
            they manage and the API will return details for a meeting that is hosted by that user.
        :type host_email: str

        documentation: https://developer.webex.com/docs/api/v1/meeting-transcripts/download-a-meeting-transcript
        """
        params = {}
        if format is not None:
            params['format'] = format
        if host_email is not None:
            params['hostEmail'] = host_email
        url = self.ep(f'meetingTranscripts/{transcript_id}/download')
        await super().get(url=url, params=params)
        # TODO: fix. Find out what the actual return type is

    def list_snippets_gen(self, transcript_id: str, **params) -> AsyncGenerator[TranscriptSnippet, None, None]:
        """
        Lists snippets of a meeting transcript specified by transcriptId.
        Use this operation to list snippets of a meeting transcript when they are ready.

        :param transcript_id: Unique identifier for the meeting transcript to which the snippets belong.
        :type transcript_id: str

        documentation: https://developer.webex.com/docs/api/v1/meeting-transcripts/list-snippets-of-a-meeting-transcript
        """
        url = self.ep(f'meetingTranscripts/{transcript_id}/snippets')
        return self.session.follow_pagination(url=url, model=TranscriptSnippet, params=params)

    async def list_snippets(self, transcript_id: str, **params) -> List[TranscriptSnippet]:
        """
        Lists snippets of a meeting transcript specified by transcriptId.
        Use this operation to list snippets of a meeting transcript when they are ready.

        :param transcript_id: Unique identifier for the meeting transcript to which the snippets belong.
        :type transcript_id: str

        documentation: https://developer.webex.com/docs/api/v1/meeting-transcripts/list-snippets-of-a-meeting-transcript
        """
        url = self.ep(f'meetingTranscripts/{transcript_id}/snippets')
        return [o async for o in self.session.follow_pagination(url=url, model=TranscriptSnippet, params=params)]

    async def snippet_detail(self, transcript_id: str, snippet_id: str) -> TranscriptSnippet:
        """
        Retrieves details for a transcript snippet specified by snippetId from the meeting transcript specified by
        transcriptId.

        :param transcript_id: Unique identifier for the meeting transcript to which the requested snippet belongs.
        :type transcript_id: str
        :param snippet_id: Unique identifier for the snippet being requested.
        :type snippet_id: str

        documentation: https://developer.webex.com/docs/api/v1/meeting-transcripts/get-a-transcript-snippet
        """
        url = self.ep(f'meetingTranscripts/{transcript_id}/snippets/{snippet_id}')
        data = await super().get(url=url)
        return TranscriptSnippet.model_validate(data)

    async def update_snippet(self, transcript_id: str, snippet_id: str, text: str, reason: str = None) -> TranscriptSnippet:
        """
        Updates details for a transcript snippet specified by snippetId from the meeting transcript specified by
        transcriptId.

        :param transcript_id: Unique identifier for the meeting transcript to which the snippet to be updated belongs.
        :type transcript_id: str
        :param snippet_id: Unique identifier for the snippet being updated.
        :type snippet_id: str
        :param text: Text for the snippet.
        :type text: str
        :param reason: Reason for snippet update; only required for Compliance Officers.
        :type reason: str

        documentation: https://developer.webex.com/docs/api/v1/meeting-transcripts/update-a-transcript-snippet
        """
        body = UpdateTranscriptSnippetBody()
        if text is not None:
            body.text = text
        if reason is not None:
            body.reason = reason
        url = self.ep(f'meetingTranscripts/{transcript_id}/snippets/{snippet_id}')
        data = await super().put(url=url, data=body.model_dump_json())
        return TranscriptSnippet.model_validate(data)

    async def delete(self, transcript_id: str, reason: str = None, comment: str = None):
        """
        Removes a transcript with a specified transcript ID. The deleted transcript cannot be recovered. If a
        Compliance Officer deletes another user's transcript, the transcript will be inaccessible to regular users
        (host, attendees), but will be still available to the Compliance Officer.

        :param transcript_id: Unique identifier for the meeting transcript.
        :type transcript_id: str
        :param reason: Reason for deleting a transcript. Only required when a Compliance Officer is operating on
            another user's transcript.
        :type reason: str
        :param comment: Explanation for deleting a transcript. The comment can be a maximum of 255 characters long.
        :type comment: str

        documentation: https://developer.webex.com/docs/api/v1/meeting-transcripts/delete-a-transcript
        """
        body = DeleteTranscriptBody()
        if reason is not None:
            body.reason = reason
        if comment is not None:
            body.comment = comment
        url = self.ep(f'meetingTranscripts/{transcript_id}')
        await super().delete(url=url, data=body.model_dump_json())
        return


class AsRecordingsApi(AsApiChild, base=''):
    """
    Recordings

    Recordings are meeting content captured in a meeting or files uploaded via the upload page for your Webex site.

    This API manages recordings. Recordings may be retrieved via download or playback links defined by `downloadUrl` or
    `playbackUrl` in the response body.

    When the recording function is paused in the meeting the recording will not contain the pause. If the recording
    function is stopped and restarted in the meeting, several recordings will be created. These recordings will be
    consolidated and available all at once.

    Refer to the `Meetings API Scopes
    <https://developer.webex.com/docs/meetings#user-level-authentication-and-scopes>`_ for the specific scopes
    required for each API.
    """

    def list_recordings_gen(self, from_: Union[str, datetime] = None, to_: Union[str, datetime] = None,
                        meeting_id: str = None, host_email: str = None, site_url: str = None,
                        integration_tag: str = None, topic: str = None, format_: RecordingFormat = None,
                        service_type: RecordingServiceType = None, status: RecordingStatus = None,
                        **params) -> AsyncGenerator[Recording, None, None]:
        """
        List Recordings

        Lists recordings. You can specify a date range, a parent meeting ID, and the maximum number of recordings to
        return.

        Only recordings of meetings hosted by or shared with the authenticated user will be listed.

        The list returned is sorted in descending order by the date and time that the recordings were created.

        * If `meetingId` is specified, only recordings associated with the specified meeting will be listed.
          NOTE: when `meetingId` is specified, parameter of `siteUrl` will be ignored.

        * If `siteUrl` is specified, recordings of the specified site will be listed; otherwise, the API lists
          recordings of all the user's sites. All available Webex sites and preferred site of the user can be
          retrieved by
          `Get Site List <https://developer.webex.com/docs/api/v1/meeting-preferences/get-site-list>`_ API.

        :param from_: Starting date and time (inclusive) for recordings to return, in any `ISO 8601
            <https://en.wikipedia.org/wiki/ISO_8601>`_ compliant format.
            `from` cannot be after `to`.
        :type from_: Union[str, datetime]
        :param to_: Ending date and time (exclusive) for List recordings to return, in any `ISO 8601
            <https://en.wikipedia.org/wiki/ISO_8601>`_ compliant format.
            `to` cannot be before `from`.
        :type to_: Union[str, datetime]
        :param meeting_id: Unique identifier for the parent meeting series, scheduled meeting, or meeting instance for
            which recordings are being requested. If a meeting series ID is specified, the operation returns an array
            of recordings for the specified meeting series. If a scheduled meeting ID is specified, the operation
            returns an array of recordings for the specified scheduled meeting. If a meeting instance ID is specified,
            the operation returns an array of recordings for the specified meeting instance. If no ID is specified,
            the operation returns an array of recordings for all meetings of the current user. When `meetingId` is
            specified, the `siteUrl` parameter is ignored.
        :type meeting_id: str
        :param host_email: Email address for the meeting host. This parameter is only used if the user or application
            calling the API has the required `admin-level meeting scopes
            <https://developer.webex.com/docs/meetings#adminorganization-level-authentication-and-scopes>`_. If set,
            the admin may specify the email of a
            user in a site they manage and the API will return recordings of that user.
        :type host_email: str
        :param site_url: URL of the Webex site from which the API lists recordings. If not specified, the API lists
            recordings from all of a user's sites. All available Webex sites and the preferred site of the user can be
            retrieved by the `Get Site List
            <https://developer.webex.com/docs/api/v1/meeting-preferences/get-site-list>`_ API.
        :type site_url: str
        :param integration_tag: External key of the parent meeting created by an integration application. This
            parameter is used by the integration application to query recordings by a key in its own domain, such as a
            Zendesk ticket ID, a Jira ID, a Salesforce Opportunity ID, etc.
        :type integration_tag: str
        :param topic: Recording's topic. If specified, the API filters recordings by topic in a case-insensitive
            manner.
        :type topic: str
        :param format_: Recording's file format. If specified, the API filters recordings by format.
        :type format_: RecordingsFormat
        :param service_type: The service type for recordings. If this item is specified, the API filters recordings by
            service-type.
        :type service_type: RecordingServiceType
        :param status: Recording's status. If not specified or `available`, retrieves recordings that are available.
            Otherwise, if specified as `deleted`, retrieves recordings that have been moved into the recycle bin. The
            `purged` status only applies if the user calling the API is a Compliance Officer and `meetingId` is
            specified.
        :type status: ListRecordingsStatus
        :return: Generator yielding :class:`RecordingObject` instances
        """
        if from_ is not None:
            if isinstance(from_, str):
                from_ = isoparse(from_)
            from_ = dt_iso_str(from_)
            params['from'] = from_
        if to_ is not None:
            if isinstance(to_, str):
                to_ = isoparse(to_)
            to_ = dt_iso_str(to_)
            params['to'] = to_
        if meeting_id is not None:
            params['meetingId'] = meeting_id
        if host_email is not None:
            params['hostEmail'] = host_email
        if site_url is not None:
            params['siteUrl'] = site_url
        if integration_tag is not None:
            params['integrationTag'] = integration_tag
        if topic is not None:
            params['topic'] = topic
        if format_ is not None:
            params['format'] = enum_str(format_)
        if service_type is not None:
            params['serviceType'] = enum_str(service_type)
        if status is not None:
            params['status'] = enum_str(status)
        url = self.ep('recordings')
        return self.session.follow_pagination(url=url, model=Recording, item_key='items', params=params)

    async def list_recordings(self, from_: Union[str, datetime] = None, to_: Union[str, datetime] = None,
                        meeting_id: str = None, host_email: str = None, site_url: str = None,
                        integration_tag: str = None, topic: str = None, format_: RecordingFormat = None,
                        service_type: RecordingServiceType = None, status: RecordingStatus = None,
                        **params) -> List[Recording]:
        """
        List Recordings

        Lists recordings. You can specify a date range, a parent meeting ID, and the maximum number of recordings to
        return.

        Only recordings of meetings hosted by or shared with the authenticated user will be listed.

        The list returned is sorted in descending order by the date and time that the recordings were created.

        * If `meetingId` is specified, only recordings associated with the specified meeting will be listed.
          NOTE: when `meetingId` is specified, parameter of `siteUrl` will be ignored.

        * If `siteUrl` is specified, recordings of the specified site will be listed; otherwise, the API lists
          recordings of all the user's sites. All available Webex sites and preferred site of the user can be
          retrieved by
          `Get Site List <https://developer.webex.com/docs/api/v1/meeting-preferences/get-site-list>`_ API.

        :param from_: Starting date and time (inclusive) for recordings to return, in any `ISO 8601
            <https://en.wikipedia.org/wiki/ISO_8601>`_ compliant format.
            `from` cannot be after `to`.
        :type from_: Union[str, datetime]
        :param to_: Ending date and time (exclusive) for List recordings to return, in any `ISO 8601
            <https://en.wikipedia.org/wiki/ISO_8601>`_ compliant format.
            `to` cannot be before `from`.
        :type to_: Union[str, datetime]
        :param meeting_id: Unique identifier for the parent meeting series, scheduled meeting, or meeting instance for
            which recordings are being requested. If a meeting series ID is specified, the operation returns an array
            of recordings for the specified meeting series. If a scheduled meeting ID is specified, the operation
            returns an array of recordings for the specified scheduled meeting. If a meeting instance ID is specified,
            the operation returns an array of recordings for the specified meeting instance. If no ID is specified,
            the operation returns an array of recordings for all meetings of the current user. When `meetingId` is
            specified, the `siteUrl` parameter is ignored.
        :type meeting_id: str
        :param host_email: Email address for the meeting host. This parameter is only used if the user or application
            calling the API has the required `admin-level meeting scopes
            <https://developer.webex.com/docs/meetings#adminorganization-level-authentication-and-scopes>`_. If set,
            the admin may specify the email of a
            user in a site they manage and the API will return recordings of that user.
        :type host_email: str
        :param site_url: URL of the Webex site from which the API lists recordings. If not specified, the API lists
            recordings from all of a user's sites. All available Webex sites and the preferred site of the user can be
            retrieved by the `Get Site List
            <https://developer.webex.com/docs/api/v1/meeting-preferences/get-site-list>`_ API.
        :type site_url: str
        :param integration_tag: External key of the parent meeting created by an integration application. This
            parameter is used by the integration application to query recordings by a key in its own domain, such as a
            Zendesk ticket ID, a Jira ID, a Salesforce Opportunity ID, etc.
        :type integration_tag: str
        :param topic: Recording's topic. If specified, the API filters recordings by topic in a case-insensitive
            manner.
        :type topic: str
        :param format_: Recording's file format. If specified, the API filters recordings by format.
        :type format_: RecordingsFormat
        :param service_type: The service type for recordings. If this item is specified, the API filters recordings by
            service-type.
        :type service_type: RecordingServiceType
        :param status: Recording's status. If not specified or `available`, retrieves recordings that are available.
            Otherwise, if specified as `deleted`, retrieves recordings that have been moved into the recycle bin. The
            `purged` status only applies if the user calling the API is a Compliance Officer and `meetingId` is
            specified.
        :type status: ListRecordingsStatus
        :return: Generator yielding :class:`RecordingObject` instances
        """
        if from_ is not None:
            if isinstance(from_, str):
                from_ = isoparse(from_)
            from_ = dt_iso_str(from_)
            params['from'] = from_
        if to_ is not None:
            if isinstance(to_, str):
                to_ = isoparse(to_)
            to_ = dt_iso_str(to_)
            params['to'] = to_
        if meeting_id is not None:
            params['meetingId'] = meeting_id
        if host_email is not None:
            params['hostEmail'] = host_email
        if site_url is not None:
            params['siteUrl'] = site_url
        if integration_tag is not None:
            params['integrationTag'] = integration_tag
        if topic is not None:
            params['topic'] = topic
        if format_ is not None:
            params['format'] = enum_str(format_)
        if service_type is not None:
            params['serviceType'] = enum_str(service_type)
        if status is not None:
            params['status'] = enum_str(status)
        url = self.ep('recordings')
        return [o async for o in self.session.follow_pagination(url=url, model=Recording, item_key='items', params=params)]

    def list_recordings_for_an_admin_or_compliance_officer_gen(self, from_: Union[str, datetime] = None,
                                                           to_: Union[str, datetime] = None, meeting_id: str = None,
                                                           site_url: str = None, integration_tag: str = None,
                                                           topic: str = None, format_: RecordingFormat = None,
                                                           service_type: RecordingServiceType = None,
                                                           status: RecordingStatus = None,
                                                           **params) -> AsyncGenerator[Recording, None, None]:
        """
        List Recordings For an Admin or Compliance Officer

        List recordings for an admin or compliance officer. You can specify a date range, a parent meeting ID, and the
        maximum number of recordings to return.

        The list returned is sorted in descending order by the date and time that the recordings were created.

        Long result sets are split into `pages <https://developer.webex.com/docs/basics#pagination>`_.

        * If `meetingId` is specified, only recordings associated with the specified meeting will be listed. Please
          note that when `meetingId` is specified, parameter of `siteUrl` will be ignored.

        * If `siteUrl` is specified, all the recordings on the specified site are listed; otherwise, all the
          recordings on the admin user's or compliance officer's preferred site are listed. All the available
          Webex sites and the admin user's or compliance officer's preferred site can be retrieved by the
          `Get Site List <https://developer.webex.com/docs/api/v1/meeting-preferences/get-site-list>`_ API.

        :param from_: Starting date and time (inclusive) for recordings to return, in any `ISO 8601
            <https://en.wikipedia.org/wiki/ISO_8601>`_ compliant format.
            `from` cannot be after `to`. The interval between `from` and `to` must be within 30 days.
        :type from_: Union[str, datetime]
        :param to_: Ending date and time (exclusive) for List recordings to return, in any `ISO 8601
            <https://en.wikipedia.org/wiki/ISO_8601>`_ compliant format.
            `to` cannot be before `from`. The interval between `from` and `to` must be within 30 days.
        :type to_: Union[str, datetime]
        :param meeting_id: Unique identifier for the parent meeting series, scheduled meeting, or meeting instance for
            which recordings are being requested. If a meeting series ID is specified, the operation returns an array
            of recordings for the specified meeting series. If a scheduled meeting ID is specified, the operation
            returns an array of recordings for the specified scheduled meeting. If a meeting instance ID is specified,
            the operation returns an array of recordings for the specified meeting instance. If not specified, the
            operation returns an array of recordings for all the current user's meetings. When `meetingId` is
            specified, the `siteUrl` parameter is ignored.
        :type meeting_id: str
        :param site_url: URL of the Webex site which the API lists recordings from. If not specified, the API lists
            recordings from user's preferred site. All available Webex sites and preferred site of the user can be
            retrieved by `Get Site List
            <https://developer.webex.com/docs/api/v1/meeting-preferences/get-site-list>`_ API.
        :type site_url: str
        :param integration_tag: External key of the parent meeting created by an integration application. This
            parameter is used by the integration application to query recordings by a key in its own domain such as a
            Zendesk ticket ID, a Jira ID, a Salesforce Opportunity ID, etc.
        :type integration_tag: str
        :param topic: Recording topic. If specified, the API filters recordings by topic in a case-insensitive manner.
        :type topic: str
        :param format_: Recording's file format. If specified, the API filters recordings by format.
        :type format_: RecordingsFormat
        :param service_type: The service type for recordings. If specified, the API filters recordings by service type.
        :type service_type: RecordingServiceType
        :param status: Recording's status. If not specified or `available`, retrieves recordings that are available. If
            specified as `deleted`, retrieves recordings that have been moved to the recycle bin. Otherwise, if
            specified as `purged`, retrieves recordings that have been purged from the recycle bin.
        :type status: ListRecordingsStatus
        :return: Generator yielding :class:`RecordingObjectForAdminAndCO` instances
        """
        if from_ is not None:
            if isinstance(from_, str):
                from_ = isoparse(from_)
            from_ = dt_iso_str(from_)
            params['from'] = from_
        if to_ is not None:
            if isinstance(to_, str):
                to_ = isoparse(to_)
            to_ = dt_iso_str(to_)
            params['to'] = to_
        if meeting_id is not None:
            params['meetingId'] = meeting_id
        if site_url is not None:
            params['siteUrl'] = site_url
        if integration_tag is not None:
            params['integrationTag'] = integration_tag
        if topic is not None:
            params['topic'] = topic
        if format_ is not None:
            params['format'] = enum_str(format_)
        if service_type is not None:
            params['serviceType'] = enum_str(service_type)
        if status is not None:
            params['status'] = enum_str(status)
        url = self.ep('admin/recordings')
        return self.session.follow_pagination(url=url, model=Recording, item_key='items',
                                              params=params)

    async def list_recordings_for_an_admin_or_compliance_officer(self, from_: Union[str, datetime] = None,
                                                           to_: Union[str, datetime] = None, meeting_id: str = None,
                                                           site_url: str = None, integration_tag: str = None,
                                                           topic: str = None, format_: RecordingFormat = None,
                                                           service_type: RecordingServiceType = None,
                                                           status: RecordingStatus = None,
                                                           **params) -> List[Recording]:
        """
        List Recordings For an Admin or Compliance Officer

        List recordings for an admin or compliance officer. You can specify a date range, a parent meeting ID, and the
        maximum number of recordings to return.

        The list returned is sorted in descending order by the date and time that the recordings were created.

        Long result sets are split into `pages <https://developer.webex.com/docs/basics#pagination>`_.

        * If `meetingId` is specified, only recordings associated with the specified meeting will be listed. Please
          note that when `meetingId` is specified, parameter of `siteUrl` will be ignored.

        * If `siteUrl` is specified, all the recordings on the specified site are listed; otherwise, all the
          recordings on the admin user's or compliance officer's preferred site are listed. All the available
          Webex sites and the admin user's or compliance officer's preferred site can be retrieved by the
          `Get Site List <https://developer.webex.com/docs/api/v1/meeting-preferences/get-site-list>`_ API.

        :param from_: Starting date and time (inclusive) for recordings to return, in any `ISO 8601
            <https://en.wikipedia.org/wiki/ISO_8601>`_ compliant format.
            `from` cannot be after `to`. The interval between `from` and `to` must be within 30 days.
        :type from_: Union[str, datetime]
        :param to_: Ending date and time (exclusive) for List recordings to return, in any `ISO 8601
            <https://en.wikipedia.org/wiki/ISO_8601>`_ compliant format.
            `to` cannot be before `from`. The interval between `from` and `to` must be within 30 days.
        :type to_: Union[str, datetime]
        :param meeting_id: Unique identifier for the parent meeting series, scheduled meeting, or meeting instance for
            which recordings are being requested. If a meeting series ID is specified, the operation returns an array
            of recordings for the specified meeting series. If a scheduled meeting ID is specified, the operation
            returns an array of recordings for the specified scheduled meeting. If a meeting instance ID is specified,
            the operation returns an array of recordings for the specified meeting instance. If not specified, the
            operation returns an array of recordings for all the current user's meetings. When `meetingId` is
            specified, the `siteUrl` parameter is ignored.
        :type meeting_id: str
        :param site_url: URL of the Webex site which the API lists recordings from. If not specified, the API lists
            recordings from user's preferred site. All available Webex sites and preferred site of the user can be
            retrieved by `Get Site List
            <https://developer.webex.com/docs/api/v1/meeting-preferences/get-site-list>`_ API.
        :type site_url: str
        :param integration_tag: External key of the parent meeting created by an integration application. This
            parameter is used by the integration application to query recordings by a key in its own domain such as a
            Zendesk ticket ID, a Jira ID, a Salesforce Opportunity ID, etc.
        :type integration_tag: str
        :param topic: Recording topic. If specified, the API filters recordings by topic in a case-insensitive manner.
        :type topic: str
        :param format_: Recording's file format. If specified, the API filters recordings by format.
        :type format_: RecordingsFormat
        :param service_type: The service type for recordings. If specified, the API filters recordings by service type.
        :type service_type: RecordingServiceType
        :param status: Recording's status. If not specified or `available`, retrieves recordings that are available. If
            specified as `deleted`, retrieves recordings that have been moved to the recycle bin. Otherwise, if
            specified as `purged`, retrieves recordings that have been purged from the recycle bin.
        :type status: ListRecordingsStatus
        :return: Generator yielding :class:`RecordingObjectForAdminAndCO` instances
        """
        if from_ is not None:
            if isinstance(from_, str):
                from_ = isoparse(from_)
            from_ = dt_iso_str(from_)
            params['from'] = from_
        if to_ is not None:
            if isinstance(to_, str):
                to_ = isoparse(to_)
            to_ = dt_iso_str(to_)
            params['to'] = to_
        if meeting_id is not None:
            params['meetingId'] = meeting_id
        if site_url is not None:
            params['siteUrl'] = site_url
        if integration_tag is not None:
            params['integrationTag'] = integration_tag
        if topic is not None:
            params['topic'] = topic
        if format_ is not None:
            params['format'] = enum_str(format_)
        if service_type is not None:
            params['serviceType'] = enum_str(service_type)
        if status is not None:
            params['status'] = enum_str(status)
        url = self.ep('admin/recordings')
        return [o async for o in self.session.follow_pagination(url=url, model=Recording, item_key='items',
                                              params=params)]

    async def get_recording_details(self, recording_id: str, host_email: str = None) -> Recording:
        """
        Get Recording Details

        Retrieves details for a recording with a specified recording ID.

        Only recordings of meetings hosted by or shared with the authenticated user may be retrieved.

        The `temporaryDirectDownloadLinks` of a recording which are retrieved by the `Get Recording Details
        <https://developer.webex.com/docs/api/v1/recordings/get-recording-details>`_ API are
        still available to Compliance Officers even if the recording has been deleted.

        :param recording_id: A unique identifier for the recording.
        :type recording_id: str
        :param host_email: Email address for the meeting host. Only used if the user or application calling the API has
            required `admin-level meeting scopes
            <https://developer.webex.com/docs/meetings#adminorganization-level-authentication-and-scopes>`_. If set,
            the admin may specify the email of a user in a site they
            manage, and the API will return recording details of that user.
        :type host_email: str
        :rtype: :class:`RecordingObjectWithDirectDownloadLinks`
        """
        params = {}
        if host_email is not None:
            params['hostEmail'] = host_email
        url = self.ep(f'recordings/{recording_id}')
        data = await super().get(url, params=params)
        r = Recording.model_validate(data)
        return r

    async def delete_a_recording(self, recording_id: str, reason: str, comment: str, host_email: str = None):
        """
        Delete a Recording

        Removes a recording with a specified recording ID. The deleted recording cannot be recovered. If a Compliance
        Officer deletes another user's recording, the recording will be inaccessible to regular users (host, attendees
        and shared), but will be still available to the Compliance Officer.

        Only recordings of meetings hosted by the authenticated user can be deleted.

        :param recording_id: A unique identifier for the recording.
        :type recording_id: str
        :param reason: Reason for deleting a recording. Only required when a Compliance Officer is operating on another
            user's recording.
        :type reason: str
        :param comment: Compliance Officer's explanation for deleting a recording. The comment can be a maximum of 255
            characters long.
        :type comment: str
        :param host_email: Email address for the meeting host. Only used if the user or application calling the API has
            the required `admin-level meeting scopes
            <https://developer.webex.com/docs/meetings#adminorganization-level-authentication-and-scopes>`_. If set,
            the admin may specify the email of a user in a site they
            manage and the API will delete a recording of that user.
        :type host_email: str
        :rtype: None
        """
        params = {}
        if host_email is not None:
            params['hostEmail'] = host_email
        body = dict()
        body['reason'] = reason
        body['comment'] = comment
        url = self.ep(f'recordings/{recording_id}')
        await super().delete(url, params=params, json=body)

    async def move_recordings_into_the_recycle_bin(self, recording_ids: list[str], site_url: str, host_email: str = None):
        """
        Move Recordings into the Recycle Bin

        Move recordings into the recycle bin with recording IDs. Recordings in the recycle bin can be recovered by
        `Restore Recordings from Recycle Bin
        <https://developer.webex.com/docs/api/v1/recordings/restore-recordings-from-recycle-bin>`_ API. If you'd like
        to empty recordings from the recycle bin, you can use
        `Purge Recordings from Recycle Bin
        <https://developer.webex.com/docs/api/v1/recordings/purge-recordings-from-recycle-bin>`_ API to purge all or
        some of them.

        Only recordings of meetings hosted by the authenticated user can be moved into the recycle bin.

            * `recordingIds` should not be empty and its maximum size is `100`.

            * All the IDs of `recordingIds` should belong to the site of `siteUrl` or the user's preferred site if
                `siteUrl` is not specified.

        :param recording_ids: Recording IDs for removing recordings into the recycle bin in batch. Please note that all
            the recording IDs should belong to the site of `siteUrl` or the user's preferred site if `siteUrl` is not
            specified.
        :type recording_ids: list[str]
        :param site_url: URL of the Webex site from which the API deletes recordings. If not specified, the API deletes
            recordings from the user's preferred site. All available Webex sites and preferred sites of a user can be
            retrieved by the `Get Site List
            <https://developer.webex.com/docs/api/v1/meeting-preferences/get-site-list>`_ API.
        :type site_url: str
        :param host_email: Email address for the meeting host. Only used if the user or application calling the API has
            the required `admin-level meeting scopes
            <https://developer.webex.com/docs/meetings#adminorganization-level-authentication-and-scopes>`_. If set,
            the admin may specify the email of a user in a site they
            manage and the API will move recordings into recycle bin of that user
        :type host_email: str
        :rtype: None
        """
        params = {}
        if host_email is not None:
            params['hostEmail'] = host_email
        body = dict()
        body['recordingIds'] = recording_ids
        body['siteUrl'] = site_url
        url = self.ep('recordings/softDelete')
        await super().post(url, params=params, json=body)

    async def restore_recordings_from_recycle_bin(self, restore_all: bool, recording_ids: list[str], site_url: str,
                                            host_email: str = None):
        """
        Restore Recordings from Recycle Bin

        Restore all or some recordings from the recycle bin. Only recordings of meetings hosted by the authenticated
        user can be restored from recycle bin.

            * If `restoreAll` is `true`, `recordingIds` should be empty.

            * If `restoreAll` is `false`, `recordingIds` should not be empty and its maximum size is `100`.

            * All the IDs of `recordingIds` should belong to the site of `siteUrl` or the user's preferred site if
                `siteUrl` is not specified.

        :param restore_all: If not specified or `false`, restores the recordings specified by `recordingIds`. If
            `true`, restores all recordings from the recycle bin.
        :type restore_all: bool
        :param recording_ids: Recording IDs for recovering recordings from the recycle bin in batch. Note that all the
            recording IDs should belong to the site of `siteUrl` or the user's preferred site if `siteUrl` is not
            specified.
        :type recording_ids: list[str]
        :param site_url: URL of the Webex site from which the API restores recordings. If not specified, the API
            restores recordings from a user's preferred site. All available Webex sites and preferred sites of a user
            can be retrieved by `Get Site List
            <https://developer.webex.com/docs/api/v1/meeting-preferences/get-site-list>`_ API.
        :type site_url: str
        :param host_email: Email address for the meeting host. This parameter is only used if the user or application
            calling the API has the required `admin-level meeting scopes
            <https://developer.webex.com/docs/meetings#adminorganization-level-authentication-and-scopes>`_. If set,
            the admin may specify the email of a
            user in a site they manage and the API will restore recordings of that user.
        :type host_email: str
        :rtype: None
        """
        params = {}
        if host_email is not None:
            params['hostEmail'] = host_email
        body = dict()
        body['restoreAll'] = restore_all
        body['recordingIds'] = recording_ids
        body['siteUrl'] = site_url
        url = self.ep('recordings/restore')
        await super().post(url, params=params, json=body)

    async def purge_recordings_from_recycle_bin(self, purge_all: bool, recording_ids: list[str], site_url: str,
                                          host_email: str = None):
        """
        Purge Recordings from Recycle Bin

        Purge recordings from recycle bin with recording IDs or purge all the recordings that are in the recycle bin.

        Only recordings of meetings hosted by the authenticated user can be purged from recycle bin.

            * If `purgeAll` is `true`, `recordingIds` should be empty.

            * If `purgeAll` is `false`, `recordingIds` should not be empty and its maximum size is `100`.

            * All the IDs of `recordingIds` should belong to the site of `siteUrl` or the user's preferred site if
                `siteUrl` is not specified.

        :param purge_all: If not specified or `false`, purges the recordings specified by `recordingIds`. If `true`,
            purges all recordings from the recycle bin.
        :type purge_all: bool
        :param recording_ids: Recording IDs for purging recordings from the recycle bin in batch. Note that all the
            recording IDs should belong to the site of `siteUrl` or the user's preferred site if `siteUrl` is not
            specified.
        :type recording_ids: list[str]
        :param site_url: URL of the Webex site from which the API purges recordings. If not specified, the API purges
            recordings from user's preferred site. All available Webex sites and preferred sites of the user can be
            retrieved by `Get Site List
            <https://developer.webex.com/docs/api/v1/meeting-preferences/get-site-list>`_ API.
        :type site_url: str
        :param host_email: Email address for the meeting host. Only used if the user or application calling the API has
            the required `admin-level meeting scopes
            <https://developer.webex.com/docs/meetings#adminorganization-level-authentication-and-scopes>`_. If set,
            the admin may specify the email of a user in a site they
            manage and the API will purge recordings from recycle bin of that user.
        :type host_email: str
        :rtype: None
        """
        params = {}
        if host_email is not None:
            params['hostEmail'] = host_email
        body = dict()
        body['purgeAll'] = purge_all
        body['recordingIds'] = recording_ids
        body['siteUrl'] = site_url
        url = self.ep('recordings/purge')
        await super().post(url, params=params, json=body)


class AsMeetingsApi(AsApiChild, base='meetings'):
    """
    Meetings API
    """
    #: meeting chats API
    chats: AsMeetingChatsApi
    #: closed captions API
    closed_captions: AsMeetingClosedCaptionsApi
    #: meeting invitees API
    invitees: AsMeetingInviteesApi
    #: meeting participants API
    participants: AsMeetingParticipantsApi
    #: preferences API
    preferences: AsMeetingPreferencesApi
    #: Q and A API
    qanda: AsMeetingQandAApi
    #: qualities API
    qualities: AsMeetingQualitiesApi
    # recordings
    recordings: AsRecordingsApi
    #: transcripts
    transcripts: AsMeetingTranscriptsApi

    def __init__(self, session: AsRestSession):
        super().__init__(session=session)
        self.chats = AsMeetingChatsApi(session=session)
        self.closed_captions = AsMeetingClosedCaptionsApi(session=session)
        self.invitees = AsMeetingInviteesApi(session=session)
        self.participants = AsMeetingParticipantsApi(session=session)
        self.preferences = AsMeetingPreferencesApi(session=session)
        self.qanda = AsMeetingQandAApi(session=session)
        self.qualities = AsMeetingQualitiesApi(session=session)
        self.recordings = AsRecordingsApi(session=session)
        self.transcripts = AsMeetingTranscriptsApi(session=session)

    async def create(self, title: str = None, agenda: str = None, password: str = None, start: str = None, end: str = None,
               timezone: str = None, recurrence: str = None, enabled_auto_record_meeting: bool = None,
               allow_any_user_to_be_co_host: bool = None, enabled_join_before_host: bool = None,
               enable_connect_audio_before_host: bool = None, join_before_host_minutes: int = None,
               exclude_password: bool = None, public_meeting: bool = None, reminder_time: int = None,
               unlocked_meeting_join_security: UnlockedMeetingJoinSecurity = None, session_type_id: int = None,
               enabled_webcast_view: bool = None, panelist_password: str = None, enable_automatic_lock: bool = None,
               automatic_lock_minutes: int = None, allow_first_user_to_be_co_host: bool = None,
               allow_authenticated_devices: bool = None, send_email: bool = None, host_email: str = None,
               site_url: str = None, meeting_options: MeetingOptions = None,
               attendee_privileges: AttendeePrivileges = None, integration_tags: List[str] = None,
               enabled_breakout_sessions: bool = None, tracking_codes: TrackingCodeItem = None,
               audio_connection_options: AudioConnectionOptions = None, adhoc: bool = None, room_id: str = None,
               template_id: str = None, scheduled_type: ScheduledType = None,
               invitees: InviteeForCreateMeeting = None, registration: Registration = None,
               simultaneous_interpretation: SimultaneousInterpretation = None,
               breakout_sessions: BreakoutSession = None) -> Meeting:
        """
        Creates a new meeting. Regular users can schedule up to 100 meetings in 24 hours and admin users up to 3000.

        :param title: Meeting title. The title can be a maximum of 128 characters long.
        :type title: str
        :param agenda: Meeting agenda. The agenda can be a maximum of 1300 characters long.
        :type agenda: str
        :param password: Meeting password. Must conform to the site's password complexity settings. Read password
            management for details.
        :type password: str
        :param start: Date and time for the start of meeting in any ISO 8601 compliant format. start cannot be before
            current date and time or after end. Duration between start and end cannot be shorter than 10 minutes or
            longer than 24 hours. Refer to the Webex Meetings guide for more information about restrictions on updating
            date and time for a meeting. Please note that when a meeting is being updated, start of the meeting will be
            accurate to minutes, not seconds or milliseconds. Therefore, if start is within the same minute as the
            current time, start will be adjusted to the upcoming minute; otherwise, start will be adjusted with seconds
            and milliseconds stripped off. For instance, if the current time is 2022-03-01T10:32:16.657+08:00, start of
            2022-03-01T10:32:28.076+08:00 or 2022-03-01T10:32:41+08:00 will be adjusted to 2022-03-01T10:33:00+08:00,
            and start of 2022-03-01T11:32:28.076+08:00 or 2022-03-01T11:32:41+08:00 will be adjusted to
            2022-03-01T11:32:00+08:00.
        :type start: str
        :param end: Date and time for the end of meeting in any ISO 8601 compliant format. end cannot be before current
            date and time or before start. Duration between start and end cannot be shorter than 10 minutes or longer
            than 24 hours. Refer to the Webex Meetings guide for more information about restrictions on updating date
            and time for a meeting. Please note that when a meeting is being updated, end of the meeting will be
            accurate to minutes, not seconds or milliseconds. Therefore, end will be adjusted with seconds and
            milliseconds stripped off. For instance, end of 2022-03-01T11:52:28.076+08:00 or 2022-03-01T11:52:41+08:00
            will be adjusted to 2022-03-01T11:52:00+08:00.
        :type end: str
        :param timezone: Time zone in which the meeting was originally scheduled (conforming with the IANA time zone
            database).
        :type timezone: str
        :param recurrence: Meeting series recurrence rule (conforming with RFC 2445). Applies only to a recurring
            meeting series, not to a meeting series with only one scheduled meeting. Multiple days or dates for monthly
            or yearly recurrence rule are not supported, only the first day or date specified is taken. For example,
            "FREQ=MONTHLY;INTERVAL=1;COUNT=10;BYMONTHDAY=10,11,12" is not supported and it will be partially supported
            as "FREQ=MONTHLY;INTERVAL=1;COUNT=10;BYMONTHDAY=10".
        :type recurrence: str
        :param enabled_auto_record_meeting: Whether or not meeting is recorded automatically.
        :type enabled_auto_record_meeting: bool
        :param allow_any_user_to_be_co_host: Whether or not to allow any attendee with a host account on the target
            site to become a cohost when joining the meeting. The target site is specified by siteUrl parameter when
            creating the meeting; if not specified, it's user's preferred site.
        :type allow_any_user_to_be_co_host: bool
        :param enabled_join_before_host: Whether or not to allow any attendee to join the meeting before the host joins
            the meeting.
        :type enabled_join_before_host: bool
        :param enable_connect_audio_before_host: Whether or not to allow any attendee to connect audio in the meeting
            before the host joins the meeting. This attribute is only applicable if the enabledJoinBeforeHost attribute
            is set to true.
        :type enable_connect_audio_before_host: bool
        :param join_before_host_minutes: The number of minutes an attendee can join the meeting before the meeting
            start time and the host joins. This attribute is only applicable if the enabledJoinBeforeHost attribute is
            set to true. Valid options are 0, 5, 10 and 15. Default is 0 if not specified.
        :type join_before_host_minutes: int
        :param exclude_password: Whether or not to exclude the meeting password from the email invitation.
        :type exclude_password: bool
        :param public_meeting: Whether or not to allow the meeting to be listed on the public calendar.
        :type public_meeting: bool
        :param reminder_time: The number of minutes before the meeting begins, that an email reminder is sent to the
            host.
        :type reminder_time: int
        :param unlocked_meeting_join_security: Specifies how the people who aren't on the invite can join the unlocked
            meeting.
        :type unlocked_meeting_join_security: UnlockedMeetingJoinSecurity
        :param session_type_id: Unique identifier for a meeting session type for the user. This attribute is required
            while scheduling webinar meeting. All available meeting session types enabled for the user can be retrieved
            by List Meeting Session Types API.
        :type session_type_id: int
        :param enabled_webcast_view: Whether or not webcast view is enabled.
        :type enabled_webcast_view: bool
        :param panelist_password: Password for panelists of a webinar meeting. Must conform to the site's password
            complexity settings. Read password management for details. If not specified, a random password conforming
            to the site's password rules will be generated automatically.
        :type panelist_password: str
        :param enable_automatic_lock: Whether or not to automatically lock the meeting after it starts.
        :type enable_automatic_lock: bool
        :param automatic_lock_minutes: The number of minutes after the meeting begins, for automatically locking it.
        :type automatic_lock_minutes: int
        :param allow_first_user_to_be_co_host: Whether or not to allow the first attendee of the meeting with a host
            account on the target site to become a cohost. The target site is specified by siteUrl parameter when
            creating the meeting; if not specified, it's user's preferred site.
        :type allow_first_user_to_be_co_host: bool
        :param allow_authenticated_devices: Whether or not to allow authenticated video devices in the meeting's
            organization to start or join the meeting without a prompt.
        :type allow_authenticated_devices: bool
        :param send_email: Whether or not to send emails to host and invitees. It is an optional field and default
            value is true.
        :type send_email: bool
        :param host_email: Email address for the meeting host. This attribute should only be set if the user or
            application calling the API has the admin-level scopes. When used, the admin may specify the email of a
            user in a site they manage to be the meeting host.
        :type host_email: str
        :param site_url: URL of the Webex site which the meeting is updated on. If not specified, the meeting is
            created on user's preferred site. All available Webex sites and preferred site of the user can be retrieved
            by Get Site List API.
        :type site_url: str
        :param meeting_options: Meeting Options.
        :type meeting_options: MeetingOptions
        :param attendee_privileges: Attendee Privileges.
        :type attendee_privileges: AttendeePrivileges
        :param integration_tags: External keys created by an integration application in its own domain, for example
            Zendesk ticket IDs, Jira IDs, Salesforce Opportunity IDs, etc. The integration application queries meetings
            by a key in its own domain. The maximum size of integrationTags is 3 and each item of integrationTags can
            be a maximum of 64 characters long. Please note that an empty or null integrationTags will delete all
            existing integration tags for the meeting implicitly. Developer can update integration tags for a
            meetingSeries but he cannot update it for a scheduledMeeting or a meeting instance.
        :type integration_tags: List[str]
        :param enabled_breakout_sessions: Whether or not breakout sessions are enabled. If the value of
            enabledBreakoutSessions is false, users can not set breakout sessions. If the value of
            enabledBreakoutSessions is true, users can update breakout sessions using the Update Breakout Sessions API.
            Updating breakout sessions are not supported by this API.
        :type enabled_breakout_sessions: bool
        :param tracking_codes: Tracking codes information. All available tracking codes and their options for the
            specified site can be retrieved by List Meeting Tracking Codes API. If an optional tracking code is missing
            from the trackingCodes array and there's a default option for this tracking code, the default option is
            assigned automatically. If the inputMode of a tracking code is select, its value must be one of the
            site-level options or the user-level value. Tracking code is not supported for a personal room meeting or
            an ad-hoc space meeting.
        :type tracking_codes: TrackingCodeItem
        :param audio_connection_options: Audio connection options.
        :type audio_connection_options: AudioConnectionOptions
        :param adhoc: Whether or not to create an ad-hoc meeting for the room specified by roomId. When true, roomId is
            required.
        :type adhoc: bool
        :param room_id: Unique identifier for the Webex space which the meeting is to be associated with. It can be
            retrieved by List Rooms. roomId is required when adhoc is true. When roomId is specified, the parameter
            hostEmail will be ignored.
        :type room_id: str
        :param template_id: Unique identifier for meeting template. Please note that start and end are optional when
            templateId is specified. The list of meeting templates that is available for the authenticated user can be
            retrieved from List Meeting Templates. This parameter is ignored for an ad-hoc meeting.
        :type template_id: str
        :param scheduled_type: When set as an attribute in a POST request body, specifies whether it's a regular
            meeting, a webinar, or a meeting scheduled in the user's personal room. If not specified, it's a regular
            meeting by default. The default value for an ad-hoc meeting is meeting and the user's input value will be
            ignored.
        :type scheduled_type: ScheduledType
        :param invitees: Invitees for meeting. The maximum size of invitees is 1000. If roomId is specified and
            invitees is missing, all the members in the space are invited implicitly. If both roomId and invitees are
            specified, only those in the invitees list are invited. coHost for each invitee is true by default if
            roomId is specified when creating a meeting, and anyone in the invitee list that is not qualified to be a
            cohost will be invited as a non-cohost invitee. The user's input value will be ignored for an ad-hoc
            meeting and the the members of the room specified by roomId except "me" will be used by default.
        :type invitees: InviteeForCreateMeeting
        :param registration: Meeting registration. When this option is enabled, meeting invitees must register personal
            information to join the meeting. Meeting invitees will receive an email with a registration link for the
            registration. When the registration form has been submitted and approved, an email with a real meeting link
            will be received. By clicking that link the meeting invitee can join the meeting. Please note that meeting
            registration does not apply to a meeting when it's a recurring meeting with a recurrence field or no
            password, or the Join Before Host option is enabled for the meeting. See Register for a Meeting in Cisco
            Webex Meetings for details. This parameter is ignored for an ad-hoc meeting.
        :type registration: Registration
        :param simultaneous_interpretation: Simultaneous interpretation information for a meeting.
        :type simultaneous_interpretation: SimultaneousInterpretation
        :param breakout_sessions: Breakout sessions are smaller groups that are split off from the main meeting or
            webinar. They allow a subset of participants to collaborate and share ideas over audio and video. Use
            breakout sessions for workshops, classrooms, or for when you need a moment to talk privately with a few
            participants outside of the main session. Please note that maximum number of breakout sessions in a meeting
            or webinar is 100. In webinars, if hosts preassign attendees to breakout sessions, the role of attendee
            will be changed to panelist. Breakout session is not supported for a meeting with simultaneous
            interpretation.
        :type breakout_sessions: BreakoutSession

        documentation: https://developer.webex.com/docs/api/v1/meetings/create-a-meeting
        """
        body = CreateMeetingBody()
        if title is not None:
            body.title = title
        if agenda is not None:
            body.agenda = agenda
        if password is not None:
            body.password = password
        if start is not None:
            body.start = start
        if end is not None:
            body.end = end
        if timezone is not None:
            body.timezone = timezone
        if recurrence is not None:
            body.recurrence = recurrence
        if enabled_auto_record_meeting is not None:
            body.enabled_auto_record_meeting = enabled_auto_record_meeting
        if allow_any_user_to_be_co_host is not None:
            body.allow_any_user_to_be_co_host = allow_any_user_to_be_co_host
        if enabled_join_before_host is not None:
            body.enabled_join_before_host = enabled_join_before_host
        if enable_connect_audio_before_host is not None:
            body.enable_connect_audio_before_host = enable_connect_audio_before_host
        if join_before_host_minutes is not None:
            body.join_before_host_minutes = join_before_host_minutes
        if exclude_password is not None:
            body.exclude_password = exclude_password
        if public_meeting is not None:
            body.public_meeting = public_meeting
        if reminder_time is not None:
            body.reminder_time = reminder_time
        if unlocked_meeting_join_security is not None:
            body.unlocked_meeting_join_security = unlocked_meeting_join_security
        if session_type_id is not None:
            body.session_type_id = session_type_id
        if enabled_webcast_view is not None:
            body.enabled_webcast_view = enabled_webcast_view
        if panelist_password is not None:
            body.panelist_password = panelist_password
        if enable_automatic_lock is not None:
            body.enable_automatic_lock = enable_automatic_lock
        if automatic_lock_minutes is not None:
            body.automatic_lock_minutes = automatic_lock_minutes
        if allow_first_user_to_be_co_host is not None:
            body.allow_first_user_to_be_co_host = allow_first_user_to_be_co_host
        if allow_authenticated_devices is not None:
            body.allow_authenticated_devices = allow_authenticated_devices
        if send_email is not None:
            body.send_email = send_email
        if host_email is not None:
            body.host_email = host_email
        if site_url is not None:
            body.site_url = site_url
        if meeting_options is not None:
            body.meeting_options = meeting_options
        if attendee_privileges is not None:
            body.attendee_privileges = attendee_privileges
        if integration_tags is not None:
            body.integration_tags = integration_tags
        if enabled_breakout_sessions is not None:
            body.enabled_breakout_sessions = enabled_breakout_sessions
        if tracking_codes is not None:
            body.tracking_codes = tracking_codes
        if audio_connection_options is not None:
            body.audio_connection_options = audio_connection_options
        if adhoc is not None:
            body.adhoc = adhoc
        if room_id is not None:
            body.room_id = room_id
        if template_id is not None:
            body.template_id = template_id
        if scheduled_type is not None:
            body.scheduled_type = scheduled_type
        if invitees is not None:
            body.invitees = invitees
        if registration is not None:
            body.registration = registration
        if simultaneous_interpretation is not None:
            body.simultaneous_interpretation = simultaneous_interpretation
        if breakout_sessions is not None:
            body.breakout_sessions = breakout_sessions
        url = self.ep()
        data = await super().post(url=url, data=body.model_dump_json())
        return Meeting.model_validate(data)

    async def get(self, meeting_id: str, current: bool = None, host_email: str = None) -> Meeting:
        """
        Retrieves details for a meeting with a specified meeting ID.

        :param meeting_id: Unique identifier for the meeting being requested.
        :type meeting_id: str
        :param current: Whether or not to retrieve only the current scheduled meeting of the meeting series, i.e. the
            meeting ready to join or start or the upcoming meeting of the meeting series. If it's true, return details
            for the current scheduled meeting of the series, i.e. the scheduled meeting ready to join or start or the
            upcoming scheduled meeting of the meeting series. If it's false or not specified, return details for the
            entire meeting series. This parameter only applies to meeting series.
        :type current: bool
        :param host_email: Email address for the meeting host. This parameter is only used if the user or application
            calling the API has the admin-level scopes. If set, the admin may specify the email of a user in a site
            they manage and the API will return details for a meeting that is hosted by that user.
        :type host_email: str

        documentation: https://developer.webex.com/docs/api/v1/meetings/get-a-meeting
        """
        params = {}
        if current is not None:
            params['current'] = str(current).lower()
        if host_email is not None:
            params['hostEmail'] = host_email
        url = self.ep(f'{meeting_id}')
        data = await super().get(url=url, params=params)
        return Meeting.model_validate(data)

    def list_gen(self, meeting_number: str = None, web_link: str = None, room_id: str = None, meeting_type: str = None,
             state: str = None, scheduled_type: str = None, current: bool = None, from_: str = None, to_: str = None,
             host_email: str = None, site_url: str = None, integration_tag: str = None,
             **params) -> AsyncGenerator[Meeting, None, None]:
        """
        Retrieves details for meetings with a specified meeting number, web link, meeting type, etc. Please note that
        there are various products in the Webex Suite such as Meetings and Events. Currently, only meetings of the
        Meetings product are supported by this API, meetings of others in the suite are not supported. Ad-hoc meetings
        created by Create a Meeting with adhoc of true and a roomId will not be listed, but the ended and ongoing
        ad-hoc meeting instances will be listed.

        :param meeting_number: Meeting number for the meeting objects being requested. meetingNumber, webLink and
            roomId are mutually exclusive. If it's an exceptional meeting from a meeting series, the exceptional
            meeting instead of the primary meeting series is returned.
        :type meeting_number: str
        :param web_link: URL encoded link to information page for the meeting objects being requested. meetingNumber,
            webLink and roomId are mutually exclusive.
        :type web_link: str
        :param room_id: Associated Webex space ID for the meeting objects being requested. meetingNumber, webLink and
            roomId are mutually exclusive.
        :type room_id: str
        :param meeting_type: Meeting type for the meeting objects being requested. This parameter will be ignored if
            meetingNumber, webLink or roomId is specified. Possible values: meetingSeries, scheduledMeeting, meeting
        :type meeting_type: str
        :param state: Meeting state for the meeting objects being requested. If not specified, return meetings of all
            states. This parameter will be ignored if meetingNumber, webLink or roomId is specified. Details of an
            ended meeting will only be available 15 minutes after the meeting has ended. inProgress meetings are not
            fully supported. The API will try to return details of an inProgress meeting 15 minutes after the meeting
            starts. However, it may take longer depending on the traffic. See the Webex Meetings guide for more
            information about the states of meetings. Possible values: active, scheduled, ready, lobby, inProgress,
            ended, missed, expired
        :type state: str
        :param scheduled_type: Scheduled type for the meeting objects being requested. Possible values: meeting,
            webinar, personalRoomMeeting
        :type scheduled_type: str
        :param current: Flag identifying to retrieve the current scheduled meeting of the meeting series or the entire
            meeting series. This parameter only applies to scenarios where meetingNumber is specified and the meeting
            is not an exceptional meeting from a meeting series. If it's true, return the scheduled meeting of the
            meeting series which is ready to join or start or the upcoming scheduled meeting of the meeting series; if
            it's false, return the entire meeting series.
        :type current: bool
        :param from_: Start date and time (inclusive) in any ISO 8601 compliant format for the meeting objects being
            requested. from cannot be after to. This parameter will be ignored if meetingNumber, webLink or roomId is
            specified.
        :type from_: str
        :param to_: End date and time (exclusive) in any ISO 8601 compliant format for the meeting objects being
            requested. to cannot be before from. This parameter will be ignored if meetingNumber, webLink or roomId is
            specified.
        :type to_: str
        :param host_email: Email address for the meeting host. This parameter is only used if the user or application
            calling the API has the admin-level scopes. If set, the admin may specify the email of a user in a site
            they manage and the API will return details for meetings that are hosted by that user.
        :type host_email: str
        :param site_url: URL of the Webex site which the API lists meetings from. If not specified, the API lists
            meetings from user's all sites. All available Webex sites of the user can be retrieved by Get Site List
            API.
        :type site_url: str
        :param integration_tag: External key created by an integration application. This parameter is used by the
            integration application to query meetings by a key in its own domain such as a Zendesk ticket ID, a Jira
            ID, a Salesforce Opportunity ID, etc.
        :type integration_tag: str

        documentation: https://developer.webex.com/docs/api/v1/meetings/list-meetings
        """
        if meeting_number is not None:
            params['meetingNumber'] = meeting_number
        if web_link is not None:
            params['webLink'] = web_link
        if room_id is not None:
            params['roomId'] = room_id
        if meeting_type is not None:
            params['meetingType'] = meeting_type
        if state is not None:
            params['state'] = state
        if scheduled_type is not None:
            params['scheduledType'] = scheduled_type
        if current is not None:
            params['current'] = current
        if from_ is not None:
            params['from'] = from_
        if to_ is not None:
            params['to'] = to_
        if host_email is not None:
            params['hostEmail'] = host_email
        if site_url is not None:
            params['siteUrl'] = site_url
        if integration_tag is not None:
            params['integrationTag'] = integration_tag
        url = self.ep()
        return self.session.follow_pagination(url=url, model=Meeting, params=params)

    async def list(self, meeting_number: str = None, web_link: str = None, room_id: str = None, meeting_type: str = None,
             state: str = None, scheduled_type: str = None, current: bool = None, from_: str = None, to_: str = None,
             host_email: str = None, site_url: str = None, integration_tag: str = None,
             **params) -> List[Meeting]:
        """
        Retrieves details for meetings with a specified meeting number, web link, meeting type, etc. Please note that
        there are various products in the Webex Suite such as Meetings and Events. Currently, only meetings of the
        Meetings product are supported by this API, meetings of others in the suite are not supported. Ad-hoc meetings
        created by Create a Meeting with adhoc of true and a roomId will not be listed, but the ended and ongoing
        ad-hoc meeting instances will be listed.

        :param meeting_number: Meeting number for the meeting objects being requested. meetingNumber, webLink and
            roomId are mutually exclusive. If it's an exceptional meeting from a meeting series, the exceptional
            meeting instead of the primary meeting series is returned.
        :type meeting_number: str
        :param web_link: URL encoded link to information page for the meeting objects being requested. meetingNumber,
            webLink and roomId are mutually exclusive.
        :type web_link: str
        :param room_id: Associated Webex space ID for the meeting objects being requested. meetingNumber, webLink and
            roomId are mutually exclusive.
        :type room_id: str
        :param meeting_type: Meeting type for the meeting objects being requested. This parameter will be ignored if
            meetingNumber, webLink or roomId is specified. Possible values: meetingSeries, scheduledMeeting, meeting
        :type meeting_type: str
        :param state: Meeting state for the meeting objects being requested. If not specified, return meetings of all
            states. This parameter will be ignored if meetingNumber, webLink or roomId is specified. Details of an
            ended meeting will only be available 15 minutes after the meeting has ended. inProgress meetings are not
            fully supported. The API will try to return details of an inProgress meeting 15 minutes after the meeting
            starts. However, it may take longer depending on the traffic. See the Webex Meetings guide for more
            information about the states of meetings. Possible values: active, scheduled, ready, lobby, inProgress,
            ended, missed, expired
        :type state: str
        :param scheduled_type: Scheduled type for the meeting objects being requested. Possible values: meeting,
            webinar, personalRoomMeeting
        :type scheduled_type: str
        :param current: Flag identifying to retrieve the current scheduled meeting of the meeting series or the entire
            meeting series. This parameter only applies to scenarios where meetingNumber is specified and the meeting
            is not an exceptional meeting from a meeting series. If it's true, return the scheduled meeting of the
            meeting series which is ready to join or start or the upcoming scheduled meeting of the meeting series; if
            it's false, return the entire meeting series.
        :type current: bool
        :param from_: Start date and time (inclusive) in any ISO 8601 compliant format for the meeting objects being
            requested. from cannot be after to. This parameter will be ignored if meetingNumber, webLink or roomId is
            specified.
        :type from_: str
        :param to_: End date and time (exclusive) in any ISO 8601 compliant format for the meeting objects being
            requested. to cannot be before from. This parameter will be ignored if meetingNumber, webLink or roomId is
            specified.
        :type to_: str
        :param host_email: Email address for the meeting host. This parameter is only used if the user or application
            calling the API has the admin-level scopes. If set, the admin may specify the email of a user in a site
            they manage and the API will return details for meetings that are hosted by that user.
        :type host_email: str
        :param site_url: URL of the Webex site which the API lists meetings from. If not specified, the API lists
            meetings from user's all sites. All available Webex sites of the user can be retrieved by Get Site List
            API.
        :type site_url: str
        :param integration_tag: External key created by an integration application. This parameter is used by the
            integration application to query meetings by a key in its own domain such as a Zendesk ticket ID, a Jira
            ID, a Salesforce Opportunity ID, etc.
        :type integration_tag: str

        documentation: https://developer.webex.com/docs/api/v1/meetings/list-meetings
        """
        if meeting_number is not None:
            params['meetingNumber'] = meeting_number
        if web_link is not None:
            params['webLink'] = web_link
        if room_id is not None:
            params['roomId'] = room_id
        if meeting_type is not None:
            params['meetingType'] = meeting_type
        if state is not None:
            params['state'] = state
        if scheduled_type is not None:
            params['scheduledType'] = scheduled_type
        if current is not None:
            params['current'] = current
        if from_ is not None:
            params['from'] = from_
        if to_ is not None:
            params['to'] = to_
        if host_email is not None:
            params['hostEmail'] = host_email
        if site_url is not None:
            params['siteUrl'] = site_url
        if integration_tag is not None:
            params['integrationTag'] = integration_tag
        url = self.ep()
        return [o async for o in self.session.follow_pagination(url=url, model=Meeting, params=params)]

    def list_of_series_gen(self, meeting_series_id: str, from_: str = None, to_: str = None, meeting_type: str = None,
                       state: str = None, is_modified: bool = None, host_email: str = None,
                       **params) -> AsyncGenerator[ScheduledMeeting, None, None]:
        """
        Lists scheduled meeting and meeting instances of a meeting series identified by meetingSeriesId. Scheduled
        meetings of an ad-hoc meeting created by Create a Meeting with adhoc of true and a roomId will not be listed,
        but the ended and ongoing meeting instances of it will be listed.
        Each scheduled meeting or meeting instance of a meeting series has its own start, end, etc. Thus, for example,
        when a daily meeting has been scheduled from 2019-04-01 to 2019-04-10, there are 10 scheduled meeting instances
        in this series, one instance for each day, and each one has its own attributes. When a scheduled meeting has
        been started and ended or is happening, there are even more ended or in-progress meeting instances.
        Use this operation to list scheduled meeting and meeting instances of a meeting series within a specific date
        range.
        Long result sets are split into pages.
        trackingCodes is not supported for ended meeting instances.

        :param meeting_series_id: Unique identifier for the meeting series. Please note that currently meeting ID of a
            scheduled personal room meeting is not supported for this API.
        :type meeting_series_id: str
        :param from_: Start date and time (inclusive) for the range for which meetings are to be returned in any ISO
            8601 compliant format. from cannot be after to.
        :type from_: str
        :param to_: End date and time (exclusive) for the range for which meetings are to be returned in any ISO 8601
            compliant format. to cannot be before from.
        :type to_: str
        :param meeting_type: Meeting type for the meeting objects being requested. If not specified, return meetings of
            all types. Possible values: scheduledMeeting, meeting
        :type meeting_type: str
        :param state: Meeting state for the meetings being requested. If not specified, return meetings of all states.
            Details of an ended meeting will only be available 15 minutes after the meeting has ended. inProgress
            meetings are not fully supported. The API will try to return details of an inProgress meeting 15 minutes
            after the meeting starts. However, it may take longer depending on the traffic. See the Webex Meetings
            guide for more information about the states of meetings. Possible values: scheduled, ready, lobby,
            inProgress, ended, missed
        :type state: str
        :param is_modified: Flag identifying whether or not only to retrieve scheduled meeting instances which have
            been modified. This parameter only applies to scheduled meetings. If it's true, only return modified
            scheduled meetings; if it's false, only return unmodified scheduled meetings; if not specified, all
            scheduled meetings will be returned.
        :type is_modified: bool
        :param host_email: Email address for the meeting host. This parameter is only used if the user or application
            calling the API has the admin-level scopes. If set, the admin may specify the email of a user in a site
            they manage and the API will return meetings that are hosted by that user.
        :type host_email: str

        documentation: https://developer.webex.com/docs/api/v1/meetings/list-meetings-of-a-meeting-series
        """
        params['meetingSeriesId'] = meeting_series_id
        if from_ is not None:
            params['from'] = from_
        if to_ is not None:
            params['to'] = to_
        if meeting_type is not None:
            params['meetingType'] = meeting_type
        if state is not None:
            params['state'] = state
        if is_modified is not None:
            params['isModified'] = is_modified
        if host_email is not None:
            params['hostEmail'] = host_email
        url = self.ep()
        return self.session.follow_pagination(url=url, model=ScheduledMeeting, params=params)

    async def list_of_series(self, meeting_series_id: str, from_: str = None, to_: str = None, meeting_type: str = None,
                       state: str = None, is_modified: bool = None, host_email: str = None,
                       **params) -> List[ScheduledMeeting]:
        """
        Lists scheduled meeting and meeting instances of a meeting series identified by meetingSeriesId. Scheduled
        meetings of an ad-hoc meeting created by Create a Meeting with adhoc of true and a roomId will not be listed,
        but the ended and ongoing meeting instances of it will be listed.
        Each scheduled meeting or meeting instance of a meeting series has its own start, end, etc. Thus, for example,
        when a daily meeting has been scheduled from 2019-04-01 to 2019-04-10, there are 10 scheduled meeting instances
        in this series, one instance for each day, and each one has its own attributes. When a scheduled meeting has
        been started and ended or is happening, there are even more ended or in-progress meeting instances.
        Use this operation to list scheduled meeting and meeting instances of a meeting series within a specific date
        range.
        Long result sets are split into pages.
        trackingCodes is not supported for ended meeting instances.

        :param meeting_series_id: Unique identifier for the meeting series. Please note that currently meeting ID of a
            scheduled personal room meeting is not supported for this API.
        :type meeting_series_id: str
        :param from_: Start date and time (inclusive) for the range for which meetings are to be returned in any ISO
            8601 compliant format. from cannot be after to.
        :type from_: str
        :param to_: End date and time (exclusive) for the range for which meetings are to be returned in any ISO 8601
            compliant format. to cannot be before from.
        :type to_: str
        :param meeting_type: Meeting type for the meeting objects being requested. If not specified, return meetings of
            all types. Possible values: scheduledMeeting, meeting
        :type meeting_type: str
        :param state: Meeting state for the meetings being requested. If not specified, return meetings of all states.
            Details of an ended meeting will only be available 15 minutes after the meeting has ended. inProgress
            meetings are not fully supported. The API will try to return details of an inProgress meeting 15 minutes
            after the meeting starts. However, it may take longer depending on the traffic. See the Webex Meetings
            guide for more information about the states of meetings. Possible values: scheduled, ready, lobby,
            inProgress, ended, missed
        :type state: str
        :param is_modified: Flag identifying whether or not only to retrieve scheduled meeting instances which have
            been modified. This parameter only applies to scheduled meetings. If it's true, only return modified
            scheduled meetings; if it's false, only return unmodified scheduled meetings; if not specified, all
            scheduled meetings will be returned.
        :type is_modified: bool
        :param host_email: Email address for the meeting host. This parameter is only used if the user or application
            calling the API has the admin-level scopes. If set, the admin may specify the email of a user in a site
            they manage and the API will return meetings that are hosted by that user.
        :type host_email: str

        documentation: https://developer.webex.com/docs/api/v1/meetings/list-meetings-of-a-meeting-series
        """
        params['meetingSeriesId'] = meeting_series_id
        if from_ is not None:
            params['from'] = from_
        if to_ is not None:
            params['to'] = to_
        if meeting_type is not None:
            params['meetingType'] = meeting_type
        if state is not None:
            params['state'] = state
        if is_modified is not None:
            params['isModified'] = is_modified
        if host_email is not None:
            params['hostEmail'] = host_email
        url = self.ep()
        return [o async for o in self.session.follow_pagination(url=url, model=ScheduledMeeting, params=params)]

    async def patch(self, meeting_id: str, title: str = None, agenda: str = None, password: str = None, start: str = None,
              end: str = None, timezone: str = None, recurrence: str = None, enabled_auto_record_meeting: bool = None,
              allow_any_user_to_be_co_host: bool = None, enabled_join_before_host: bool = None,
              enable_connect_audio_before_host: bool = None, join_before_host_minutes: int = None,
              exclude_password: bool = None, public_meeting: bool = None, reminder_time: int = None,
              unlocked_meeting_join_security: UnlockedMeetingJoinSecurity = None, session_type_id: int = None,
              enabled_webcast_view: bool = None, panelist_password: str = None, enable_automatic_lock: bool = None,
              automatic_lock_minutes: int = None, allow_first_user_to_be_co_host: bool = None,
              allow_authenticated_devices: bool = None, send_email: bool = None, host_email: str = None,
              site_url: str = None, meeting_options: MeetingOptions = None,
              attendee_privileges: AttendeePrivileges = None, integration_tags: List[str] = None,
              enabled_breakout_sessions: bool = None, tracking_codes: TrackingCodeItem = None,
              audio_connection_options: AudioConnectionOptions = None) -> PatchMeetingResponse:
        """
        Updates details for a meeting with a specified meeting ID. This operation applies to meeting series and
        scheduled meetings. It doesn't apply to ended or in-progress meeting instances. Ad-hoc meetings created by
        Create a Meeting with adhoc of true and a roomId cannot be updated.

        :param meeting_id: Unique identifier for the meeting to be updated. This parameter applies to meeting series
            and scheduled meetings. It doesn't apply to ended or in-progress meeting instances. Please note that
            currently meeting ID of a scheduled personal room meeting is not supported for this API.
        :type meeting_id: str
        :param title: Meeting title. The title can be a maximum of 128 characters long.
        :type title: str
        :param agenda: Meeting agenda. The agenda can be a maximum of 1300 characters long.
        :type agenda: str
        :param password: Meeting password. Must conform to the site's password complexity settings. Read password
            management for details.
        :type password: str
        :param start: Date and time for the start of meeting in any ISO 8601 compliant format. start cannot be before
            current date and time or after end. Duration between start and end cannot be shorter than 10 minutes or
            longer than 24 hours. Refer to the Webex Meetings guide for more information about restrictions on updating
            date and time for a meeting. Please note that when a meeting is being updated, start of the meeting will be
            accurate to minutes, not seconds or milliseconds. Therefore, if start is within the same minute as the
            current time, start will be adjusted to the upcoming minute; otherwise, start will be adjusted with seconds
            and milliseconds stripped off. For instance, if the current time is 2022-03-01T10:32:16.657+08:00, start of
            2022-03-01T10:32:28.076+08:00 or 2022-03-01T10:32:41+08:00 will be adjusted to 2022-03-01T10:33:00+08:00,
            and start of 2022-03-01T11:32:28.076+08:00 or 2022-03-01T11:32:41+08:00 will be adjusted to
            2022-03-01T11:32:00+08:00.
        :type start: str
        :param end: Date and time for the end of meeting in any ISO 8601 compliant format. end cannot be before current
            date and time or before start. Duration between start and end cannot be shorter than 10 minutes or longer
            than 24 hours. Refer to the Webex Meetings guide for more information about restrictions on updating date
            and time for a meeting. Please note that when a meeting is being updated, end of the meeting will be
            accurate to minutes, not seconds or milliseconds. Therefore, end will be adjusted with seconds and
            milliseconds stripped off. For instance, end of 2022-03-01T11:52:28.076+08:00 or 2022-03-01T11:52:41+08:00
            will be adjusted to 2022-03-01T11:52:00+08:00.
        :type end: str
        :param timezone: Time zone in which the meeting was originally scheduled (conforming with the IANA time zone
            database).
        :type timezone: str
        :param recurrence: Meeting series recurrence rule (conforming with RFC 2445). Applies only to a recurring
            meeting series, not to a meeting series with only one scheduled meeting. Multiple days or dates for monthly
            or yearly recurrence rule are not supported, only the first day or date specified is taken. For example,
            "FREQ=MONTHLY;INTERVAL=1;COUNT=10;BYMONTHDAY=10,11,12" is not supported and it will be partially supported
            as "FREQ=MONTHLY;INTERVAL=1;COUNT=10;BYMONTHDAY=10".
        :type recurrence: str
        :param enabled_auto_record_meeting: Whether or not meeting is recorded automatically.
        :type enabled_auto_record_meeting: bool
        :param allow_any_user_to_be_co_host: Whether or not to allow any attendee with a host account on the target
            site to become a cohost when joining the meeting. The target site is specified by siteUrl parameter when
            creating the meeting; if not specified, it's user's preferred site.
        :type allow_any_user_to_be_co_host: bool
        :param enabled_join_before_host: Whether or not to allow any attendee to join the meeting before the host joins
            the meeting.
        :type enabled_join_before_host: bool
        :param enable_connect_audio_before_host: Whether or not to allow any attendee to connect audio in the meeting
            before the host joins the meeting. This attribute is only applicable if the enabledJoinBeforeHost attribute
            is set to true.
        :type enable_connect_audio_before_host: bool
        :param join_before_host_minutes: The number of minutes an attendee can join the meeting before the meeting
            start time and the host joins. This attribute is only applicable if the enabledJoinBeforeHost attribute is
            set to true. Valid options are 0, 5, 10 and 15. Default is 0 if not specified.
        :type join_before_host_minutes: int
        :param exclude_password: Whether or not to exclude the meeting password from the email invitation.
        :type exclude_password: bool
        :param public_meeting: Whether or not to allow the meeting to be listed on the public calendar.
        :type public_meeting: bool
        :param reminder_time: The number of minutes before the meeting begins, that an email reminder is sent to the
            host.
        :type reminder_time: int
        :param unlocked_meeting_join_security: Specifies how the people who aren't on the invite can join the unlocked
            meeting.
        :type unlocked_meeting_join_security: UnlockedMeetingJoinSecurity
        :param session_type_id: Unique identifier for a meeting session type for the user. This attribute is required
            while scheduling webinar meeting. All available meeting session types enabled for the user can be retrieved
            by List Meeting Session Types API.
        :type session_type_id: int
        :param enabled_webcast_view: Whether or not webcast view is enabled.
        :type enabled_webcast_view: bool
        :param panelist_password: Password for panelists of a webinar meeting. Must conform to the site's password
            complexity settings. Read password management for details. If not specified, a random password conforming
            to the site's password rules will be generated automatically.
        :type panelist_password: str
        :param enable_automatic_lock: Whether or not to automatically lock the meeting after it starts.
        :type enable_automatic_lock: bool
        :param automatic_lock_minutes: The number of minutes after the meeting begins, for automatically locking it.
        :type automatic_lock_minutes: int
        :param allow_first_user_to_be_co_host: Whether or not to allow the first attendee of the meeting with a host
            account on the target site to become a cohost. The target site is specified by siteUrl parameter when
            creating the meeting; if not specified, it's user's preferred site.
        :type allow_first_user_to_be_co_host: bool
        :param allow_authenticated_devices: Whether or not to allow authenticated video devices in the meeting's
            organization to start or join the meeting without a prompt.
        :type allow_authenticated_devices: bool
        :param send_email: Whether or not to send emails to host and invitees. It is an optional field and default
            value is true.
        :type send_email: bool
        :param host_email: Email address for the meeting host. This attribute should only be set if the user or
            application calling the API has the admin-level scopes. When used, the admin may specify the email of a
            user in a site they manage to be the meeting host.
        :type host_email: str
        :param site_url: URL of the Webex site which the meeting is updated on. If not specified, the meeting is
            created on user's preferred site. All available Webex sites and preferred site of the user can be retrieved
            by Get Site List API.
        :type site_url: str
        :param meeting_options: Meeting Options.
        :type meeting_options: MeetingOptions
        :param attendee_privileges: Attendee Privileges.
        :type attendee_privileges: AttendeePrivileges
        :param integration_tags: External keys created by an integration application in its own domain, for example
            Zendesk ticket IDs, Jira IDs, Salesforce Opportunity IDs, etc. The integration application queries meetings
            by a key in its own domain. The maximum size of integrationTags is 3 and each item of integrationTags can
            be a maximum of 64 characters long. Please note that an empty or null integrationTags will delete all
            existing integration tags for the meeting implicitly. Developer can update integration tags for a
            meetingSeries but he cannot update it for a scheduledMeeting or a meeting instance.
        :type integration_tags: List[str]
        :param enabled_breakout_sessions: Whether or not breakout sessions are enabled. If the value of
            enabledBreakoutSessions is false, users can not set breakout sessions. If the value of
            enabledBreakoutSessions is true, users can update breakout sessions using the Update Breakout Sessions API.
            Updating breakout sessions are not supported by this API.
        :type enabled_breakout_sessions: bool
        :param tracking_codes: Tracking codes information. All available tracking codes and their options for the
            specified site can be retrieved by List Meeting Tracking Codes API. If an optional tracking code is missing
            from the trackingCodes array and there's a default option for this tracking code, the default option is
            assigned automatically. If the inputMode of a tracking code is select, its value must be one of the
            site-level options or the user-level value. Tracking code is not supported for a personal room meeting or
            an ad-hoc space meeting.
        :type tracking_codes: TrackingCodeItem
        :param audio_connection_options: Audio connection options.
        :type audio_connection_options: AudioConnectionOptions

        documentation: https://developer.webex.com/docs/api/v1/meetings/patch-a-meeting
        """
        body = PatchMeetingBody()
        if title is not None:
            body.title = title
        if agenda is not None:
            body.agenda = agenda
        if password is not None:
            body.password = password
        if start is not None:
            body.start = start
        if end is not None:
            body.end = end
        if timezone is not None:
            body.timezone = timezone
        if recurrence is not None:
            body.recurrence = recurrence
        if enabled_auto_record_meeting is not None:
            body.enabled_auto_record_meeting = enabled_auto_record_meeting
        if allow_any_user_to_be_co_host is not None:
            body.allow_any_user_to_be_co_host = allow_any_user_to_be_co_host
        if enabled_join_before_host is not None:
            body.enabled_join_before_host = enabled_join_before_host
        if enable_connect_audio_before_host is not None:
            body.enable_connect_audio_before_host = enable_connect_audio_before_host
        if join_before_host_minutes is not None:
            body.join_before_host_minutes = join_before_host_minutes
        if exclude_password is not None:
            body.exclude_password = exclude_password
        if public_meeting is not None:
            body.public_meeting = public_meeting
        if reminder_time is not None:
            body.reminder_time = reminder_time
        if unlocked_meeting_join_security is not None:
            body.unlocked_meeting_join_security = unlocked_meeting_join_security
        if session_type_id is not None:
            body.session_type_id = session_type_id
        if enabled_webcast_view is not None:
            body.enabled_webcast_view = enabled_webcast_view
        if panelist_password is not None:
            body.panelist_password = panelist_password
        if enable_automatic_lock is not None:
            body.enable_automatic_lock = enable_automatic_lock
        if automatic_lock_minutes is not None:
            body.automatic_lock_minutes = automatic_lock_minutes
        if allow_first_user_to_be_co_host is not None:
            body.allow_first_user_to_be_co_host = allow_first_user_to_be_co_host
        if allow_authenticated_devices is not None:
            body.allow_authenticated_devices = allow_authenticated_devices
        if send_email is not None:
            body.send_email = send_email
        if host_email is not None:
            body.host_email = host_email
        if site_url is not None:
            body.site_url = site_url
        if meeting_options is not None:
            body.meeting_options = meeting_options
        if attendee_privileges is not None:
            body.attendee_privileges = attendee_privileges
        if integration_tags is not None:
            body.integration_tags = integration_tags
        if enabled_breakout_sessions is not None:
            body.enabled_breakout_sessions = enabled_breakout_sessions
        if tracking_codes is not None:
            body.tracking_codes = tracking_codes
        if audio_connection_options is not None:
            body.audio_connection_options = audio_connection_options
        url = self.ep(f'{meeting_id}')
        data = await super().patch(url=url, data=body.model_dump_json())
        return PatchMeetingResponse.model_validate(data)

    async def update(self, meeting_id: str, title: str = None, agenda: str = None, password: str = None, start: str = None,
               end: str = None, timezone: str = None, recurrence: str = None, enabled_auto_record_meeting: bool = None,
               allow_any_user_to_be_co_host: bool = None, enabled_join_before_host: bool = None,
               enable_connect_audio_before_host: bool = None, join_before_host_minutes: int = None,
               exclude_password: bool = None, public_meeting: bool = None, reminder_time: int = None,
               unlocked_meeting_join_security: UnlockedMeetingJoinSecurity = None, session_type_id: int = None,
               enabled_webcast_view: bool = None, panelist_password: str = None, enable_automatic_lock: bool = None,
               automatic_lock_minutes: int = None, allow_first_user_to_be_co_host: bool = None,
               allow_authenticated_devices: bool = None, send_email: bool = None, host_email: str = None,
               site_url: str = None, meeting_options: MeetingOptions = None,
               attendee_privileges: AttendeePrivileges = None, integration_tags: List[str] = None,
               enabled_breakout_sessions: bool = None, tracking_codes: TrackingCodeItem = None,
               audio_connection_options: AudioConnectionOptions = None) -> PatchMeetingResponse:
        """
        Updates details for a meeting with a specified meeting ID. This operation applies to meeting series and
        scheduled meetings. It doesn't apply to ended or in-progress meeting instances. Ad-hoc meetings created by
        Create a Meeting with adhoc of true and a roomId cannot be updated.

        :param meeting_id: Unique identifier for the meeting to be updated. This parameter applies to meeting series
            and scheduled meetings. It doesn't apply to ended or in-progress meeting instances. Please note that
            currently meeting ID of a scheduled personal room meeting is not supported for this API.
        :type meeting_id: str
        :param title: Meeting title. The title can be a maximum of 128 characters long.
        :type title: str
        :param agenda: Meeting agenda. The agenda can be a maximum of 1300 characters long.
        :type agenda: str
        :param password: Meeting password. Must conform to the site's password complexity settings. Read password
            management for details.
        :type password: str
        :param start: Date and time for the start of meeting in any ISO 8601 compliant format. start cannot be before
            current date and time or after end. Duration between start and end cannot be shorter than 10 minutes or
            longer than 24 hours. Refer to the Webex Meetings guide for more information about restrictions on updating
            date and time for a meeting. Please note that when a meeting is being updated, start of the meeting will be
            accurate to minutes, not seconds or milliseconds. Therefore, if start is within the same minute as the
            current time, start will be adjusted to the upcoming minute; otherwise, start will be adjusted with seconds
            and milliseconds stripped off. For instance, if the current time is 2022-03-01T10:32:16.657+08:00, start of
            2022-03-01T10:32:28.076+08:00 or 2022-03-01T10:32:41+08:00 will be adjusted to 2022-03-01T10:33:00+08:00,
            and start of 2022-03-01T11:32:28.076+08:00 or 2022-03-01T11:32:41+08:00 will be adjusted to
            2022-03-01T11:32:00+08:00.
        :type start: str
        :param end: Date and time for the end of meeting in any ISO 8601 compliant format. end cannot be before current
            date and time or before start. Duration between start and end cannot be shorter than 10 minutes or longer
            than 24 hours. Refer to the Webex Meetings guide for more information about restrictions on updating date
            and time for a meeting. Please note that when a meeting is being updated, end of the meeting will be
            accurate to minutes, not seconds or milliseconds. Therefore, end will be adjusted with seconds and
            milliseconds stripped off. For instance, end of 2022-03-01T11:52:28.076+08:00 or 2022-03-01T11:52:41+08:00
            will be adjusted to 2022-03-01T11:52:00+08:00.
        :type end: str
        :param timezone: Time zone in which the meeting was originally scheduled (conforming with the IANA time zone
            database).
        :type timezone: str
        :param recurrence: Meeting series recurrence rule (conforming with RFC 2445). Applies only to a recurring
            meeting series, not to a meeting series with only one scheduled meeting. Multiple days or dates for monthly
            or yearly recurrence rule are not supported, only the first day or date specified is taken. For example,
            "FREQ=MONTHLY;INTERVAL=1;COUNT=10;BYMONTHDAY=10,11,12" is not supported and it will be partially supported
            as "FREQ=MONTHLY;INTERVAL=1;COUNT=10;BYMONTHDAY=10".
        :type recurrence: str
        :param enabled_auto_record_meeting: Whether or not meeting is recorded automatically.
        :type enabled_auto_record_meeting: bool
        :param allow_any_user_to_be_co_host: Whether or not to allow any attendee with a host account on the target
            site to become a cohost when joining the meeting. The target site is specified by siteUrl parameter when
            creating the meeting; if not specified, it's user's preferred site.
        :type allow_any_user_to_be_co_host: bool
        :param enabled_join_before_host: Whether or not to allow any attendee to join the meeting before the host joins
            the meeting.
        :type enabled_join_before_host: bool
        :param enable_connect_audio_before_host: Whether or not to allow any attendee to connect audio in the meeting
            before the host joins the meeting. This attribute is only applicable if the enabledJoinBeforeHost attribute
            is set to true.
        :type enable_connect_audio_before_host: bool
        :param join_before_host_minutes: The number of minutes an attendee can join the meeting before the meeting
            start time and the host joins. This attribute is only applicable if the enabledJoinBeforeHost attribute is
            set to true. Valid options are 0, 5, 10 and 15. Default is 0 if not specified.
        :type join_before_host_minutes: int
        :param exclude_password: Whether or not to exclude the meeting password from the email invitation.
        :type exclude_password: bool
        :param public_meeting: Whether or not to allow the meeting to be listed on the public calendar.
        :type public_meeting: bool
        :param reminder_time: The number of minutes before the meeting begins, that an email reminder is sent to the
            host.
        :type reminder_time: int
        :param unlocked_meeting_join_security: Specifies how the people who aren't on the invite can join the unlocked
            meeting.
        :type unlocked_meeting_join_security: UnlockedMeetingJoinSecurity
        :param session_type_id: Unique identifier for a meeting session type for the user. This attribute is required
            while scheduling webinar meeting. All available meeting session types enabled for the user can be retrieved
            by List Meeting Session Types API.
        :type session_type_id: int
        :param enabled_webcast_view: Whether or not webcast view is enabled.
        :type enabled_webcast_view: bool
        :param panelist_password: Password for panelists of a webinar meeting. Must conform to the site's password
            complexity settings. Read password management for details. If not specified, a random password conforming
            to the site's password rules will be generated automatically.
        :type panelist_password: str
        :param enable_automatic_lock: Whether or not to automatically lock the meeting after it starts.
        :type enable_automatic_lock: bool
        :param automatic_lock_minutes: The number of minutes after the meeting begins, for automatically locking it.
        :type automatic_lock_minutes: int
        :param allow_first_user_to_be_co_host: Whether or not to allow the first attendee of the meeting with a host
            account on the target site to become a cohost. The target site is specified by siteUrl parameter when
            creating the meeting; if not specified, it's user's preferred site.
        :type allow_first_user_to_be_co_host: bool
        :param allow_authenticated_devices: Whether or not to allow authenticated video devices in the meeting's
            organization to start or join the meeting without a prompt.
        :type allow_authenticated_devices: bool
        :param send_email: Whether or not to send emails to host and invitees. It is an optional field and default
            value is true.
        :type send_email: bool
        :param host_email: Email address for the meeting host. This attribute should only be set if the user or
            application calling the API has the admin-level scopes. When used, the admin may specify the email of a
            user in a site they manage to be the meeting host.
        :type host_email: str
        :param site_url: URL of the Webex site which the meeting is updated on. If not specified, the meeting is
            created on user's preferred site. All available Webex sites and preferred site of the user can be retrieved
            by Get Site List API.
        :type site_url: str
        :param meeting_options: Meeting Options.
        :type meeting_options: MeetingOptions
        :param attendee_privileges: Attendee Privileges.
        :type attendee_privileges: AttendeePrivileges
        :param integration_tags: External keys created by an integration application in its own domain, for example
            Zendesk ticket IDs, Jira IDs, Salesforce Opportunity IDs, etc. The integration application queries meetings
            by a key in its own domain. The maximum size of integrationTags is 3 and each item of integrationTags can
            be a maximum of 64 characters long. Please note that an empty or null integrationTags will delete all
            existing integration tags for the meeting implicitly. Developer can update integration tags for a
            meetingSeries but he cannot update it for a scheduledMeeting or a meeting instance.
        :type integration_tags: List[str]
        :param enabled_breakout_sessions: Whether or not breakout sessions are enabled. If the value of
            enabledBreakoutSessions is false, users can not set breakout sessions. If the value of
            enabledBreakoutSessions is true, users can update breakout sessions using the Update Breakout Sessions API.
            Updating breakout sessions are not supported by this API.
        :type enabled_breakout_sessions: bool
        :param tracking_codes: Tracking codes information. All available tracking codes and their options for the
            specified site can be retrieved by List Meeting Tracking Codes API. If an optional tracking code is missing
            from the trackingCodes array and there's a default option for this tracking code, the default option is
            assigned automatically. If the inputMode of a tracking code is select, its value must be one of the
            site-level options or the user-level value. Tracking code is not supported for a personal room meeting or
            an ad-hoc space meeting.
        :type tracking_codes: TrackingCodeItem
        :param audio_connection_options: Audio connection options.
        :type audio_connection_options: AudioConnectionOptions

        documentation: https://developer.webex.com/docs/api/v1/meetings/update-a-meeting
        """
        body = PatchMeetingBody()
        if title is not None:
            body.title = title
        if agenda is not None:
            body.agenda = agenda
        if password is not None:
            body.password = password
        if start is not None:
            body.start = start
        if end is not None:
            body.end = end
        if timezone is not None:
            body.timezone = timezone
        if recurrence is not None:
            body.recurrence = recurrence
        if enabled_auto_record_meeting is not None:
            body.enabled_auto_record_meeting = enabled_auto_record_meeting
        if allow_any_user_to_be_co_host is not None:
            body.allow_any_user_to_be_co_host = allow_any_user_to_be_co_host
        if enabled_join_before_host is not None:
            body.enabled_join_before_host = enabled_join_before_host
        if enable_connect_audio_before_host is not None:
            body.enable_connect_audio_before_host = enable_connect_audio_before_host
        if join_before_host_minutes is not None:
            body.join_before_host_minutes = join_before_host_minutes
        if exclude_password is not None:
            body.exclude_password = exclude_password
        if public_meeting is not None:
            body.public_meeting = public_meeting
        if reminder_time is not None:
            body.reminder_time = reminder_time
        if unlocked_meeting_join_security is not None:
            body.unlocked_meeting_join_security = unlocked_meeting_join_security
        if session_type_id is not None:
            body.session_type_id = session_type_id
        if enabled_webcast_view is not None:
            body.enabled_webcast_view = enabled_webcast_view
        if panelist_password is not None:
            body.panelist_password = panelist_password
        if enable_automatic_lock is not None:
            body.enable_automatic_lock = enable_automatic_lock
        if automatic_lock_minutes is not None:
            body.automatic_lock_minutes = automatic_lock_minutes
        if allow_first_user_to_be_co_host is not None:
            body.allow_first_user_to_be_co_host = allow_first_user_to_be_co_host
        if allow_authenticated_devices is not None:
            body.allow_authenticated_devices = allow_authenticated_devices
        if send_email is not None:
            body.send_email = send_email
        if host_email is not None:
            body.host_email = host_email
        if site_url is not None:
            body.site_url = site_url
        if meeting_options is not None:
            body.meeting_options = meeting_options
        if attendee_privileges is not None:
            body.attendee_privileges = attendee_privileges
        if integration_tags is not None:
            body.integration_tags = integration_tags
        if enabled_breakout_sessions is not None:
            body.enabled_breakout_sessions = enabled_breakout_sessions
        if tracking_codes is not None:
            body.tracking_codes = tracking_codes
        if audio_connection_options is not None:
            body.audio_connection_options = audio_connection_options
        url = self.ep(f'{meeting_id}')
        data = await super().put(url=url, data=body.model_dump_json())
        return PatchMeetingResponse.model_validate(data)

    async def delete(self, meeting_id: str, host_email: str = None, send_email: bool = None):
        """
        Deletes a meeting with a specified meeting ID. The deleted meeting cannot be recovered. This operation applies
        to meeting series and scheduled meetings. It doesn't apply to ended or in-progress meeting instances. Ad-hoc
        meetings created by Create a Meeting with adhoc of true and a roomId cannot be deleted.

        :param meeting_id: Unique identifier for the meeting to be deleted. This parameter applies to meeting series
            and scheduled meetings. It doesn't apply to ended or in-progress meeting instances.
        :type meeting_id: str
        :param host_email: Email address for the meeting host. This parameter is only used if the user or application
            calling the API has the admin-level scopes. If set, the admin may specify the email of a user in a site
            they manage and the API will delete a meeting that is hosted by that user.
        :type host_email: str
        :param send_email: Whether or not to send emails to host and invitees. It is an optional field and default
            value is true.
        :type send_email: bool

        documentation: https://developer.webex.com/docs/api/v1/meetings/delete-a-meeting
        """
        params = {}
        if host_email is not None:
            params['hostEmail'] = host_email
        if send_email is not None:
            params['sendEmail'] = send_email
        url = self.ep(f'{meeting_id}')
        await super().delete(url=url, params=params)
        return

    async def join(self, meeting_id: str = None, meeting_number: str = None, web_link: str = None, join_directly: bool = None,
             email: str = None, display_name: str = None, password: str = None,
             expiration_minutes: int = None) -> JoinMeetingResponse:
        """
        Retrieves a meeting join link for a meeting with a specified meetingId, meetingNumber, or webLink that allows
        users to join the meeting directly without logging in and entering a password.

        :param meeting_id: Unique identifier for the meeting. This parameter applies to meeting series and scheduled
            meetings. It doesn't apply to ended or in-progress meeting instances. Please note that currently meeting ID
            of a scheduled personal room meeting is also supported for this API.
        :type meeting_id: str
        :param meeting_number: Meeting number. Applies to meeting series, scheduled meeting, and meeting instances, but
            not to meeting instances which have ended.
        :type meeting_number: str
        :param web_link: Link to a meeting information page where the meeting client is launched if the meeting is
            ready to start or join.
        :type web_link: str
        :param join_directly: Whether or not to redirect to joinLink. It is an optional field and default value is
            true.
        :type join_directly: bool
        :param email: Email address of meeting participant. If the user is a guest issuer, email is required.
        :type email: str
        :param display_name: Display name of meeting participant. The maximum length of displayName is 128 characters.
            If the user is a guest issuer, displayName is required.
        :type display_name: str
        :param password: It's required when the meeting is protected by a password and the current user is not
            privileged to view it if they are not a host, cohost or invitee of the meeting.
        :type password: str
        :param expiration_minutes: Expiration duration of joinLink in minutes. Must be between 1 and 60.
        :type expiration_minutes: int

        documentation: https://developer.webex.com/docs/api/v1/meetings/join-a-meeting
        """
        body = JoinMeetingBody()
        if meeting_id is not None:
            body.meeting_id = meeting_id
        if meeting_number is not None:
            body.meeting_number = meeting_number
        if web_link is not None:
            body.web_link = web_link
        if join_directly is not None:
            body.join_directly = join_directly
        if email is not None:
            body.email = email
        if display_name is not None:
            body.display_name = display_name
        if password is not None:
            body.password = password
        if expiration_minutes is not None:
            body.expiration_minutes = expiration_minutes
        url = self.ep('join')
        data = await super().post(url=url, data=body.model_dump_json())
        return JoinMeetingResponse.model_validate(data)

    async def update_simultaneous_interpretation(self, meeting_id: str, enabled: bool,
                                           interpreters:
                                           InterpreterForSimultaneousInterpretation =
                                           None) -> SimultaneousInterpretation:
        """
        Updates simultaneous interpretation options of a meeting with a specified meeting ID. This operation applies to
        meeting series and scheduled meetings. It doesn't apply to ended or in-progress meeting instances.

        :param meeting_id: Unique identifier for the meeting. Does not support meeting IDs for a scheduled personal
            room meeting.
        :type meeting_id: str
        :param enabled: Whether or not simultaneous interpretation is enabled.
        :type enabled: bool
        :param interpreters: Interpreters for meeting.
        :type interpreters: InterpreterForSimultaneousInterpretation

        documentation: https://developer.webex.com/docs/api/v1/meetings/update-meeting-simultaneous-interpretation
        """
        body = SimultaneousInterpretation()
        if enabled is not None:
            body.enabled = enabled
        if interpreters is not None:
            body.interpreters = interpreters
        url = self.ep(f'{meeting_id}/simultaneousInterpretation')
        data = await super().put(url=url, data=body.model_dump_json())
        return SimultaneousInterpretation.model_validate(data)

    async def survey(self, meeting_id: str) -> GetMeetingSurveyResponse:
        """
        Retrieves details for a meeting survey identified by meetingId.

        :param meeting_id: Unique identifier for the meeting. Please note that only the meeting ID of a scheduled
            webinar is supported for this API.
        :type meeting_id: str

        documentation: https://developer.webex.com/docs/api/v1/meetings/get-a-meeting-survey
        """
        url = self.ep(f'{meeting_id}/survey')
        data = await super().get(url=url)
        return GetMeetingSurveyResponse.model_validate(data)

    def list_survey_results_gen(self, meeting_id: str, meeting_start_time_from: str = None,
                            meeting_start_time_to: str = None, **params) -> AsyncGenerator[SurveyResult, None, None]:
        """
        Retrieves results for a meeting survey identified by meetingId.

        :param meeting_id: Unique identifier for the meeting. Please note that only the meeting ID of a scheduled
            webinar is supported for this API.
        :type meeting_id: str
        :param meeting_start_time_from: Start date and time (inclusive) in any ISO 8601 compliant format for the
            meeting objects being requested. meetingStartTimeFrom cannot be after meetingStartTimeTo. This parameter
            will be ignored if meetingId is the unique identifier for the specific meeting instance. When meetingId is
            not the unique identifier for the specific meeting instance, the meetingStartTimeFrom, if not specified,
            equals meetingStartTimeTo minus 1 month; if meetingStartTimeTo is also not specified, the default value for
            meetingStartTimeFrom is 1 month before the current date and time.
        :type meeting_start_time_from: str
        :param meeting_start_time_to: End date and time (exclusive) in any ISO 8601 compliant format for the meeting
            objects being requested. meetingStartTimeTo cannot be prior to meetingStartTimeFrom. This parameter will be
            ignored if meetingId is the unique identifier for the specific meeting instance. When meetingId is not the
            unique identifier for the specific meeting instance, if meetingStartTimeFrom is also not specified, the
            default value for meetingStartTimeTo is the current date and time;For example,if meetingStartTimeFrom is a
            month ago, the default value for meetingStartTimeTo is 1 month after meetingStartTimeFrom.Otherwise it is
            the current date and time.
        :type meeting_start_time_to: str

        documentation: https://developer.webex.com/docs/api/v1/meetings/list-meeting-survey-results
        """
        if meeting_start_time_from is not None:
            params['meetingStartTimeFrom'] = meeting_start_time_from
        if meeting_start_time_to is not None:
            params['meetingStartTimeTo'] = meeting_start_time_to
        url = self.ep(f'{meeting_id}/surveyResults')
        return self.session.follow_pagination(url=url, model=SurveyResult, params=params)

    async def list_survey_results(self, meeting_id: str, meeting_start_time_from: str = None,
                            meeting_start_time_to: str = None, **params) -> List[SurveyResult]:
        """
        Retrieves results for a meeting survey identified by meetingId.

        :param meeting_id: Unique identifier for the meeting. Please note that only the meeting ID of a scheduled
            webinar is supported for this API.
        :type meeting_id: str
        :param meeting_start_time_from: Start date and time (inclusive) in any ISO 8601 compliant format for the
            meeting objects being requested. meetingStartTimeFrom cannot be after meetingStartTimeTo. This parameter
            will be ignored if meetingId is the unique identifier for the specific meeting instance. When meetingId is
            not the unique identifier for the specific meeting instance, the meetingStartTimeFrom, if not specified,
            equals meetingStartTimeTo minus 1 month; if meetingStartTimeTo is also not specified, the default value for
            meetingStartTimeFrom is 1 month before the current date and time.
        :type meeting_start_time_from: str
        :param meeting_start_time_to: End date and time (exclusive) in any ISO 8601 compliant format for the meeting
            objects being requested. meetingStartTimeTo cannot be prior to meetingStartTimeFrom. This parameter will be
            ignored if meetingId is the unique identifier for the specific meeting instance. When meetingId is not the
            unique identifier for the specific meeting instance, if meetingStartTimeFrom is also not specified, the
            default value for meetingStartTimeTo is the current date and time;For example,if meetingStartTimeFrom is a
            month ago, the default value for meetingStartTimeTo is 1 month after meetingStartTimeFrom.Otherwise it is
            the current date and time.
        :type meeting_start_time_to: str

        documentation: https://developer.webex.com/docs/api/v1/meetings/list-meeting-survey-results
        """
        if meeting_start_time_from is not None:
            params['meetingStartTimeFrom'] = meeting_start_time_from
        if meeting_start_time_to is not None:
            params['meetingStartTimeTo'] = meeting_start_time_to
        url = self.ep(f'{meeting_id}/surveyResults')
        return [o async for o in self.session.follow_pagination(url=url, model=SurveyResult, params=params)]

    def list_tracking_codes_gen(self, service: str, site_url: str = None,
                            host_email: str = None) -> AsyncGenerator[TrackingCode, None, None]:
        """
        Lists tracking codes on a site by a meeting host. The result indicates which tracking codes and what options
        can be used to create or update a meeting on the specified site.

        :param service: Service for schedule or sign-up pages.
        :type service: str
        :param site_url: URL of the Webex site which the API retrieves the tracking code from. If not specified, the
            API retrieves the tracking code from the user's preferred site. All available Webex sites and preferred
            sites of a user can be retrieved by Get Site List API.
        :type site_url: str
        :param host_email: Email address for the meeting host. This parameter is only used if a user or application
            calling the API has the admin-level scopes. The admin may specify the email of a user on a site they manage
            and the API will return meeting participants of the meetings that are hosted by that user.
        :type host_email: str

        documentation: https://developer.webex.com/docs/api/v1/meetings/list-meeting-tracking-codes
        """
        params = dict()
        params['service'] = service
        if site_url is not None:
            params['siteUrl'] = site_url
        if host_email is not None:
            params['hostEmail'] = host_email
        url = self.ep('trackingCodes')
        return self.session.follow_pagination(url=url, model=TrackingCode, params=params)

    async def list_tracking_codes(self, service: str, site_url: str = None,
                            host_email: str = None) -> List[TrackingCode]:
        """
        Lists tracking codes on a site by a meeting host. The result indicates which tracking codes and what options
        can be used to create or update a meeting on the specified site.

        :param service: Service for schedule or sign-up pages.
        :type service: str
        :param site_url: URL of the Webex site which the API retrieves the tracking code from. If not specified, the
            API retrieves the tracking code from the user's preferred site. All available Webex sites and preferred
            sites of a user can be retrieved by Get Site List API.
        :type site_url: str
        :param host_email: Email address for the meeting host. This parameter is only used if a user or application
            calling the API has the admin-level scopes. The admin may specify the email of a user on a site they manage
            and the API will return meeting participants of the meetings that are hosted by that user.
        :type host_email: str

        documentation: https://developer.webex.com/docs/api/v1/meetings/list-meeting-tracking-codes
        """
        params = dict()
        params['service'] = service
        if site_url is not None:
            params['siteUrl'] = site_url
        if host_email is not None:
            params['hostEmail'] = host_email
        url = self.ep('trackingCodes')
        return [o async for o in self.session.follow_pagination(url=url, model=TrackingCode, params=params)]


class AsMembershipApi(AsApiChild, base='memberships'):
    """
    Manipulating Team Memberships as a Compliance Officer

    As a Compliance Officer, you can indirectly manage the memberships of a team to which you do not belong.
    Individuals added to the team's general space are automatically considered team members. Therefore, you can
    utilize your standard privilege of adding individuals to a space or room by adding them to the team's general
    space.

    The team ID contains the general room ID with a different prefix. To locate the general room ID of a team, you need
    to decode and recode the team ID using the new prefix. Below is a command-line example for this process. Note that
    the final sed replacement is used to remove padding characters.

    Example: echo "Y2lzY29zcGFyazovL3VzL1RFQU0vYjQ5ODhmODAtN2QzMS0xMWVkLTk4Y2MtNWY5MTFhZWU1OTA0" | base64 -d | sed
    's/TEAM/ROOM/' | base64 | sed 's/\=.//'
    """

    def list_gen(self, room_id: str = None, person_id: str = None, person_email: str = None,
             **params) -> AsyncGenerator[Membership, None, None]:
        """
        Lists all room memberships. By default, lists memberships for rooms to which the authenticated user belongs.
        Use query parameters to filter the response.
        Use roomId to list memberships for a room, by ID.
        NOTE: For moderated team spaces, the list of memberships will include only the space moderators if the user
        is a team member but not a direct participant of the space.
        Use either personId or personEmail to filter the results. The roomId parameter is required when using these
        parameters.
        Long result sets will be split into pages.

        :param room_id: List memberships associated with a room, by ID.
        :type room_id: str
        :param person_id: List memberships associated with a person, by ID. The roomId parameter is required
            when using this parameter.
        :type person_id: str
        :param person_email: List memberships associated with a person, by email address. The roomId parameter
            is required when using this parameter.
        :type person_email: str
        """
        if room_id is not None:
            params['roomId'] = room_id
        if person_id is not None:
            params['personId'] = person_id
        if person_email is not None:
            params['personEmail'] = person_email
        url = self.ep()
        # noinspection PyTypeChecker
        return self.session.follow_pagination(url=url, model=Membership, params=params)

    async def list(self, room_id: str = None, person_id: str = None, person_email: str = None,
             **params) -> List[Membership]:
        """
        Lists all room memberships. By default, lists memberships for rooms to which the authenticated user belongs.
        Use query parameters to filter the response.
        Use roomId to list memberships for a room, by ID.
        NOTE: For moderated team spaces, the list of memberships will include only the space moderators if the user
        is a team member but not a direct participant of the space.
        Use either personId or personEmail to filter the results. The roomId parameter is required when using these
        parameters.
        Long result sets will be split into pages.

        :param room_id: List memberships associated with a room, by ID.
        :type room_id: str
        :param person_id: List memberships associated with a person, by ID. The roomId parameter is required
            when using this parameter.
        :type person_id: str
        :param person_email: List memberships associated with a person, by email address. The roomId parameter
            is required when using this parameter.
        :type person_email: str
        """
        if room_id is not None:
            params['roomId'] = room_id
        if person_id is not None:
            params['personId'] = person_id
        if person_email is not None:
            params['personEmail'] = person_email
        url = self.ep()
        # noinspection PyTypeChecker
        return [o async for o in self.session.follow_pagination(url=url, model=Membership, params=params)]

    async def create(self, room_id: str, person_id: str = None, person_email: str = None,
               is_moderator: bool = None) -> Membership:
        """
        Add someone to a room by Person ID or email address, optionally making them a moderator.

        :param room_id: The room ID.
        :type room_id: str
        :param person_id: The person ID.
        :type person_id: str
        :param person_email: The email address of the person.
        :type person_email: str
        :param is_moderator: Whether or not the participant is a room moderator.
        :type is_moderator: bool
        """
        body = {}
        if room_id is not None:
            body['roomId'] = room_id
        if person_id is not None:
            body['personId'] = person_id
        if person_email is not None:
            body['personEmail'] = person_email
        if is_moderator is not None:
            body['isModerator'] = is_moderator
        url = self.ep()
        data = await super().post(url=url, json=body)
        return Membership.model_validate(data)

    async def details(self, membership_id: str) -> Membership:
        """
        Get details for a membership by ID.
        Specify the membership ID in the membershipId URI parameter.

        :param membership_id: The unique identifier for the membership.
        :type membership_id: str
        """
        url = self.ep(f'{membership_id}')
        data = await super().get(url=url)
        return Membership.model_validate(data)

    async def update(self, update: Membership) -> Membership:
        """
        Updates properties for a membership by ID

        :param update: new settings; ID has to be set in update.

            These can be updated:
                is_moderator: bool: Whether or not the participant is a room moderator.

                is_room_hidden: bool: When set to true, hides direct spaces in the teams client. Any new message will
                make the room visible again.
        :type update: Membership

        """
        if update.id is None:
            raise ValueError('ID has to be set')
        data = update.update()
        url = self.ep(f'{update.id}')
        data = await super().put(url=url, json=data)
        return Membership.model_validate(data)

    async def delete(self, membership_id: str):
        """
        Deletes a membership by ID.
        Specify the membership ID in the membershipId URI parameter.
        The membership for the last moderator of a Team's General space may not be deleted; promote another user to
        team moderator first.

        :param membership_id: The unique identifier for the membership.
        :type membership_id: str
        """
        url = self.ep(f'{membership_id}')
        await super().delete(url=url)
        return


class AsMessagesApi(AsApiChild, base='messages'):
    """

    """

    def list_gen(self, room_id: str, parent_id: str = None, mentioned_people: List[str] = None, before: datetime = None,
             before_message: str = None, **params) -> AsyncGenerator[Message, None, None]:
        """
        Lists all messages in a room.  Each message will include content attachments if present.

        The list sorts the messages in descending order by creation date.

        Long result sets will be split into pages.

        :param room_id: List messages in a room, by ID.
        :type room_id: str
        :param parent_id: List messages with a parent, by ID.
        :type parent_id: str
        :param mentioned_people: List messages with these people mentioned, by ID. Use me as a shorthand
            for the current API user. Only me or the person ID of the current user may be specified. Bots must include
            this parameter to list messages in group rooms (spaces).
        :type mentioned_people: List[str]
        :param before: List messages sent before a date and time.
        :type before: str
        :param before_message: List messages sent before a message, by ID.
        :type before_message: str
        """
        if room_id is not None:
            params['roomId'] = room_id
        if parent_id is not None:
            params['parentId'] = parent_id
        if mentioned_people is not None:
            params['mentionedPeople'] = mentioned_people
        if before is not None:
            params['before'] = dt_iso_str(before)
        if before_message is not None:
            params['beforeMessage'] = before_message
        url = self.ep()
        # noinspection PyTypeChecker
        return self.session.follow_pagination(url=url, model=Message, params=params)

    async def list(self, room_id: str, parent_id: str = None, mentioned_people: List[str] = None, before: datetime = None,
             before_message: str = None, **params) -> List[Message]:
        """
        Lists all messages in a room.  Each message will include content attachments if present.

        The list sorts the messages in descending order by creation date.

        Long result sets will be split into pages.

        :param room_id: List messages in a room, by ID.
        :type room_id: str
        :param parent_id: List messages with a parent, by ID.
        :type parent_id: str
        :param mentioned_people: List messages with these people mentioned, by ID. Use me as a shorthand
            for the current API user. Only me or the person ID of the current user may be specified. Bots must include
            this parameter to list messages in group rooms (spaces).
        :type mentioned_people: List[str]
        :param before: List messages sent before a date and time.
        :type before: str
        :param before_message: List messages sent before a message, by ID.
        :type before_message: str
        """
        if room_id is not None:
            params['roomId'] = room_id
        if parent_id is not None:
            params['parentId'] = parent_id
        if mentioned_people is not None:
            params['mentionedPeople'] = mentioned_people
        if before is not None:
            params['before'] = dt_iso_str(before)
        if before_message is not None:
            params['beforeMessage'] = before_message
        url = self.ep()
        # noinspection PyTypeChecker
        return [o async for o in self.session.follow_pagination(url=url, model=Message, params=params)]

    def list_direct_gen(self, parent_id: str = None, person_id: str = None, person_email: str = None,
                    **params) -> AsyncGenerator[Message, None, None]:
        """
        List all messages in a 1:1 (direct) room. Use the personId or personEmail query parameter to specify the
        room. Each message will include content attachments if present.
        The list sorts the messages in descending order by creation date.

        :param parent_id: List messages with a parent, by ID.
        :type parent_id: str
        :param person_id: List messages in a 1:1 room, by person ID.
        :type person_id: str
        :param person_email: List messages in a 1:1 room, by person email.
        :type person_email: str
        """
        if parent_id is not None:
            params['parentId'] = parent_id
        if person_id is not None:
            params['personId'] = person_id
        if person_email is not None:
            params['personEmail'] = person_email
        url = self.ep('direct')
        # noinspection PyTypeChecker
        return self.session.follow_pagination(url=url, model=Message, params=params)

    async def list_direct(self, parent_id: str = None, person_id: str = None, person_email: str = None,
                    **params) -> List[Message]:
        """
        List all messages in a 1:1 (direct) room. Use the personId or personEmail query parameter to specify the
        room. Each message will include content attachments if present.
        The list sorts the messages in descending order by creation date.

        :param parent_id: List messages with a parent, by ID.
        :type parent_id: str
        :param person_id: List messages in a 1:1 room, by person ID.
        :type person_id: str
        :param person_email: List messages in a 1:1 room, by person email.
        :type person_email: str
        """
        if parent_id is not None:
            params['parentId'] = parent_id
        if person_id is not None:
            params['personId'] = person_id
        if person_email is not None:
            params['personEmail'] = person_email
        url = self.ep('direct')
        # noinspection PyTypeChecker
        return [o async for o in self.session.follow_pagination(url=url, model=Message, params=params)]

    async def create(self, room_id: str = None, parent_id: str = None, to_person_id: str = None, to_person_email: str = None,
               text: str = None, markdown: str = None, html: str = None, files: List[str] = None,
               attachments: List[Union[dict, MessageAttachment]] = None) -> Message:
        """
        Post a plain text, rich text or html message, and optionally, a file attachment, to a room.

        The files parameter is an array, which accepts multiple values to allow for future expansion, but currently
        only one file may be included with the message. File previews are only rendered for attachments of 1MB or less.

        html formatting is limited to the following markup h1,h2,h3,ul,ol,u,i,b and links.

        :param room_id: The room ID of the message.
        :type room_id: str
        :param parent_id: The parent message to reply to.
        :type parent_id: str
        :param to_person_id: The person ID of the recipient when sending a private 1:1 message.
        :type to_person_id: str
        :param to_person_email: The email address of the recipient when sending a private 1:1 message.
        :type to_person_email: str
        :param text: The message, in plain text. If markdown is specified this parameter may be optionally used
            to provide alternate text for UI clients that do not support rich text. The maximum message length is 7439
            bytes.
        :type text: str
        :param markdown: The message, in Markdown format. The maximum message length is 7439 bytes.
        :type markdown: str
        :param html: The message, in HTML format. The maximum message length is 7439 bytes.
        :type html: str
        :param files: The public URL to a binary file or a path to a local file to be posted into the room.
            Only one file is allowed
            per message. Uploaded files are automatically converted into a format that all Webex clients can render. For
            the supported media types and the behavior of uploads, see the Message Attachments Guide.
        :type files: List[str]
        :param attachments: Content attachments to attach to the message. Only one card per message
            is supported. See the Cards Guide for more information.
        :type attachments: List[Attachment]
        :rtype: Message
        """
        # TODO: handle local files for attachments
        body = {}
        if room_id is not None:
            body['roomId'] = room_id
        if parent_id is not None:
            body['parentId'] = parent_id
        if to_person_id is not None:
            body['toPersonId'] = to_person_id
        if to_person_email is not None:
            body['toPersonEmail'] = to_person_email
        if text is not None:
            body['text'] = text
        if markdown is not None:
            body['markdown'] = markdown
        if html is not None:
            body['html'] = html
        if attachments is not None:
            body['attachments'] = [a.model_dump(by_alias=True) if isinstance(a, MessageAttachment) else a
                                   for a in attachments]
        if files is not None:
            body['files'] = files

        url = self.ep()
        if files and os.path.isfile(files[0]):
            # this is a local file
            open_file = open(files[0], mode='rb')
            try:
                c_type = mimetypes.guess_type(files[0])[0] or 'text/plain'
                body['files'] = (os.path.basename(files[0]),
                                 open_file,
                                 c_type)
                multipart = MultipartEncoder(body)
                headers = {'Content-type': multipart.content_type}
                data = await super().post(url=url, headers=headers, data=multipart)
            finally:
                open_file.close()
        else:
            data = await super().post(url=url, json=body)
        return Message.model_validate(data)

    async def edit(self, message: Message) -> Message:
        """
        Update a message you have posted not more than 10 times.
        Specify the messageId of the message you want to edit.
        Edits of messages containing files or attachments are not currently supported.
        If a user attempts to edit a message containing files or attachments a 400 Bad Request will be returned by
        the API with a message stating that the feature is currently unsupported.
        There is also a maximum number of times a user can edit a message. The maximum currently supported is 10
        edits per message.
        If a user attempts to edit a message greater that the maximum times allowed the API will return 400 Bad
        Request with a message stating the edit limit has been reached.
        While only the roomId and text or markdown attributes are required in the request body, a common pattern for
        editing message is to first call GET /messages/{id} for the message you wish to edit and to then update the
        text or markdown attribute accordingly, passing the updated message object in the request body of the PUT
        /messages/{id} request.

        :param message: the updated message, id has to be set in the message
            attributes supported for update:

                * room_id: str: The room ID of the message.
                * text: str: The message, in plain text. If markdown is specified this parameter may be optionally used
                  to provide alternate text for UI clients that do not support rich text. The maximum message length
                  is 7439 bytes.
                * markdown: str: The message, in Markdown format. If this attribute is set ensure that the request does
                  NOT contain an html attribute.
                * html: str: The message, in HTML format. The maximum message length is 7439 bytes.
        """
        data = message.model_dump_json(include={'room_id', 'text', 'markdown', 'html'})
        if not message.id:
            raise ValueError('ID has to be set')
        url = self.ep(f'{message.id}')
        data = await super().put(url=url, data=data)
        return Message.model_validate(data)

    async def details(self, message_id: str) -> Message:
        """
        Show details for a message, by message ID.
        Specify the message ID in the messageId parameter in the URI.

        :param message_id: The unique identifier for the message.
        :type message_id: str
        """
        url = self.ep(f'{message_id}')
        data = await super().get(url=url)
        return Message.model_validate(data)

    async def delete(self, message_id: str):
        """
        Delete a message, by message ID.
        Specify the message ID in the messageId parameter in the URI.

        :param message_id: The unique identifier for the message.
        :type message_id: str
        """
        url = self.ep(f'{message_id}')
        await super().delete(url=url)
        return


class AsOrganizationApi(AsApiChild, base='organizations'):
    async def list(self, calling_data: bool = None) -> list[Organization]:
        """
        List all organizations visible by your account. The results will not be paginated.

        :param calling_data: Include XSI endpoint values in the response (if applicable) for the organization.
            Default: false
        :type calling_data: bool
        :return: list of Organizations
        """
        params = calling_data and {'callingData': 'true'} or None
        data = await self.get(url=self.ep(), params=params)
        return TypeAdapter(list[Organization]).validate_python(data['items'])

    async def details(self, org_id: str, calling_data: bool = None) -> Organization:
        """
        Get Organization Details

        Shows details for an organization, by ID.

        :param org_id: The unique identifier for the organization.
        :type org_id: str
        :param calling_data: Include XSI endpoint values in the response (if applicable) for the organization.
            Default: false
        :type calling_data: bool
        :return: org details
        :rtype: :class:`Organization`
        """
        url = self.ep(org_id)
        params = calling_data and {'callingData': 'true'} or None
        data = await self.get(url=url, params=params)
        return Organization.model_validate(data)

    async def delete(self, org_id: str):
        """
        Delete Organization

        Deletes an organization, by ID. It may take up to 10 minutes for the organization to be deleted after the
        response is returned.

        :param org_id: The unique identifier for the organization.
        :type org_id: str
        """
        url = self.ep(org_id)
        await super().delete(url=url)


class AsOrganizationContactsApi(AsApiChild, base='contacts/organizations'):
    """
    Organization Contacts

    Organizational contacts are entities that can be created, imported, or synchronized with Webex. Searching and
    viewing contacts require an auth token with a `scope
    <https://developer.webex.com/docs/integrations#scopes>`_ of `Identity:contact` or `Identity:SCIM`, while adding,
    updating, and removing contacts in your Organization requires an administrator auth token with the
    `Identity:contact` or `Identity:SCIM` scope. An admin can only operate on the contacts list for his org or a
    managed org.

    **Note**:

    * `broadworks-connector` entitled callers are limited to org contacts with either source=`CH` or
      source=`Webex4Broadworks`, while non-entitled callers are limited to source=`CH`.

    * The `orgId` used in the path for this API are the org UUIDs. They follow a xxxx-xxxx-xxxx-xxxx pattern. If you
      have an orgId in base64 encoded format (starting with Y2.....) you need to base64 decode the id and extract the
      UUID from the slug, before you use it in your API call.
    """

    async def create(self, org_id: str, contact: Contact) -> Contact:
        """
        Create a Contact

        Creating a new contact for a given organization requires an org admin role.

        At least one of the following body parameters: `phoneNumbers`, `emails`, `sipAddresses` is required to create a
        new contact for source "CH",
        `displayName` is required to create a new contact for source "Webex4Broadworks".

        Use the optional `groupIds` field to add group IDs in an array within the organisation contact. This will
        become a group contact.

        :param org_id: Webex Identity assigned organization identifier for the user's organization or the organization
            he manages.
        :type org_id: str
        :param contact: The contact to create.
        :type contact: Contact
        :rtype: None
        """
        body = contact.create()
        body['schemas'] = 'urn:cisco:codev:identity:contact:core:1.0'
        url = self.ep(f'{org_id}/contacts')
        data = await super().post(url, json=body)
        return Contact.model_validate(data)

    async def get(self, org_id: str, contact_id: str) -> Contact:
        """
        Get a Contact

        Shows details for an organization contact by ID.
        Specify the organization ID in the `orgId` parameter in the URI, and specify the contact ID in the `contactId`
        parameter in the URI.

        **NOTE**:
        The `orgId` used in the path for this API are the org UUIDs. They follow a xxxx-xxxx-xxxx-xxxx pattern. If you
        have an orgId in base64 encoded format (starting with Y2.....) you need to base64 decode the id and extract
        the UUID from the slug, before you use it in your API call.

        :param org_id: Webex Identity assigned organization identifier for the user's organization or the organization
            he manages.
        :type org_id: str
        :param contact_id: The contact ID.
        :type contact_id: str
        :rtype: :class:`Contact`
        """
        url = self.ep(f'{org_id}/contacts/{contact_id}')
        data = await super().get(url)
        r = Contact.model_validate(data)
        return r

    async def update(self, org_id: str, contact_id: str, update: Contact):
        """
        Update a Contact

        Update details for contact by ID. Only an admin can update a contact.
        Specify the organization ID in the `orgId` parameter in the URI, and specify the contact ID in the `contactId`
        parameter in the URI.

        Use the optional `groupIds` field to update the group IDs by changing the existing array. You can add or remove
        one or all groups. To remove all associated groups, pass an empty array in the `groupIds` field.

        :param org_id: Webex Identity assigned organization identifier for the user's organization or the organization
            he manages.
        :type org_id: str
        :param contact_id: The contact ID.
        :type contact_id: str
        :param update: the update
        :type update: Contact
        :rtype: None
        """
        body = update.create()
        url = self.ep(f'{org_id}/contacts/{contact_id}')
        await super().patch(url, json=body)

    async def delete(self, org_id: str, contact_id: str):
        """
        Delete a Contact

        Remove a contact from the organization. Only an admin can remove a contact.

        Specify the organization ID in the `orgId` parameter in the URI, and specify the contact ID in the `contactId`
        parameter in the URI.

        :param org_id: Webex Identity assigned organization identifier for the user's organization or the organization
            he manages.
        :type org_id: str
        :param contact_id: The contact ID.
        :type contact_id: str
        :rtype: None
        """
        url = self.ep(f'{org_id}/contacts/{contact_id}')
        await super().delete(url)

    def list_gen(self, org_id: str, keyword: str = None, source: str = None, limit: int = None,
             group_ids: list[str] = None) -> AsyncGenerator[Contact, None, None]:
        """
        List Contacts

        List contacts in the organization. The default limit is `100`.

        `keyword` can be the value of "displayName", "firstName", "lastName", "email". An empty string of `keyword`
        means get all contacts.

        `groupIds` is a comma separated list group IDs. Results are filtered based on those group IDs.

        Long result sets will be split into `pages
        <https://developer.webex.com/docs/basics#pagination>`_.

        :param org_id: The organization ID.
        :type org_id: str
        :param keyword: List contacts with a keyword.
        :type keyword: str
        :param source: List contacts with source.
        :type source: str
        :param limit: Limit the maximum number of contact in the response.
        + Default: 100
        :type limit: int
        :param group_ids: Filter contacts based on groups.
        :type group_ids: list[str]
        """
        params = {}
        if keyword is not None:
            params['keyword'] = keyword
        if source is not None:
            params['source'] = source
        if limit is not None:
            params['limit'] = limit
        if group_ids is not None:
            params['groupIds'] = ','.join(group_ids)
        url = self.ep(f'{org_id}/contacts/search')
        return self.session.follow_pagination(url, params=params, item_key='result', model=Contact)

    async def list(self, org_id: str, keyword: str = None, source: str = None, limit: int = None,
             group_ids: list[str] = None) -> List[Contact]:
        """
        List Contacts

        List contacts in the organization. The default limit is `100`.

        `keyword` can be the value of "displayName", "firstName", "lastName", "email". An empty string of `keyword`
        means get all contacts.

        `groupIds` is a comma separated list group IDs. Results are filtered based on those group IDs.

        Long result sets will be split into `pages
        <https://developer.webex.com/docs/basics#pagination>`_.

        :param org_id: The organization ID.
        :type org_id: str
        :param keyword: List contacts with a keyword.
        :type keyword: str
        :param source: List contacts with source.
        :type source: str
        :param limit: Limit the maximum number of contact in the response.
        + Default: 100
        :type limit: int
        :param group_ids: Filter contacts based on groups.
        :type group_ids: list[str]
        """
        params = {}
        if keyword is not None:
            params['keyword'] = keyword
        if source is not None:
            params['source'] = source
        if limit is not None:
            params['limit'] = limit
        if group_ids is not None:
            params['groupIds'] = ','.join(group_ids)
        url = self.ep(f'{org_id}/contacts/search')
        return [o async for o in self.session.follow_pagination(url, params=params, item_key='result', model=Contact)]

    async def bulk_create_or_update(self, org_id: str, contacts: List[Contact]) -> BulkResponse:
        """
        Bulk Create or Update Contacts

        Create or update contacts in bulk. Update an existing contact by specifying the contact ID in the `contactId`
        parameter in the request body.

        :param org_id: Webex Identity assigned organization identifier for the user's organization or the organization
            he manages.
        :type org_id: str
        :param contacts: Contains a list of contacts to be created/updated.
        :type contacts: list[BulkCreateContact]
        :rtype: None
        """
        body = dict()
        body['schemas'] = 'urn:cisco:codev:identity:contact:core:1.0'
        body['contacts'] = TypeAdapter(list[Contact]).dump_python(contacts, mode='json', by_alias=True,
                                                                  exclude_unset=True)
        url = self.ep(f'{org_id}/contacts/bulk')
        data = await super().post(url, json=body)
        return BulkResponse.model_validate(data)

    async def bulk_delete(self, org_id: str, object_ids: List[str]):
        """
        Bulk Delete Contacts

        Delete contacts in bulk.

        :param org_id: Webex Identity assigned organization identifier for the user's organization or the organization
            he manages.
        :type org_id: str
        :param object_ids: List of UUIDs for the contacts.
        :type object_ids: list[str]
        :rtype: None
        """
        body = dict()
        body['schemas'] = 'urn:cisco:codev:identity:contact:core:1.0'
        body['objectIds'] = object_ids
        url = self.ep(f'{org_id}/contacts/bulk/delete')
        await super().post(url, json=body)


class AsPeopleApi(AsApiChild, base='people'):
    """
    People

        **As of January 2024, the Webex APIs have been fully upgraded to support the
        industry-standard** :class:`SCIM2.0 <wxc_sdk.scim.ScimV2Api>`
        **protocol, which is
        used for user and group management, provisioning, and maintenance. Developers
        are advised to use this API instead of the people API, due to its higher
        performance and readily available connectors. Users created via SCIM can be
        licensed using the /licenses API, even in large quantities, using
        the new** :meth:`PATCH method <wxc_sdk.licenses.AsLicensesApi.assign_licenses_to_users>`.

    People are registered users of Webex. Searching and viewing People requires an auth token with a
    `scope <https://developer.webex.com/docs/integrations#scopes>`_
    of `spark:people_read`. Viewing the list of all People in your organization requires an administrator auth token
    with `spark-admin:people_read` scope. Adding, updating, and removing People requires an administrator auth token
    with the `spark-admin:people_write` and `spark-admin:people_read` scope.

    A person's call settings are for `Webex Calling` and necessitate Webex Calling licenses.

    To learn more about managing people in a room see the :class:`Memberships API
    <wxc_sdk.memberships.AsMembershipApi>`. For information about how to allocate Hybrid
    Services licenses to people, see the `Managing Hybrid Services
    <https://developer.webex.com/docs/api/guides/managing-hybrid-services-licenses>`_ guide.
    """

    def list_gen(self, email: str = None, display_name: str = None, id_list: list[str] = None, org_id: str = None,
             roles: str = None, calling_data: bool = None, location_id: str = None, exclude_status: bool = None,
             **params) -> AsyncGenerator[Person, None, None]:
        """
        List people in your organization. For most users, either the email or displayName parameter is required. Admin
        users can omit these fields and list all users in their organization.

        Response properties associated with a user's presence status, such as status or lastActivity, will only be
        returned for people within your organization or an organization you manage. Presence information will not be
        returned if the authenticated user has disabled status sharing.

        Admin users can include Webex Calling (BroadCloud) user details in the response by specifying callingData
        parameter as true. Admin users can list all users in a location or with a specific phone number. Admin users
        will receive an enriched payload with additional administrative fields like liceneses,roles etc. These fields
        are shown when accessing a user via GET /people/{id}, not when doing a GET /people?id=

        Lookup by email is only supported for people within the same org or where a partner admin relationship is in
        place.

        Lookup by roles is only supported for Admin users for the people within the same org.

        :param email: List people with this email address. For non-admin requests, either this or displayName are
            required.
        :type email: str
        :param display_name: List people whose name starts with this string. For non-admin requests, either this or
            email are required.
        :type display_name: str
        :param id_list: List people by ID. Accepts up to 85 person IDs. If this parameter is provided then presence
            information (such as the last_activity or status properties) will not be included in the response.
        :type id_list: list[str]
        :param org_id: List people in this organization. Only admin users of another organization (such as partners)
            may use this parameter.
        :type org_id: str
        :param roles: List of roleIds separated by commas.
        :type roles: str
        :param calling_data: Include Webex Calling user details in the response. Default: False
        :type calling_data: bool
        :param location_id: List people present in this location.
        :type location_id: str
        :param exclude_status: Omit people status/availability to enhance query performance.
        :type exclude_status: bool
        :return: yield :class:`Person` instances
        """
        params.update((to_camel(k), v)
                      for i, (k, v) in enumerate(locals().items())
                      if i and v is not None and k != 'params')
        if calling_data:
            params['callingData'] = 'true'
            # apparently there is a performance problem with getting too many users w/ calling data at the same time
            if CALLING_DATA_TIMEOUT_PROTECTION:
                params['max'] = params.get('max', MAX_USERS_WITH_CALLING_DATA)
        id_list = params.pop('idList', None)
        if id_list:
            params['id'] = ','.join(id_list)
        ep = self.ep()
        # noinspection PyTypeChecker
        return self.session.follow_pagination(url=ep, model=Person, params=params)

    async def list(self, email: str = None, display_name: str = None, id_list: list[str] = None, org_id: str = None,
             roles: str = None, calling_data: bool = None, location_id: str = None, exclude_status: bool = None,
             **params) -> List[Person]:
        """
        List people in your organization. For most users, either the email or displayName parameter is required. Admin
        users can omit these fields and list all users in their organization.

        Response properties associated with a user's presence status, such as status or lastActivity, will only be
        returned for people within your organization or an organization you manage. Presence information will not be
        returned if the authenticated user has disabled status sharing.

        Admin users can include Webex Calling (BroadCloud) user details in the response by specifying callingData
        parameter as true. Admin users can list all users in a location or with a specific phone number. Admin users
        will receive an enriched payload with additional administrative fields like liceneses,roles etc. These fields
        are shown when accessing a user via GET /people/{id}, not when doing a GET /people?id=

        Lookup by email is only supported for people within the same org or where a partner admin relationship is in
        place.

        Lookup by roles is only supported for Admin users for the people within the same org.

        :param email: List people with this email address. For non-admin requests, either this or displayName are
            required.
        :type email: str
        :param display_name: List people whose name starts with this string. For non-admin requests, either this or
            email are required.
        :type display_name: str
        :param id_list: List people by ID. Accepts up to 85 person IDs. If this parameter is provided then presence
            information (such as the last_activity or status properties) will not be included in the response.
        :type id_list: list[str]
        :param org_id: List people in this organization. Only admin users of another organization (such as partners)
            may use this parameter.
        :type org_id: str
        :param roles: List of roleIds separated by commas.
        :type roles: str
        :param calling_data: Include Webex Calling user details in the response. Default: False
        :type calling_data: bool
        :param location_id: List people present in this location.
        :type location_id: str
        :param exclude_status: Omit people status/availability to enhance query performance.
        :type exclude_status: bool
        :return: yield :class:`Person` instances
        """
        params.update((to_camel(k), v)
                      for i, (k, v) in enumerate(locals().items())
                      if i and v is not None and k != 'params')
        if calling_data:
            params['callingData'] = 'true'
            # apparently there is a performance problem with getting too many users w/ calling data at the same time
            if CALLING_DATA_TIMEOUT_PROTECTION:
                params['max'] = params.get('max', MAX_USERS_WITH_CALLING_DATA)
        id_list = params.pop('idList', None)
        if id_list:
            params['id'] = ','.join(id_list)
        ep = self.ep()
        # noinspection PyTypeChecker
        return [o async for o in self.session.follow_pagination(url=ep, model=Person, params=params)]

    async def create(self, settings: Person, calling_data: bool = False, min_response: bool = None) -> Person:
        """
        Create a Person

        Create a new user account for a given organization. Only an admin can create a new user account.

        At least one of the following body parameters is required to create a new user: displayName, firstName,
        lastName.

        Currently, users may have only one email address associated with their account. The emails parameter is an
        array, which accepts multiple values to allow for future expansion, but currently only one email address will
        be used for the new user.

        Admin users can include `Webex calling` (BroadCloud) user details in the response by specifying `callingData`
        parameter as true. It may happen that the POST request with calling data returns a 400 status, but the person
        was created still. One way to get into this state is if an invalid phone number is assigned to a user. The
        people API aggregates calls to several other microservices, and one may have failed. A best practice is to
        check if the user exists before retrying. This can be done with the user's email address and a GET /people.

        When doing attendee management, to make the new user an attendee for a site: append #attendee to the siteUrl
        parameter (eg: mysite.webex.com#attendee).

        **NOTES**:

        * For creating a `Webex Calling` user, you must provide `phoneNumbers` or `extension`, `locationId`, and
          `licenses` string in the same request.

        * `SipAddresses` are assigned via an asynchronous process. This means that the POST response may not show the
          SIPAddresses immediately. Instead, you can verify them with a separate GET to /people, after they were newly
          configured.

        * When assigning multiple licenses in a single request, the system will assign all valid and available
          licenses. If any requested licenses cannot be assigned, the operation will continue with the remaining
          licenses. As a result, it is possible that not all requested licenses are assigned to the user.

        :param settings: settings for new user
        :type settings: Person
        :param calling_data: Include Webex Calling user details in the response.
        :type calling_data: bool
        :param min_response: Set to `true` to improve performance by omitting person details and returning only the ID
            in the response when successful. If unsuccessful the response will have optional error details.
        :type min_response: bool
        :return: new user
        :rtype: Person
        """
        params = {}
        if calling_data is not None:
            params['callingData'] = str(calling_data).lower()
        if min_response is not None:
            params['minResponse'] = str(min_response).lower()
        url = self.ep()
        data = settings.create_update()
        return Person.model_validate(await self.post(url, json=data, params=params))

    async def details(self, person_id: str, calling_data: bool = False) -> Person:
        """
        Get Person Details

        Shows details for a person, by ID.

        Response properties associated with a user's presence status, such as `status` or `lastActivity`, will only be
        displayed for people within your organization or an organization you manage. Presence information will not be
        shown if the authenticated user has `disabled status sharing
        <https://help.webex.com/nkzs6wl/>`_.

        Admin users can include `Webex Calling` (BroadCloud) user details in the response by specifying `callingData`
        parameter as `true`.

        :param person_id: A unique identifier for the person.
        :type person_id: str
        :param calling_data: Include Webex Calling user details in the response. Default: false
        :type calling_data: bool
        :return: person details
        :rtype: Person
        """
        ep = self.ep(path=person_id)
        params = calling_data and {'callingData': 'true'} or None
        return Person.model_validate(await self.get(ep, params=params))

    async def delete_person(self, person_id: str):
        """
        Delete a Person

        Remove a person from the system.

        Required Administrator Roles:

        The following administrators have permission to use this API:

        Customer Organization:

        - Full administrator
        - User administrator

        Partner/External Access:

        - External full administrator

        Note: External read-only administrators, provisioning administrators, and device administrators cannot
        delete users.

        :param person_id: A unique identifier for the person.
        :return:
        """
        ep = self.ep(path=person_id)
        await self.delete(ep)

    async def update(self, person: Person, calling_data: bool = False, show_all_types: bool = False,
               min_response: bool = None) -> Person:
        """
        Update a Person

        Update details for a person, by ID.

        Specify the person ID in the `personId` parameter in the URI. Only an admin can update a person details.

        Include all details for the person. This action expects all user details to be present in the request. A common
        approach is to first `GET the person's details
        <https://developer.webex.com/docs/api/v1/people/get-person-details>`_, make changes, then PUT both the changed and unchanged values.

        Admin users can include `Webex Calling` (BroadCloud) user details in the response by specifying `callingData`
        parameter as true.

        When doing attendee management, to update a user from host role to an attendee for a site append `#attendee` to
        the respective `siteUrl` and remove the meeting host license for this site from the license array.

        To update a person from an attendee role to a host for a site, add the meeting license for this site in the
        meeting array, and remove that site from the `siteurl` parameter.

        To remove the attendee privilege for a user on a meeting site, remove the `sitename#attendee` from the
        `siteUrl`s array. The `showAllTypes` parameter must be set to `true`.

        **NOTE**:

        * The `locationId` can only be set when assigning a calling license to a user. It cannot be changed if a user
          is already an existing calling user.

        * The `extension` field should be used to update the Webex Calling extension for a person. The extension value
          should not include the location routing prefix. The `work_extension` type in the `phoneNumbers` object as seen
          in the response payload of `List People
          <https://developer.webex.com/docs/api/v1/people/list-people>`_ or `Get Person Details
          extension for a person.

        * When updating a user with multiple email addresses using a PUT request, ensure that the primary email address
          is listed first in the array. Note that the order of email addresses returned by a GET request is not
          guaranteed..

        * The People API is a combination of several microservices, each responsible for specific attributes of a
          person. As a result, a PUT request that returns an error response code may still have altered some values of
          the person's data. Therefore, it is recommended to perform a GET request after encountering an error to verify
          the current state of the resource.

        * Some licenses are implicitly assigned by the system and cannot be admin controlled. They are necessary for
          the baseline function of the Webex system. If you get an error about implicitly assigned licensed that cannot
          be removed, please ensure you have the corresponding license in your PUT request.

        * When assigning multiple licenses in a single request, the system will assign all valid and available
          licenses. If any requested licenses cannot be assigned, the operation will continue with the remaining
          licenses. As a result, it is possible that not all requested licenses are assigned to the user.

        :param person: The person to update
        :type person: Person
        :param calling_data: Include Webex Calling user details in the response. Default: False
        :type calling_data: bool
        :param show_all_types: Include additional user data like #attendee role
        :type show_all_types: bool
        :param min_response: Set to `true` to improve performance by omitting person details in the response. If
            unsuccessful the response will have optional error details.
        :type min_response: bool
        :return: Person details
        :rtype: Person
        """
        params = {}
        if calling_data:
            params['callingData'] = 'true'
        if show_all_types:
            params['showAllTypes'] = 'true'
        if min_response:
            params['minResponse'] = 'true'

        # some attributes should not be included in update
        data = person.create_update()
        ep = self.ep(path=person.person_id)
        return Person.model_validate(await self.put(url=ep, json=data, params=params))

    async def me(self, calling_data: bool = False) -> Person:
        """
        Show the profile for the authenticated user. This is the same as GET /people/{personId} using the Person ID
        associated with your Auth token.

        Admin users can include Webex Calling (BroadCloud) user details in the response by specifying callingData
        parameter as true.

        :param calling_data: True -> return calling data
        :type calling_data: bool
        :rtype: Person
        :return: profile of authenticated user
        """
        ep = self.ep('me')
        params = calling_data and {'callingData': 'true'} or None
        data = await self.get(ep, params=params)
        result = Person.model_validate(data)
        return result


class AsPersonSettingsApiChild(AsApiChild, base=''):
    """
    Base class for all classes implementing person settings APIs
    """

    feature = None

    def __init__(self, *, session: AsRestSession, selector: ApiSelector = ApiSelector.person):
        # set parameters to get the correct URL templates
        #
        #               selector                    feature_prefix  url template
        # workspaces    workspaces                      /features/      workspaces/{person_id}/features/{feature}{path}
        # locations     telephony/config/locations      /               telephony/config/locations/{person_id}{path}
        # person        people                          /features       people/{person_id}/features/{feature}{path}
        # virtual line  telephony/config/virtualLines   /               telephony/config/virtualLines/{person_id}/{feature}
        self.feature_prefix = '/features/'
        if selector == ApiSelector.workspace:
            self.selector = 'workspaces'
        elif selector == ApiSelector.location:
            self.selector = 'telephony/config/locations'
            self.feature_prefix = '/'
        elif selector == ApiSelector.virtual_line:
            self.selector = 'telephony/config/virtualLines'
            self.feature_prefix = '/'
        elif selector == ApiSelector.person:
            self.selector = 'people'
        else:
            raise ValueError(f'Invalid selector: {selector}')
        super().__init__(session=session, base=self.selector)

    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(base='')
        if cls.feature is None:
            raise TypeError('feature has to be defined')

    def f_ep(self, person_id: str, path: str = None) -> str:
        """
        person specific feature endpoint like v1/people/{uid}/features/....

        :meta private:
        :param person_id: Unique identifier for the person.
        :type person_id: str
        :param path: path in the endpoint after the feature base URL
        :type path: str
        :return: full endpoint
        :rtype: str
        """
        path = path and f'/{path}' or ''
        # url templates:
        #
        #               selector                        feature_prefix  url template
        # workspaces    workspaces                      /features/      workspaces/{person_id}/features/{feature}{path}
        # locations     telephony/config/locations      /               telephony/config/locations/{person_id}{path}
        # person        people                          /features       people/{person_id}/features/{feature}{path}
        # virtual line  telephony/config/virtualLines   /               telephony/config/virtualLines/{person_id}/{feature}
        selector = self.selector
        feature_prefix = self.feature_prefix
        # some paths need to be remapped
        alternates = {
            ('workspaces', 'anonymousCallReject'): ('telephony/config/workspaces', '/'),
            ('workspaces', 'bargeIn'): ('telephony/config/workspaces', '/'),
            ('workspaces', 'callBridge'): ('telephony/config/workspaces', '/'),
            ('workspaces', 'callPolicies'): ('telephony/config/workspaces', '/'),
            ('workspaces', 'doNotDisturb'): ('telephony/config/workspaces', '/'),
            ('workspaces', 'emergencyCallbackNumber'): ('telephony/config/workspaces', '/'),
            ('workspaces', 'musicOnHold'): ('telephony/config/workspaces', '/'),
            ('workspaces', 'outgoingPermission/digitPatterns'): ('telephony/config/workspaces', '/'),
            ('workspaces', 'privacy'): ('telephony/config/workspaces', '/'),
            ('workspaces', 'priorityAlert'): ('telephony/config/workspaces', '/'),
            ('workspaces', 'pushToTalk'): ('telephony/config/workspaces', '/'),
            ('workspaces', 'selectiveAccept'): ('telephony/config/workspaces', '/'),
            ('workspaces', 'selectiveForward'): ('telephony/config/workspaces', '/'),
            ('workspaces', 'selectiveReject'): ('telephony/config/workspaces', '/'),
            ('workspaces', 'sequentialRing'): ('telephony/config/workspaces', '/'),
            ('workspaces', 'simultaneousRing'): ('telephony/config/workspaces', '/'),
            ('workspaces', 'voicemail'): ('telephony/config/workspaces', '/'),
            ('people', 'agent'): ('telephony/config/people', '/'),
            ('people', 'callBridge'): ('telephony/config/people', '/features/'),
            ('people', 'emergencyCallbackNumber'): ('telephony/config/people', '/'),
            ('people', 'outgoingPermission/'): ('telephony/config/people', '/'),
            ('people', 'outgoingPermission/accessCodes'): ('telephony/config/people', '/'),
            ('people', 'outgoingPermission/digitPatterns'): ('telephony/config/people', '/'),
            ('people', 'musicOnHold'): ('telephony/config/people', '/'),
            ('people', 'selectiveAccept'): ('telephony/config/people', '/'),
            ('people', 'selectiveForward'): ('telephony/config/people', '/'),
            ('people', 'selectiveReject'): ('telephony/config/people', '/'),
        }
        if selector == 'people' and self.feature == 'voicemail' and path == '/passcode':
            # this is a new endpoint for users and is the only VM endpoint with a different URL structure
            return self.session.ep(f'telephony/config/people/{person_id}/voicemail/passcode')
        selector, feature_prefix = alternates.get((selector, self.feature), (selector, feature_prefix))
        return self.session.ep(f'{selector}/{person_id}{feature_prefix}{self.feature}{path}')


class AsAgentCallerIdApi(AsPersonSettingsApiChild):
    """
    API to manage agent caller id settings

    Also used for virtual lines
    """
    feature = 'agent'

    async def available_caller_ids(self, entity_id: str, org_id: str = None) -> list[AgentCallerId]:
        """
        Retrieve Agent's List of Available Caller IDs

        Get the list of call queues and hunt groups available for caller ID use by this person, virtual line, or
        workspace as an agent.

        This API requires a full, user, or read-only administrator or location administrator auth token with a scope
        of `spark-admin:people_read`.

        :param entity_id: Unique identifier for the person, virtual line, or workspace.
        :type entity_id: str
        :param org_id: ID of the organization in which the entity resides. Only admin users of another organization
            (such as partners) may use this parameter as the default is the same organization as the token used to
            access API.
        :type org_id: str
        :rtype: list[AvailableCallerIdObject]
        """
        ep = self.f_ep(entity_id, 'availableCallerIds')
        params = org_id and {'orgId': org_id} or None
        data = await self.get(ep, params=params)
        return TypeAdapter(list[AgentCallerId]).validate_python(data['availableCallerIds'])

    async def read(self, entity_id: str) -> AgentCallerId:
        """
        Retrieve Agent's Caller ID Information

        Retrieve the Agent's Caller ID Information.

        Each agent will be able to set their outgoing Caller ID as either the Call Queue's Caller ID, Hunt Group's
        Caller ID or their own configured Caller ID.

        This API requires a full admin or read-only administrator or location administrator auth token with a scope
        of `spark-admin:telephony_config_read`.

        :param entity_id: Unique identifier for the person, virtual line, or workspace
        :type entity_id: str
        :rtype: AgentCallerId
        """
        url = self.f_ep(entity_id, 'callerId')
        data = await super().get(url)
        r = AgentCallerId.model_validate(data['selectedCallerId'])
        return r

    async def configure(self, entity_id: str, selected_caller_id: str = None):
        """
        Modify Agent's Caller ID Information.

        Each Agent will be able to set their outgoing Caller ID as either the designated Call Queue's Caller ID or Hunt
        Group's Caller ID or their own configured Caller ID

        This API requires a full or user administrator or location administrator auth token with
        the `spark-admin:telephony_config_write` scope.

        :param entity_id: Unique identifier for the person, virtual line, or workspace
        :type entity_id: str
        :param selected_caller_id: The unique identifier of the call queue or hunt group to use for the agent's caller
            ID. Set to null to use the agent's own caller ID.
        :type selected_caller_id: str
        :rtype: None
        """
        body = {'selectedCallerId': selected_caller_id}
        url = self.f_ep(entity_id, 'callerId')
        await super().put(url, json=body)


class AsAppSharedLineApi(AsApiChild, base='telephony/config/people'):
    """
    Webex app shared line API
    """

    def f_ep(self, person_id: str, application_id: str, path: str) -> str:
        """

        :meta private:
        """
        return super().ep(f'{person_id}/applications/{application_id}/{path}')

    def search_members_gen(self, person_id: str, order: str = None, location: str = None,
                       name: str = None, phone_number: str = None, extension: str = None,
                       **params) -> AsyncGenerator[AvailableMember, None, None]:
        """
        Search Shared-Line Appearance Members

        Get members available for shared-line assignment to a Webex Calling Apps.

        Like most hardware devices, applications support assigning additional shared lines which can monitored and
        utilized by the application.

        This API requires a full, user, or location administrator auth token with the
        `spark-admin:telephony_config_read` scope.

        :param person_id: A unique identifier for the person.
        :type person_id: str
        :param order: Order the Route Lists according to number, ascending or descending.
        :type order: str
        :param location: Location ID for the user.
        :type location: str
        :param name: Search for users with names that match the query.
        :type name: str
        :param phone_number: Search for users with numbers that match the query.
        :type phone_number: str
        :param extension: Search for users with extensions that match the query.
        :type extension: str
        :return: Generator yielding :class:`AvailableSharedLineMember` instances
        """
        if order is not None:
            params['order'] = order
        if location is not None:
            params['location'] = location
        if name is not None:
            params['name'] = name
        if phone_number is not None:
            params['phoneNumber'] = phone_number
        if extension is not None:
            params['extension'] = extension
        url = self.ep(f'{person_id}/applications/availableMembers')
        return self.session.follow_pagination(url=url, model=AvailableMember,
                                              item_key='members', params=params)

    async def search_members(self, person_id: str, order: str = None, location: str = None,
                       name: str = None, phone_number: str = None, extension: str = None,
                       **params) -> List[AvailableMember]:
        """
        Search Shared-Line Appearance Members

        Get members available for shared-line assignment to a Webex Calling Apps.

        Like most hardware devices, applications support assigning additional shared lines which can monitored and
        utilized by the application.

        This API requires a full, user, or location administrator auth token with the
        `spark-admin:telephony_config_read` scope.

        :param person_id: A unique identifier for the person.
        :type person_id: str
        :param order: Order the Route Lists according to number, ascending or descending.
        :type order: str
        :param location: Location ID for the user.
        :type location: str
        :param name: Search for users with names that match the query.
        :type name: str
        :param phone_number: Search for users with numbers that match the query.
        :type phone_number: str
        :param extension: Search for users with extensions that match the query.
        :type extension: str
        :return: Generator yielding :class:`AvailableSharedLineMember` instances
        """
        if order is not None:
            params['order'] = order
        if location is not None:
            params['location'] = location
        if name is not None:
            params['name'] = name
        if phone_number is not None:
            params['phoneNumber'] = phone_number
        if extension is not None:
            params['extension'] = extension
        url = self.ep(f'{person_id}/applications/availableMembers')
        return [o async for o in self.session.follow_pagination(url=url, model=AvailableMember,
                                              item_key='members', params=params)]

    def search_members_old_gen(self, person_id: str, application_id: str, max_: str = None,
                           start: str = None, location: str = None, name: str = None,
                           number: str = None, order: str = None,
                           extension: str = None, **params) -> AsyncGenerator[AvailableMember, None, None]:
        """
        Search Shared-Line Appearance Members

        Get members available for shared-line assignment to a Webex Calling Apps Desktop device.

        Deprecated: This operation has been marked as deprecated, which means it could be removed at some point in the
        future.

        This API requires a full or user administrator or location administrator auth token with
        the `spark-admin:people_read` scope.

        :param person_id: A unique identifier for the person.
        :type person_id: str
        :param application_id: A unique identifier for the application.
        :type application_id: str
        :param max_: Number of records per page.
        :type max_: str
        :param start: Page number.
        :type start: str
        :param location: Location ID for the user.
        :type location: str
        :param name: Search for users with names that match the query.
        :type name: str
        :param number: Search for users with numbers that match the query.
        :type number: str
        :param order: Sort by first name (`fname`) or last name (`lname`).
        :type order: str
        :param extension: Search for users with extensions that match the query.
        :type extension: str
        :return: Generator yielding :class:`AvailableMember` instances
        """
        if max_ is not None:
            params['max'] = max_
        if start is not None:
            params['start'] = start
        if location is not None:
            params['location'] = location
        if name is not None:
            params['name'] = name
        if number is not None:
            params['number'] = number
        if order is not None:
            params['order'] = order
        if extension is not None:
            params['extension'] = extension
        url = self.f_ep(person_id=person_id, application_id=application_id, path='availableMembers')
        return self.session.follow_pagination(url, model=AvailableMember, params=params, item_key='members')

    async def search_members_old(self, person_id: str, application_id: str, max_: str = None,
                           start: str = None, location: str = None, name: str = None,
                           number: str = None, order: str = None,
                           extension: str = None, **params) -> List[AvailableMember]:
        """
        Search Shared-Line Appearance Members

        Get members available for shared-line assignment to a Webex Calling Apps Desktop device.

        Deprecated: This operation has been marked as deprecated, which means it could be removed at some point in the
        future.

        This API requires a full or user administrator or location administrator auth token with
        the `spark-admin:people_read` scope.

        :param person_id: A unique identifier for the person.
        :type person_id: str
        :param application_id: A unique identifier for the application.
        :type application_id: str
        :param max_: Number of records per page.
        :type max_: str
        :param start: Page number.
        :type start: str
        :param location: Location ID for the user.
        :type location: str
        :param name: Search for users with names that match the query.
        :type name: str
        :param number: Search for users with numbers that match the query.
        :type number: str
        :param order: Sort by first name (`fname`) or last name (`lname`).
        :type order: str
        :param extension: Search for users with extensions that match the query.
        :type extension: str
        :return: Generator yielding :class:`AvailableMember` instances
        """
        if max_ is not None:
            params['max'] = max_
        if start is not None:
            params['start'] = start
        if location is not None:
            params['location'] = location
        if name is not None:
            params['name'] = name
        if number is not None:
            params['number'] = number
        if order is not None:
            params['order'] = order
        if extension is not None:
            params['extension'] = extension
        url = self.f_ep(person_id=person_id, application_id=application_id, path='availableMembers')
        return [o async for o in self.session.follow_pagination(url, model=AvailableMember, params=params, item_key='members')]

    async def get_members(self, person_id: str) -> DeviceMembersResponse:
        """
        Get Shared-Line Appearance Members

        Get primary and secondary members assigned to a shared line on a Webex Calling Apps.

        Like most hardware devices, applications support assigning additional shared lines which can monitored and
        utilized by the application.

        This API requires a full, user, or location administrator auth token with the
        `spark-admin:telephony_config_read` scope.

        :param person_id: A unique identifier for the person.
        :type person_id: str
        :rtype: :class:`DeviceMembersResponse`
        """
        url = self.ep(f'{person_id}/applications/members')
        data = await super().get(url)
        r = DeviceMembersResponse.model_validate(data)
        return r

    async def update_members(self, person_id: str,
                       members: list[Union[DeviceMember, AvailableMember]] = None):
        """
        Put Shared-Line Appearance Members New

        Add or modify primary and secondary users assigned to shared-lines on a Webex Calling Apps.

        Like most hardware devices, applications support assigning additional shared lines which can monitored and
        utilized by the application.

        This API requires a full, user, or location administrator auth token with the
        `spark-admin:telephony_config_write` scope.

        :param person_id: A unique identifier for the person.
        :type person_id: str
        :param members: List of members to be added or modified for shared-line assignment to a Webex Calling Apps.
        :type members: list[Union[DeviceMember, AvailableMember]]
        :rtype: None
        """
        members_for_update = []
        for member in members or []:
            if isinstance(member, AvailableMember):
                member = DeviceMember.from_available(member)
            else:
                member = member.model_copy(deep=True)
            members_for_update.append(member)

        if members_for_update:
            # now assign port indices
            port = 1
            for member in members_for_update:
                member.port = port
                port += member.line_weight

        # create body
        if members_for_update:
            members = [m.model_dump(mode='json', exclude_none=True, by_alias=True,
                                    include={'member_id', 'port',
                                             'primary_owner', 'line_type', 'line_weight', 'line_label',
                                             'allow_call_decline_enabled'})
                       for m in members_for_update]
            body = {'members': members}
        else:
            body = None

        url = self.ep(f'{person_id}/applications/members')
        await super().put(url, json=body)


class AsAppServicesApi(AsApiChild, base=''):
    """
    API for person's app services settings
    """

    shared_line: AsAppSharedLineApi

    def __init__(self, *, session: AsRestSession):
        """

        :meta private:
        """
        super().__init__(session=session)
        self.shared_line = AsAppSharedLineApi(session=session)

    def f_ep(self, person_id: str):
        """

        :meta private:
        """
        return self.ep(f'people/{person_id}/features/applications')

    async def read(self, person_id: str, org_id: str = None) -> AppServicesSettings:
        """
        Retrieve a person's Application Services Settings New

        Gets mobile and PC applications settings for a user.

        Application services let you determine the ringing behavior for calls made to people in certain
        scenarios. You
        can also specify which devices can download the Webex Calling app.

        Requires a full, user, or read-only administrator or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param person_id: Unique identifier for the person.
        :type person_id: str
        :param org_id: ID of the organization in which the person resides. Only admin users of another organization
            (such as partners) may use this parameter as the default is the same organization as the token used to
            access API.
        :type org_id: str
        :rtype: :class:`AppServicesSettings`
        """
        ep = self.f_ep(person_id=person_id)
        params = org_id and {'orgId': org_id} or None
        data = await self.get(ep, params=params)
        return AppServicesSettings.model_validate(data)

    async def configure(self, person_id: str, settings: AppServicesSettings, org_id: str = None):
        """
        Modify a person's Application Services Settings

        Application services let you determine the ringing behavior for calls made to users in certain scenarios. You
        can also specify which devices users can download the Webex Calling app on.

        This API requires a full or user administrator or location administrator auth token with the
        spark-admin:people_write scope.

        :param person_id: Unique identifier for the person.
        :type person_id: str
        :param settings: settings for update
        :type settings: :class:`AppServicesSettings`
        :param org_id: Person is in this organization. Only admin users of another organization (such as partners)
            may use this parameter as the default is the same organization as the token used to access API.
        :type org_id: str
        """
        ep = self.f_ep(person_id=person_id)
        params = org_id and {'orgId': org_id} or None
        data = settings.update()
        await self.put(ep, params=params, json=data)


class AsAvailableNumbersApi(AsApiChild, base='telephony/config'):
    """
    API for person's available numbers

    Also used for virtual lines, workspaces

    Available methods

        ========================================== ============= ==========  ====
        Method                                     Virtual Lines Workspaces  User
        ========================================== ============= ==========  ====
        GET Call Forward Available Phone Numbers        X             X        X
        GET ECBN Available Phone Numbers                X             X        X
        GET Fax Message Available Phone Numbers         X                      X
        GET Available Phone Numbers                     X             X
        Get Call Intercept Available Phone Numbers                    X        X
        GET Primary Available Phone Numbers                                    X
        GET Secondary Available Phone Numbers                         X        X
        ========================================== ============= ==========  ====

    """

    # lookup for allowed entities for each function
    existing = {'callForwarding': {'virtualLines', 'workspaces', 'people'},
                'emergencyCallbackNumber': {'virtualLines', 'workspaces', 'people'},
                'faxMessage': {'virtualLines', 'people', 'workspaces'},
                '': {'virtualLines', 'workspaces'},
                'callIntercept': {'workspaces', 'people'},
                'primary': {'people'},
                'secondary': {'people', 'workspaces'}}

    def __init__(self, *, session: AsRestSession, selector: ApiSelector = ApiSelector.person):
        super().__init__(session=session)
        if selector == ApiSelector.person:
            self.selector = 'people'
        elif selector == ApiSelector.virtual_line:
            self.selector = 'virtualLines'
        elif selector == ApiSelector.workspace:
            self.selector = 'workspaces'

    def f_ep(self, available_for: str = None, entity_id: str = None) -> str:
        """
        Get endpoint URL

        :meta private:
        :param available_for: selector, something like callForwarding, faxMessage, ...
        :param entity_id: entity id if needed
        :return: url
        """
        # does this feature exist
        allowed = self.existing.get(available_for)
        if allowed is None:
            raise ValueError(f'Invalid feature: {available_for}')
        if self.selector not in allowed:
            raise ValueError(f'endpoint {available_for} does not exist for {self.selector}')
        entity_id = entity_id and f'{entity_id}/' or ''
        available_for = available_for and f'{available_for}/' or ''
        url = self.ep(f'{self.selector}/{entity_id}{available_for}availableNumbers')
        return url

    def primary_gen(self, location_id: str = None, phone_number: list[str] = None,
                license_type: AvailablePhoneNumberLicenseType = None,
                org_id: str = None,
                **params) -> AsyncGenerator[AvailableNumber, None, None]:
        """
        Get Person Primary Available Phone Numbers

        Available for: user

        List numbers that are available to be assigned as a person's primary phone number.
        By default, this API returns standard and mobile numbers from all locations that are unassigned. The parameters
        `licenseType` and `locationId` must align with the person's settings to determine the appropriate number for
        assignment.
        Failure to provide these parameters may result in the unsuccessful assignment of the returned number.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full, read-only or location administrator auth token with a scope
        of `spark-admin:telephony_config_read`.

        :param location_id: Return the list of phone numbers for this location within the given organization. The
            maximum length is 36.
        :type location_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param license_type: This is used to search numbers according to the person's `licenseType` to which the number
            will be assigned. Possible input values
        :type license_type: AvailablePhoneNumberLicenseType
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableNumber` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if location_id is not None:
            params['locationId'] = location_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        if license_type is not None:
            params['licenseType'] = enum_str(license_type)
        url = self.f_ep('primary')
        return self.session.follow_pagination(url=url, model=AvailableNumber, item_key='phoneNumbers', params=params)

    async def primary(self, location_id: str = None, phone_number: list[str] = None,
                license_type: AvailablePhoneNumberLicenseType = None,
                org_id: str = None,
                **params) -> List[AvailableNumber]:
        """
        Get Person Primary Available Phone Numbers

        Available for: user

        List numbers that are available to be assigned as a person's primary phone number.
        By default, this API returns standard and mobile numbers from all locations that are unassigned. The parameters
        `licenseType` and `locationId` must align with the person's settings to determine the appropriate number for
        assignment.
        Failure to provide these parameters may result in the unsuccessful assignment of the returned number.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full, read-only or location administrator auth token with a scope
        of `spark-admin:telephony_config_read`.

        :param location_id: Return the list of phone numbers for this location within the given organization. The
            maximum length is 36.
        :type location_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param license_type: This is used to search numbers according to the person's `licenseType` to which the number
            will be assigned. Possible input values
        :type license_type: AvailablePhoneNumberLicenseType
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableNumber` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if location_id is not None:
            params['locationId'] = location_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        if license_type is not None:
            params['licenseType'] = enum_str(license_type)
        url = self.f_ep('primary')
        return [o async for o in self.session.follow_pagination(url=url, model=AvailableNumber, item_key='phoneNumbers', params=params)]

    def secondary_gen(self, entity_id: str, phone_number: list[str] = None,
                  org_id: str = None,
                  **params) -> AsyncGenerator[AvailableNumber, None, None]:
        """
        Get Person Secondary Available Phone Numbers

        Available for: user, workspace

        List standard numbers that are available to be assigned as a person's secondary phone number.
        These numbers are associated with the location of the person specified in the request URL, can be active or
        inactive, and are unassigned.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full, read-only or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableNumber` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        url = self.f_ep('secondary', entity_id=entity_id)
        return self.session.follow_pagination(url=url, model=AvailableNumber, item_key='phoneNumbers', params=params)

    async def secondary(self, entity_id: str, phone_number: list[str] = None,
                  org_id: str = None,
                  **params) -> List[AvailableNumber]:
        """
        Get Person Secondary Available Phone Numbers

        Available for: user, workspace

        List standard numbers that are available to be assigned as a person's secondary phone number.
        These numbers are associated with the location of the person specified in the request URL, can be active or
        inactive, and are unassigned.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full, read-only or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableNumber` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        url = self.f_ep('secondary', entity_id=entity_id)
        return [o async for o in self.session.follow_pagination(url=url, model=AvailableNumber, item_key='phoneNumbers', params=params)]

    def fax_message_gen(self, entity_id: str, phone_number: list[str] = None,
                    org_id: str = None,
                    **params) -> AsyncGenerator[AvailableNumber, None, None]:
        """
        Get Fax Message Available Phone Numbers

        Available for: user, virtual line, workspace

        List standard numbers that are available to be assigned as a FAX message number.
        These numbers are associated with the location of the person specified in the request URL, can be active or
        inactive, and are unassigned.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full, read-only or location administrator auth token with a scope
        of `spark-admin:telephony_config_read`.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`PersonSecondaryAvailableNumberObject` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        url = self.f_ep('faxMessage', entity_id=entity_id)
        return self.session.follow_pagination(url=url, model=AvailableNumber, item_key='phoneNumbers', params=params)

    async def fax_message(self, entity_id: str, phone_number: list[str] = None,
                    org_id: str = None,
                    **params) -> List[AvailableNumber]:
        """
        Get Fax Message Available Phone Numbers

        Available for: user, virtual line, workspace

        List standard numbers that are available to be assigned as a FAX message number.
        These numbers are associated with the location of the person specified in the request URL, can be active or
        inactive, and are unassigned.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full, read-only or location administrator auth token with a scope
        of `spark-admin:telephony_config_read`.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`PersonSecondaryAvailableNumberObject` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        url = self.f_ep('faxMessage', entity_id=entity_id)
        return [o async for o in self.session.follow_pagination(url=url, model=AvailableNumber, item_key='phoneNumbers', params=params)]

    def call_forward_gen(self, entity_id: str, phone_number: list[str] = None,
                     owner_name: str = None, extension: str = None,
                     org_id: str = None,
                     **params) -> AsyncGenerator[AvailableNumber, None, None]:
        """
        Get Call Forward Available Phone Numbers

        Available for: user, virtual line, workspace

        List service and standard numbers that are available to be assigned as call forward number.
        These numbers are associated with the location of the person specified in the request URL, can be active or
        inactive, and are assigned to an owning entity.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full, read-only or location administrator auth token with a scope
        of `spark-admin:telephony_config_read`.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param owner_name: Return the list of phone numbers that are owned by the given `ownerName`. Maximum length is
            255.
        :type owner_name: str
        :param extension: Returns the list of PSTN phone numbers with the given `extension`.
        :type extension: str
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableNumber` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        if owner_name is not None:
            params['ownerName'] = owner_name
        if extension is not None:
            params['extension'] = extension
        url = self.f_ep('callForwarding', entity_id=entity_id)
        return self.session.follow_pagination(url=url, model=AvailableNumber, item_key='phoneNumbers', params=params)

    async def call_forward(self, entity_id: str, phone_number: list[str] = None,
                     owner_name: str = None, extension: str = None,
                     org_id: str = None,
                     **params) -> List[AvailableNumber]:
        """
        Get Call Forward Available Phone Numbers

        Available for: user, virtual line, workspace

        List service and standard numbers that are available to be assigned as call forward number.
        These numbers are associated with the location of the person specified in the request URL, can be active or
        inactive, and are assigned to an owning entity.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full, read-only or location administrator auth token with a scope
        of `spark-admin:telephony_config_read`.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param owner_name: Return the list of phone numbers that are owned by the given `ownerName`. Maximum length is
            255.
        :type owner_name: str
        :param extension: Returns the list of PSTN phone numbers with the given `extension`.
        :type extension: str
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableNumber` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        if owner_name is not None:
            params['ownerName'] = owner_name
        if extension is not None:
            params['extension'] = extension
        url = self.f_ep('callForwarding', entity_id=entity_id)
        return [o async for o in self.session.follow_pagination(url=url, model=AvailableNumber, item_key='phoneNumbers', params=params)]

    def ecbn_gen(self, entity_id: str, phone_number: list[str] = None,
             owner_name: str = None, org_id: str = None,
             **params) -> AsyncGenerator[AvailableNumber, None, None]:
        """
        Get ECBN Available Phone Numbers

        Available for: user, virtual line, workspace

        List standard numbers that are available to be assigned as emergency callback number.
        These numbers are associated with the location of the person specified in the request URL, can be active or
        inactive, and are assigned to an owning entity.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full, read-only or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param entity_id: Unique identifier for the person.
        :type entity_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param owner_name: Return the list of phone numbers that are owned by the given `ownerName`. Maximum length is
            255.
        :type owner_name: str
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableNumber` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        if owner_name is not None:
            params['ownerName'] = owner_name
        url = self.f_ep('emergencyCallbackNumber', entity_id=entity_id)
        return self.session.follow_pagination(url=url, model=AvailableNumber, item_key='phoneNumbers', params=params)

    async def ecbn(self, entity_id: str, phone_number: list[str] = None,
             owner_name: str = None, org_id: str = None,
             **params) -> List[AvailableNumber]:
        """
        Get ECBN Available Phone Numbers

        Available for: user, virtual line, workspace

        List standard numbers that are available to be assigned as emergency callback number.
        These numbers are associated with the location of the person specified in the request URL, can be active or
        inactive, and are assigned to an owning entity.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full, read-only or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param entity_id: Unique identifier for the person.
        :type entity_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param owner_name: Return the list of phone numbers that are owned by the given `ownerName`. Maximum length is
            255.
        :type owner_name: str
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableNumber` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        if owner_name is not None:
            params['ownerName'] = owner_name
        url = self.f_ep('emergencyCallbackNumber', entity_id=entity_id)
        return [o async for o in self.session.follow_pagination(url=url, model=AvailableNumber, item_key='phoneNumbers', params=params)]

    def available_gen(self, location_id: str = None, phone_number: list[str] = None,
                  org_id: str = None,
                  **params) -> AsyncGenerator[AvailableNumber, None, None]:
        """
        Get Available Phone Numbers

        Available for: virtual line, workspace

        List standard numbers that are available to be assigned as phone number.
        By default, this API returns numbers from all locations that are unassigned. To select the suitable number for
        assignment, ensure the entities location ID is provided as the `locationId` request parameter.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full, read-only or location administrator auth token with a scope
        of `spark-admin:telephony_config_read`.

        :param location_id: Return the list of phone numbers for this location within the given organization. The
            maximum length is 36.
        :type location_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableNumber` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if location_id is not None:
            params['locationId'] = location_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        url = self.f_ep('')
        return self.session.follow_pagination(url=url, model=AvailableNumber, item_key='phoneNumbers', params=params)

    async def available(self, location_id: str = None, phone_number: list[str] = None,
                  org_id: str = None,
                  **params) -> List[AvailableNumber]:
        """
        Get Available Phone Numbers

        Available for: virtual line, workspace

        List standard numbers that are available to be assigned as phone number.
        By default, this API returns numbers from all locations that are unassigned. To select the suitable number for
        assignment, ensure the entities location ID is provided as the `locationId` request parameter.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full, read-only or location administrator auth token with a scope
        of `spark-admin:telephony_config_read`.

        :param location_id: Return the list of phone numbers for this location within the given organization. The
            maximum length is 36.
        :type location_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableNumber` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if location_id is not None:
            params['locationId'] = location_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        url = self.f_ep('')
        return [o async for o in self.session.follow_pagination(url=url, model=AvailableNumber, item_key='phoneNumbers', params=params)]

    def call_intercept_gen(self, entity_id: str, phone_number: list[str] = None,
                       owner_name: str = None, extension: str = None,
                       org_id: str = None,
                       **params) -> AsyncGenerator[AvailableNumber, None, None]:
        """
        Get Call Intercept Available Phone Numbers

        Available for: user, workspace

        List service and standard numbers that are available to be assigned as call intercept number.

        These numbers are associated with the location specified in the request URL, can be active or inactive, and are
        assigned to an owning entity.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full, read-only or location administrator auth token with a scope
        of `spark-admin:telephony_config_read`.

        :param entity_id: Unique identifier for the person.
        :type entity_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param owner_name: Return the list of phone numbers that are owned by the given `ownerName`. Maximum length is
            255.
        :type owner_name: str
        :param extension: Returns the list of PSTN phone numbers with the given `extension`.
        :type extension: str
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableNumber` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        if owner_name is not None:
            params['ownerName'] = owner_name
        if extension is not None:
            params['extension'] = extension
        url = self.f_ep('callIntercept', entity_id=entity_id)
        return self.session.follow_pagination(url=url, model=AvailableNumber, item_key='phoneNumbers', params=params)

    async def call_intercept(self, entity_id: str, phone_number: list[str] = None,
                       owner_name: str = None, extension: str = None,
                       org_id: str = None,
                       **params) -> List[AvailableNumber]:
        """
        Get Call Intercept Available Phone Numbers

        Available for: user, workspace

        List service and standard numbers that are available to be assigned as call intercept number.

        These numbers are associated with the location specified in the request URL, can be active or inactive, and are
        assigned to an owning entity.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full, read-only or location administrator auth token with a scope
        of `spark-admin:telephony_config_read`.

        :param entity_id: Unique identifier for the person.
        :type entity_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param owner_name: Return the list of phone numbers that are owned by the given `ownerName`. Maximum length is
            255.
        :type owner_name: str
        :param extension: Returns the list of PSTN phone numbers with the given `extension`.
        :type extension: str
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableNumber` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        if owner_name is not None:
            params['ownerName'] = owner_name
        if extension is not None:
            params['extension'] = extension
        url = self.f_ep('callIntercept', entity_id=entity_id)
        return [o async for o in self.session.follow_pagination(url=url, model=AvailableNumber, item_key='phoneNumbers', params=params)]


class AsBargeApi(AsPersonSettingsApiChild):
    """
    API for barge settings; also used for virtual lines and workspaces
    """

    feature = 'bargeIn'

    async def read(self, entity_id: str, org_id: str = None) -> BargeSettings:
        """
        Retrieve Barge In Settings

        The Barge In feature enables you to use a Feature Access Code (FAC) to answer a call that was directed to
        another subscriber, or barge-in on the call if it was already answered. Barge In can be used across locations.

        This API requires a full, user, or read-only administrator auth token with a scope of spark-admin:people_read
        or a user auth token with spark:people_read scope can be used by an entity to read their own settings.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param org_id: entity is in this organization. Only admin users of another organization (such as partners)
            may use this parameter as the default is the same organization as the token used to access API.
        :type org_id: str
        :return: barge settings for specific user
        :rtype: BargeSettings
        """
        ep = self.f_ep(person_id=entity_id)
        params = org_id and {'orgId': org_id} or None
        return BargeSettings.model_validate(await self.get(ep, params=params))

    async def configure(self, entity_id: str, barge_settings: BargeSettings, org_id: str = None):
        """
        Configure Barge In Settings

        The Barge In feature enables you to use a Feature Access Code (FAC) to answer a call that was directed to
        another subscriber, or barge-in on the call if it was already answered. Barge In can be used across locations.

        This API requires a full or user administrator auth token with the spark-admin:people_write scope or a user
        auth token with spark:people_write scope can be used by an entity to update their own settings.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param barge_settings: new setting to be applied
        :type barge_settings: BargeSettings
        :param org_id: entity is in this organization. Only admin users of another organization (such as partners)
            may use this parameter as the default is the same organization as the token used to access API.
        """
        ep = self.f_ep(person_id=entity_id)
        params = org_id and {'orgId': org_id} or None
        await self.put(ep, params=params, data=barge_settings.model_dump_json())


class AsCallBridgeApi(AsPersonSettingsApiChild):
    """
    User Call Settings with Call Bridge Feature

    Also used for virtual lines

    Not supported for Webex for Government (FedRAMP)

    Person Call Settings supports modifying Webex Calling settings for a specific person.

    Viewing People requires a full, user, or read-only administrator auth token with a scope
    of `spark-admin:people_read` or, for select APIs, a user auth token with `spark:people_read` scope can be used by a
    person to read their own settings.

    Configuring People settings requires a full or user administrator auth token with the `spark-admin:people_write`
    scope or, for select APIs, a user auth token with `spark:people_write` scope can be used by a person to update
    their own settings.
    """

    feature = 'callBridge'

    async def read(self, entity_id: str, org_id: str = None) -> CallBridgeSetting:
        """
        Read Call Bridge Settings

        Retrieve Bridge settings.

        This API requires a full, user or read-only administrator or location administrator auth token with a scope
        of `spark-admin:people_read`.

        :param entity_id: Unique identifier for the person.
        :type entity_id: str
        :param org_id: ID of the organization in which the person resides. Only admin users of another organization
            (such as partners) may use this parameter as the default is the same organization as the token used to
            access API.
        :type org_id: str
        :rtype: :class:'CallBridgeSetting'
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.f_ep(entity_id)
        data = await super().get(url, params=params)
        return CallBridgeSetting.model_validate(data)

    async def configure(self, entity_id: str, setting: CallBridgeSetting,
                  org_id: str = None):
        """
        Configure Call Bridge Settings

        Configure Call Bridge settings.

        This API requires a full or user administrator or location administrator auth token with
        the `spark-admin:people_write` scope.

        :param entity_id: Unique identifier for the person.
        :type entity_id: str
        :param setting: new call bridge settings
        :type setting: :class:'CallBridgeSetting'
        :param org_id: ID of the organization in which the person resides. Only admin users of another organization
            (such as partners) may use this parameter as the default is the same organization as the token used to
            access API.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = setting.model_dump(mode='json', exclude_none=True, by_alias=True)
        url = self.f_ep(entity_id)
        await super().put(url, params=params, json=body)


class AsCallInterceptApi(AsPersonSettingsApiChild):
    """
    API for call intercept settings

    Also used for virtual lines and workspaces
    """

    feature = 'intercept'

    async def read(self, entity_id: str, org_id: str = None) -> InterceptSetting:
        """
        Read Call Intercept Settings

        Retrieves Call Intercept Settings

        The intercept feature gracefully takes an entity’s phone out of service, while providing callers with
        informative announcements and alternative routing options. Depending on the service configuration, none,
        some, or all incoming calls to the specified entity are intercepted. Also depending on the service
        configuration, outgoing calls are intercepted or rerouted to another location.

        This API requires a full, user, or read-only administrator auth token with a scope of spark-admin:people_read.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param org_id: entity is in this organization. Only admin users of another organization (such as partners)
            may use this parameter as the default is the same organization as the token used to access API.
        :type org_id: str
        :return: user's call intercept settings
        :rtype: InterceptSetting
        """
        ep = self.f_ep(person_id=entity_id)
        params = org_id and {'orgId': org_id} or None
        return InterceptSetting.model_validate(await self.get(ep, params=params))

    async def configure(self, entity_id: str, intercept: InterceptSetting, org_id: str = None):
        """
        Configure Call Intercept Settings

        Configures Call Intercept Settings

        The intercept feature gracefully takes an entity’s phone out of service, while providing callers with
        informative announcements and alternative routing options. Depending on the service configuration, none, some,
        or all incoming calls to the specified entity are intercepted. Also depending on the service configuration,
        outgoing calls are intercepted or rerouted to another location.

        This API requires a full or user administrator auth token with the spark-admin:people_write scope.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param intercept: new intercept settings
        :type intercept: InterceptSetting
        :param org_id: entity is in this organization. Only admin users of another organization (such as partners)
            may use this parameter as the default is the same organization as the token used to access API.
        :type org_id: str
        """
        ep = self.f_ep(person_id=entity_id)
        params = org_id and {'orgId': org_id} or None
        data = intercept.update()
        await self.put(ep, params=params, json=data)

    async def greeting(self, entity_id: str, content: Union[BufferedReader, str],
                 upload_as: str = None, org_id: str = None):
        """
        Configure Call Intercept Greeting

        ConfigureCall Intercept Greeting by uploading a Waveform Audio File Format, .wav, encoded audio
        file.

        Your request will need to be a multipart/form-data request rather than JSON, using the audio/wav Content-Type.

        This API requires a full or user administrator auth token with the spark-admin:people_write scope or a user
        auth token with spark:people_write scope can be used by an entity to update their settings.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param content: the file to be uploaded, can be a path to a file or a buffered reader (opened file); if a
            reader referring to an open file is passed then make sure to open the file as binary b/c otherwise the
            content length might be calculated wrong
        :type content: Union[BufferedReader, str]
        :param upload_as: filename for the content. Only required if content is a reader; has to be a .wav file name.
        :type upload_as: str
        :param org_id: entity is in this organization. Only admin users of another organization (such as partners)
            may use this parameter as the default is the same organization as the token used to access API.
        :type org_id: str
        """
        if isinstance(content, str):
            upload_as = os.path.basename(content)
            content = open(content, mode='rb')
            must_close = True
        else:
            must_close = False
            # an existing reader
            if not upload_as:
                raise ValueError('upload_as is required')
        encoder = MultipartEncoder({'file': (upload_as, content, 'audio/wav')})
        ep = self.f_ep(person_id=entity_id, path='actions/announcementUpload/invoke')
        params = org_id and {'orgId': org_id} or None
        try:
            await self.post(ep, data=encoder, headers={'Content-Type': encoder.content_type},
                      params=params)
        finally:
            if must_close:
                content.close()
        return


class AsCallRecordingApi(AsPersonSettingsApiChild):
    """
    API for recording settings

    Also used for virtual lines, workspaces
    """

    feature = 'callRecording'

    async def read(self, entity_id: str, org_id: str = None) -> CallRecordingSetting:
        """
        Read Call Recording Settings

        Retrieve Call Recording Settings

        The Call Recording feature provides a hosted mechanism to record the calls placed and received on the Carrier
        platform for replay and archival. This feature is helpful for quality assurance, security, training, and more.

        This API requires a full or user administrator auth token with the spark-admin:people_write scope.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param org_id: entity is in this organization. Only admin users of another organization (such as partners)
            may use this parameter as the default is the same organization as the token used to access API.
        :type org_id: str
        """
        ep = self.f_ep(person_id=entity_id)
        params = org_id and {'orgId': org_id} or None
        return CallRecordingSetting.model_validate(await self.get(ep, params=params))

    async def configure(self, entity_id: str, recording: CallRecordingSetting, org_id: str = None):
        """
        Configure Call Recording Settings for a entity

        Configure Call Recording Settings

        The Call Recording feature provides a hosted mechanism to record the calls placed and received on the Carrier
        platform for replay and archival. This feature is helpful for quality assurance, security, training, and more.

        This API requires a full or user administrator auth token with the spark-admin:people_write scope.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param recording: the new recording settings
        :type recording: CallRecordingSetting
        :param org_id: entity is in this organization. Only admin users of another organization (such as partners)
            may use this parameter as the default is the same organization as the token used to access API.
        :type org_id: str
        """
        ep = self.f_ep(person_id=entity_id)
        params = org_id and {'orgId': org_id} or None
        data = recording.update()
        await self.put(ep, params=params, json=data)


class AsCallWaitingApi(AsPersonSettingsApiChild):
    """
    API for person's call waiting settings

    Also used for virtual lines, workspaces
    """

    feature = 'callWaiting'

    async def read(self, entity_id: str, org_id: str = None) -> bool:
        """
        Read Call Waiting Settings for

        Retrieve Call Waiting Settings

        With this feature, an entity can place an active call on hold and answer an incoming call. When enabled,
        while you are on an active call, a tone alerts you of an incoming call and you can choose to answer or
        ignore the call.

        This API requires a full, user, or read-only administrator auth token with a scope of spark-admin:people_read.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param org_id: entity is in this organization. Only admin users of another organization (such as partners)
            may use this parameter as the default is the same organization as the token used to access API.
        :type org_id: str
        :return: call waiting setting
        :rtype: bool
        """
        ep = self.f_ep(person_id=entity_id)
        params = org_id and {'orgId': org_id} or None
        data = await self.get(ep, params=params)
        return data['enabled']

    async def configure(self, entity_id: str, enabled: bool, org_id: str = None):
        """
        Configure Call Waiting Settings

        Configure an entity's Call Waiting Settings

        With this feature, a entity can place an active call on hold and answer an incoming call. When enabled,
        while you are on an active call, a tone alerts you of an incoming call and you can choose to answer or ignore
        the call.

        This API requires a full or user administrator auth token with the spark-admin:people_write scope.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param enabled: true if the Call Waiting feature is enabled.
        :type enabled: bool
        :param org_id: entity is in this organization. Only admin users of another organization (such as partners)
            may use this parameter as the default is the same organization as the token used to access API.
        :type org_id: str
        """
        ep = self.f_ep(person_id=entity_id)
        params = org_id and {'orgId': org_id} or None
        data = json.dumps({'enabled': enabled})
        await self.put(ep, params=params, json=data)


class AsCallerIdApi(AsPersonSettingsApiChild):
    """
    API for caller id settings

    Also used for: virtual lines, workspaces
    """

    feature = 'callerId'

    async def read(self, entity_id: str, org_id: str = None) -> CallerId:
        """
        Retrieve Caller ID Settings

        Caller ID settings control how a entity’s information is displayed when making outgoing calls.

        This API requires a full, user, or read-only administrator auth token with a scope of spark-admin:people_read
        or a user auth token with spark:people_read scope can be used by a entity to read their settings.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param org_id: entity is in this organization. Only admin users of another organization (such as partners)
            may use this parameter as the default is the same organization as the token used to access API.
        :type org_id: str
        """
        ep = self.f_ep(person_id=entity_id)
        params = org_id and {'orgId': org_id} or None
        return CallerId.model_validate(await self.get(ep, params=params))

    async def configure(self, entity_id: str, org_id: str = None,
                  selected: CallerIdSelectedType = None,
                  custom_number: str = None,
                  first_name: str = None,
                  last_name: str = None,
                  external_caller_id_name_policy: ExternalCallerIdNamePolicy = None,
                  custom_external_caller_id_name: str = None):
        """
        Configure a Caller ID Settings

        Caller ID settings control how a entity’s information is displayed when making outgoing calls.

        This API requires a full or user administrator auth token with the spark-admin:people_write scope or a user
        auth token with spark:people_write scope can be used by a entity to update their own settings.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param org_id: entity is in this organization. Only admin users of another organization (such as partners)
            may use this parameter as the default is the same organization as the token used to access API.
        :type org_id: str
        :param selected: Which type of outgoing Caller ID will be used.
        :type selected: CallerIdSelectedType
        :param custom_number: This value must be an assigned number from the entity\'s location.
        :type custom_number: str
        :param first_name: entity's Caller ID first name. Characters of %, +, `, " and Unicode characters are not
            allowed.

        :type first_name: str
        :param last_name: entity's Caller ID last name. Characters of %, +, `, " and Unicode characters are not
            allowed.
        :type last_name: str
        :param external_caller_id_name_policy: Designates which type of External Caller ID Name policy is used.
            Default is DIRECT_LINE.
        :type external_caller_id_name_policy: ExternalCallerIdNamePolicy
        :param custom_external_caller_id_name: Custom External Caller Name, which will be shown if External Caller ID
            Name is OTHER.
        :type custom_external_caller_id_name: str

        """
        data = {to_camel(k): v for i, (k, v) in enumerate(locals().items())
                if i > 2 and v is not None}
        params = org_id and {'orgId': org_id} or None
        ep = self.f_ep(person_id=entity_id)
        await self.put(ep, params=params, json=data)

    async def configure_settings(self, entity_id: str, settings: CallerId, org_id: str = None):
        """
        Configure a Caller ID Settings

        Caller ID settings control how a entity’s information is displayed when making outgoing calls.

        This API requires a full or user administrator auth token with the spark-admin:people_write scope or a user
        auth token with spark:people_write scope can be used by a entity to update their own settings.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param settings: new settings
        :type settings: CallerId
        :param org_id: entity is in this organization. Only admin users of another organization (such as partners)
            may use this parameter as the default is the same organization as the token used to access API.
        :type org_id: str

        Example:

            .. code-block:: python

                api = self.api.telephony.virtual_lines.caller_id
                caller_id_settings = api.read(entity_id=self.target.id)
                caller_id_settings.block_in_forward_calls_enabled = True
                api.configure_settings(entity_id=self.target.id, settings=caller_id_settings)

        """
        params = org_id and {'orgId': org_id} or None
        data = settings.update()
        ep = self.f_ep(person_id=entity_id)
        await self.put(ep, params=params, json=data)


class AsCallingBehaviorApi(AsPersonSettingsApiChild):
    """
    API for person's calling behavior settings
    """

    feature = 'callingBehavior'

    async def read(self, person_id: str, org_id: str = None) -> CallingBehavior:
        """
        Read Person's Calling Behavior

        Retrieves the calling behavior and UC Manager Profile settings for the person which includes overall calling
        behavior and calling UC Manager Profile ID.

        Webex Calling Behavior controls which Webex telephony application is to be used.

        An organization has an organization-wide default Calling Behavior that may be overridden for individual persons.

        In addition, UC Manager Profiles are applicable if your organization uses Jabber in Team Messaging mode or
        Calling in Webex Teams (Unified CM).

        The UC Manager Profile also has an organization-wide default and may be overridden for individual persons.

        This API requires a full, user, or read-only administrator auth token with a scope of spark-admin:people_read.

        :param person_id: Unique identifier for the person.
        :type person_id: str
        :param org_id: Person is in this organization. Only admin users of another organization (such as partners)
            may use this parameter as the default is the same organization as the token used to access API.
        :type org_id: str
        :return: calling behavior setting
        :rtype: CallingBehavior
        """
        ep = self.f_ep(person_id=person_id)
        params = org_id and {'orgId': org_id} or None
        data = await self.get(ep, params=params)
        return CallingBehavior.model_validate(data)

    async def configure(self, person_id: str, settings: CallingBehavior,
                  org_id: str = None):
        """
        Configure a Person's Calling Behavior

        Modifies the calling behavior settings for the person which includes overall calling behavior and UC Manager
        Profile ID.

        Webex Calling Behavior controls which Webex telephony application is to be used.

        An organization has an organization-wide default Calling Behavior that may be overridden for individual persons.

        In addition, UC Manager Profiles are applicable if your organization uses Jabber in Team Messaging mode or
        Calling in Webex Teams (Unified CM).

        The UC Manager Profile also has an organization-wide default and may be overridden for individual persons.

        This API requires a full or user administrator auth token with the spark-admin:people_write scope.

        :param person_id: Unique identifier for the person.
        :type person_id: str
        :param settings: new settings
        :type settings: CallingBehavior
        :param org_id: Person is in this organization. Only admin users of another organization (such as partners)
            may use this parameter as the default is the same organization as the token used to access API.
        :type org_id: str
        """
        ep = self.f_ep(person_id=person_id)
        params = org_id and {'orgId': org_id} or None
        data = settings.model_dump_json(exclude_none=False, exclude={'effective_behavior_type'}, exclude_unset=True)
        await self.put(ep, params=params, data=data)


class AsDndApi(AsPersonSettingsApiChild):
    """
    API for person's DND settings. Also used for workspaces and virtual lines
    """

    feature = 'doNotDisturb'

    async def read(self, entity_id: str, org_id: str = None) -> DND:
        """
        Read Do Not Disturb Settings for an entity

        Retrieve an entity's Do Not Disturb Settings

        When enabled, this feature will give all incoming calls the busy treatment. Optionally, you can enable a Ring
        Reminder to play a brief tone on your desktop phone when you receive incoming calls.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param org_id: Entity is in this organization. Only admin users of another organization (such as partners) may
            use this parameter as the default is the same organization as the token used to access API.
        :type org_id: str
        :return: DND settings
        :rtype: DND
        """
        ep = self.f_ep(entity_id)
        params = org_id and {'orgId': org_id} or None
        return DND.model_validate(await self.get(ep, params=params))

    async def configure(self, entity_id: str, dnd_settings: DND, org_id: str = None):
        """
        Configure Do Not Disturb Settings for an entity

        Configure an entity's Do Not Disturb Settings

        When enabled, this feature will give all incoming calls the busy treatment. Optionally, you can enable a Ring
        Reminder to play a brief tone on your desktop phone when you receive incoming calls.

        This API requires a full or user administrator auth token with the spark-admin:people_write scope or a user
        auth token with spark:people_write scope can be used by an entity to update their settings.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param dnd_settings: new setting to be applied
        :type dnd_settings: DND
        :param org_id: Entity is in this organization. Only admin users of another organization (such as partners)
            may use this parameter as the default is the same organization as the token used to access API.
        """
        ep = self.f_ep(entity_id)
        params = org_id and {'orgId': org_id} or None
        await self.put(ep, params=params, json=dnd_settings.model_dump(mode='json', by_alias=True, exclude_unset=True))


class AsECBNApi(AsPersonSettingsApiChild):
    """
    Emergency Callback Configurations can be enabled at the organization level, Users without individual telephone
    numbers, such as extension-only users, must be set up with accurate Emergency Callback Numbers (ECBN) to enable
    them to make emergency calls. These users can either utilize the default ECBN for their location or be assigned
    another specific telephone number from that location for emergency purposes.

    Viewing these settings requires a full or read-only administrator auth token with a scope of
    `spark-admin:telephony_config_read`. Modifying these settings requires a full administrator auth token with a
    scope of `spark-admin:telephony_config_write`.
    """

    feature = 'emergencyCallbackNumber'

    async def read(self, entity_id: str,
             org_id: str = None) -> PersonECBN:
        """
        Get an entity's Emergency Callback Number

        Retrieve an entity's emergency callback number settings. Also applies to workspaces and virtual lines.

        Emergency Callback Configurations can be enabled at the organization level, Users without individual telephone
        numbers, such as extension-only users, must be set up with accurate Emergency Callback Numbers (ECBN) and
        Emergency Service Addresses to enable them to make emergency calls. These users can either utilize the default
        ECBN for their location or be assigned another specific telephone number from that location for emergency
        purposes.

        To retrieve an entity's callback number requires a full, user or read-only administrator or location
        administrator auth token with a scope of `spark-admin:telephony_config_read`.

        :param entity_id: Unique identifier for the entity, virtual line, or workspace
        :type entity_id: str
        :param org_id: ID of the organization within which the entity resides. Only admin users of another organization
            (such as partners) may use this parameter as the default is the same organization as the token used to
            access API.
        :type org_id: str
        :rtype: :class:`PersonECBN`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.f_ep(entity_id)
        data = await super().get(url, params=params)
        r = PersonECBN.model_validate(data)
        return r

    async def configure(self, entity_id: str,
                  selected: SelectedECBN,
                  location_member_id: str = None, org_id: str = None):
        """
        Update an entity's Emergency Callback Number.

        Update an entity's emergency callback number settings. Also applies to workspaces and virtual lines.

        Emergency Callback Configurations can be enabled at the organization level, Users without individual telephone
        numbers, such as extension-only users, must be set up with accurate Emergency Callback Numbers (ECBN) to
        enable them to make emergency calls. These users can either utilize the default ECBN for their location or be
        assigned another specific telephone number from that location for emergency purposes.

        To update an emergency callback number requires a full, location, user, or read-only administrator auth token
        with a scope of `spark-admin:telephony_config_write`.

        :param entity_id: Unique identifier for the entity, virtual line, or workspace.
        :type entity_id: str
        :param selected: The source from which the emergency calling line ID (CLID) is selected for an actual emergency
            call.
        :type selected: SelectedECBN
        :param location_member_id: Member ID of person/workspace/virtual line within the location.
        :type location_member_id: str
        :param org_id: ID of the organization within which the entity resides. Only admin users of another organization
            (such as partners) may use this parameter as the default is the same organization as the token used to
            access API.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        body['selected'] = enum_str(selected)
        if location_member_id is not None:
            body['locationMemberId'] = location_member_id
        url = self.f_ep(entity_id)
        await super().put(url, params=params, json=body)

    async def dependencies(self, entity_id: str,
                     org_id: str = None) -> ECBNDependencies:
        """
        Retrieve an entity's Emergency Callback Number Dependencies

        Retrieve Emergency Callback Number dependencies for an entity. Also applies to workspaces and virtual lines.

        Emergency Callback Configurations can be enabled at the organization level, Users without individual telephone
        numbers, such as extension-only users, must be set up with accurate Emergency Call Back Numbers (ECBN) to
        enable them to make emergency calls. These users can either utilize the default ECBN for their location or be
        assigned another specific telephone number from that location for emergency purposes.

        Retrieving the dependencies requires a full, user or read-only administrator or location administrator auth
        token with a scope of `spark-admin:telephony_config_read`.

        :param entity_id: Unique identifier for the person, virtual line, or workspace
        :type entity_id: str
        :param org_id: Retrieve Emergency Callback Number attributes for this organization.
        :type org_id: str
        :rtype: :class:`ECBNDependencies`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.f_ep(entity_id, 'dependencies')
        data = await super().get(url, params=params)
        r = ECBNDependencies.model_validate(data)
        return r


class AsExecAssistantApi(AsPersonSettingsApiChild):
    """
    API for person's exec assistant settings
    """

    feature = 'executiveAssistant'

    async def read(self, person_id: str, org_id: str = None) -> ExecAssistantType:
        """
        Retrieve Executive Assistant Settings for a Person

        Retrieve the executive assistant settings for the specified personId.

        People with the executive service enabled, can select from a pool of assistants who have been assigned the
        executive assistant service and who can answer or place calls on their behalf. Executive assistants can set
        the call forward destination and join or leave an executive's pool.

        This API requires a full, user, or read-only administrator auth token with a scope of spark-admin:people_read.

        :param person_id: Unique identifier for the person.
        :type person_id: str
        :param org_id: Person is in this organization. Only admin users of another organization (such as partners)
            may use this parameter as the default is the same organization as the token used to access API.
        :type org_id: str
        :return: exec assistant setting
        :rtype: :class:`ExecAssistantType`
        """
        ep = self.f_ep(person_id=person_id)
        params = org_id and {'orgId': org_id} or None
        data = await self.get(ep, params=params)
        h: _Helper = _Helper.model_validate(data)
        return h.exec_type

    async def configure(self, person_id: str, setting: ExecAssistantType, org_id: str = None):
        """
        Modify Executive Assistant Settings for a Person

        Modify the executive assistant settings for the specified personId.

        People with the executive service enabled, can select from a pool of assistants who have been assigned the
        executive assistant service and who can answer or place calls on their behalf. Executive assistants can set
        the call forward destination and join or leave an executive's pool.

        This API requires a full or user administrator auth token with the spark-admin:people_write scope.

        :param person_id: Unique identifier for the person.
        :type person_id: str
        :param setting: New exex assistant settings
        :type setting: :class:`ExecAssistantType`
        :param org_id: Person is in this organization. Only admin users of another organization (such as partners)
            may use this parameter as the default is the same organization as the token used to access API.
        :type org_id: str
        """
        ep = self.f_ep(person_id=person_id)
        h = _Helper(exec_type=setting)
        params = org_id and {'orgId': org_id} or None
        data = h.model_dump_json()
        await self.put(ep, params=params, data=data)


class AsExecutiveSettingsApi(AsApiChild, base=''):
    """
    Person executive settings
    """

    async def alert_settings(self, person_id: str, org_id: str = None) -> ExecAlert:
        """
        Get Person Executive Alert Settings

        Get executive alert settings for the specified person.

        Executive Alert settings in Webex allow you to control how calls are routed to executive assistants, including
        alerting mode, rollover options, and caller ID presentation. You can configure settings such as sequential or
        simultaneous alerting, and specify what happens when calls aren't answered.

        This API requires a full, user, read-only or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param person_id: A unique identifier for the person.
        :type person_id: str
        :param org_id: Organization ID for the person.
        :type org_id: str
        :rtype: :class:`ExecAlert`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'telephony/config/people/{person_id}/executive/alert')
        data = await super().get(url, params=params)
        r = ExecAlert.model_validate(data)
        return r

    async def update_alert_settings(self, person_id: str,
                              settings: ExecAlert,
                              org_id: str = None):
        """
        Modify Person Executive Alert Settings

        Update executive alert settings for the specified person.

        Executive Alert settings in Webex allow you to control how calls are routed to executive assistants, including
        alerting mode, rollover options, and caller ID presentation. You can configure settings such as sequential or
        simultaneous alerting, and specify what happens when calls aren't answered.

        This API requires a full, user, or location administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param person_id: A unique identifier for the person.
        :type person_id: str
        :param settings: Alert Settings for the person.
        :type settings: ExecutiveAlertSettings
        :param org_id: Organization ID for the person.
        :type org_id: str
        :rtype: None
        """
        body = settings.update()
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'telephony/config/people/{person_id}/executive/alert')
        await super().put(url, params=params, json=body)

    async def assigned_assistants(self, person_id: str, org_id: str = None) -> list[ExecOrAssistant]:
        """
        Get Person Executive Assigned Assistants

        Get list of assigned executive assistants for the specified person.

        As an executive, you can add assistants to your executive pool to manage calls for you. You can set when and
        which types of calls they can handle. Assistants can opt in when needed or opt out when not required.

        This API requires a full, user, read-only or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param person_id: A unique identifier for the person.
        :type person_id: str
        :param org_id: Organization ID for the person.
        :type org_id: str
        :rtype: list[ExecOrAssistant]
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'telephony/config/people/{person_id}/executive/assignedAssistants')
        data = await super().get(url, params=params)
        r = TypeAdapter(list[ExecOrAssistant]).validate_python(data['assistants'])
        return r

    async def update_assigned_assistants(self, person_id: str, assistant_ids: list[str] = None,
                                   org_id: str = None):
        """
        Modify Person Executive Assigned Assistants

        Update assigned executive assistants for the specified person.

        As an executive, you can add assistants to your executive pool to manage calls for you. You can set when and
        which types of calls they can handle. Assistants can opt in when needed or opt out when not required.

        This API requires a full, user, or location administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param person_id: A unique identifier for the person.
        :type person_id: str
        :param assistant_ids: List of people to be assigned as assistant. To remove all assigned assistants, set
            `assistantIds` to `null`.
        :type assistant_ids: list[str]
        :param org_id: Organization ID for the person.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        if assistant_ids is not None:
            body['assistantIds'] = assistant_ids
        url = self.ep(f'telephony/config/people/{person_id}/executive/assignedAssistants')
        await super().put(url, params=params, json=body)

    async def executive_assistant_settings(self, person_id: str,
                                     org_id: str = None) -> AssistantSettings:
        """
        Get Person Executive Assistant Settings

        Get executive assistant settings for the specified person when person is configured as executive assistant.

        Executive assistants can make, answer, intercept, and route calls appropriately on behalf of their executive.
        Assistants can also set the call forwarding destination, and join or leave an executive’s pool.

        This API requires a full, user, read-only or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param person_id: A unique identifier for the person.
        :type person_id: str
        :param org_id: Organization ID for the person.
        :type org_id: str
        :rtype: :class:`AssistantSettings`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'telephony/config/people/{person_id}/executive/assistant')
        data = await super().get(url, params=params)
        r = AssistantSettings.model_validate(data)
        return r

    async def update_executive_assistant_settings(self, person_id: str, settings: AssistantSettings, org_id: str = None):
        """
        Modify Person Executive Assistant Settings

        Update executive assistant settings for the specified person when person is configured as executive assistant.

        Executive assistants can make, answer, intercept, and route calls appropriately on behalf of their executive.
        Assistants can also set the call forwarding destination, and join or leave an executive’s pool.

        This API requires a full, user, or location administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param person_id: A unique identifier for the person.
        :type person_id: str
        :param settings: Person Executive Assistant Settings
        :type settings: :class:`AssistantSettings`
        :param org_id: Organization ID for the person.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = settings.update()
        url = self.ep(f'telephony/config/people/{person_id}/executive/assistant')
        await super().put(url, params=params, json=body)

    async def executive_available_assistants(self, person_id: str, name: str = None, phone_number: str = None,
                                       org_id: str = None,
                                       **params) -> List[ExecOrAssistant]:
        """
        Get Person Executive Available Assistants

        Retrieves a list of people available for assignment as executive assistants to the specified person.

        As an executive, you can add assistants to your executive pool to manage calls for you. You can set when and
        which types of calls they can handle. Assistants can opt in when needed or opt out when not required.

        This API requires a full, user, read-only or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param person_id: A unique identifier for the person.
        :type person_id: str
        :param name: Only return people with the matching name (person's first and last name combination).
        :type name: str
        :param phone_number: Only return people with the matching phone number or extension.
        :type phone_number: str
        :param org_id: Organization ID for the person.
        :type org_id: str
        :return: list of available assistants
        """
        if org_id is not None:
            params['orgId'] = org_id
        if name is not None:
            params['name'] = name
        if phone_number is not None:
            params['phoneNumber'] = phone_number
        url = self.ep(f'telephony/config/people/{person_id}/executive/availableAssistants')
        data = await super().get(url, params=params)
        r = TypeAdapter(list[ExecOrAssistant]).validate_python(data['assistants'])
        return r

    async def executive_call_filtering_settings(self, person_id: str,
                                          org_id: str = None) -> ExecCallFiltering:
        """
        Get Person Executive Call Filtering Settings

        Retrieve the executive call filtering settings for the specified person.

        Executive Call Filtering in Webex allows you to control which calls are allowed to reach the executive
        assistant based on custom criteria, such as specific phone numbers or call types. You can enable or disable
        call filtering and configure filter rules to manage incoming calls.

        This API requires a full, user, read-only or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`

        :param person_id: A unique identifier for the person.
        :type person_id: str
        :param org_id: Organization ID for the user.
        :type org_id: str
        :rtype: :class:`ExecCallFiltering`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'telephony/config/people/{person_id}/executive/callFiltering')
        data = await super().get(url, params=params)
        r = ExecCallFiltering.model_validate(data)
        return r

    async def update_executive_call_filtering_settings(self, person_id: str, settings: ExecCallFiltering,
                                                 org_id: str = None):
        """
        Modify Person Executive Call Filtering Settings

        Update the executive call filtering settings for the specified person.

        Executive Call Filtering in Webex allows you to control which calls are allowed to reach the executive
        assistant based on custom criteria, such as specific phone numbers or call types. You can enable or disable
        call filtering and configure filter rules to manage incoming calls.

        This API requires a full, user, or location administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param person_id: A unique identifier for the person.
        :type person_id: str
        :param settings: Person Executive Call Filtering Settings
        :type settings: ExecCallFiltering
        :param org_id: Organization ID for the user.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = settings.update()
        url = self.ep(f'telephony/config/people/{person_id}/executive/callFiltering')
        await super().put(url, params=params, json=body)

    async def create_call_filtering_criteria(self, person_id: str, settings: ExecCallFilteringCriteria,
                                       org_id: str = None) -> str:
        """
        Add Person Executive Call Filtering Criteria

        Create a new executive call filtering criteria configuration for the specified person.

        Executive Call Filtering Criteria in Webex allows you to define detailed filter rules for incoming calls. This
        API creates a new filter rule with the specified configuration, including schedule, phone numbers, and call
        routing preferences.

        This API requires a full, user or location administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param person_id: A unique identifier for the person.
        :type person_id: str
        :param settings: Call Filtering Settings
        :type settings: ExecCallFilteringCriteria
        :param org_id: Organization ID for the user.
        :type org_id: str
        :rtype: str
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = settings.create()
        url = self.ep(f'telephony/config/people/{person_id}/executive/callFiltering/criteria')
        data = await super().post(url, params=params, json=body)
        r = data['id']
        return r

    async def delete_call_filtering_criteria(self, person_id: str, id: str, org_id: str = None):
        """
        Delete Person Executive Call Filtering Criteria

        Delete a specific executive call filtering criteria configuration for the specified person.

        Executive Call Filtering Criteria in Webex allows you to manage detailed filter rules for incoming calls. This
        API removes a specific filter rule by its unique identifier.

        This API requires a full, user or location administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param person_id: A unique identifier for the person.
        :type person_id: str
        :param id: The `id` parameter specifies the unique identifier for the executive call filtering criteria.
            Example: `Y2lzY29zcGFyazovL3VzL0NSSVRFUklBL2RHVnpkRjltYVd4MFpYST0`.
        :type id: str
        :param org_id: Organization ID for the user.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'telephony/config/people/{person_id}/executive/callFiltering/criteria/{id}')
        await super().delete(url, params=params)

    async def get_filtering_criteria(self, person_id: str, id: str,
                               org_id: str = None) -> ExecCallFilteringCriteria:
        """
        Get Person Executive Call Filtering Criteria Settings

        Retrieve the executive call filtering criteria settings for the specified person.

        Executive Call Filtering Criteria in Webex allows you to retrieve the detailed configuration for a specific
        filter rule. This includes schedule settings, phone number filters, and call routing preferences for executive
        call filtering.

        This API requires a full, user, read-only or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param person_id: Unique identifier for the person.
        :type person_id: str
        :param id: The `id` parameter specifies the unique identifier for the executive call filtering criteria.
        :type id: str
        :param org_id: Organization ID for the user.
        :type org_id: str
        :rtype: :class:`ExecCallFilteringCriteria`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'telephony/config/people/{person_id}/executive/callFiltering/criteria/{id}')
        data = await super().get(url, params=params)
        r = ExecCallFilteringCriteria.model_validate(data)
        return r

    async def update_call_filtering_criteria(self, person_id: str, id: str,
                                       settings: ExecCallFilteringCriteria,
                                       org_id: str = None):
        """
        Modify Person Executive Call Filtering Criteria Settings

        Update the executive call filtering settings for the specified person.

        Executive Call Filtering in Webex allows you to control which calls are allowed to reach the executive
        assistant based on custom criteria, such as specific phone numbers or call types. You can enable or disable
        call filtering and configure filter rules to manage incoming calls.

        This API requires a full, user or location administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param person_id: A unique identifier for the person.
        :type person_id: str
        :param id: The `id` parameter specifies the unique identifier for the executive call filtering criteria.
            Example: `Y2lzY29zcGFyazovL3VzL0NSSVRFUklBL2RHVnpkRjltYVd4MFpYST0`.
        :type id: str
        :param settings: Call Filtering Settings
        :type settings: :class:`ExecCallFilteringCriteria`
        :param org_id: Organization ID for the user.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = settings.update()
        url = self.ep(f'telephony/config/people/{person_id}/executive/callFiltering/criteria/{id}')
        await super().put(url, params=params, json=body)

    async def screening_settings(self, person_id: str, org_id: str = None) -> ExecScreening:
        """
        Get Person Executive Screening Settings

        Get executive screening settings for the specified person.

        Executive Screening in Webex allows you to manage how incoming calls are screened and alerted based on your
        preferences. You can enable or disable executive screening and configure alert types and locations for
        notifications.

        This API requires a full, user, read-only or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param person_id: A unique identifier for the person.
        :type person_id: str
        :param org_id: Organization ID for the person.
        :type org_id: str
        :rtype: :class:`ExecScreening`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'telephony/config/people/{person_id}/executive/screening')
        data = await super().get(url, params=params)
        r = ExecScreening.model_validate(data)
        return r

    async def update_screening_settings(self, person_id: str, settings: ExecScreening,
                                  org_id: str = None):
        """
        Modify Person Executive Screening Settings

        Update executive screening settings for the specified person.

        Executive Screening in Webex allows you to manage how incoming calls are screened and alerted based on your
        preferences. You can enable or disable executive screening and configure alert types and locations for
        notifications.

        This API requires a full, user, or location administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param person_id: A unique identifier for the person.
        :type person_id: str
        :param settings: Screening Settings
        :type settings: ExecScreening
        :param org_id: Organization ID for the person.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = settings.model_dump(mode='json', by_alias=True, exclude_unset=True)
        url = self.ep(f'telephony/config/people/{person_id}/executive/screening')
        await super().put(url, params=params, json=body)


class AsFeatureAccessApi(AsApiChild, base='telephony'):
    """
    End user Feature Access API
    """

    async def read_default(self) -> FeatureAccessSettings:
        """
        Read Default Feature Access Settings for Person

        Read the default feature access configuration for users in the organization. It allows administrators to review
        the baseline feature availability settings that will be applied to new users by default, ensuring consistency
        in user experience and policy enforcement.

        This API is part of the organizational-level user configuration management for feature access. It is used to
        define the default settings that control which Webex features are enabled or disabled when users are
        provisioned. In Control Hub, this corresponds to the "Default User Settings" under Calling or Telephony,
        providing centralized control over user capabilities across the organization.

        To call this API, an administrator must use a full, or read-only administrator auth token with the
        `spark-admin:telephony_config_read` scope.

        :rtype: :class:`FeatureAccessSettings`
        """
        url = self.ep('config/people/settings/permissions')
        data = await super().get(url)
        r = FeatureAccessSettings.model_validate(data)
        return r

    async def update_default(self, settings: FeatureAccessSettings):
        """
        Update Default Person Feature Access Configuration

        Updates the default feature access configuration for users in the organization. It allows administrators to
        modify the baseline settings that determine which Webex features are enabled or disabled for users by default,
        ensuring new users are provisioned with consistent access controls.

        This API is part of the organization-level user configuration management for feature access. It supports
        defining and updating default settings that apply automatically to all newly onboarded users. In Control Hub,
        this corresponds to the "Default User Settings" section for Calling or Telephony, enabling centralized and
        scalable configuration of user capabilities.

        To use this API, an administrator must authenticate with a full, or device administrator auth token. The token
        must include the `spark-admin:telephony_config_write` scope.

        :param settings: The feature access settings to be updated.
        :type settings: :class:`FeatureAccessSettings`

        :rtype: None
        """
        body = settings.update()
        url = self.ep('config/people/settings/permissions')
        await super().put(url, json=body)

    async def read(self, person_id: str) -> UserFeatureAccessSettings:
        """
        Read Feature Access Settings for a Person

        Read the feature access configuration for the current user within the organization. It allows administrators to
        read the telephony settings, including device and location configurations, specific to that user’s role and
        access privileges. This API is useful for managing and verifying user-specific feature access within the
        broader telephony system.

        The feature is part of the organization’s telephony configuration management. It provides insight into the
        settings and permissions that control how telephony services are assigned and configured for individual users.
        This functionality is available through the Control Hub and allows for the management of user access to
        various telephony-related features.

        To access this API, the user must possess a full, or read-only administrator role. The authentication token
        used must include the `spark-admin:telephony_config_read` scope, granting the necessary permissions to read
        the telephony configuration for the user in question.

        :param person_id: User ID of the Organization.
        :type person_id: str
        :rtype: :class:`UserFeatureAccessSettings`
        """
        url = self.ep(f'config/people/{person_id}/settings/permissions')
        data = await super().get(url)
        r = UserFeatureAccessSettings.model_validate(data)
        return r

    async def update(self,
               person_id: str,
               settings: FeatureAccessSettings):
        """
        Update a Person’s Feature Access Configuration

        Update the feature access configuration for the current user within the organization. It enables administrators
        to modify the telephony settings, including device and location configurations, specific to the user’s role
        and access privileges. This API is useful for making adjustments to user-specific feature access within the
        telephony system.

        The feature is part of the organization’s telephony configuration management. It provides control over the
        settings and permissions that govern how telephony services are assigned and configured for individual users.
        This functionality is available through the Control Hub and enables the modification of user access to various
        telephony-related features.

        To use this API, an administrator must authenticate with a full, or device administrator auth token. The
        authentication token used must include the `spark-admin:telephony_config_write` scope, granting the necessary
        permissions to update the telephony configuration for the user in question.


        :param person_id: User ID of the Organization.
        :type person_id: str
        :param settings: The feature access settings to be updated.
        :type settings: :class:`FeatureAccessSettings`
        :rtype: None
        """
        body = settings.update()
        url = self.ep(f'config/people/{person_id}/settings/permissions')
        await super().put(url, json=body)

    async def reset(self, person_id: str):
        """
        Reset a Person’s Feature Access Configuration to the Organization’s Default Settings

        Reset of a user’s feature access configuration to the organization’s default settings. It ensures that any
        specific feature configurations set by an administrator for an individual user are overridden and replaced
        with the global configuration of the organization. This process helps to maintain consistency in feature
        access across all users, especially when administrators want to ensure that a user is subject to the
        organization's global settings rather than personalized settings.

        The overall feature, managed through the organization's Control Hub, involves the configuration and
        customization of feature access for users. Administrators can tailor these settings to individual users based
        on their roles or needs, but sometimes a global reset to the default configuration is necessary. The reset API
        simplifies this by programmatically resetting a user’s feature access, which can be crucial when managing
        large teams or updating organizational policies that affect user privileges across multiple devices or
        locations.

        To use this API, an administrator must authenticate with a full, or device administrator auth token. This
        ensures the individual has the necessary privileges to make changes to user configurations. Furthermore, the
        authentication token used must include the `spark-admin:telephony_config_write` scope, which grants the
        required permissions to modify the telephony configuration for the user. This combination of roles and scopes
        ensures that only authorized administrators can reset the feature access configuration.

        :param person_id: User ID of the Organization.
        :type person_id: str
        :rtype: None
        """
        url = self.ep(f'config/people/{person_id}/settings/permissions/actions/reset/invoke')
        await super().post(url)


class AsHotelingApi(AsPersonSettingsApiChild):
    """
    API for person's hoteling settings
    """

    # TODO: this seems to be wrong. For workspace devices methods exist with complete coverage for all hoteling settings

    feature = 'hoteling'

    async def read(self, person_id: str, org_id: str = None) -> bool:
        """
        Read Hoteling Settings for a Person

        Retrieve a person's hoteling settings.

        As an administrator, you can enable hoteling for people so that their phone profile (phone number, features,
        and calling plan) is temporarily loaded onto a shared (host) phone.

        This API requires a full, user, or read-only administrator auth token with a scope of spark-admin:people_read.

        :param person_id: Unique identifier for the person.
        :type person_id: str
        :param org_id: Person is in this organization. Only admin users of another organization (such as partners)
            may use this parameter as the default is the same organization as the token used to access API.
        :type org_id: str
        :return: hoteling setting
        :rtype: bool
        """
        ep = self.f_ep(person_id=person_id)
        params = org_id and {'orgId': org_id} or None
        data = await self.get(ep, params=params)
        return data['enabled']

    async def configure(self, person_id: str, enabled: bool, org_id: str = None):
        """
        Configure Hoteling Settings for a Person

        Configure a person's hoteling settings.

        As an administrator, you can enable hoteling for people so that their phone profile (phone number, features,
        and calling plan) is temporarily loaded onto a shared (host) phone.

        This API requires a full or user administrator auth token with the spark-admin:people_write scope.

        :param person_id: Unique identifier for the person.
        :type person_id: str
        :param enabled: When true, allow this person to connect to a Hoteling host device.
        :type enabled: bool
        :param org_id: Person is in this organization. Only admin users of another organization (such as partners)
            may use this parameter as the default is the same organization as the token used to access API.
        :type org_id: str
        """
        ep = self.f_ep(person_id=person_id)
        params = org_id and {'orgId': org_id} or None
        data = json.dumps({'enabled': enabled})
        await self.put(ep, params=params, json=data)


class AsIncomingPermissionsApi(AsPersonSettingsApiChild):
    """
    API for incoming permissions settings

    Also used for virtual lines, workspaces
    """

    feature = 'incomingPermission'

    async def read(self, entity_id: str, org_id: str = None) -> IncomingPermissions:
        """
        Read Incoming Permission Settings

        Retrieve Incoming Permission Settings

        You can change the incoming calling permissions for an entity if you want them to be different from your
        organization's default.

        This API requires a full, user, or read-only administrator auth token with a scope of spark-admin:people_read.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param org_id: entity is in this organization. Only admin users of another organization (such as partners)
            may use this parameter as the default is the same organization as the token used to access API.
        :type org_id: str
        :return: incoming permission settings for specific user
        :rtype: :class:`IncomingPermissions`
        """
        ep = self.f_ep(person_id=entity_id)
        params = org_id and {'orgId': org_id} or None
        return IncomingPermissions.model_validate(await self.get(ep, params=params))

    async def configure(self, entity_id: str, settings: IncomingPermissions, org_id: str = None):
        """
        Configure incoming permissions settings

        The Barge In feature enables you to use a Feature Access Code (FAC) to answer a call that was directed to
        another subscriber, or barge-in on the call if it was already answered. Barge In can be used across locations.

        This API requires a full or user administrator auth token with the spark-admin:people_write scope or a user
        auth token with spark:people_write scope can be used by an entity to update their own settings.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param settings: new setting to be applied
        :type settings: :class:`IncomingPermissions`
        :param org_id: entity is in this organization. Only admin users of another organization (such as partners)
            may use this parameter as the default is the same organization as the token used to access API.
        :type org_id: str
        """
        ep = self.f_ep(person_id=entity_id)
        params = org_id and {'orgId': org_id} or None
        await self.put(ep, params=params, data=settings.model_dump_json())


class AsMSTeamsSettingApi(AsApiChild, base='telephony/config/people'):
    async def read(self, person_id: str,
             org_id: str = None) -> MSTeamsSettings:
        """
        Retrieve a Person's MS Teams Settings

        At a person level, MS Teams settings allow access to retrieving the `HIDE WEBEX APP` and `PRESENCE SYNC`
        settings.

        To retrieve a person's MS Teams settings requires a full or read-only administrator auth token with a scope
        of `spark-admin:telephony_config_read`.

        :param person_id: Unique identifier for the person.
        :type person_id: str
        :param org_id: ID of the organization in which the person resides. Only admin users of another organization
            (such as partners) may use this parameter since the default is the same organization as the token used to
            access the API.
        :type org_id: str
        :rtype: :class:`MSTeamsSettings`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'{person_id}/settings/msTeams')
        data = await super().get(url, params=params)
        r = MSTeamsSettings.model_validate(data)
        return r

    async def configure(self, person_id: str,
                  setting_name: str, value: bool,
                  org_id: str = None):
        """
        Configure a Person's MS Teams Setting

        MS Teams settings can be configured at the person level.

        Requires a full administrator auth token with a scope of `spark-admin:telephony_config_write`.

        :param person_id: Unique identifier for the person.
        :type person_id: str
        :param setting_name: The enum value to be set to `HIDE_WEBEX_APP`.
        :type setting_name: str
        :param value: The boolean value to update the `HIDE_WEBEX_APP` setting, either `true` or `false`. Set to `null`
            to delete the `HIDE_WEBEX_APP` setting.
        :type value: bool
        :param org_id: ID of the organization in which the person resides. Only admin users of another organization
            (such as partners) may use this parameter since the default is the same organization as the token used to
            access the API.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        body['settingName'] = setting_name
        body['value'] = value
        url = self.ep(f'{person_id}/settings/msTeams')
        await super().put(url, params=params, json=body)


class AsModeManagementApi(AsApiChild, base='telephony/config/people'):
    """
    Operating Mode Management for Person Call Settings

    Person Call Settings supports modifying Webex Calling settings for a specific person.

    Viewing People requires a full, user, or read-only administrator or location administrator auth token with a scope
    of `spark-admin:people_read` or, for select APIs, a user auth token with `spark:people_read` scope can be used by
    a person to read their own settings.

    Configuring People settings requires a full, or user administrator or location administrator auth token with the
    `spark-admin:people_write` scope or, for select APIs, a user auth token with `spark:people_write` scope can be
    used by a person to update their own settings.

    Call Settings API access can be restricted via Control Hub by a full administrator. Restricting access causes the
    APIs to throw a `403 Access Forbidden` error.

    See details about `features available by license type for Webex Calling
    <https://help.webex.com/en-us/article/n1qbbp7/Features-available-by-license-type-for-Webex-Calling>`_.
    """

    def available_features_gen(self, person_id: str = None, name: str = None,
                           phone_number: str = None, extension: str = None, order: str = None,
                           org_id: str = None,
                           **params) -> AsyncGenerator[AvailableFeature, None, None]:
        """
        Retrieve the List of Available Features.

        Retrieve a list of feature identifiers that can be assigned to a user for `Mode Management`. Feature
        identifiers reference feature instances like `Auto Attendants`, `Call Queues`, and `Hunt Groups`.

        Features with mode-based call forwarding enabled can be assigned to a user for `Mode Management`.

        Retrieving this list requires a full, read-only, or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param person_id: A unique identifier for the user.
        :type person_id: str
        :param name: List features whose `name` contains this string.
        :type name: str
        :param phone_number: List features whose phoneNumber contains this matching string.
        :type phone_number: str
        :param extension: List features whose `extension` contains this matching string.
        :type extension: str
        :param order: Sort the list of features based on `name`, `phoneNumber`, or `extension`, either `asc`, or
            `desc`.
        :type order: str
        :param org_id: Retrieve features list from this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableFeature` instances
        """
        if name is not None:
            params['name'] = name
        if phone_number is not None:
            params['phoneNumber'] = phone_number
        if extension is not None:
            params['extension'] = extension
        if order is not None:
            params['order'] = order
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'{person_id}/modeManagement/availableFeatures')
        return self.session.follow_pagination(url=url, model=AvailableFeature,
                                              item_key='features', params=params)

    async def available_features(self, person_id: str = None, name: str = None,
                           phone_number: str = None, extension: str = None, order: str = None,
                           org_id: str = None,
                           **params) -> List[AvailableFeature]:
        """
        Retrieve the List of Available Features.

        Retrieve a list of feature identifiers that can be assigned to a user for `Mode Management`. Feature
        identifiers reference feature instances like `Auto Attendants`, `Call Queues`, and `Hunt Groups`.

        Features with mode-based call forwarding enabled can be assigned to a user for `Mode Management`.

        Retrieving this list requires a full, read-only, or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param person_id: A unique identifier for the user.
        :type person_id: str
        :param name: List features whose `name` contains this string.
        :type name: str
        :param phone_number: List features whose phoneNumber contains this matching string.
        :type phone_number: str
        :param extension: List features whose `extension` contains this matching string.
        :type extension: str
        :param order: Sort the list of features based on `name`, `phoneNumber`, or `extension`, either `asc`, or
            `desc`.
        :type order: str
        :param org_id: Retrieve features list from this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableFeature` instances
        """
        if name is not None:
            params['name'] = name
        if phone_number is not None:
            params['phoneNumber'] = phone_number
        if extension is not None:
            params['extension'] = extension
        if order is not None:
            params['order'] = order
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'{person_id}/modeManagement/availableFeatures')
        return [o async for o in self.session.follow_pagination(url=url, model=AvailableFeature,
                                              item_key='features', params=params)]

    async def assigned_features(self, person_id: str = None,
                          org_id: str = None) -> list[ModeManagementFeature]:
        """
        Retrieve the List of Features Assigned to a User for Mode Management.

        Retrieve a list of feature identifiers that are already assigned to a user for `Mode Management`. Feature
        identifiers reference feature instances like `Auto Attendants`, `Call Queues`, and `Hunt Groups`.
        A maximum of 50 features can be assigned to a user for `Mode Management`.

        Features with mode-based call forwarding enabled can be assigned to a user for `Mode Management`.

        Retrieving this list requires a full, read-only, or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param person_id: A unique identifier for the user.
        :type person_id: str
        :param org_id: Retrieve features list from this organization.
        :type org_id: str
        :rtype: list[ModeManagementFeature]
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'{person_id}/modeManagement/features')
        data = await super().get(url, params=params)
        r = TypeAdapter(list[ModeManagementFeature]).validate_python(data['features'])
        return r

    async def assign_features(self, feature_ids: list[str], person_id: str = None, org_id: str = None):
        """
        Assign a List of Features to a User for Mode Management.

        Assign a user a list of feature identifiers for `Mode Management`. Feature identifiers reference feature
        instances like `Auto Attendants`, `Call Queues`, and `Hunt Groups`.
        A maximum of 50 features can be assigned to a user for `Mode Management`.

        Updating mode management settings for a user requires a full, or location administrator auth token with a scope
        of `spark-admin:telephony_config_write`.

        :param feature_ids: Array of feature IDs.
        :type feature_ids: list[str]
        :param person_id: A unique identifier for the user.
        :type person_id: str
        :param org_id: Retrieve features list from this organization.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        body['featureIds'] = feature_ids
        url = self.ep(f'{person_id}/modeManagement/features')
        await super().put(url, params=params, json=body)


class AsMonitoringApi(AsPersonSettingsApiChild):
    """
    API for person's call monitoring settings, also used for workspaces
    """

    feature = 'monitoring'

    async def read(self, entity_id: str, org_id: str = None) -> Monitoring:
        """
        Retrieve an entity's Monitoring Settings

        Retrieves the monitoring settings of the entity, which shows specified people, places, virtual lines or call
        park extensions that are being monitored.

        Monitors the line status which indicates if a person, place or virtual line is on a call and if a call has been
        parked on that extension.

        This API requires a full, user, or read-only administrator or location administrator auth token with a scope of
        `spark-admin:people_read`.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param org_id: ID of the organization in which the entity resides. Only admin users of another organization
            (such as partners) may use this parameter as the default is the same organization as the token used to
            access API.
        :type org_id: str
        :return: monitoring settings
        :rtype: :class:`Monitoring`
        """
        ep = self.f_ep(person_id=entity_id)
        params = org_id and {'orgId': org_id} or None
        data = await self.get(ep, params=params)
        return Monitoring.model_validate(data)

    async def configure(self, entity_id: str, settings: Monitoring, org_id: str = None):
        """
        Modify an entity's Monitoring Settings

        Modifies the monitoring settings of the entity.

        Monitors the line status of specified people, places, virtual lines or call park extension. The line status
        indicates if a person, place or virtual line is on a call and if a call has been parked on that extension.

        This API requires a full or user administrator or location administrator auth token with the
        `spark-admin:people_write` scope.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param settings: settings for update
        :type settings: :class:`Monitoring`
        :param org_id: Person is in this organization. Only admin users of another organization (such as partners)
            may use this parameter as the default is the same organization as the token used to access API.
        :type org_id: str
        """
        ep = self.f_ep(person_id=entity_id)
        params = org_id and {'orgId': org_id} or None
        data = {}
        if settings.call_park_notification_enabled is not None:
            data['enableCallParkNotification'] = settings.call_park_notification_enabled
        if settings.monitored_elements is not None:
            id_list = []
            for me in settings.monitored_elements:
                if isinstance(me, str):
                    id_list.append(me)
                else:
                    id_list.append(me.member and me.member.member_id or me.cpe and me.cpe.cpe_id)
            data['monitoredElements'] = id_list
        await self.put(ep, params=params, json=data)


class AsMusicOnHoldApi(AsPersonSettingsApiChild):

    feature = 'musicOnHold'

    async def read(self, entity_id: str, org_id: str = None) -> MusicOnHold:
        """
        Retrieve Music On Hold Settings for a Person, virtual line, or workspace.

        Retrieve the music on hold settings.

        Music on hold is played when a caller is put on hold, or the call is parked.

        Retrieving a person's music on hold settings requires a full, user or read-only administrator or location
        administrator auth token with a scope of `spark-admin:telephony_config_read`.

        :param entity_id: Unique identifier for the person, virtual line, or workspace.
        :type entity_id: str
        :param org_id: ID of the organization in which the person resides. Only admin users of another organization
            (such as partners) may use this parameter as the default is the same organization as the token used to
            access API.
        :type org_id: str
        :rtype: :class:`GetMusicOnHoldObject`
        """
        params = org_id and {'orgId': org_id} or None
        url = self.f_ep(entity_id)
        data = await super().get(url, params=params)
        r = MusicOnHold.model_validate(data)
        return r

    async def configure(self, entity_id: str, settings: MusicOnHold,
                  org_id: str = None):
        """
        Configure Music On Hold Settings for a Personvirtual line, or workspace.

        Configure music on hold settings.

        Music on hold is played when a caller is put on hold, or the call is parked.

        To configure music on hold settings for a person, music on hold setting must be enabled for this location.

        Updating a person's music on hold settings requires a full or user administrator or location administrator auth
        token with a scope of `spark-admin:telephony_config_write`.

        :param entity_id: Unique identifier for the person, virtual line, or workspace.
        :type entity_id: str
        :param settings: new MOH settings
        :type settings: MusicOnHold
        :param org_id: ID of the organization in which the entity resides. Only admin users of another organization
            (such as partners) may use this parameter as the default is the same organization as the token used to
            access API.
        :type org_id: str
        :rtype: None
        """
        params = org_id and {'orgId': org_id} or None
        body = settings.update()
        url = self.f_ep(entity_id)
        await super().put(url, params=params, json=body)


class AsNumbersApi(AsPersonSettingsApiChild):
    """
    API for person's numbers
    """

    feature = 'numbers'

    async def read(self, person_id: str, prefer_e164_format: bool = None, org_id: str = None) -> PersonNumbers:
        """
        Get a person's phone numbers including alternate numbers.

        A person can have one or more phone numbers and/or extensions via which they can be called.

        This API requires a full or user administrator auth token with
        the spark-admin:people_read scope.

        :param person_id: Unique identifier for the person.
        :type person_id: str
        :param prefer_e164_format: Return phone numbers in E.164 format.
        :type prefer_e164_format: bool
        :param org_id: Person is in this organization. Only admin users of another organization (such as partners) may
            use this parameter as the default is the same organization as the token used to access API.
        :type org_id: str
        :return: Alternate numbers of the user
        :rtype: :class:`PersonNumbers`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        if prefer_e164_format is not None:
            params['preferE164Format'] = str(prefer_e164_format).lower()
        ep = self.f_ep(person_id=person_id)
        return PersonNumbers.model_validate(await self.get(ep, params=params))

    async def update(self, person_id: str, update: UpdatePersonNumbers, org_id: str = None):
        """
        Assign or unassign alternate phone numbers to a person.

        Each location has a set of phone numbers that can be assigned to people, workspaces, or features. Phone
        numbers must follow E.164 format for all countries, except for the United States, which can also follow the
        National format. Active phone numbers are in service.

        Assigning or Unassigning an alternate phone number to a person requires a full administrator auth token with
        a scope of spark-admin:telephony_config_write.

        :param person_id: Unique identifier of the person.
        :type person_id: str
        :param update: Update to apply
        :type update: :class:`UpdatePersonNumbers`
        :param org_id: organization to work on
        :type org_id: str
        """
        url = self.session.ep(f'telephony/config/people/{person_id}/numbers')
        params = org_id and {'orgId': org_id} or None
        body = update.model_dump_json()
        await self.put(url=url, params=params, data=body)


class AsAccessCodesApi(AsPersonSettingsApiChild):
    """
    API for outgoing permission access codes: locations, persons, workspaces, virtual lines
    """
    feature = 'outgoingPermission/accessCodes'

    async def read(self, entity_id: str, org_id: str = None) -> AuthCodes:
        """
        Retrieve Access codes.

        Access codes are used to bypass permissions.

        This API requires a full or read-only administrator auth token with a scope of spark-admin:workspaces_read or
        a user auth token with spark:workspaces_read scope can be used to read entity settings.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param org_id: ID of the organization within which the entity resides. Only admin users of another
            organization (such as partners) may use this parameter as the default is the same organization as the token
            used to access API.
        :type org_id: str
        :return: authorization codes
        :rtype: :class:`AuthCodes`
        """
        url = self.f_ep(entity_id)
        params = org_id and {'orgId': org_id} or None
        data = await self.get(url, params=params)
        return AuthCodes.model_validate(data)

    async def modify(self, entity_id: str, use_custom_access_codes: bool = None,
               delete_codes: list[Union[str, AuthCode]] = None, org_id: str = None):
        """
        Modify Access Codes

        Access codes are used to bypass permissions.

        This API requires a full or user administrator or location administrator auth token with
        the `spark-admin:telephony_config_write` scope.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param use_custom_access_codes: When `true`, use custom settings for the access codes category of outgoing call
            permissions.
        :type use_custom_access_codes: bool
        :param delete_codes: Indicates access codes to delete.
        :type delete_codes: list[str]
        :param org_id: ID of the organization in which the entity resides. Only admin users of another organization
            (such as partners) may use this parameter as the default is the same organization as the token used to
            access the API.
        :type org_id: str
        :rtype: None
        """
        url = self.f_ep(entity_id)
        params = org_id and {'orgId': org_id} or None
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        if use_custom_access_codes is not None:
            body['useCustomAccessCodes'] = use_custom_access_codes
        if delete_codes is not None:
            body['deleteCodes'] = [ac.code if isinstance(ac, AuthCode) else ac
                                   for ac in delete_codes]
        await super().put(url, params=params, json=body)

    async def create(self, entity_id: str, code: str, description: str, org_id: str = None):
        """
        Create new Access codes.

        Access codes are used to bypass permissions.

        This API requires a full or user administrator auth token with the spark-admin:workspaces_write scope or a
        user auth token with spark:workspaces_write scope can be used to update workspace settings.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param code: Indicates an access code.
        :type code: str
        :param description: Indicates the description of the access code.
        :type description: str
        :param org_id: ID of the organization within which the entity resides. Only admin users of another
            organization (such as partners) may use this parameter as the default is the same organization as the token
            used to access API.
        :type org_id: str
        """
        url = self.f_ep(entity_id)
        params = org_id and {'orgId': org_id} or None
        body = {'code': code,
                'description': description}
        await self.post(url, params=params, json=body)

    async def delete(self, entity_id: str, org_id: str = None):
        """
        Delete Access Code

        Deletes all Access codes for the given entity.

        Access codes are used to bypass permissions.

        This API requires a full or user administrator or location administrator auth token with the
        `spark-admin:workspaces_write` scope or a user auth token with `spark:workspaces_write` scope can be used to
        update entity settings.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param org_id: ID of the organization within which the entity resides. Only admin users of another
            organization (such as partners) may use this parameter as the default is the same organization as the
            token used to access the API.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.f_ep(entity_id)
        await super().delete(url, params=params)


class AsDigitPatternsApi(AsPersonSettingsApiChild):
    feature = 'outgoingPermission/digitPatterns'

    async def get_digit_patterns(self, entity_id: str,
                           org_id: str = None) -> DigitPatterns:
        """
        Retrieve Digit Patterns

        Retrieve digit patterns.

        Digit patterns are used to bypass permissions.

        Retrieving digit patterns requires a full or user or read-only administrator or location administrator auth
        token with a scope of `spark-admin:telephony_config_read`.

        :param entity_id: Unique identifier for location, person, workspace, or virtual line.
        :type entity_id: str
        :param org_id: ID of the organization in which the entity resides. Only admin users of another organization
            (such as partners) may use this parameter as the default is the same organization as the token used to
            access the API.
        :type org_id: str
        :rtype: :class:`UserOutgoingPermissionDigitPatternGetListObject`
        """
        params = org_id and {'orgId': org_id} or None
        url = self.f_ep(entity_id)
        data = await super().get(url, params=params)
        r = DigitPatterns.model_validate(data)
        return r

    async def details(self, entity_id: str, digit_pattern_id: str,
                org_id: str = None) -> DigitPattern:
        """
        Retrieve Digit Pattern Details

        Retrieve the digit pattern details.

        Digit patterns are used to bypass permissions.

        Retrieving the digit pattern details requires a full or user or read-only administrator or location
        administrator auth token with a scope of `spark-admin:telephony_config_read`.

        :param entity_id: Unique identifier for location, person, workspace, or virtual line.
        :type entity_id: str
        :param digit_pattern_id: Unique identifier for the digit pattern.
        :type digit_pattern_id: str
        :param org_id: ID of the organization in which the entity resides. Only admin users of another organization
            (such as partners) may use this parameter as the default is the same organization as the token used to
            access the API.
        :type org_id: str
        :rtype: :class:`UserDigitPatternObject`
        """
        params = org_id and {'orgId': org_id} or None
        url = self.f_ep(entity_id, path=digit_pattern_id)
        data = await super().get(url, params=params)
        r = DigitPattern.model_validate(data)
        return r

    async def create(self, entity_id: str, pattern: DigitPattern, org_id: str = None) -> str:
        """
        Create Digit Patterns

        Create new digit pattern.

        Digit patterns are used to bypass permissions.

        Creating the digit pattern requires a full or user or location administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param entity_id: Unique identifier for location, person, workspace, or virtual line.
        :type entity_id: str
        :param pattern: new digit pattern
        :type pattern: :class:`DigitPattern`
        :param org_id: ID of the organization in which the entity resides. Only admin users of another organization
            (such as partners) may use this parameter as the default is the same organization as the token used to
            access the API.
        :type org_id: str
        :rtype: str
        """
        params = org_id and {'orgId': org_id} or None
        body = pattern.create_or_update()
        url = self.f_ep(entity_id)
        data = await super().post(url, params=params, json=body)
        r = data['id']
        return r

    async def update_category_control_settings(self, entity_id: str,
                                         use_custom_digit_patterns: bool = None,
                                         org_id: str = None):
        """
        Modify the Digit Pattern Category Control Settings for the entity

        Modifies whether this user uses the specified digit patterns when placing outbound calls or not.

        Updating the digit pattern category control settings requires a full or user or location administrator auth
        token with a scope of `spark-admin:telephony_config_write`.

        :param entity_id: Unique identifier for location, person, workspace, or virtual line.
        :type entity_id: str
        :param use_custom_digit_patterns: When `true`, use custom settings for the digit patterns category of outgoing
            call permissions.
        :type use_custom_digit_patterns: bool
        :param org_id: ID of the organization in which the entity resides. Only admin users of another organization
            (such as partners) may use this parameter as the default is the same organization as the token used to
            access the API.
        :type org_id: str
        :rtype: None
        """
        params = org_id and {'orgId': org_id} or None
        body = dict()
        if use_custom_digit_patterns is not None:
            body['useCustomDigitPatterns'] = use_custom_digit_patterns
        url = self.f_ep(entity_id)
        await super().put(url, params=params, json=body)

    async def update(self, entity_id: str, settings: DigitPattern, org_id: str = None):
        """
        Modify Digit Patterns

        Modify Digit Patterns

        Digit patterns are used to bypass permissions.

        Updating the digit pattern requires a full or user or location administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param entity_id: Unique identifier for location, person, workspace, or virtual line.
        :type entity_id: str
        :param settings: new digit pattern settings
        :type settings: :class:`DigitPattern`
        :param org_id: ID of the organization in which the entity resides. Only admin users of another organization
            (such as partners) may use this parameter as the default is the same organization as the token used to
            access the API.
        :type org_id: str
        :rtype: None
        """
        params = org_id and {'orgId': org_id} or None
        body = settings.create_or_update()
        url = self.f_ep(entity_id, path=settings.id)
        await super().put(url, params=params, json=body)

    async def delete(self, entity_id: str, digit_pattern_id: str, org_id: str = None):
        """
        Delete a Digit Pattern

        Delete Digit Pattern.

        Digit patterns are used to bypass permissions.

        Deleting the digit pattern requires a full or user or location administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param entity_id: Unique identifier for location, person, workspace, or virtual line.
        :type entity_id: str
        :param digit_pattern_id: Unique identifier for the digit pattern.
        :type digit_pattern_id: str
        :param org_id: ID of the organization in which the entity resides. Only admin users of another organization
            (such as partners) may use this parameter as the default is the same organization as the token used to
            access the API.
        :type org_id: str
        :rtype: None
        """
        params = org_id and {'orgId': org_id} or None
        url = self.f_ep(entity_id, path=digit_pattern_id)
        await super().delete(url, params=params)

    async def delete_all(self, entity_id: str, org_id: str = None):
        """
        Delete all Digit Patterns.

        Digit patterns are used to bypass permissions.

        Deleting the digit patterns requires a full or user or location administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param entity_id: Unique identifier for location, person, workspace, or virtual line.
        :type entity_id: str
        :param org_id: ID of the organization in which the entity resides. Only admin users of another organization
            (such as partners) may use this parameter as the default is the same organization as the token used to
            access the API.
        :type org_id: str
        :rtype: None
        """
        params = org_id and {'orgId': org_id} or None
        url = self.f_ep(entity_id)
        await super().delete(url, params=params)


class AsTransferNumbersApi(AsPersonSettingsApiChild):
    """
    API for outgoing permission auto transfer numbers
    """
    feature = 'outgoingPermission/autoTransferNumbers'

    async def read(self, entity_id: str, org_id: str = None) -> AutoTransferNumbers:
        """
        Retrieve Transfer Numbers Settings .

        When calling a specific call type, this entity will be automatically transferred to another number. The
        person assigned the Auto Transfer Number can then approve the call and send it through or reject the call
        type. You can add up to 3 numbers.

        This API requires a full or read-only administrator auth token with a scope of spark-admin:workspaces_read or
        a user auth token with spark:workspaces_read scope can be used to read entity settings.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param org_id: entity is in this organization. Only admin users of another organization (such as partners)
            may use this parameter as the default is the same organization as the token used to access API.
        :type org_id: str
        :return: auto transfer numbers
        :rtype: :class:`AutoTransferNumbers`
        """
        url = self.f_ep(entity_id)
        params = org_id and {'orgId': org_id} or None
        data = await self.get(url, params=params)
        return AutoTransferNumbers.model_validate(data)

    async def configure(self, entity_id: str, settings: AutoTransferNumbers, org_id: str = None):
        """
        Modify Transfer Numbers Settings for an entity.

        When calling a specific call type, this entity will be automatically transferred to another number.
        The person assigned the Auto Transfer Number can then approve the call and send it through or reject the
        call type. You can add up to 3 numbers.

        This API requires a full or user administrator auth token with the spark-admin:workspaces_write scope or a
        user auth token with spark:workspaces_write scope can be used to update entity settings.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param settings: new auto transfer numbers
        :type settings: :class:`AutoTransferNumbers`
        :param org_id: entity is in this organization. Only admin users of another organization (such as partners)
            may use this parameter as the default is the same organization as the token used to access API.
        :type org_id: str
        """
        url = self.f_ep(entity_id)
        params = org_id and {'orgId': org_id} or None
        body = settings.model_dump_json()
        await self.put(url, params=params, data=body)


class AsOutgoingPermissionsApi(AsPersonSettingsApiChild):
    """
    API for outgoing permissions settings

    Also used for user, workspace, location, and virtual line outgoing permissions
    """
    transfer_numbers: AsTransferNumbersApi
    access_codes: AsAccessCodesApi
    digit_patterns: AsDigitPatternsApi

    feature = 'outgoingPermission'

    def __getattribute__(self, item):
        if item == 'access_codes' and self.selector == ApiSelector.location:
            raise AttributeError("access_codes API is not available for locations. Use the telephony access_codes API")
        return super().__getattribute__(item)

    def __init__(self, *, session: AsRestSession,
                 selector: ApiSelector = 'person'):
        super().__init__(session=session, selector=selector)
        self.transfer_numbers = AsTransferNumbersApi(session=session, selector=selector)
        if selector == ApiSelector.location:
            # Apparently there is a difference between access code API for locations on one hand and users,
            # workspaces, and virtual lines one the other.
            # For locations, we can create multiple access codes at once.
            self.access_codes = None    # instead use the access_codes API at the telephony level
        else:
            self.access_codes = AsAccessCodesApi(session=session, selector=selector)
        self.digit_patterns = AsDigitPatternsApi(session=session, selector=selector)

    async def read(self, entity_id: str, org_id: str = None) -> OutgoingPermissions:
        """
        Retrieve Outgoing Calling Permissions Settings

        You can change the outgoing calling permissions for a person if you want them to be different from your
        organization's default.

        This API requires a full, user, or read-only administrator auth token with a scope of spark-admin:people_read.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param org_id: Entity is in this organization. Only admin users of another organization (such as partners)
            may use this parameter as the default is the same organization as the token used to access API.
        :type org_id: str
        :return: outgoing permission settings for specific user
        :rtype: :class:`OutgoingPermissions`
        """
        ep = self.f_ep(entity_id)
        params = org_id and {'orgId': org_id} or None
        return OutgoingPermissions.model_validate(await self.get(ep, params=params))

    async def configure(self, entity_id: str, settings: OutgoingPermissions, drop_call_types: set[str] = None,
                  org_id: str = None):
        """
        Configure Outgoing Calling Permissions Settings

        Turn on outgoing call settings for this entity to override the calling settings from the location that are
        used by default.

        This API requires a full or user administrator auth token with the spark-admin:people_write scope or a user
        auth token with spark:people_write scope can be used by a person to update their own settings.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param settings: new setting to be applied
        :type settings: :class:`OutgoingPermissions`
        :param drop_call_types: set of call type names to be excluded from updates. Default is the set of call_types
            known to be not supported for updates
        :type drop_call_types: set[str]
        :param org_id: Entity is in this organization. Only admin users of another organization (such as partners)
            may use this parameter as the default is the same organization as the token used to access API.
        """
        ep = self.f_ep(entity_id)
        params = org_id and {'orgId': org_id} or None
        await self.put(ep, params=params, data=settings.model_dump_json(drop_call_types=drop_call_types))


class AsPersonForwardingApi(AsPersonSettingsApiChild):
    """
    API for person's call forwarding settings

    Also used for virtual lines, workspaces
    """

    feature = 'callForwarding'

    async def read(self, entity_id: str, org_id: str = None) -> PersonForwardingSetting:
        """
        Retrieve an entity's Call Forwarding Settings

        Three types of call forwarding are supported:

        * Always – forwards all incoming calls to the destination you choose.

        * When busy – forwards all incoming calls to the destination you chose while the phone is in use or the person
          is busy.

        * When no answer – forwarding only occurs when you are away or not answering your phone.

        In addition, the Business Continuity feature will send calls to a destination of your choice if your phone is
        not connected to the network for any reason, such as power outage, failed Internet connection, or wiring problem

        This API requires a full, user, or read-only administrator auth token with a scope of spark-admin:people_read
        or a user auth token with spark:people_read scope can be used by a person to read their own settings.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param org_id: Entity is in this organization. Only admin users of another organization (such as partners)
            may use this parameter as the default is the same organization as the token used to access API.
        :type org_id: str
        :return: user's forwarding settings
        :rtype: PersonForwardingSetting
        """
        ep = self.f_ep(person_id=entity_id)
        params = org_id and {'orgId': org_id} or None
        return PersonForwardingSetting.model_validate(await self.get(ep, params=params))

    async def configure(self, entity_id: str, forwarding: PersonForwardingSetting, org_id: str = None):
        """
        Configure an Entity's Call Forwarding Settings

        Three types of call forwarding are supported:

        * Always – forwards all incoming calls to the destination you choose.

        * When busy – forwards all incoming calls to the destination you chose while the phone is in use or the entity
          is busy.

        * When no answer – forwarding only occurs when you are away or not answering your phone.

        In addition, the Business Continuity feature will send calls to a destination of your choice if your phone is
        not connected to the network for any reason, such as power outage, failed Internet connection, or wiring problem

        This API requires a full or user administrator auth token with the spark-admin:people_write scope or a user
        auth token with spark:people_write scope can be used by a person to update their settings.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param forwarding: new forwarding settings
        :type forwarding: PersonForwardingSetting
        :param org_id: Person is in this organization. Only admin users of another organization (such as partners)
            may use this parameter as the default is the same organization as the token used to access API.
        :type org_id: str

        Example:

            .. code-block:: python

                api = self.api.telephony.virtual_lines.forwarding

                forwarding = api.read(entity_id=self.target.id)
                always = CallForwardingAlways(
                    enabled=True,
                    destination='9999',
                    destination_voicemail_enabled=True,
                    ring_reminder_enabled=True)
                forwarding.call_forwarding.always = always
                api.configure(entity_id=self.target.id, forwarding=forwarding)

        """
        ep = self.f_ep(person_id=entity_id)
        params = org_id and {'orgId': org_id} or None
        data = forwarding.update()
        await self.put(ep, params=params, json=data)


class AsPersonalAssistantApi(AsApiChild, base=''):
    """
    API for personal assistant settings

    """

    async def get(self, person_id: str, org_id: str = None) -> PersonalAssistant:
        """
        Get Personal Assistant

        Retrieve Personal Assistant details for a specific user.

        Personal Assistant is used to manage a user's incoming calls when they are away.

        Retrieving Personal Assistant details requires a full, user, or read-only administrator auth token with a scope
        of `spark-admin:telephony_config_read`.

        :param person_id: Unique identifier for the person.
        :type person_id: str
        :param org_id: Get Personal Assistant details for the organization.
        :type org_id: str
        :rtype: :class:`PersonalAssistant`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'telephony/config/people/{person_id}/features/personalAssistant')
        data = await super().get(url, params=params)
        r = PersonalAssistant.model_validate(data)
        return r

    async def update(self, person_id: str, settings: PersonalAssistant, org_id: str = None):
        """
        Update Personal Assistant

        Update Personal Assistant details for a specific user.

        Personal Assistant is used to manage a user's incoming calls when they are away.

        Updating Personal Assistant details requires a full or user administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param person_id: Unique identifier for the person.
        :type person_id: str
        :param settings: Personal Assistant settings.
        :type settings: PersonalAssistant
        :param org_id: Update Personal Assistant details for the organization.
        :type org_id: str
        :rtype: None
        """
        params = {'orgId': org_id} if org_id is not None else None
        body = settings.model_dump(mode='json', exclude_unset=True, by_alias=True)
        url = self.ep(f'telephony/config/people/{person_id}/features/personalAssistant')
        await super().put(url, params=params, json=body)


class AsPreferredAnswerApi(AsApiChild, base='telephony/config/people'):

    # noinspection PyMethodOverriding
    def ep(self, person_id: str) -> str:
        """
        :meta private:
        """
        return super().ep(f'{person_id}/preferredAnswerEndpoint')

    async def read(self, person_id: str, org_id: str = None) -> PreferredAnswerResponse:
        """
        Get Preferred Answer Endpoint
        Get the person's preferred answer endpoint (if any) and the list of endpoints available for selection. These
        endpoints can be used by the following Call Control API's that allow the person to specify an endpointId to
        use for the call:

        /v1/telephony/calls/dial

        /v1/telephony/calls/retrieve

        /v1/telephony/calls/pickup

        /v1/telephony/calls/barge-in

        /v1/telephony/calls/answer

        This API requires spark:telephony_config_read or spark-admin:telephony_config_read scope.

        :param person_id: A unique identifier for the person.
        :param org_id: ID of the organization in which the person resides. Only admin users of another organization
            (such as partners) may use this parameter as the default is the same organization as the token used to
            access
            API.
        :return: person's preferred answer endpoint settings
        :rtype: PreferredAnswerResponse
        """
        params = org_id and {'orgId': org_id} or None
        ep = self.ep(person_id=person_id)
        return PreferredAnswerResponse.model_validate(await self.get(ep, params=params))

    async def modify(self, person_id: str, preferred_answer_endpoint_id: str, org_id: str = None):
        """
        Modify Preferred Answer Endpoint
        Sets or clears the person’s preferred answer endpoint. To clear the preferred answer endpoint the p
        preferred_answer_endpoint_id parameter must be set to None.
        This API requires spark:telephony_config_write or spark-admin:telephony_config_write scope.

        :param person_id: A unique identifier for the person.
        :param preferred_answer_endpoint_id: Person’s preferred answer endpoint.
        :param org_id: ID of the organization in which the person resides. Only admin users of another organization
            (such as partners) may use this parameter as the default is the same organization as the token used to
            access
            API.
        """
        params = org_id and {'orgId': org_id} or None
        ep = self.ep(person_id=person_id)
        await self.put(ep, params=params, json={'preferredAnswerEndpointId': preferred_answer_endpoint_id})


class AsPrivacyApi(AsPersonSettingsApiChild):
    """
    API for privacy settings for users, vírtual lines and workspaces
    """

    feature = 'privacy'

    async def read(self, entity_id: str, org_id: str = None) -> Privacy:
        """
        Get Privacy Settings for an entity

        Get privacy settings for the specified entity id.

        The privacy feature enables the entity's line to be monitored by others and determine if they can be reached
        by Auto Attendant services.

        This API requires a full, user, or read-only administrator auth token with a scope of spark-admin:people_read.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param org_id: Entity is in this organization. Only admin users of another organization (such as partners)
            may use this parameter as the default is the same organization as the token used to access API.
        :type org_id: str
        :return: privacy settings
        :rtype: :class:`Privacy`
        """
        ep = self.f_ep(person_id=entity_id)
        params = org_id and {'orgId': org_id} or None
        data = await self.get(ep, params=params)
        return Privacy.model_validate(data)

    async def configure(self, entity_id: str, settings: Privacy, org_id: str = None):
        """
        Configure an entity's Privacy Settings

        Configure an entity's privacy settings for the specified entity ID.

        The privacy feature enables the entity's line to be monitored by others and determine if they can be reached by
        Auto Attendant services.

        This API requires a full or user administrator or location administrator auth token with
        the spark-admin:people_write scope.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param settings: settings for update
        :type settings: :class:`Monitoring`
        :param org_id: Entity is in this organization. Only admin users of another organization (such as partners)
            may use this parameter as the default is the same organization as the token used to access API.
        :type org_id: str
        """
        ep = self.f_ep(person_id=entity_id)
        params = org_id and {'orgId': org_id} or None
        data = settings.model_dump(mode='json', by_alias=True, exclude_none=True)
        if settings.monitoring_agents is not None:
            id_list = []
            for ma in settings.monitoring_agents:
                if isinstance(ma, str):
                    id_list.append(ma)
                else:
                    id_list.append(ma.agent_id)
            data['monitoringAgents'] = id_list
        await self.put(ep, params=params, json=data)


class AsPushToTalkApi(AsPersonSettingsApiChild):
    """
    API for person's PTT settings

    Also used for virtual lines and workspaces
    """

    feature = 'pushToTalk'

    async def read(self, entity_id: str, org_id: str = None) -> PushToTalkSettings:
        """
        Read Push-to-Talk Settings for an entity

        Push-to-Talk allows the use of desk phones as either a one-way or two-way intercom that connects people in
        different parts of your organization.

        This API requires a full, user, or read-only administrator auth token with a scope of spark-admin:people_read.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param org_id: Entity is in this organization. Only admin users of another organization (such as partners)
            may use this parameter as the default is the same organization as the token used to access API.
        :type org_id: str
        :return: PTT settings for specific entity
        :rtype: PushToTalkSettings
        """
        ep = self.f_ep(person_id=entity_id)
        params = org_id and {'orgId': org_id} or None
        return PushToTalkSettings.model_validate(await self.get(ep, params=params))

    async def configure(self, entity_id: str, settings: PushToTalkSettings, org_id: str = None):
        """
        Configure Push-to-Talk Settings for an entity

        Configure an entity's Push-to-Talk Settings

        Push-to-Talk allows the use of desk phones as either a one-way or two-way intercom that connects people in
        different parts of your organization.

        This API requires a full or user administrator auth token with the spark-admin:people_write scope.

        :param entity_id: Unique identifier for the person.
        :type entity_id: str
        :param settings: new setting to be applied. For members only the ID needs to be set
        :type settings: PushToTalkSettings
        :param org_id: Entity is in this organization. Only admin users of another organization (such as partners)
            may use this parameter as the default is the same organization as the token used to access API.
        """
        ep = self.f_ep(person_id=entity_id)
        params = org_id and {'orgId': org_id} or None
        if settings.members:
            # for an update member is just a list of IDs
            body_settings = settings.model_copy(deep=True)
            members = [m.member_id if isinstance(m, MonitoredMember) else m
                       for m in settings.members]
            body_settings.members = members
        else:
            body_settings = settings
        body = body_settings.model_dump_json(exclude_none=False,
                                             exclude_unset=True)
        await self.put(ep, params=params, data=body)


class AsReceptionistApi(AsPersonSettingsApiChild):
    """
    API for person's receptionist client settings
    """

    feature = 'reception'

    async def read(self, person_id: str, org_id: str = None) -> ReceptionistSettings:
        """
        Read Receptionist Client Settings for a Person

        Retrieve a Person's Receptionist Client Settings

        To help support the needs of your front-office personnel, you can set up people or workspaces as telephone
        attendants so that they can screen all incoming calls to certain numbers within your organization.

        This API requires a full, user, or read-only administrator auth token with a scope of spark-admin:people_read.

        :param person_id: Unique identifier for the person.
        :type person_id: str
        :param org_id: Person is in this organization. Only admin users of another organization (such as partners)
            may use this parameter as the default is the same organization as the token used to access API.
        :type org_id: str
        :return: receptionist client settings
        :rtype: :class:`ReceptionistSettings`
        """
        ep = self.f_ep(person_id=person_id)
        params = org_id and {'orgId': org_id} or None
        data = await self.get(ep, params=params)
        return ReceptionistSettings.model_validate(data)

    async def configure(self, person_id: str, settings: ReceptionistSettings, org_id: str = None):
        """
        Modify Executive Assistant Settings for a Person

        Modify the executive assistant settings for the specified personId.

        People with the executive service enabled, can select from a pool of assistants who have been assigned the
        executive assistant service and who can answer or place calls on their behalf. Executive assistants can set
        the call forward destination and join or leave an executive's pool.

        This API requires a full or user administrator auth token with the spark-admin:people_write scope.

        :param person_id: Unique identifier for the person.
        :type person_id: str
        :param settings: New receptionist client settings
        :type settings: :class:`ReceptionistSettings`
        :param org_id: Person is in this organization. Only admin users of another organization (such as partners)
            may use this parameter as the default is the same organization as the token used to access API.
        :type org_id: str
        """
        if settings.enabled is None:
            raise ValueError('enabled is a mandatory parameter for updates')
        if settings.monitored_members and not settings.enabled:
            raise ValueError('when setting members enabled has to be True')
        ep = self.f_ep(person_id=person_id)
        params = org_id and {'orgId': org_id} or None
        data = settings.model_dump(mode='json', by_alias=True, exclude_none=True)
        if settings.monitored_members is not None:
            id_list = []
            for me in settings.monitored_members:
                if isinstance(me, str):
                    id_list.append(me)
                else:
                    id_list.append(me.member_id)
            data['monitoredMembers'] = id_list
        await self.put(ep, params=params, json=data)


class AsScheduleApi(AsApiChild, base='telephony/config/locations'):
    """
    Schedules API
    """

    def __init__(self, *, session: AsRestSession, base: ScheduleApiBase):
        super().__init__(session=session, base=base.value)
        if base == ScheduleApiBase.people:
            self.after_id = '/features/schedules'
        elif base == ScheduleApiBase.locations:
            self.after_id = '/schedules'
        else:
            raise ValueError('unexpected value for base')

    def _endpoint(self, *, obj_id: str, schedule_type: ScheduleTypeOrStr = None, schedule_id: str = None,
                  event_id: str = None):
        """
        location specific feature endpoint like v1/telephony/config/locations/{obj_id}/schedules/.... or
        v1/people/{obj_id}/features/schedules/....

        :meta private:
        :param obj_id: Unique identifier for the location or user
        :type obj_id: str
        :param schedule_type: type of schedule
        :type schedule_type: ScheduleType
        :param schedule_id: schedule id
        :type schedule_id: str
        :return: full endpoint
        :rtype: str
        """
        ep = self.ep(path=f'{obj_id}{self.after_id}')
        if schedule_type is not None:
            schedule_type = ScheduleType.type_or_str(schedule_type)
            ep = f'{ep}/{schedule_type.value}/{schedule_id}'
            if event_id is not None:
                event_id = event_id and f'/{event_id}' or ''
                ep = f'{ep}/events{event_id}'
        return ep

    def list_gen(self, obj_id: str, org_id: str = None, schedule_type: ScheduleType = None,
             name: str = None, **params) -> AsyncGenerator[Schedule, None, None]:
        """
        List of schedules for a person or location

        List schedules for a person or location in an organization.

        Schedules are used to support calling features and can be defined at the location or person level.
        businessHours schedules allow you to apply specific call settings at different times of the day or week
        by defining one or more events. holidays schedules define exceptions to normal business hours by defining one
        or more events.

        This API requires a full, user, or read-only administrator auth token with a scope of spark-admin:people_read.

        :param obj_id: Return the list of schedules for this location or user
        :type obj_id: str
        :param org_id: List schedules for this organization.
        :type org_id: str
        :param schedule_type: Type of the schedule.
            businessHours - Business hours schedule type.
            holidays - Holidays schedule type.
        :param name: Only return schedules with the matching name.
        :return: yields schedules
        """
        url = self._endpoint(obj_id=obj_id)
        if schedule_type is not None:
            params['type'] = schedule_type.value
        if name is not None:
            params['name'] = name
        if org_id is not None:
            params['orgId'] = org_id

        # noinspection PyTypeChecker
        return self.session.follow_pagination(url=url, model=Schedule, params=params or None)

    async def list(self, obj_id: str, org_id: str = None, schedule_type: ScheduleType = None,
             name: str = None, **params) -> List[Schedule]:
        """
        List of schedules for a person or location

        List schedules for a person or location in an organization.

        Schedules are used to support calling features and can be defined at the location or person level.
        businessHours schedules allow you to apply specific call settings at different times of the day or week
        by defining one or more events. holidays schedules define exceptions to normal business hours by defining one
        or more events.

        This API requires a full, user, or read-only administrator auth token with a scope of spark-admin:people_read.

        :param obj_id: Return the list of schedules for this location or user
        :type obj_id: str
        :param org_id: List schedules for this organization.
        :type org_id: str
        :param schedule_type: Type of the schedule.
            businessHours - Business hours schedule type.
            holidays - Holidays schedule type.
        :param name: Only return schedules with the matching name.
        :return: yields schedules
        """
        url = self._endpoint(obj_id=obj_id)
        if schedule_type is not None:
            params['type'] = schedule_type.value
        if name is not None:
            params['name'] = name
        if org_id is not None:
            params['orgId'] = org_id

        # noinspection PyTypeChecker
        return [o async for o in self.session.follow_pagination(url=url, model=Schedule, params=params or None)]

    async def details(self, obj_id: str, schedule_type: ScheduleTypeOrStr, schedule_id: str,
                org_id: str = None) -> Schedule:
        """
        Get details for a schedule

        Retrieve schedule details.

        A time schedule establishes a set of times during the day or holidays in the year in which a feature, for
        example auto attendants, can perform a specific action.

        Retrieving schedule details requires a full or read-only administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param obj_id: Retrieve schedule details in this location or user
        :type obj_id: str
        :param schedule_type: Type of the schedule.
            businessHours - Business hours schedule type.
            holidays - Holidays schedule type.
        :type schedule_type: ScheduleTypeOrStr
        :param schedule_id: Retrieve the schedule with the matching id.
        :type schedule_id: str
        :param org_id: Retrieve schedule details from this organization.
        :type org_id: str
        :return:
        """
        params = org_id and {'orgId': org_id} or None
        url = self._endpoint(obj_id=obj_id, schedule_type=schedule_type, schedule_id=schedule_id)
        data = await self.get(url, params=params)
        result = Schedule.model_validate(data)
        return result

    async def create(self, obj_id: str, schedule: Schedule, org_id: str = None) -> str:
        """
        Create a schedule

        Create new schedule for the given location or user

        A time schedule establishes a set of times during the day or holidays in the year in which a feature, for
        example auto attendants, can perform a specific action.

        Creating a schedule requires a full administrator auth token with a scope of spark-admin:telephony_config_write.

        :param obj_id: Create the schedule for this location or user
        :type obj_id: str
        :param schedule: Schedule to be created
        :type schedule: Schedule
        :param org_id: Create the schedule for this organization.
        :type org_id: str
        :return: id of the newly created schedule.
        :rtype: str
        """
        schedule_data = schedule.create_update()
        params = org_id and {'orgId': org_id} or None
        url = self._endpoint(obj_id=obj_id)
        data = await self.post(url, data=schedule_data, params=params)
        result = data['id']
        return result

    async def update(self, obj_id: str, schedule: Schedule, schedule_type: ScheduleTypeOrStr = None,
               schedule_id: str = None, org_id: str = None) -> str:
        """
        Update a schedule

        Update the designated schedule.

        A time schedule establishes a set of times during the day or holidays in the year in which a feature, for
        example auto attendants, can perform a specific action.

        Updating a schedule requires a full administrator auth token with a scope of spark-admin:telephony_config_write.

        NOTE: The schedule id will change upon modification of the Schedule name

        :param obj_id: Location or user for which this schedule exists
        :type obj_id: str
        :param schedule: data for the update
        :type schedule: Schedule
        :param schedule_type: Type of the schedule. Default: schedule_type from schedule
            businessHours - Business hours schedule type.
            holidays - Holidays schedule type.
        :type schedule_type: ScheduleTypeOrStr
        :param schedule_id: Update schedule with the matching id. Default: schedule_id from schedule
        :type schedule_id: str
        :param org_id: Update schedule from this organization.
        :type org_id: str
        :return: schedule id
        """
        schedule_type = schedule_type or schedule.schedule_type
        schedule_id = schedule_id or schedule.schedule_id
        schedule_data = schedule.create_update(update=True)
        params = org_id and {'orgId': org_id} or None
        url = self._endpoint(obj_id=obj_id, schedule_type=schedule_type, schedule_id=schedule_id)
        data = await self.put(url, data=schedule_data, params=params)
        return data['id']

    async def delete_schedule(self, obj_id: str, schedule_type: ScheduleTypeOrStr, schedule_id: str,
                        org_id: str = None):
        """
        Delete a schedule

        Delete the designated schedule.

        A time schedule establishes a set of times during the day or holidays in the year in which a feature, for
        example auto attendants, can perform a specific action.

        Deleting a schedule requires a full administrator auth token with a scope of spark-admin:telephony_config_write.

        :param obj_id: Location or user from which to delete a schedule.
        :type obj_id: str
        :param schedule_type: Type of the schedule.
            businessHours - Business hours schedule type.
            holidays - Holidays schedule type.
        :type schedule_type: ScheduleTypeOrStr
        :param schedule_id: Delete the schedule with the matching id.
        :type schedule_id: str
        :param org_id: Retrieve schedule details from this organization.
        :type org_id: str
        :return:
        """
        url = self._endpoint(obj_id=obj_id, schedule_type=schedule_type, schedule_id=schedule_id)
        params = org_id and {'orgId': org_id} or None
        await self.delete(url, params=params)

    async def event_details(self, obj_id: str, schedule_type: ScheduleTypeOrStr, schedule_id: str,
                      event_id: str, org_id: str = None) -> Event:
        """
        Get details for a schedule event

        Retrieve schedule event details.

        A time schedule establishes a set of times during the day or holidays in the year in which a feature, for
        example auto attendants, can perform a specific action.

        Retrieving schedule event details requires a full or read-only administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param obj_id: Retrieve schedule event details for this location or user
        :type obj_id: str
        :param schedule_type: Type of the schedule.
            businessHours - Business hours schedule type.
            holidays - Holidays schedule type.
        :type schedule_type: ScheduleTypeOrStr
        :param schedule_id: Retrieve schedule event details for schedule with the matching id.
        :type schedule_id: str
        :param event_id: Retrieve the schedule event with the matching schedule event id.
        :type event_id: str
        :param org_id: Retrieve schedule event details from this organization.
        :type org_id: str
        :return:
        """
        params = org_id and {'orgId': org_id} or None
        url = self._endpoint(obj_id=obj_id, schedule_type=schedule_type, schedule_id=schedule_id,
                             event_id=event_id)
        data = await self.get(url, params=params)
        result = Event.model_validate(data)
        return result

    async def event_create(self, obj_id: str, schedule_type: ScheduleTypeOrStr, schedule_id: str,
                     event: Event, org_id: str = None) -> str:
        """
        Create a schedule event

        Create new event for the given location or user schedule.

        A time schedule establishes a set of times during the day or holidays in the year in which a feature, for
        example auto attendants, can perform a specific action.

        Creating a schedule event requires a full administrator auth token with a scope of
        spark-admin:telephony_config_write.

        :param obj_id: Create the schedule for this location.
        :type obj_id: str
        :param schedule_type: Type of the schedule.
            businessHours - Business hours schedule type.
            holidays - Holidays schedule type.
        :type schedule_type: ScheduleTypeOrStr
        :param schedule_id: Create event for a given schedule id.
        :type schedule_id: str
        :param event: event data
        :type event: Event
        :param org_id: Retrieve schedule event details from this organization.
        :type org_id: str
        :return: event id
        :rtype: str
        """
        params = org_id and {'orgId': org_id} or None
        url = self._endpoint(obj_id=obj_id, schedule_type=schedule_type, schedule_id=schedule_id,
                             event_id='')
        data = event.model_dump_json(exclude={'event_id'})
        data = await self.post(url, data=data, params=params)
        return data['id']

    async def event_update(self, obj_id: str, schedule_type: ScheduleTypeOrStr, schedule_id: str,
                     event: Event, event_id: str = None, org_id: str = None) -> str:
        """
        Update a schedule event

        Update the designated schedule event.

        A time schedule establishes a set of times during the day or holidays in the year in which a feature, for
        example auto attendants, can perform a specific action.

        Updating a schedule event requires a full administrator auth token with a scope of
        spark-admin:telephony_config_write.

        NOTE: The schedule event id will change upon modification of the schedule event name.

        :param obj_id: Location or user for which this schedule event exists.
        :type obj_id: str
        :param schedule_type: Type of the schedule.
            businessHours - Business hours schedule type.
            holidays - Holidays schedule type.
        :type schedule_type: ScheduleTypeOrStr
        :param schedule_id: Update schedule event with the matching schedule id.
        :type schedule_id: str
        :param event: update settings
        :type event: Event
        :param event_id: Update the schedule event with the matching schedule event id. Default: event id from event
        :type event_id: str
        :param org_id: Update schedule from this organization.
        :type org_id: str
        :return: event id; changed if name changed
        """
        event_id = event_id or event.event_id
        params = org_id and {'orgId': org_id} or None
        url = self._endpoint(obj_id=obj_id, schedule_type=schedule_type, schedule_id=schedule_id,
                             event_id=event_id)
        event_data = event.model_dump_json(exclude={'event_id'})
        data = await self.put(url, data=event_data, params=params)
        return data['id']

    async def event_delete(self, obj_id: str, schedule_type: ScheduleTypeOrStr, schedule_id: str,
                     event_id: str, org_id: str = None):
        """
        Delete a schedule event

        Delete the designated schedule event.

        A time schedule establishes a set of times during the day or holidays in the year in which a feature, for
        example auto attendants, can perform a specific action.

        Deleting a schedule event requires a full administrator auth token with a scope
        of spark-admin:telephony_config_write.

        :param obj_id: Location or user from which to delete a schedule.
        :type obj_id: str
        :param schedule_type: Type of the schedule.
            businessHours - Business hours schedule type.
            holidays - Holidays schedule type.
        :type schedule_type: ScheduleTypeOrStr
        :param schedule_id: Delete schedule event with the matching schedule id.
        :type schedule_id: str
        :param event_id: Delete the schedule event with the matching schedule event id. Default: event id from event
        :type event_id: str
        :param org_id: Delete schedule from this organization.
        :type org_id: str
        """
        params = org_id and {'orgId': org_id} or None
        url = self._endpoint(obj_id=obj_id, schedule_type=schedule_type, schedule_id=schedule_id,
                             event_id=event_id)
        await self.delete(url, params=params)


class AsSelectiveAcceptApi(AsPersonSettingsApiChild):
    """
    API for selective accept settings

    For now only used for workspaces
    """

    feature = 'selectiveAccept'

    async def read_criteria(self, entity_id: str, id: str,
                      org_id: str = None) -> SelectiveAcceptCriteria:
        """
        Retrieve Selective Accept Criteria for an entity

        Retrieve Selective Accept Criteria Settings for an entity.

        With the Selective Accept feature, you can accept calls at specific times from specific callers.
        Schedules can also be set up for this feature during certain times of the day or days of the week.

        **NOTE**: This API is only available for professional licensed workspaces.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param id: Unique identifier for the criteria.
        :type id: str
        :param org_id: ID of the organization within which the entity resides. Only admin users of another
            organization (such as partners) may use this parameter as the default is the same organization as the
            token used to access API.
        :type org_id: str
        :rtype: :class:`SelectiveAcceptCriteria`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.f_ep(entity_id, f'criteria/{id}')
        data = await super().get(url, params=params)
        r = SelectiveAcceptCriteria.model_validate(data)
        return r

    async def configure_criteria(self, entity_id: str, id: str, settings: SelectiveAcceptCriteria,
                           org_id: str = None):
        """
        Modify Selective Accept Criteria for an entity

        Modify Selective Accept Criteria Settings for an entity.

        With the Selective Accept feature, you can accept calls at specific times from specific callers
        Schedules can also be set up for this feature during certain times of the day or days of the week.

        **NOTE**: This API is only available for professional licensed workspaces.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param id: Unique identifier for the criteria.
        :type id: str
        :param settings: new settings to be applied.
        :type settings: SelectiveAcceptCriteria
        :param org_id: ID of the organization within which the entity resides. Only admin users of another
            organization (such as partners) may use this parameter as the default is the same organization as the
            token used to access API.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = settings.update()
        url = self.f_ep(entity_id, f'criteria/{id}')
        await super().put(url, params=params, json=body)

    async def delete_criteria(self, entity_id: str, id: str, org_id: str = None):
        """
        Delete Selective Accept Criteria for an entity

        Delete Selective Accept criteria Settings for an entity.

        With the Selective Accept feature, you can accept calls at specific times from specific callers.
        Schedules can also be set up for this feature during certain times of the day or days of the week.

        **NOTE**: This API is only available for professional licensed workspaces.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param id: Unique identifier for the criteria.
        :type id: str
        :param org_id: ID of the organization within which the entity resides. Only admin users of another
            organization (such as partners) may use this parameter as the default is the same organization as the
            token used to access API.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.f_ep(entity_id, f'criteria/{id}')
        await super().delete(url, params=params)

    async def create_criteria(self, entity_id: str, settings: SelectiveAcceptCriteria, org_id: str = None) -> str:
        """
        Create Selective Accept Criteria for an entity

        Create Selective Accept Criteria Settings for an entity.

        With the Selective Accept feature, you can reject calls at specific times from specific callers. This setting
        takes precedence over Selectively Accept Calls.
        Schedules can also be set up for this feature during certain times of the day or days of the week.

        **NOTE**: This API is only available for professional licensed workspaces.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param settings: new settings to be applied.
        :type settings: SelectiveAcceptCriteria
        :param org_id: ID of the organization within which the entity resides. Only admin users of another
            organization (such as partners) may use this parameter as the default is the same organization as the
            token used to access API.
        :type org_id: str
        :rtype: str
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = settings.update()
        url = self.f_ep(entity_id, 'criteria')
        data = await super().post(url, params=params, json=body)
        r = data['id']
        return r

    async def read(self, entity_id: str,
             org_id: str = None) -> SelectiveAccept:
        """
        Retrieve Selective Accept Settings for an entity.

        With the Selective Accept feature, you can accept calls at specific times from specific callers.
        Schedules can also be set up for this feature during certain times of the day or days of the week.

        **NOTE**: This API is only available for professional licensed workspaces.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param org_id: ID of the organization within which the entity resides. Only admin users of another
            organization (such as partners) may use this parameter as the default is the same organization as the
            token used to access API.
        :type org_id: str
        :rtype: :class:`SelectiveAccept`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.f_ep(entity_id)
        data = await super().get(url, params=params)
        r = SelectiveAccept.model_validate(data)
        return r

    async def configure(self, entity_id: str, settings: SelectiveAccept, org_id: str = None):
        """
        Modify Selective Accept Settings for an entity.

        With the Selective Accept feature, you can accept calls at specific times from specific callers.
        Schedules can also be set up for this feature during certain times of the day or days of the week.

        **NOTE**: This API is only available for professional licensed workspaces.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param settings: new settings to be applied.
        :type settings: SelectiveAccept
        :param org_id: ID of the organization within which the entity resides. Only admin users of another
            organization (such as partners) may use this parameter as the default is the same organization as the
            token used to access API.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = settings.update()
        url = self.f_ep(entity_id)
        await super().put(url, params=params, json=body)


class AsSelectiveForwardApi(AsPersonSettingsApiChild):
    """
    API for selective forward settings

    For now only used for workspaces
    """

    feature = 'selectiveForward'

    async def read_criteria(self, entity_id: str, id: str,
                      org_id: str = None) -> SelectiveForwardCriteria:
        """
        Retrieve Selective Forward Criteria for a Workspace

        Retrieve Selective Forward Criteria Settings for a Workspace.

        With the Selective Forward feature, you can forward calls at specific times from specific callers. This setting
        takes precedence over call forwarding.
        Schedules can also be set up for this feature during certain times of the day or days of the week.


        **NOTE**: This API is only available for professional licensed workspaces.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param id: Unique identifier for the criteria.
        :type id: str
        :param org_id: ID of the organization within which the entity resides. Only admin users of another
            organization (such as partners) may use this parameter as the default is the same organization as the
            token used to access API.
        :type org_id: str
        :rtype: :class:`SelectiveCriteria`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.f_ep(entity_id, f'criteria/{id}')
        data = await super().get(url, params=params)
        r = SelectiveForwardCriteria.model_validate(data)
        return r

    async def configure_criteria(self, entity_id: str, id: str, settings: SelectiveForwardCriteria,
                           org_id: str = None):
        """
        Modify Selective Forward Criteria for a Workspace

        Modify Selective Forward Call Criteria Settings for a Workspace.

        With the Selective Forward feature, you can forward calls at specific times from specific callers. This setting
        takes precedence over call forwarding.
        Schedules can also be set up for this feature during certain times of the day or days of the week.

        **NOTE**: This API is only available for professional licensed workspaces.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param id: Unique identifier for the criteria.
        :type id: str
        :param settings: new settings to be applied.
        :type settings: SelectiveForwardCriteria
        :param org_id: ID of the organization within which the entity resides. Only admin users of another
            organization (such as partners) may use this parameter as the default is the same organization as the
            token used to access API.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = settings.update()
        url = self.f_ep(entity_id, f'criteria/{id}')
        await super().put(url, params=params, json=body)

    async def delete_criteria(self, entity_id: str, id: str, org_id: str = None):
        """
        Delete Selective Forward Criteria for a Workspace

        Delete Selective Forward Call criteria Settings for a workspace.

        With the Selective Forward feature, you can forward calls at specific times from specific callers. This setting
        takes precedence over call forwarding.
        Schedules can also be set up for this feature during certain times of the day or days of the week.

        **NOTE**: This API is only available for professional licensed workspaces.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param id: Unique identifier for the criteria.
        :type id: str
        :param org_id: ID of the organization within which the entity resides. Only admin users of another
            organization (such as partners) may use this parameter as the default is the same organization as the
            token used to access API.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.f_ep(entity_id, f'criteria/{id}')
        await super().delete(url, params=params)

    async def create_criteria(self, entity_id: str, settings: SelectiveForwardCriteria,
                        org_id: str = None) -> str:
        """
        Create Selective Forward Criteria for a Workspace

        Create Selective Forward Call Criteria Settings for a Workspace.

        With the Selective Forward feature, you can forward calls at specific times from specific callers. This setting
        takes precedence over call forwarding.
        Schedules can also be set up for this feature during certain times of the day or days of the week.

        This API requires a full, user or location administrator auth token with the `spark-admin:workspaces_write`
        scope or a user auth token with a scope of `spark:workspaces_write` to update workspace settings.

        **NOTE**: This API is only available for professional licensed workspaces.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param settings: new settings to be applied.
        :type settings: SelectiveCriteria
        :param org_id: ID of the organization within which the entity resides. Only admin users of another
            organization (such as partners) may use this parameter as the default is the same organization as the
            token used to access API.
        :type org_id: str
        :rtype: str
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = settings.update()

        url = self.f_ep(entity_id, 'criteria')
        data = await super().post(url, params=params, json=body)
        r = data['id']
        return r

    async def read(self, entity_id: str,
             org_id: str = None) -> SelectiveForward:
        """
        Retrieve Selective Forward Settings for a Workspace

        Retrieve Selective Forward Call Settings for a Workspace.

        With the Selective Forward feature, you can forward calls at specific times from specific callers. This setting
        takes precedence over call forwarding.
        Schedules can also be set up for this feature during certain times of the day or days of the week.

        **NOTE**: This API is only available for professional licensed workspaces.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param org_id: ID of the organization within which the entity resides. Only admin users of another
            organization (such as partners) may use this parameter as the default is the same organization as the
            token used to access API.
        :type org_id: str
        :rtype: :class:`SelectiveForward`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.f_ep(entity_id)
        data = await super().get(url, params=params)
        r = SelectiveForward.model_validate(data)
        return r

    async def configure(self, entity_id: str, settings: SelectiveForward,
                  org_id: str = None):
        """
        Modify Selective Forward Settings for a Workspace

        Modify Selective Forward Call Settings for a Workspace.

        With the Selective Forward feature, you can forward calls at specific times from specific callers. This setting
        takes precedence over call forwarding.
        Schedules can also be set up for this feature during certain times of the day or days of the week.

        **NOTE**: This API is only available for professional licensed workspaces.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param settings: Settings for new criteria
        :type settings: SelectiveForward
        :param org_id: ID of the organization within which the entity resides. Only admin users of another
            organization (such as partners) may use this parameter as the default is the same organization as the
            token used to access API.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = settings.update()
        url = self.f_ep(entity_id)
        await super().put(url, params=params, json=body)


class AsSelectiveRejectApi(AsPersonSettingsApiChild):
    """
    API for selective reject settings

    For now only used for workspaces
    """

    feature = 'selectiveReject'

    async def read_criteria(self, entity_id: str, id: str,
                      org_id: str = None) -> SelectiveRejectCriteria:
        """
        Retrieve Selective Reject Criteria for an entity

        Retrieve Selective Reject Criteria Settings for an entity.

        With the Selective Reject feature, you can reject calls at specific times from specific callers. This setting
        takes precedence over Selectively Accept Calls.
        Schedules can also be set up for this feature during certain times of the day or days of the week.

        **NOTE**: This API is only available for professional licensed workspaces.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param id: Unique identifier for the criteria.
        :type id: str
        :param org_id: ID of the organization within which the entity resides. Only admin users of another
            organization (such as partners) may use this parameter as the default is the same organization as the
            token used to access API.
        :type org_id: str
        :rtype: :class:`SelectiveRejectCriteria`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.f_ep(entity_id, f'criteria/{id}')
        data = await super().get(url, params=params)
        r = SelectiveRejectCriteria.model_validate(data)
        return r

    async def configure_criteria(self, entity_id: str, id: str, settings: SelectiveRejectCriteria,
                           org_id: str = None):
        """
        Modify Selective Reject Criteria for an entity

        Modify Selective Reject Criteria Settings for an entity.

        With the Selective Reject feature, you can reject calls at specific times from specific callers. This setting
        takes precedence over Selectively Accept Calls.
        Schedules can also be set up for this feature during certain times of the day or days of the week.

        **NOTE**: This API is only available for professional licensed workspaces.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param id: Unique identifier for the criteria.
        :type id: str
        :param settings: new settings to be applied.
        :type settings: SelectiveRejectCriteria
        :param org_id: ID of the organization within which the entity resides. Only admin users of another
            organization (such as partners) may use this parameter as the default is the same organization as the
            token used to access API.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = settings.update()
        url = self.f_ep(entity_id, f'criteria/{id}')
        await super().put(url, params=params, json=body)

    async def delete_criteria(self, entity_id: str, id: str, org_id: str = None):
        """
        Delete Selective Reject Criteria for an entity

        Delete Selective Reject criteria Settings for an entity.

        With the Selective Reject feature, you can reject calls at specific times from specific callers. This setting
        takes precedence over Selectively Accept Calls.
        Schedules can also be set up for this feature during certain times of the day or days of the week.

        **NOTE**: This API is only available for professional licensed workspaces.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param id: Unique identifier for the criteria.
        :type id: str
        :param org_id: ID of the organization within which the entity resides. Only admin users of another
            organization (such as partners) may use this parameter as the default is the same organization as the
            token used to access API.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.f_ep(entity_id, f'criteria/{id}')
        await super().delete(url, params=params)

    async def create_criteria(self, entity_id: str, settings: SelectiveRejectCriteria, org_id: str = None) -> str:
        """
        Create Selective Reject Criteria for an entity

        Create Selective Reject Criteria Settings for an entity.

        With the Selective Reject feature, you can reject calls at specific times from specific callers. This setting
        takes precedence over Selectively Accept Calls.
        Schedules can also be set up for this feature during certain times of the day or days of the week.

        **NOTE**: This API is only available for professional licensed workspaces.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param settings: new settings to be applied.
        :type settings: SelectiveRejectCriteria
        :param org_id: ID of the organization within which the entity resides. Only admin users of another
            organization (such as partners) may use this parameter as the default is the same organization as the
            token used to access API.
        :type org_id: str
        :rtype: str
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = settings.update()
        url = self.f_ep(entity_id, 'criteria')
        data = await super().post(url, params=params, json=body)
        r = data['id']
        return r

    async def read(self, entity_id: str,
             org_id: str = None) -> SelectiveReject:
        """
        Retrieve Selective Reject Settings for an entity.

        With the Selective Reject feature, you can reject calls at specific times from specific callers. This setting
        takes precedence over Selectively Accept Calls.
        Schedules can also be set up for this feature during certain times of the day or days of the week.

        **NOTE**: This API is only available for professional licensed workspaces.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param org_id: ID of the organization within which the entity resides. Only admin users of another
            organization (such as partners) may use this parameter as the default is the same organization as the
            token used to access API.
        :type org_id: str
        :rtype: :class:`SelectiveReject`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.f_ep(entity_id)
        data = await super().get(url, params=params)
        r = SelectiveReject.model_validate(data)
        return r

    async def configure(self, entity_id: str, settings: SelectiveReject, org_id: str = None):
        """
        Modify Selective Reject Settings for an entity.

        With the Selective Reject feature, you can reject calls at specific times from specific callers. This setting
        takes precedence over Selectively Accept Calls.
        Schedules can also be set up for this feature during certain times of the day or days of the week.

        **NOTE**: This API is only available for professional licensed workspaces.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param settings: new settings to be applied.
        :type settings: SelectiveReject
        :param org_id: ID of the organization within which the entity resides. Only admin users of another
            organization (such as partners) may use this parameter as the default is the same organization as the
            token used to access API.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = settings.update()
        url = self.f_ep(entity_id)
        await super().put(url, params=params, json=body)


class AsSingleNumberReachApi(AsApiChild, base='telephony/config'):
    """
    Features: Single Number Reach

    Features: Single Number Reach APIs support reading and writing of Webex Calling Single Number Reach settings for a
    specific organization.

    Viewing these read-only organization settings requires a full or read-only administrator auth token with a scope of
    `spark-admin:telephony_config_read`.

    Modifying these organization settings requires a full administrator auth token with a scope of
    `spark-admin:telephony_config_write`.

    A partner administrator can retrieve or change settings in a customer's organization using the optional `orgId`
    query parameter.
    """

    def available_phone_numbers_gen(self, location_id: str, phone_number: list[str] = None,
                                org_id: str = None,
                                **params) -> AsyncGenerator[AvailableNumber, None, None]:
        """
        Get Single Number Reach Primary Available Phone Numbers

        List the service and standard PSTN numbers that are available to be assigned as the single number reach's
        primary phone number.
        These numbers are associated with the location specified in the request URL, can be active or inactive, and are
        unassigned.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full, read-only or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param location_id: Return the list of phone numbers for this location within the given organization. The
            maximum length is 36.
        :type location_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`SingleNumberReachPrimaryAvailableNumberObject` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        url = self.ep(f'locations/{location_id}/singleNumberReach/availableNumbers')
        return self.session.follow_pagination(url=url, model=AvailableNumber,
                                              item_key='phoneNumbers', params=params)

    async def available_phone_numbers(self, location_id: str, phone_number: list[str] = None,
                                org_id: str = None,
                                **params) -> List[AvailableNumber]:
        """
        Get Single Number Reach Primary Available Phone Numbers

        List the service and standard PSTN numbers that are available to be assigned as the single number reach's
        primary phone number.
        These numbers are associated with the location specified in the request URL, can be active or inactive, and are
        unassigned.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full, read-only or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param location_id: Return the list of phone numbers for this location within the given organization. The
            maximum length is 36.
        :type location_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`SingleNumberReachPrimaryAvailableNumberObject` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        url = self.ep(f'locations/{location_id}/singleNumberReach/availableNumbers')
        return [o async for o in self.session.follow_pagination(url=url, model=AvailableNumber,
                                              item_key='phoneNumbers', params=params)]

    async def read(self, person_id: str) -> SingleNumberReach:
        """
        Get Single Number Reach Settings For A Person

        Retrieve Single Number Reach settings for the given person.

        Single number reach allows you to set up your work calls ring any phone number. This means that your office
        phone, mobile phone, or any other designated devices can ring at the same time, ensuring you don't miss
        important calls.

        Retrieving Single number reach settings requires a full or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param person_id: Unique identifier for the person.
        :type person_id: str
        :rtype: :class:`SingleNumberReach`
        """
        url = self.ep(f'people/{person_id}/singleNumberReach')
        data = await super().get(url)
        r = SingleNumberReach.model_validate(data)
        return r

    async def update(self, person_id: str,
               alert_all_numbers_for_click_to_dial_calls_enabled: bool = None):
        """
        Update Single number reach settings for a person.

        Single number reach allows you to set up your work calls ring any phone number. This means that your office
        phone, mobile phone, or any other designated devices can ring at the same time, ensuring you don't miss
        important calls.

        Updating a single number reach settings for a person requires a full administrator or location administrator
        auth token with a scope of `spark-admin:telephony_config_write`.

        :param person_id: Unique identifier for the person.
        :type person_id: str
        :param alert_all_numbers_for_click_to_dial_calls_enabled: Flag to enable alerting single number reach numbers
            for click to dial calls.
        :type alert_all_numbers_for_click_to_dial_calls_enabled: bool
        :rtype: None
        """
        body = dict()
        if alert_all_numbers_for_click_to_dial_calls_enabled is not None:
            body['alertAllNumbersForClickToDialCallsEnabled'] = alert_all_numbers_for_click_to_dial_calls_enabled
        url = self.ep(f'people/{person_id}/singleNumberReach')
        await super().put(url, json=body)

    async def create_snr(self, person_id: str, settings: SingleNumberReachNumber) -> str:
        """
        Create Single Number Reach For a Person

        Create a single number reach for a person in an organization.

        Single number reach allows you to set up your work calls ring any phone number. This means that your office
        phone, mobile phone, or any other designated devices can ring at the same time, ensuring you don't miss
        important calls.

        Creating a single number reach for a person requires a full administrator or location administrator auth token
        with a scope of `spark-admin:telephony_config_write`.

        :param person_id: Unique identifier for the person.
        :type person_id: str
        :param settings: Settings for new SNR number
        :type settings: SingleNumberReachNumber
        :rtype: str
        """
        body = settings.create_update()
        url = self.ep(f'people/{person_id}/singleNumberReach/numbers')
        data = await super().post(url, json=body)
        r = data['id']
        return r

    async def delete_snr(self, person_id: str, id: str, org_id: str = None):
        """
        Delete A Single Number Reach Number

        Delete Single number reach number for a person.

        Single number reach allows you to setu p your work calls ring any phone number. This means that your office
        phone, mobile phone, or any other designated devices can ring at the same time, ensuring you don't miss
        important calls.

        Deleting a Single number reach number requires a full administrator or location administrator auth token with a
        scope of `spark-admin:telephony_config_write`.

        :param person_id: Unique identifier for the person.
        :type person_id: str
        :param id: Unique identifier for single number reach.
        :type id: str
        :param org_id: Unique identifier for the organization.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'people/{person_id}/singleNumberReach/numbers/{id}')
        await super().delete(url, params=params)

    async def update_snr(self, person_id: str, settings: SingleNumberReachNumber) -> str:
        """
        Update Single number reach settings for a number.

        Single number reach allows you to set up your work calls ring any phone number. This means that your office
        phone, mobile phone, or any other designated devices can ring at the same time, ensuring you don't miss
        important calls.

        The response returns an ID that can change if the phoneNumber is modified, as the ID contains base64 encoded
        phone number data.

        Updating a single number reach settings for a number requires a full administrator or location administrator
        auth token with a scope of `spark-admin:telephony_config_write`.

        :param person_id: Unique identifier for the person.
        :type person_id: str
        :param settings: Settings for new SNR number
        :type settings: SingleNumberReachNumber
        :rtype: str
        """
        body = settings.create_update()
        url = self.ep(f'people/{person_id}/singleNumberReach/numbers/{settings.id}')
        data = await super().put(url, json=body)
        return data['id']


class AsVoicemailApi(AsPersonSettingsApiChild):
    """
    API for person's call voicemail settings. Also used for virtual lines and workspaces
    """

    feature = 'voicemail'

    async def read(self, entity_id: str, org_id: str = None) -> VoicemailSettings:
        """
        Read Voicemail Settings for an entity

        The voicemail feature transfers callers to voicemail based on your settings. You can then retrieve voice
        messages via Voicemail. Voicemail audio is sent in Waveform Audio File Format, .wav, format.

        Optionally, notifications can be sent to a mobile phone via text or email. These notifications will not include
        the voicemail files.

        This API requires a full, user, or read-only administrator auth token with a scope of spark-admin:people_read
        or a user auth token with spark:people_read scope can be used by a person to read their settings.

        :param entity_id: Unique identifier for the entity
        :type entity_id: str
        :param org_id: Entity is in this organization. Only admin users of another organization (such as partners)
            may use this parameter as the default is the same organization as the token used to access API.
        :type org_id: str
        :return: entity's voicemail settings
        :rtype: VoicemailSettings
        """
        url = self.f_ep(entity_id)
        params = org_id and {'orgId': org_id} or None
        return VoicemailSettings.model_validate(await self.get(url, params=params))

    async def configure(self, entity_id: str, settings: VoicemailSettings, org_id: str = None):
        """
        Configure Voicemail Settings for an entity

        The voicemail feature transfers callers to voicemail based on your settings. You can then retrieve voice
        messages via Voicemail. Voicemail audio is sent in Waveform Audio File Format, .wav, format.

        Optionally, notifications can be sent to a mobile phone via text or email. These notifications will not
        include the voicemail files.

        This API requires a full or user administrator auth token with the spark-admin:people_write scope or a user
        auth token with spark:people_write scope can be used by a person to update their settings.
        :return:
        """
        # some settings can't be part of an update
        data = settings.update()
        url = self.f_ep(entity_id)
        params = org_id and {'orgId': org_id} or None
        await self.put(url, json=data, params=params)

    async def _configure_greeting(self, *, entity_id: str, content: Union[BufferedReader, str],
                            upload_as: str = None, org_id: str = None,
                            greeting_key: str):
        """
        handle greeting configuration

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param content: the file to be uploaded, can be a path to a file or a buffered reader (opened file); if a
            reader referring to an open file is passed then make sure to open the file as binary b/c otherwise the
            content length might be calculated wrong
        :type content: Union[BufferedReader, str]
        :param upload_as: filename for the content. Only required if content is a reader; has to be a .wav file name.
        :type upload_as: str
        :param org_id: Entity is in this organization. Only admin users of another organization (such as partners)
            may use this parameter as the default is the same organization as the token used to access API.
        :type org_id: str
        :param greeting_key: 'uploadBusyGreeting' or 'uploadNoAnswerGreeting'
        """
        if isinstance(content, str):
            upload_as = os.path.basename(content)
            content = open(content, mode='rb')
            must_close = True
        else:
            must_close = False
            # an existing reader
            if not upload_as:
                raise ValueError('upload_as is required')
        encoder = MultipartEncoder({'file': (upload_as, content, 'audio/wav')})
        ep = self.f_ep(entity_id, path=f'actions/{greeting_key}/invoke')
        params = org_id and {'orgId': org_id} or None
        try:
            await self.post(ep, data=encoder, headers={'Content-Type': encoder.content_type},
                      params=params)
        finally:
            if must_close:
                content.close()

    def configure_busy_greeting(self, entity_id: str, content: Union[BufferedReader, str],
                                upload_as: str = None, org_id: str = None):
        """
        Configure Busy Voicemail Greeting for an entity

        Your request will need to be a multipart/form-data request rather than JSON, using the audio/wav Content-Type.

        This API requires a full or user administrator auth token with the spark-admin:people_write scope or a user
        auth token with spark:people_write scope can be used by a person to update their settings.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param content: the file to be uploaded, can be a path to a file or a buffered reader (opened file); if a
            reader referring to an open file is passed then make sure to open the file as binary b/c otherwise the
            content length might be calculated wrong
        :type content: Union[BufferedReader, str]
        :param upload_as: filename for the content. Only required if content is a reader; has to be a .wav file name.
        :type upload_as: str
        :param org_id: Entity is in this organization. Only admin users of another organization (such as partners)
            may use this parameter as the default is the same organization as the token used to access API.
        :type org_id: str
        """
        self._configure_greeting(entity_id=entity_id, content=content, upload_as=upload_as, org_id=org_id,
                                 greeting_key='uploadBusyGreeting')

    def configure_no_answer_greeting(self, entity_id: str, content: Union[BufferedReader, str],
                                     upload_as: str = None, org_id: str = None):
        """
        Configure No Answer Voicemail Greeting for an entity

        Configure an entity's No Answer Voicemail Greeting by uploading a Waveform Audio File Format, .wav, encoded
        audio file.

        Your request will need to be a multipart/form-data request rather than JSON, using the audio/wav Content-Type.

        This API requires a full or user administrator auth token with the spark-admin:people_write scope or a user
        auth token with spark:people_write scope can be used by a person to update their settings.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param content: the file to be uploaded, can be a path to a file or a buffered reader (opened file); if a
            reader referring to an open file is passed then make sure to open the file as binary b/c otherwise the
            content length might be calculated wrong
        :type content: Union[BufferedReader, str]
        :param upload_as: filename for the content. Only required if content is a reader; has to be a .wav file name.
        :type upload_as: str
        :param org_id: Entity is in this organization. Only admin users of another organization (such as partners)
            may use this parameter as the default is the same organization as the token used to access API.
        :type org_id: str
        """
        self._configure_greeting(entity_id=entity_id, content=content, upload_as=upload_as, org_id=org_id,
                                 greeting_key='uploadNoAnswerGreeting')

    async def modify_passcode(self, entity_id: str, passcode: str, org_id: str = None):
        """
        Modify an entity's voicemail passcode.

        Modifying an entity's voicemail passcode requires a full administrator, user administrator or location
        administrator auth token with a scope of `spark-admin:telephony_config_write`.

        :param entity_id: Modify voicemail passcode for this entity.
        :type entity_id: str
        :param passcode: Voicemail access passcode. The minimum length of the passcode is 6 and the maximum length is
            30.
        :type passcode: str
        :param org_id: Modify voicemail passcode for an entity in this organization.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        body['passcode'] = passcode
        url = self.f_ep(entity_id, 'passcode')
        await super().put(url, params=params, json=body)

    async def reset_pin(self, entity_id: str, org_id: str = None):
        """
        Reset Voicemail PIN

        Reset a voicemail PIN for an entity.

        The voicemail feature transfers callers to voicemail based on your settings. You can then retrieve voice
        messages via Voicemail.  A voicemail PIN is used to retrieve your voicemail messages.

        This API requires a full or user administrator or location administrator auth token with
        the`spark-admin:people_write` scope.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param org_id: ID of the organization in which the person resides. Only admin users of another organization
            (such as partners) may use this parameter as the default is the same organization as the token used to
            access API.
        :type org_id: str
        :rtype: None
        """
        params = org_id and {'orgId': org_id} or None
        url = self.f_ep(entity_id, 'actions/resetPin/invoke')
        await super().post(url, params=params)


class AsPersonSettingsApi(AsApiChild, base='people'):
    """
    API for all user level settings
    """

    #: agent caller id Api
    agent_caller_id: AsAgentCallerIdApi
    app_shared_line: AsAppSharedLineApi
    #: Person's Application Services Settings
    appservices: AsAppServicesApi
    #: Available numbers for a person
    available_numbers: AsAvailableNumbersApi
    #: Barge In Settings for a Person
    barge: AsBargeApi
    #: Call bridge settings for a person
    call_bridge: AsCallBridgeApi
    #: Call Intercept Settings for a Person
    call_intercept: AsCallInterceptApi
    #: Call Recording Settings for a Person
    call_recording: AsCallRecordingApi
    #: Call Waiting Settings for a Person
    call_waiting: AsCallWaitingApi
    #: Caller ID Settings for a Person
    caller_id: AsCallerIdApi
    #: Person's Calling Behavior
    calling_behavior: AsCallingBehaviorApi
    #: Do Not Disturb Settings for a Person
    dnd: AsDndApi
    #: ECBN settings
    ecbn: AsECBNApi
    #: Executive Assistant settings for a Person
    exec_assistant: AsExecAssistantApi
    #: Executive settings for a person
    executive: AsExecutiveSettingsApi
    #: Feature access settings for a person
    feature_access: AsFeatureAccessApi
    #: Forwarding Settings for a Person
    forwarding: AsPersonForwardingApi
    #: Hoteling Settings for a Person
    hoteling: AsHotelingApi
    #: Person's mode management settings
    mode_management: AsModeManagementApi
    #: Person's Monitoring Settings
    monitoring: AsMonitoringApi
    # ; MS Teams settings
    ms_teams: AsMSTeamsSettingApi
    #: music on hold settings
    music_on_hold: AsMusicOnHoldApi
    #: Phone Numbers for a Person
    numbers: AsNumbersApi
    #: Incoming Permission Settings for a Person
    permissions_in: AsIncomingPermissionsApi
    #: Person's Outgoing Calling Permissions Settings
    permissions_out: AsOutgoingPermissionsApi
    #: Personal Assistant Settings
    personal_assistant: AsPersonalAssistantApi
    #: Preferred answer endpoint settings
    preferred_answer: AsPreferredAnswerApi
    #: Person's Privacy Settings
    privacy: AsPrivacyApi
    #: Push-to-Talk Settings for a Person
    push_to_talk: AsPushToTalkApi
    #: Receptionist Client Settings for a Person
    receptionist: AsReceptionistApi
    #: Schedules for a Person
    schedules: AsScheduleApi
    #: selective accept settings
    selective_accept: AsSelectiveAcceptApi
    #: selective forward settings
    selective_forward: AsSelectiveForwardApi
    #: selective reject settings
    selective_reject: AsSelectiveRejectApi
    #: single nunber reach settings
    single_number_reach: AsSingleNumberReachApi

    #: Voicemail Settings for a Person
    voicemail: AsVoicemailApi

    def __init__(self, session: AsRestSession):
        """

        :meta private:
        """
        super().__init__(session=session)
        self.agent_caller_id = AsAgentCallerIdApi(session=session)
        self.app_shared_line = AsAppSharedLineApi(session=session)
        self.appservices = AsAppServicesApi(session=session)
        self.available_numbers = AsAvailableNumbersApi(session=session)
        self.barge = AsBargeApi(session=session)
        self.call_bridge = AsCallBridgeApi(session=session)
        self.call_intercept = AsCallInterceptApi(session=session)
        self.call_recording = AsCallRecordingApi(session=session)
        self.call_waiting = AsCallWaitingApi(session=session)
        self.calling_behavior = AsCallingBehaviorApi(session=session)
        self.caller_id = AsCallerIdApi(session=session)
        self.dnd = AsDndApi(session=session)
        self.ecbn = AsECBNApi(session=session)
        self.exec_assistant = AsExecAssistantApi(session=session)
        self.executive = AsExecutiveSettingsApi(session=session)
        self.feature_access = AsFeatureAccessApi(session=session)
        self.forwarding = AsPersonForwardingApi(session=session)
        self.hoteling = AsHotelingApi(session=session)
        self.mode_management = AsModeManagementApi(session=session)
        self.monitoring = AsMonitoringApi(session=session)
        self.ms_teams = AsMSTeamsSettingApi(session=session)
        self.music_on_hold = AsMusicOnHoldApi(session=session)
        self.numbers = AsNumbersApi(session=session)
        self.permissions_in = AsIncomingPermissionsApi(session=session)
        self.permissions_out = AsOutgoingPermissionsApi(session=session)
        self.personal_assistant = AsPersonalAssistantApi(session=session)
        self.preferred_answer = AsPreferredAnswerApi(session=session)
        self.privacy = AsPrivacyApi(session=session)
        self.push_to_talk = AsPushToTalkApi(session=session)
        self.receptionist = AsReceptionistApi(session=session)
        self.schedules = AsScheduleApi(session=session, base=ScheduleApiBase.people)
        self.selective_accept = AsSelectiveAcceptApi(session=session, selector=ApiSelector.person)
        self.selective_forward = AsSelectiveForwardApi(session=session, selector=ApiSelector.person)
        self.selective_reject = AsSelectiveRejectApi(session=session, selector=ApiSelector.person)
        self.single_number_reach = AsSingleNumberReachApi(session=session)
        self.voicemail = AsVoicemailApi(session=session)

    # This endpoint is also available in the voicemail API and is only kept here for backward compatibility.

    async def reset_vm_pin(self, person_id: str, org_id: str = None):
        """
        Reset Voicemail PIN

        Reset a voicemail PIN for a person.

        The voicemail feature transfers callers to voicemail based on your settings. You can then retrieve voice
        messages via Voicemail. A voicemail PIN is used to retrieve your voicemail messages.

        This API requires a full or user administrator auth token with the spark-admin:people_write scope.

        This endpoint is also available in the voicemail API and is only kept here for backward compatibility.

        :param person_id: Unique identifier for the person.
        :param org_id: Person is in this organization. Only admin users of another organization (such as partners) may
            use this parameter as the default is the same organization as the token used to access API.
        """
        params = org_id and {'orgId': org_id} or None
        url = self.ep(f'{person_id}/features/voicemail/actions/resetPin/invoke')
        await self.post(url, params=params)

    async def devices(self, person_id: str, org_id: str = None) -> DeviceList:
        """
        Get all devices for a person.

        This requires a full or read-only administrator or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param person_id: Person to retrieve devices for
        :type person_id: str
        :param org_id: organization that person belongs to
        :type org_id: str
        :return: device info for user
        :rtype: DeviceList
        """
        params = org_id and {'orgId': org_id} or None
        url = self.session.ep(f'telephony/config/people/{person_id}/devices')
        data = await self.get(url=url, params=params)
        return DeviceList.model_validate(data)

    async def modify_hoteling_settings_primary_devices(self, person_id: str, hoteling: Hoteling,
                                                 org_id: str = None):
        """
        Modify Hoteling Settings for a Person's Primary Devices

        Modify hoteling login configuration on a person's Webex Calling Devices which are in effect when the device is
        the user's primary device and device type is PRIMARY. To view the current hoteling login settings, see the
        `hoteling` field in `Get Person Devices
        <https://developer.webex.com/docs/api/v1/device-call-settings/get-person-devices>`_.

        Modifying devices for a person requires a full administrator or location administrator auth token with a scope
        of `spark-admin:telephony_config_write`.

        :param person_id: ID of the person associated with the device.
        :type person_id: str
        :param hoteling: Modify person Device Hoteling Setting.
        :type hoteling: Hoteling
        :param org_id: Organization to which the person belongs.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        body['hoteling'] = hoteling.model_dump(mode='json', by_alias=True, exclude_none=True)
        url = self.session.ep(f'telephony/config/people/{person_id}/devices/settings/hoteling')
        await super().put(url, params=params, json=body)

    async def get_call_captions_settings(self, person_id: str, org_id: str = None) -> UserCallCaptions:
        """
        Get the user call captions settings

        Retrieve the user's call captions settings.

        **NOTE**: The call captions feature is not supported for Webex Calling Standard users or users assigned to
        locations in India.

        The call caption feature allows the customer to enable and manage closed captions and transcript functionality
        (rolling caption panel) in Webex Calling, without requiring the user to escalate the call to a meeting.

        This API requires a full, user, read-only, or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param person_id: Unique identifier for the person.
        :type person_id: str
        :param org_id: Unique identifier for the organization.
        :type org_id: str
        :rtype: :class:`UserCallCaptions`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.session.ep(f'telephony/config/people/{person_id}/callCaptions')
        data = await super().get(url, params=params)
        r = UserCallCaptions.model_validate(data)
        return r

    async def update_call_captions_settings(self, person_id: str, settings: UserCallCaptions, org_id: str = None):
        """
        Update the user call captions settings

        Update the user's call captions settings.

        **NOTE**: The call captions feature is not supported for Webex Calling Standard users or users assigned to
        locations in India.

        The call caption feature allows the customer to enable and manage closed captions and transcript functionality
        (rolling caption panel) in Webex Calling, without requiring the user to escalate the call to a meeting.

        This API requires a full, user or location administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param person_id: Unique identifier for the person.
        :type person_id: str
        :param settings: User call captions settings.
        :type settings: UserCallCaptions
        :param org_id: Unique identifier for the organization.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = settings.update()
        url = self.session.ep(f'telephony/config/people/{person_id}/callCaptions')
        await super().put(url, params=params, json=body)


class AsReportsApi(AsApiChild, base='devices'):
    """
    Reports

    To access these endpoints, you must use an administrator token with the `analytics:read_all` `scope
    <https://developer.webex.com/docs/integrations#scopes>`_. The
    authenticated user must be a read-only or full administrator of the organization to which the report belongs.

    To use this endpoint the org needs to be licensed for the Pro Pack.

    Reports available via `Webex Control Hub
    <https://admin.webex.com>`_ may be generated and downloaded via the Reports API. To access this API,
    the authenticated user must be a read-only or full administrator of the organization to which the report belongs.

    For more information about Reports, see the `Admin API
    <https://developer.webex.com/docs/admin#reports-api>`_ guide.

    """

    async def list_templates(self) -> list[ReportTemplate]:
        """
        List all the available report templates that can be generated.

        CSV (comma separated value) reports for Webex services are only supported for organizations based in the
        North American region. Organizations based in other regions will return blank CSV files for any Webex reports.

        :return: list of report templates
        :rtype: list[ReportTemplate]
        """
        # TODO: https://developer.webex.com/docs/api/v1/report-templates/list-report-templates, documentation bug
        #   "Template Attributes" is actually "items"
        # TODO: https://developer.webex.com/docs/api/v1/report-templates/list-report-templates, documentation bug
        #   "validations"/"validations" is actually "validations"
        # TODO: https://developer.webex.com/docs/api/v1/report-templates/list-report-templates, documentation bug
        #   "id" is actually "Id"
        # TODO: https://developer.webex.com/docs/api/v1/report-templates/list-report-templates, documentation bug
        #   "startDate", "endDate" not documented
        url = self.session.ep('report/templates')
        data = await self.get(url=url)
        result = TypeAdapter(list[ReportTemplate]).validate_python(data['items'])
        return result

    def list_gen(self, report_id: str = None, service: str = None, template_id: str = None, from_date: date = None,
             to_date: date = None) -> AsyncGenerator[Report, None, None]:
        """
        Lists all reports. Use query parameters to filter the response. The parameters are optional. However, `from`
        and `to` parameters should be provided together.

        CSV reports for Teams services are only supported for organizations based in the North American region.
        Organizations based in a different region will return blank CSV files for any Teams reports.

        Reports are usually provided in zip format. A Content-header `application/zip` or `application/octet-stream`
        does indicate the zip format. There is usually no .zip file extension.

        :param report_id: List reports by ID.
        :param service: List reports which use this service.
        :param template_id: List reports with this report template ID.
        :param from_date: List reports that were created on or after this date.
        :param to_date: List reports that were created before this date.
        :return: yields :class:`Report` instances
        """
        # TODO: https://developer.webex.com/docs/api/v1/report-templates/list-report-templates, documentation bug
        #   "Report Attributes" is actually "items"
        # TODO: https://developer.webex.com/docs/api/v1/report-templates/list-report-templates, documentation bug
        #   missing attribute: downloadDomain
        # TODO: https://developer.webex.com/docs/api/v1/report-templates/list-report-templates, documentation bug
        #   "id" is actually "Id"
        # TODO: https://developer.webex.com/docs/api/v1/report-templates/list-report-templates, documentation bug
        #   "scheduledFrom" is actually "scheduleFrom"

        params = {to_camel(k.split('_')[0] if k.endswith('date') else k): v for k, v in locals().items()
                  if k not in {'self', 'from_date', 'to_date'} and v is not None}
        if from_date:
            params['from'] = from_date.strftime('%Y-%m-%d')
        if to_date:
            params['to'] = to_date.strftime('%Y-%m-%d')

        url = self.session.ep('reports')
        return self.session.follow_pagination(url=url, params=params, model=Report, item_key='items')

    async def list(self, report_id: str = None, service: str = None, template_id: str = None, from_date: date = None,
             to_date: date = None) -> List[Report]:
        """
        Lists all reports. Use query parameters to filter the response. The parameters are optional. However, `from`
        and `to` parameters should be provided together.

        CSV reports for Teams services are only supported for organizations based in the North American region.
        Organizations based in a different region will return blank CSV files for any Teams reports.

        Reports are usually provided in zip format. A Content-header `application/zip` or `application/octet-stream`
        does indicate the zip format. There is usually no .zip file extension.

        :param report_id: List reports by ID.
        :param service: List reports which use this service.
        :param template_id: List reports with this report template ID.
        :param from_date: List reports that were created on or after this date.
        :param to_date: List reports that were created before this date.
        :return: yields :class:`Report` instances
        """
        # TODO: https://developer.webex.com/docs/api/v1/report-templates/list-report-templates, documentation bug
        #   "Report Attributes" is actually "items"
        # TODO: https://developer.webex.com/docs/api/v1/report-templates/list-report-templates, documentation bug
        #   missing attribute: downloadDomain
        # TODO: https://developer.webex.com/docs/api/v1/report-templates/list-report-templates, documentation bug
        #   "id" is actually "Id"
        # TODO: https://developer.webex.com/docs/api/v1/report-templates/list-report-templates, documentation bug
        #   "scheduledFrom" is actually "scheduleFrom"

        params = {to_camel(k.split('_')[0] if k.endswith('date') else k): v for k, v in locals().items()
                  if k not in {'self', 'from_date', 'to_date'} and v is not None}
        if from_date:
            params['from'] = from_date.strftime('%Y-%m-%d')
        if to_date:
            params['to'] = to_date.strftime('%Y-%m-%d')

        url = self.session.ep('reports')
        return [o async for o in self.session.follow_pagination(url=url, params=params, model=Report, item_key='items')]

    async def create(self, template_id: int, start_date: date = None, end_date: date = None, site_list: str = None) -> str:
        """
        Create a new report. For each templateId, there are a set of validation rules that need to be followed. For
        example, for templates belonging to Webex, the user needs to provide siteUrl. These validation rules can be
        retrieved via the Report Templates API.

        CSV reports for Teams services are only supported for organizations based in the North American region.
        Organizations based in a different region will return blank CSV files for any Teams reports.

        :param template_id: Unique ID representing valid report templates.
        :type template_id: int
        :param start_date: Data in the report will be from this date onwards.
        :type start_date: date
        :param end_date: Data in the report will be until this date.
        :type end_date: date
        :param site_list: Sites belonging to user's organization. This attribute is needed for site-based templates.
        :type site_list: str
        :return: The unique identifier for the report.
        :rtype: str
        """
        # TODO: https://developer.webex.com/docs/api/v1/reports/create-a-report, documentation bug
        #   result actually is something like: {'items': {'Id': 'Y2...lMg'}}
        body = {'templateId': template_id}
        if start_date:
            body['startDate'] = start_date.strftime('%Y-%m-%d')
        if end_date:
            body['endDate'] = end_date.strftime('%Y-%m-%d')
        if site_list:
            body['siteList'] = site_list
        url = self.session.ep('reports')
        data = await self.post(url=url, json=body)
        result = data['items']['Id']
        return result

    async def details(self, report_id: str) -> Report:
        """
        Shows details for a report, by report ID.

        Specify the report ID in the reportId parameter in the URI.

        CSV reports for Teams services are only supported for organizations based in the North American region.
        Organizations based in a different region will return blank CSV files for any Teams reports.

        :param report_id: The unique identifier for the report.
        :type report_id: str
        :return: report details
        :rtype: Report
        """
        # TODO: https://developer.webex.com/docs/api/v1/reports/create-a-report, documentation bug
        #   result actually is something like: {'items': [{'title': 'Engagement Report', 'service': 'Webex Calling',
        #   'startDate': '2021-12-14', 'endDate': '2022-01-13', 'siteList': '', 'created': '2022-01-14 11:16:59',
        #   'createdBy': 'Y2lz..GM', 'scheduleFrom': 'api', 'status': 'done', 'downloadDomain':
        #   'https://reportdownload-a.webex.com/',  'downloadURL':
        #   'https://reportdownload-a.webex.com/api?reportId=Y2lz3ZA',  'Id': 'Y23ZA'}], 'numberOfReports': 1}
        url = self.session.ep(f'reports/{report_id}')
        data = await self.get(url=url)
        result = Report.model_validate(data['items'][0])
        return result

    async def delete(self, report_id: str):
        """
        Remove a report from the system.

        Specify the report ID in the reportId parameter in the URI

        CSV reports for Teams services are only supported for organizations based in the North American region.
        Organizations based in a different region will return blank CSV files for any Teams reports.

        :param report_id: The unique identifier for the report.
        :type report_id: str
        """
        url = self.session.ep(f'reports/{report_id}')
        await super().delete(url=url)

    async def download(self, url: str) -> List[dict]:
        """
        Download a report from the given URL and yield the rows as dicts

        :param url: download URL
        :type url: str
        :return: list of dicts (one per row)
        :rtype: list[dict]
        """
        raise NotImplementedError('async download not implemented; use sync call instead')
        


class AsRolesApi(AsApiChild, base='roles'):
    """
    Roles

    A persona for an authenticated user, corresponding to a set of privileges within an organization. This roles
    resource can be accessed only by an admin and shows only roles relevant to an admin.
    """

    async def list(self) -> list[IdAndName]:
        """
        List Roles

        List all roles.

        :rtype: list[Role]
        """
        url = self.ep()
        data = await super().get(url)
        r = TypeAdapter(list[IdAndName]).validate_python(data['items'])
        return r

    async def details(self, role_id: str) -> IdAndName:
        """
        Get Role Details

        Shows details for a role, by ID.

        Specify the role ID in the `roleId` parameter in the URI.

        :param role_id: The unique identifier for the role.
        :type role_id: str
        :rtype: :class:`Role`
        """
        url = self.ep(f'{role_id}')
        data = await super().get(url)
        r = IdAndName.model_validate(data)
        return r


class AsRoomTabsApi(AsApiChild, base='room/tabs'):
    """
    A Room Tab represents a URL shortcut that is added as a persistent tab to a Webex room (space) tab row. Use this
    API to list tabs of any Webex room that you belong to. Room Tabs can also be updated to point to a different
    content URL, or deleted to remove the tab from the room.
    Just like in the Webex app, you must be a member of the room in order to list its Room Tabs.
    """

    def list_tabs_gen(self, room_id: str, **params) -> AsyncGenerator[RoomTab, None, None]:
        """
        Lists all Room Tabs of a room specified by the roomId query parameter.

        :param room_id: ID of the room for which to list room tabs.
        :type room_id: str
        """
        if room_id is not None:
            params['roomId'] = room_id
        url = self.ep()
        return self.session.follow_pagination(url=url, model=RoomTab, params=params)

    async def list_tabs(self, room_id: str, **params) -> List[RoomTab]:
        """
        Lists all Room Tabs of a room specified by the roomId query parameter.

        :param room_id: ID of the room for which to list room tabs.
        :type room_id: str
        """
        if room_id is not None:
            params['roomId'] = room_id
        url = self.ep()
        return [o async for o in self.session.follow_pagination(url=url, model=RoomTab, params=params)]

    async def create_tab(self, room_id: str, content_url: str, display_name: str) -> RoomTab:
        """
        Add a tab with a specified URL to a room.

        :param room_id: A unique identifier for the room.
        :type room_id: str
        :param content_url: URL of the Room Tab. Must use https protocol.
        :type content_url: str
        :param display_name: User-friendly name for the room tab.
        :type display_name: str
        """
        body = {}
        if room_id is not None:
            body['roomId'] = room_id
        if content_url is not None:
            body['contentUrl'] = content_url
        if display_name is not None:
            body['displayName'] = display_name
        url = self.ep()
        data = await super().post(url=url, json=body)
        return RoomTab.model_validate(data)

    async def tab_details(self, tab_id: str) -> RoomTab:
        """
        Get details for a Room Tab with the specified room tab ID.

        :param tab_id: The unique identifier for the Room Tab.
        :type tab_id: str
        """
        url = self.ep(f'{tab_id}')
        data = await super().get(url=url)
        return RoomTab.model_validate(data)

    async def update_tab(self, tab_id: str, room_id: str, content_url: str, display_name: str) -> RoomTab:
        """
        Updates the content URL of the specified Room Tab ID.

        :param tab_id: The unique identifier for the Room Tab.
        :type tab_id: str
        :param room_id: ID of the room that contains the room tab in question.
        :type room_id: str
        :param content_url: Content URL of the Room Tab. URL must use https protocol.
        :type content_url: str
        :param display_name: User-friendly name for the room tab.
        :type display_name: str
        """
        body = {}
        if room_id is not None:
            body['roomId'] = room_id
        if content_url is not None:
            body['contentUrl'] = content_url
        if display_name is not None:
            body['displayName'] = display_name
        url = self.ep(f'{tab_id}')
        data = await super().put(url=url, json=body)
        return RoomTab.model_validate(data)

    async def delete_tab(self, tab_id: str):
        """
        Deletes a Room Tab with the specified ID.

        :param tab_id: The unique identifier for the Room Tab to delete.
        :type tab_id: str
        """
        url = self.ep(f'{tab_id}')
        await super().delete(url=url)
        return


class AsRoomsApi(AsApiChild, base='rooms'):
    """
    Rooms are virtual meeting places where people post messages and collaborate to get work done. This API is used to
    manage the rooms themselves. Rooms are created and deleted with this API. You can also update a room to change
    its title, for example.
    To create a team room, specify the a teamId in the POST payload. Note that once a room is added to a team,
    it cannot be moved. To learn more about managing teams, see the Teams API.
    To manage people in a room see the Memberships API.
    To post content see the Messages API.
    """

    def list_gen(self, team_id: str = None, type_: RoomType = None, org_public_spaces: bool = None,
             from_: datetime = None, to_: datetime = None, sort_by: str = None,
             **params) -> AsyncGenerator[Room, None, None]:
        """
        List rooms.

        The title of the room for 1:1 rooms will be the display name of the other person.
        By default, lists rooms to which the authenticated user belongs.

        Long result sets will be split into pages.

        Known Limitations:

        The underlying database does not support natural sorting by lastactivity and will only sort on limited set of
        results, which are pulled from the database in order of roomId. For users or bots in more than 3000 spaces
        this can result in anomalies such as spaces that have had recent activity not being returned in the results
        when sorting by lastacivity.

        :param team_id: List rooms associated with a team, by ID.
        :type team_id: str
        :param type_: List rooms by type.
            Possible values: direct, group
        :type type_: RoomType
        :param org_public_spaces: Shows the org's public spaces joined and unjoined. When set the result list is sorted
            by the madePublic timestamp.
        :type org_public_spaces: bool
        :param from_: Filters rooms, that were made public after this time. See madePublic timestamp
        :type from_: datetime
        :param to_: Filters rooms, that were made public before this time. See maePublic timestamp
        :type to_: datetime
        :param sort_by: Sort results.
            Possible values: id, lastactivity, created
        :type sort_by: str
        """
        if team_id is not None:
            params['teamId'] = team_id
        if type_ is not None:
            params['type'] = enum_str(type_)
        if sort_by is not None:
            params['sortBy'] = sort_by
        if org_public_spaces is not None:
            params['orgPublicSpaces'] = org_public_spaces
        if from_ is not None:
            params['from'] = dt_iso_str(from_)
        if to_ is not None:
            params['to'] = dt_iso_str(to_)
        url = self.ep()
        # noinspection PyTypeChecker
        return self.session.follow_pagination(url=url, model=Room, params=params)

    async def list(self, team_id: str = None, type_: RoomType = None, org_public_spaces: bool = None,
             from_: datetime = None, to_: datetime = None, sort_by: str = None,
             **params) -> List[Room]:
        """
        List rooms.

        The title of the room for 1:1 rooms will be the display name of the other person.
        By default, lists rooms to which the authenticated user belongs.

        Long result sets will be split into pages.

        Known Limitations:

        The underlying database does not support natural sorting by lastactivity and will only sort on limited set of
        results, which are pulled from the database in order of roomId. For users or bots in more than 3000 spaces
        this can result in anomalies such as spaces that have had recent activity not being returned in the results
        when sorting by lastacivity.

        :param team_id: List rooms associated with a team, by ID.
        :type team_id: str
        :param type_: List rooms by type.
            Possible values: direct, group
        :type type_: RoomType
        :param org_public_spaces: Shows the org's public spaces joined and unjoined. When set the result list is sorted
            by the madePublic timestamp.
        :type org_public_spaces: bool
        :param from_: Filters rooms, that were made public after this time. See madePublic timestamp
        :type from_: datetime
        :param to_: Filters rooms, that were made public before this time. See maePublic timestamp
        :type to_: datetime
        :param sort_by: Sort results.
            Possible values: id, lastactivity, created
        :type sort_by: str
        """
        if team_id is not None:
            params['teamId'] = team_id
        if type_ is not None:
            params['type'] = enum_str(type_)
        if sort_by is not None:
            params['sortBy'] = sort_by
        if org_public_spaces is not None:
            params['orgPublicSpaces'] = org_public_spaces
        if from_ is not None:
            params['from'] = dt_iso_str(from_)
        if to_ is not None:
            params['to'] = dt_iso_str(to_)
        url = self.ep()
        # noinspection PyTypeChecker
        return [o async for o in self.session.follow_pagination(url=url, model=Room, params=params)]

    async def create(self, title: str, team_id: str = None, classification_id: str = None, is_locked: bool = None,
               is_public: bool = None, description: str = None, is_announcement_only: bool = None) -> Room:
        """
        Creates a room. The authenticated user is automatically added as a member of the room. See the Memberships
        API to learn how to add more people to the room.

        To create a 1:1 room, use the Create Messages endpoint to send a message directly to another person by using
        the toPersonId or toPersonEmail parameters.
        Bots are not able to create and simultaneously classify a room. A bot may update a space classification after
        a person of the same owning organization joined the space as the first human user.

        A space can only be put into announcement mode when it is locked.

        :param title: A user-friendly name for the room.
        :type title: str
        :param team_id: The ID for the team with which this room is associated.
        :type team_id: str
        :param classification_id: The classificationId for the room.
        :type classification_id: str
        :param is_locked: Set the space as locked/moderated and the creator becomes a moderator
        :type is_locked: bool
        :param is_public: The room is public and therefore discoverable within the org. Anyone can find and join that
            room. When true the description must be filled in.
        :type is_public: bool
        :param description: The description of the space.
        :type description: str
        :param is_announcement_only: Sets the space into announcement Mode.
        :type is_announcement_only: bool
        """
        body = {}
        if title is not None:
            body['title'] = title
        if team_id is not None:
            body['teamId'] = team_id
        if classification_id is not None:
            body['classificationId'] = classification_id
        if is_locked is not None:
            body['isLocked'] = is_locked
        if is_public is not None:
            body['isPublic'] = is_public
        if description is not None:
            body['description'] = description
        if is_announcement_only is not None:
            body['isAnnouncementOnly'] = is_announcement_only
        url = self.ep()
        data = await super().post(url=url, json=body)
        return Room.model_validate(data)

    async def details(self, room_id: str) -> Room:
        """
        Shows details for a room, by ID.
        The title of the room for 1:1 rooms will be the display name of the other person.
        Specify the room ID in the roomId parameter in the URI.

        :param room_id: The unique identifier for the room.
        :type room_id: str
        """
        url = self.ep(f'{room_id}')
        data = await super().get(url=url)
        return Room.model_validate(data)

    async def meeting_details(self, room_id: str) -> GetRoomMeetingDetailsResponse:
        """

        The meetingInfo API is deprecated and will be EOL on Jan 31, 2025. Meetings in the WSMP must be scheduled and
        licensed via the meetings backend.
        The `Create a Meeting
        <https://developer.webex.com/docs/api/v1/meetings/create-a-meeting>`_ endpoint will provide the SIP address for
        the meeting to call.

        Shows Webex meeting details for a room such as the SIP address, meeting URL, toll-free and toll dial-in numbers.
        Specify the room ID in the roomId parameter in the URI.

        :param room_id: The unique identifier for the room.
        :type room_id: str
        """
        url = self.ep(f'{room_id}/meetingInfo')
        data = await super().get(url=url)
        return GetRoomMeetingDetailsResponse.model_validate(data)

    async def update(self, update: Room) -> Room:
        """
        Updates details for a room, by ID.

        Specify the room ID in the `roomId` parameter in the URI.

        A space can only be put into announcement mode when it is locked.

        Any space participant or compliance officer can convert a space from public to private. Only a compliance
        officer can convert a space from private to public and only if the space is classified with the lowest
        category (usually `public`), and the space has a description.

        To remove a `description` please use a space character ` ` by itself.

        When using this method for moving a space under a team, ensure that all moderators in
        the space are also team members. If a moderator is not part of the team, demote or remove them as a moderator.
        Alternatively, add the non-team moderators to the team. This ensures compliance with the requirement that all
        space moderators must be team members for successful operation execution.

        :update: update to apply. ID and title have to be set. Only can update:

            * title: str: A user-friendly name for the room.
            * classification_id: str: The classificationId for the room.
            * team_id: str: The teamId to which this space should be assigned. Only unowned spaces can be assigned
              to a team. Assignment between teams is unsupported.
            * is_locked: bool: Set the space as locked/moderated and the creator becomes a moderator
            * is_public: The room is public and therefore discoverable within the org. Anyone can find and join that
              room. When `true` the `description` must be filled in.
            * description: The description of the space.
            * is_announcement_only: bool: Sets the space into announcement mode or clears the anouncement Mode (false)
            * is_read_only: bool: A compliance officer can set a direct room as read-only, which will disallow any
              new information exchanges in this space, while maintaining historical data.
        """
        update: Room
        data = update.model_dump_json(include={'title', 'classification_id', 'team_id', 'is_locked', 'is_public',
                                               'description', 'is_announcement_only', 'is_read_only'})
        if update.id is None:
            raise ValueError('ID has to be set')
        url = self.ep(f'{update.id}')
        data = await super().put(url=url, data=data)
        return Room.model_validate(data)

    async def delete(self, room_id: str):
        """
        Deletes a room, by ID. Deleted rooms cannot be recovered.
        As a security measure to prevent accidental deletion, when a non moderator deletes the room they are removed
        from the room instead.
        Deleting a room that is part of a team will archive the room instead.
        Specify the room ID in the roomId parameter in the URI.

        :param room_id: The unique identifier for the room.
        :type room_id: str
        """
        url = self.ep(f'{room_id}')
        await super().delete(url=url)
        return


class AsScimApiChild(AsApiChild, base='identity/scim'):

    def ep(self, path: str = None):
        """
        endpoint URL for given path

        :meta private:
        :param path: path after APIChild subclass specific endpoint URI prefix
        :type path: str
        :return: endpoint URL
        :rtype: str
        """
        path = path and f'/{path}' or ''
        return f'https://webexapis.com/{self.base}{path}'


class AsSCIM2BulkApi(AsScimApiChild, base='identity/scim'):
    """
    Bulk Manage SCIM 2 Users and Groups

    The bulk API allows you to create, update, and remove multiple users and groups in Webex.  The number of Bulk
    operations in a single request is limited to 100.

    Bulk deletion of Users is irreversible. Please test in a test org or sandbox
    before running the command in your production system.

    """

    async def bulk_request(self, org_id: str, fail_on_errors: int,
                     operations: list[BulkOperation]) -> BulkResponse:
        """
        User bulk API

        **Authorization**

        OAuth token rendered by Identity Broker.

        One of the following OAuth scopes is required:

        - `identity:people_rw`

        **Usage**:

        1. The input JSON must conform to the following schema: 'urn:ietf:params:scim:api:messages:2.0:BulkRequest'.

        2. The request must be accompanied with a body in JSON format according to the standard SCIM schema definition.
           The maximum number of operations in a request is 100; an error is thrown if the limit is exceeded.

        3. `failOnErrors` parameter

           An integer specifies the number of errors that the service provider will accept before the operation is
           terminated and an error response is returned.
           It is OPTIONAL in a request.
           Maximum number of operations allowed to fail before the server stops processing the request. The value must
           be between 1 and 100.

        4. `operations` parameter

           Contains a list of bulk operations for POST/PATCH/DELETE operations. (REQUIRED)

           + `operations.method`

             The HTTP method of the current operation. Possible values are POST, PATCH or DELETE.
           + `operations.path

             The Resource's relative path. If the method is POST the value must specify a Resource type endpoint;
             e.g., /Users or /Groups whereas all other method values must specify the path to a specific Resource;
             e.g., /Users/2819c223-7f76-453a-919d-413861904646.
           + `operations.data`

             The Resource data as it would appear for a single POST or PATCH Resource operation.
             It is REQUIRED in a request when method is POST and PATCH.
             Refer to corresponding wiki for SCIM 2.0 POST, PATCH and DELETE API.
           + `operations.bulkId`

             The transient identifier of a newly created resource, unique within a bulk request and created by the
             client.
             The bulkId serves as a surrogate resource id enabling clients to uniquely identify newly created
             resources in
             the response and cross-reference new resources in and across operations within a bulk request.
             It is REQUIRED when "method" is "POST".

        :param org_id: Webex Identity assigned organization identifier for user's organization.
        :type org_id: str
        :param fail_on_errors: An integer specifying the maximum number of errors that the service provider will accept
            before the operation is terminated and an error response is returned.
        :type fail_on_errors: int
        :param operations: Contains a list of bulk operations for POST/PATCH/DELETE operations.
        :type operations: list[BulkOperation]
        :rtype: :class:`BulkResponse`

        Example:

            .. code-block:: python

                # bulk operations to create a bunch of users from a list of ScimUser instances
                new_scim_users: list[ScimUser]
                operations = [BulkOperation(method=BulkMethod.post, path='/Users',
                                            bulk_id=str(uuid.uuid4()),
                                            data=scim_user.create_update())
                              for scim_user in new_scim_users]
                bulk_response = self.api.scim.bulk.bulk_request(org_id=org_id, fail_on_errors=1,
                                                                operations=operations)
        """
        body = dict()
        body['schemas'] = ['urn:ietf:params:scim:api:messages:2.0:BulkRequest']
        body['failOnErrors'] = fail_on_errors
        body['operations'] = TypeAdapter(list[BulkOperation]).dump_python(
            operations, mode='json', by_alias=True, exclude_none=True)
        url = self.ep(f'{org_id}/v2/Bulk')
        data = await super().post(url, json=body)
        r = BulkResponse.model_validate(data)
        return r


class AsSCIM2GroupsApi(AsScimApiChild, base='identity/scim'):
    """
    SCIM 2 Groups

    Implementation of the SCIM 2.0 group part for group management in a standards based manner. Please also see the
    `SCIM Specification
    <http://www.simplecloud.info/>`_. The schema and API design follows the standard SCIM 2.0 definition with
    detailed in
    `SCIM 2.0 schema
    <https://datatracker.ietf.org/doc/html/rfc7643>`_ and `SCIM 2.0 Protocol
    """

    async def create(self, org_id: str, group: ScimGroup) -> ScimGroup:
        """
        Create a group

        Create a new group for a given organization. The group may optionally be created with group members.

        **Authorization**

        OAuth token returned by Identity Broker.

        One of the following OAuth scopes is required:

        - `identity:people_rw`

        The following administrators can use this API:

        - `id_full_admin`
        - `id_group_admin`

        **Usage**:

        1. The input JSON must conform to one of the following schemas:

        - `urn:ietf:params:scim:schemas:core:2.0:Group`
        - `urn:scim:schemas:extension:cisco:webexidentity:2.0:Group`

        2. Unrecognized schemas (ID/section) are ignored.

        3. Read-only attributes provided as input values are ignored.

        :param org_id: The ID of the organization to which this group belongs. If not specified, the organization ID
            from the OAuth token is used.
        :type org_id: str
        :param group: Group settings
        :type group: ScimGroup
        :rtype: :class:`ScimGroup`
        """
        body = group.create_update()
        url = self.ep(f'{org_id}/v2/Groups')
        data = await super().post(url, json=body)
        r = ScimGroup.model_validate(data)
        return r

    async def details(self, org_id: str, group_id: str, excluded_attributes: str = None) -> ScimGroup:
        """
        Get a group

        Retrieve details for a group, by ID.

        Optionally, members can be retrieved with this request. The maximum number of members returned is 500.

        **Authorization**

        OAuth token rendered by Identity Broker.

        One of the following OAuth scopes is required:

        - `identity:people_rw`
        - `identity:people_read`

        The following administrators can use this API:

        - `id_full_admin`
        - `id_group_admin`
        - `id_readonly_admin`
        - `id_device_admin`

        :param org_id: The ID of the organization to which this group belongs. If not specified, the organization ID
            from the OAuth token is used.
        :type org_id: str
        :param group_id: A unique identifier for the group.
        :type group_id: str
        :param excluded_attributes: Attributes to be excluded from the return.
        :type excluded_attributes: str
        :rtype: :class:`ScimGroup`
        """
        params = {}
        if excluded_attributes is not None:
            params['excludedAttributes'] = excluded_attributes
        url = self.ep(f'{org_id}/v2/Groups/{group_id}')
        data = await super().get(url, params=params)
        r = ScimGroup.model_validate(data)
        return r

    async def search(self, org_id: str, filter: str = None, excluded_attributes: str = None, attributes: str = None,
               start_index: int = None, count: int = None, sort_by: str = None, sort_order: str = None,
               include_members: bool = None, member_type: str = None) -> SearchGroupResponse:
        """
        Search groups

        Retrieve a list of groups in the organization.

        Long result sets are split into `pages
        <https://developer.webex.com/docs/basics#pagination>`_.

        **Authorization**

        An OAuth token rendered by Identity Broker.

        One of the following OAuth scopes is required:

        - `identity:people_rw`
        - `identity:people_read`

        The following administrators can use this API:

        - `id_full_admin`
        - `id_group_admin`
        - `id_readonly_admin`
        - `id_device_admin`

        :param org_id: The ID of the organization to which this group belongs. If not specified, the organization ID
            from the OAuth token is used.
        :type org_id: str
        :param filter: The url encoded filter. The example content is 'displayName Eq "group1@example.com" or
            displayName Eq "group2@example.com"'.

            For more filter patterns, see https://datatracker.ietf.org/doc/html/rfc7644#section-3.4.2.2. If the value is
            empty, the API returns all groups under the organization.
        :type filter: str
        :param excluded_attributes: Attributes to be excluded from the return.
        :type excluded_attributes: str
        :param attributes: The attributes to return.
        :type attributes: str
        :param start_index: An integer indicating the 1-based index of the first query result. The default is 1.
        :type start_index: int
        :param count: An integer indicating the desired maximum number of query results per page. The default is 100.
        :type count: int
        :param sort_by: A string indicating the attribute whose value be used to order the returned responses. Now we
            only allow `displayName, id, meta.lastModified` to sort.
        :type sort_by: str
        :param sort_order: A string indicating the order in which the `sortBy` parameter is applied. Allowed values are
            `ascending` and `descending`.
        :type sort_order: str
        :param include_members: Default "false". If false, no members returned.
        :type include_members: bool
        :param member_type: Filter the members by member type. Sample data: `user`, `machine`, `group`.
        :type member_type: str
        :rtype: :class:`SearchGroupResponse`
        """
        params = {}
        if filter is not None:
            params['filter'] = filter
        if excluded_attributes is not None:
            params['excludedAttributes'] = excluded_attributes
        if attributes is not None:
            params['attributes'] = attributes
        if start_index is not None:
            params['startIndex'] = start_index
        if count is not None:
            params['count'] = count
        if sort_by is not None:
            params['sortBy'] = sort_by
        if sort_order is not None:
            params['sortOrder'] = sort_order
        if include_members is not None:
            params['includeMembers'] = str(include_members).lower()
        if member_type is not None:
            params['memberType'] = member_type
        url = self.ep(f'{org_id}/v2/Groups')
        data = await super().get(url, params=params)
        r = SearchGroupResponse.model_validate(data)
        return r

    async def search_all_gen(self, org_id: str, filter: str = None, excluded_attributes: str = None,
                             attributes: str= None,
                             count: int = None, sort_by: str = None, sort_order: str = None,
                             include_members: bool = None, member_type: str = None) -> AsyncGenerator[ScimGroup,
                             None, None]:
        params = {k: v for k, v in locals().items()
                  if k not in {'self', 'count'} and v is not None}
        start_index = None
        while True:
            paginated_result = await self.search(**params, start_index=start_index, count=count)
            for r in paginated_result.resources:
                yield r
            # prepare getting the next page
            count = paginated_result.items_per_page
            start_index = paginated_result.start_index + paginated_result.items_per_page
            if start_index > paginated_result.total_results:
                break
        return

    async def search_all(self, org_id: str, filter: str = None, excluded_attributes: str = None, attributes: str = None,
                         count: int = None, sort_by: str = None, sort_order: str = None,
                         include_members: bool = None, member_type: str = None) -> list[ScimGroup]:
        params = {k: v for k, v in locals().items()
                  if k not in {'self'} and v is not None}
        return [u async for u in self.search_all_gen(**params)]
        

    async def members(self, org_id: str, group_id: str, start_index: int = None, count: int = None,
                member_type: str = None) -> GroupMemberResponse:
        """
        Get Group Members

        Returns the members of a group.

        - The default maximum number of members returned is 500.
        - Control parameters are available to page through the members and to control the size of the results.
        - Long result sets are split into `pages <https://developer.webex.com/docs/basics#pagination>`_.

        **Note**

        Location groups are different from SCIM groups. You cannot search for identities in a location via groups.

        **Authorization**

        OAuth token returned by the Identity Broker.

        One of the following OAuth scopes is required:

        - `identity:people_rw`
        - `identity:people_read`

        The following administrators can use this API:

        - `id_full_admin`
        - `id_group_admin`
        - `id_readonly_admin`
        - `id_device_admin`

        :param org_id: The ID of the organization to which this group belongs. If not specified, the organization ID
            from the OAuth token is used.
        :type org_id: str
        :param group_id: A unique identifier for the group.
        :type group_id: str
        :param start_index: The index to start for group pagination.
        :type start_index: int
        :param count: Non-negative integer that specifies the desired number of search results per page. The maximum
            value for the count is 500.
        :type count: int
        :param member_type: Filter the members by member type. Sample data: `user`, `machine`, `group`.
        :type member_type: str
        :rtype: :class:`GroupMemberResponse`
        """
        params = {}
        if start_index is not None:
            params['startIndex'] = start_index
        if count is not None:
            params['count'] = count
        if member_type is not None:
            params['memberType'] = member_type
        url = self.ep(f'{org_id}/v2/Groups/{group_id}/Members')
        data = await super().get(url, params=params)
        r = GroupMemberResponse.model_validate(data)
        return r

    async def members_all_gen(self, org_id: str, group_id: str, start_index: int = None, count: int = None,
                              member_type: str = None) -> AsyncGenerator[ScimGroupMember, None, None]:
        params = {k: v for k, v in locals().items()
                  if k not in {'self', 'count'} and v is not None}
        start_index = None
        while True:
            paginated_result = await self.members(**params, start_index=start_index, count=count)
            for r in paginated_result.members:
                yield r
            # prepare getting the next page
            count = paginated_result.items_per_page
            start_index = paginated_result.start_index + paginated_result.items_per_page
            if start_index > paginated_result.member_size:
                break
        return

    async def members_all(self, org_id: str, group_id: str, start_index: int = None, count: int = None,
                          member_type: str = None) -> list[ScimGroupMember]:
        params = {k: v for k, v in locals().items()
                  if k not in {'self'} and v is not None}
        return [u async for u in self.members_all_gen(**params)]
        

    async def update(self, org_id: str, group: ScimGroup) -> ScimGroup:
        """
        Update a group with PUT

        Replace the contents of the Group.

        **Authorization**

        OAuth token returned by Identity Broker.

        One of the following OAuth scopes is required:

        - `identity:people_rw`

        The following administrators can use this API:

        - `id_full_admin`
        - `id_group_admin`

        **Usage**:

        1. The input JSON must conform to one of the following schemas:

        - `urn:ietf:params:scim:schemas:core:2.0:Group`
        - `urn:scim:schemas:extension:cisco:webexidentity:2.0:Group`

        2. Unrecognized schemas (ID/section) are ignored.

        3. Read-only attributes provided as input values are ignored.

        4. The group `id` is not changed.

        5. All attributes are cleaned up if a new value is not provided by the client.

        6. The values, `meta` and `created` are not changed.

        :param org_id: The ID of the organization to which this group belongs. If not specified, the organization ID
            from the OAuth token is used.
        :type org_id: str
        :rtype: :class:`ScimGroup`
        """
        body = group.create_update()
        url = self.ep(f'{org_id}/v2/Groups/{group.id}')
        data = await super().put(url, json=body)
        r = ScimGroup.model_validate(data)
        return r

    async def patch(self, org_id: str, group_id: str, schemas: list[str],
              operations: list[PatchUserOperation]) -> ScimGroup:
        """
        Update a group with PATCH

        Update group attributes with PATCH.

        **Authorization**

        OAuth token returned by Identity Broker.

        One of the following OAuth scopes is required:

        - `identity:people_rw`

        The following administrators can use this API:

        - `id_full_admin`
        - `id_group_admin`

        **Usage**:

        1. The input JSON must conform to one of the following schemas:

        - `urn:ietf:params:scim:schemas:core:2.0:Group`
        - `urn:scim:schemas:extension:cisco:webexidentity:2.0:Group`

        2. Unrecognized schemas (ID/section) are ignored.

        3. Read-only attributes provided as input values are ignored.

        4. Each operation on an attribute must be compatible with the attribute's mutability.

        5. Each PATCH operation represents a single action to be applied to the
        same SCIM resource specified by the request URI. Operations are
        applied sequentially in the order they appear in the array. Each
        operation in the sequence is applied to the target resource; the
        resulting resource becomes the target of the next operation.
        Evaluation continues until all operations are successfully applied or
        until an error condition is encountered.

        **Add operations**:

        The `add` operation is used to add a new attribute value to an existing resource. The operation must contain a
        `value` member whose content specifies the value to be added. The value may be a quoted value, or it may be a
        JSON object containing the sub-attributes of the complex attribute specified in the operation's `path`. The
        result of the add operation depends upon the target location indicated by `path` references:

        - If omitted, the target location is assumed to be the resource itself. The `value` parameter contains a set of
          attributes to be added to the resource.
        - If the target location does not exist, the attribute and value are added.
        - If the target location specifies a complex attribute, a set of sub-attributes shall be specified in the
          `value` parameter.
        - If the target location specifies a multi-valued attribute, a new value is added to the attribute.
        - If the target location specifies a single-valued attribute, the existing value is replaced.
        - If the target location specifies an attribute that does not exist (has no value), the attribute is added with
          the new value.
        - If the target location exists, the value is replaced.
        - If the target location already contains the value specified, no changes should be made to the resource.

        **Replace operations**:

        The `replace` operation replaces the value at the target location specified by the `path`. The operation
        performs the following functions, depending on the target location specified by `path`:

        - If the `path` parameter is omitted, the target is assumed to be the resource itself. In this case, the
          `value` attribute shall contain a list of one or more attributes that are to be replaced.
        - If the target location is a single-value attribute, the value of the attribute is replaced.
        - If the target location is a multi-valued attribute and no filter is specified, the attribute and all values
          are replaced.
        - If the target location path specifies an attribute that does not exist, the service provider shall treat the
          operation as an "add".
        - If the target location specifies a complex attribute, a set of sub-attributes SHALL be specified in the
          `value` parameter, which replaces any existing values or adds where an attribute did not previously exist.
          Sub-attributes that are not specified in the `value` parameters are left unchanged.
        - If the target location is a multi-valued attribute and a value selection ("valuePath") filter is specified
          that matches one or more values of the multi-valued attribute, then all matching record values will be
          replaced.
        - If the target location is a complex multi-valued attribute with a value selection filter ("valuePath") and a
          specific sub-attribute (e.g., "addresses[type eq "work"].streetAddress"), the matching sub-attribute of all
          matching records is replaced.
        - If the target location is a multi-valued attribute for which a value selection filter ("valuePath") has been
          supplied and no record match was made, the service provider will indicate the failure by returning HTTP status
          code 400 and a `scimType` error code of `noTarget`.

        **Remove operations**:

        The `remove` operation removes the value at the target location specified by the required attribute `path`. The
        operation performs the following functions, depending on the target location specified by `path`:

        - If `path` is unspecified, the operation fails with HTTP status code 400 and a "scimType" error code of
          "noTarget".
        - If the target location is a single-value attribute, the attribute and its associated value is removed, and
          the attribute will be considered unassigned.
        - If the target location is a multi-valued attribute and no filter is specified, the attribute and all values
          are removed, and the attribute SHALL be considered unassigned.
        - If the target location is a multi-valued attribute and a complex filter is specified comparing a `value`, the
          values matched by the filter are removed. If no other values remain after the removal of the selected values,
          the multi-valued attribute will be considered unassigned.
        - If the target location is a complex multi-valued attribute and a complex filter is specified based on the
          attribute`s sub-attributes, the matching records are removed. Sub-attributes whose values have been removed
          will be considered unassigned. If the complex multi-valued attribute has no remaining records, the attribute
          will be considered unassigned.

        :param org_id: The ID of the organization to which this group belongs. If not specified, the organization ID
            from the OAuth token is used.
        :type org_id: str
        :param group_id: A unique identifier for the group.
        :type group_id: str
        :param schemas: Input JSON schemas.
        :type schemas: list[str]
        :param operations: A list of patch operations.
        :type operations: list[PatchGroupOperations]
        :rtype: :class:`ScimGroup`
        """
        body = dict()
        body['schemas'] = schemas
        body['Operations'] = TypeAdapter(list[PatchUserOperation]).dump_python(operations, mode='json', by_alias=True,
                                                                               exclude_none=True)
        url = self.ep(f'{org_id}/v2/Groups/{group_id}')
        data = await super().patch(url, json=body)
        r = ScimGroup.model_validate(data)
        return r

    async def delete(self, org_id: str, group_id: str):
        """
        Delete a group

        Remove a group from the system.

        **Authorization**

        OAuth token rendered by Identity Broker.

        One of the following OAuth scopes is required:

        - `identity:people_rw`

        The following administrators can use this API:

        - `id_full_admin`
        - `id_group_admin`

        :param org_id: The ID of the organization to which this group belongs. If not specified, the organization ID
            from the OAuth token is used.
        :type org_id: str
        :param group_id: A unique identifier for the group.
        :type group_id: str
        :rtype: None
        """
        url = self.ep(f'{org_id}/v2/Groups/{group_id}')
        await super().delete(url)


class AsSCIM2UsersApi(AsScimApiChild, base='identity/scim'):
    """
    SCIM 2 Users

    Implementation of the SCIM 2.0 user part for user management in a standards based manner. Please also
    see the `SCIM Specification <http://www.simplecloud.info/>`_. The schema and API design follows the standard
    SCIM 2.0 definition with detailed in
    `SCIM 2.0 schema <https://datatracker.ietf.org/doc/html/rfc7643>`_ and SCIM 2.0 Protocol
    """

    async def create(self, org_id: str, user: ScimUser) -> ScimUser:
        """
        Create a user

        The SCIM 2 /Users API provides a programmatic way to manage users in Webex Identity using The Internet
        Engineering Task Force standard SCIM 2.0 standard as specified by `RFC 7643 SCIM 2.0 Core Schema
        <https://datatracker.ietf.org/doc/html/rfc7643>`_ and
        `RFC 7644 SCIM 2.0 Core Protocol
        <https://datatracker.ietf.org/doc/html/rfc7644>`_.  The WebEx SCIM 2.0  APIs allow clients supporting the
        SCIM 2.0 standard to
        manage users, and groups within Webex.  Webex supports the following SCIM 2.0 Schemas:

        • urn:ietf:params:scim:schemas:core:2.0:User
        • urn:ietf:params:scim:schemas:extension:enterprise:2.0:User
        • urn:scim:schemas:extension:cisco:webexidentity:2.0:User

        **Authorization**

        OAuth token rendered by Identity Broker.

        One of the following OAuth scopes is required:

        - `identity:people_rw`

        The following administrators can use this API:

        - `id_full_admin`
        - `id_user_admin`

        **Usage**:

        1. Input JSON must contain schema: "urn:ietf:params:scim:schemas:core:2.0:User".

        1. Support 3 schemas :
        - "urn:ietf:params:scim:schemas:core:2.0:User"
        - "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"
        - "urn:scim:schemas:extension:cisco:webexidentity:2.0:User"

        1. Unrecognized schemas (ID/section) are ignored.

        1. Read-only attributes provided as input values are ignored.

        :param org_id: Webex Identity assigned organization identifier for user's organization.
        :type org_id: str
        :param user: User settings
        :type user: ScimUser
        :rtype: :class:`ScimUser`
        """
        body = user.create_update()
        url = self.ep(f'{org_id}/v2/Users')
        data = await super().post(url, json=body)
        r = ScimUser.model_validate(data)
        return r

    async def details(self, org_id: str, user_id: str) -> ScimUser:
        """
        Get a user

        **Authorization**

        OAuth token rendered by Identity Broker.

        One of the following OAuth scopes is required:

        - `identity:people_rw`
        - `identity:people_read`

        The following administrators can use this API:

        - `id_full_admin`
        - `id_user_admin`
        - `id_readonly_admin`
        - `id_device_admin`

        :param org_id: Webex Identity assigned organization identifier for user's organization.
        :type org_id: str
        :param user_id: Webex Identity assigned user identifier.
        :type user_id: str
        :rtype: :class:`ScimUser`
        """
        url = self.ep(f'{org_id}/v2/Users/{user_id}')
        data = await super().get(url)
        r = ScimUser.model_validate(data)
        return r

    async def search(self, org_id: str, filter: str = None, attributes: str = None,
               excluded_attributes: str = None, sort_by: str = None, sort_order: str = None,
               start_index: int = None, count: int = None, return_groups: bool = None,
               include_group_details: bool = None, group_usage_types: str = None) -> SearchUserResponse:
        """
        Search users

        **Authorization**

        OAuth token rendered by Identity Broker.

        One of the following OAuth scopes is required:

        - `identity:people_rw`
        - `identity:people_read`

        The following administrators can use this API:

        - `id_full_admin`
        - `id_user_admin`
        - `id_readonly_admin`
        - `id_device_admin`

        :param org_id: Webex Identity assigned organization identifier for user's organization.
        :type org_id: str
        :param filter: The url encoded filter. If the value is empty, the API will return all users under the
            organization.

            The examples below show some search filters:

            - userName eq "user1@example.com"
            - userName sw "user1@example"
            - userName ew "example"
            - phoneNumbers [ type eq "mobile" and value eq "14170120"]
            - urn:scim:schemas:extension:cisco:webexidentity:2.0:User:meta.organizationId eq
              "0ae87ade-8c8a-4952-af08-318798958d0c"
            - More filter patterns, please check https://datatracker.ietf.org/doc/html/rfc7644#section-3.4.2.2"

            .. list-table::
               :header-rows: 1

               * - **Attributes**
                 - **Operators**
               * - **SCIM Core**
                 -
               * - `id`
                 - eq
               * - `userName`
                 - eq sw ew
               * - `name.familyName`
                 - eq sw ew
               * - `name.givenName`
                 - eq sw
               * - `name.middleName`
                 - eq sw
               * - `name.formatted`
                 - eq sw
               * - `displayName`
                 - eq sw ew
               * - `nickName`
                 - eq sw ew
               * - `emails.display`
                 - eq sw ew
               * - `emails.value`
                 - eq sw ew
               * - `phoneNumbers.value`
                 - eq sw ew
               * - `phoneNumbers.display`
                 - eq sw ew
               * - **Enterprise Extensions**
                 -
               * - `employeeNumber`
                 - eq sw ew
               * - `costCenter`
                 - eq sw ew
               * - `organization`
                 - eq sw ew
               * - `division`
                 - eq sw ew
               * - `department`
                 - eq sw ew
               * - `manager.value`
                 - eq
               * - `manager.displayName`
                 - eq sw ew

        :type filter: str
        :param attributes: A multi-valued list of strings indicating the names of resource attributes to return in the
            response, like 'userName,department,emails'. It supports the SCIM id
            'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User,userName'. The default is empty, all
            attributes will be returned
        :type attributes: str
        :param excluded_attributes: A multi-valued list of strings indicating the names of resource attributes to be
            removed from the default set of attributes to return. The default is empty, all attributes will be
            returned
        :type excluded_attributes: str
        :param sort_by: A string indicating the attribute whose value be used to order the returned responses. Now we
            only allow 'userName, id, meta.lastModified' to sort.
        :type sort_by: str
        :param sort_order: A string indicating the order in which the 'sortBy' parameter is applied. Allowed values are
            'ascending' and 'descending'.
        :type sort_order: str
        :param start_index: An integer indicating the 1-based index of the first query result. The default is 1.
        :type start_index: int
        :param count: An integer indicating the desired maximum number of query results per page.  The default is 100.
        :type count: int
        :param return_groups: Define whether the group information needs to be returned.  The default is false.
        :type return_groups: str
        :param include_group_details: Define whether the group information with details need been returned. The default
            is false.
        :type include_group_details: str
        :param group_usage_types: Returns groups with details of the specified group type
        :type group_usage_types: str
        :rtype: :class:`SearchUserResponse`
        """
        params = {}
        if filter is not None:
            params['filter'] = filter
        if attributes is not None:
            params['attributes'] = attributes
        if excluded_attributes is not None:
            params['excludedAttributes'] = excluded_attributes
        if sort_by is not None:
            params['sortBy'] = sort_by
        if sort_order is not None:
            params['sortOrder'] = sort_order
        if start_index is not None:
            params['startIndex'] = start_index
        if count is not None:
            params['count'] = count
        if return_groups is not None:
            params['returnGroups'] = return_groups
        if include_group_details is not None:
            params['includeGroupDetails'] = include_group_details
        if group_usage_types is not None:
            params['groupUsageTypes'] = group_usage_types
        url = self.ep(f'{org_id}/v2/Users')
        data = await super().get(url, params=params)
        r = SearchUserResponse.model_validate(data)
        return r

    async def search_all_gen(self, org_id: str, filter: str = None, attributes: str = None,
                             excluded_attributes: str = None,
                             sort_by: str = None, sort_order: str = None, count: int = None, return_groups: str = None,
                             include_group_details: str = None,
                             group_usage_types: str = None) -> AsyncGenerator[ScimUser, None, None]:
        params = {k: v for k, v in locals().items()
                  if k not in {'self', 'count'} and v is not None}
        start_index = None
        while True:
            paginated_result = await self.search(**params, start_index=start_index, count=count)
            for r in paginated_result.resources:
                yield r
            # prepare getting the next page
            count = paginated_result.items_per_page
            start_index = paginated_result.start_index + paginated_result.items_per_page
            if start_index > paginated_result.total_results:
                break
        return

    async def search_all(self, org_id: str, filter: str = None, attributes: str = None,
                         excluded_attributes: str = None,
                         sort_by: str = None, sort_order: str = None, count: int = None, return_groups: str = None,
                         include_group_details: str = None,
                         group_usage_types: str = None) -> list[ScimUser]:
        params = {k: v for k, v in locals().items()
                  if k not in {'self'} and v is not None}
        return [u async for u in self.search_all_gen(**params)]
        

    async def update(self, org_id: str, user: ScimUser) -> ScimUser:
        """
        Update a user with PUT

        **Authorization**

        OAuth token rendered by Identity Broker.

        One of the following OAuth scopes is required:

        - `identity:people_rw`

        The following administrators can use this API:

        - `id_full_admin`
        - `id_user_admin`

        **Usage**:

        1. Input JSON must contain schema: "urn:ietf:params:scim:schemas:core:2.0:User".

        2. Support 3 schemas:

        - "urn:ietf:params:scim:schemas:core:2.0:User"
        - "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"
        - "urn:scim:schemas:extension:cisco:webexidentity:2.0:User"

        3. Unrecognized schemas (ID/section) are ignored.

        4. Read-only attributes provided as input values are ignored.

        5. User `id` will not be changed.

        6. `meta`.`created` will not be changed.

        7. The PUT API replaces the contents of the user's data with the data in the request body.  All attributes
        specified in the request body will replace all existing attributes for the userId specified in the URL.
        Should you wish to replace or change some attributes as opposed to all attributes please refer to the SCIM
        PATCH operation https://developer.webex.com/docs/api/v1/scim2-user/update-a-user-with-patch .

        :param org_id: Webex Identity assigned organization identifier for user's organization.
        :type org_id: str
        :param user: user to be updates
        :rtype: :class:`ScimUser`
        """
        body = user.create_update()
        url = self.ep(f'{org_id}/v2/Users/{user.id}')
        data = await super().put(url, json=body)
        r = ScimUser.model_validate(data)
        return r

    async def patch(self, org_id: str, user_id: str,
              operations: list[PatchUserOperation]) -> ScimUser:
        """
        Update a user with PATCH

        **Authorization**

        OAuth token rendered by Identity Broker.

        One of the following OAuth scopes is required:

        - `identity:people_rw`

        The following administrators can use this API:

        - `id_full_admin`
        - `id_user_admin`

        **Usage**:

        1. The PATCH API replaces individual attributes of the user's data in the request body.
        The PATCH api supports `add`, `remove` and `replace` operations on any individual
        attribute allowing only specific attributes of the user's object to be modified.

        2. Each operation against an attribute must be compatible with the attribute's mutability.

        3. Each PATCH operation represents a single action to be applied to the
        same SCIM resource specified by the request URI.  Operations are
        applied sequentially in the order they appear in the array.  Each
        operation in the sequence is applied to the target resource; the
        resulting resource becomes the target of the next operation.
        Evaluation continues until all operations are successfully applied or
        until an error condition is encountered.

        **Add operations**:

        The `add` operation is used to add a new attribute value to an existing resource.
        The operation must contain a `value` member whose content specifies the value to be added.
        The value may be a quoted value, or it may be a JSON object containing the sub-attributes of the complex
        attribute specified in the operation's `path`.
        The result of the add operation depends upon the target location indicated by `path` references:

        - If omitted, the target location is assumed to be the resource itself.  The `value` parameter contains a set
          of attributes to be added to the resource.
        - If the target location does not exist, the attribute and value are added.
        - If the target location specifies a complex attribute, a set of sub-attributes shall be specified in the
          `value` parameter.
        - If the target location specifies a multi-valued attribute, a new value is added to the attribute.
        - If the target location specifies a single-valued attribute, the existing value is replaced.
        - If the target location specifies an attribute that does not exist (has no value), the attribute is added with
          the new value.
        - If the target location exists, the value is replaced.
        - If the target location already contains the value specified, no changes should be made to the resource.

        **Replace operations**:

        The `replace` operation replaces the value at the target location specified by the `path`.
        The operation performs the following functions, depending on the target location specified by `path`:

        - If the `path` parameter is omitted, the target is assumed to be the resource itself.  In this case, the
          `value` attribute shall contain a list of one or more attributes that are to be replaced.
        - If the target location is a single-value attribute, the value of the attribute is replaced.
        - If the target location is a multi-valued attribute and no filter is specified, the attribute and all values
          are replaced.
        - If the target location path specifies an attribute that does not exist, the service provider shall treat the
          operation as an "add".
        - If the target location specifies a complex attribute, a set of sub-attributes SHALL be specified in the
          `value` parameter, which replaces any existing values or adds where an attribute did not previously exist.
          Sub-attributes that are not specified in the `value` parameters are left unchanged.
        - If the target location is a multi-valued attribute and a value selection ("valuePath") filter is specified
          that matches one or more values of the multi-valued attribute, then all matching record values will be
          replaced.
        - If the target location is a complex multi-valued attribute with a value selection filter ("valuePath") and a
          specific sub-attribute (e.g., "addresses[type eq "work"].streetAddress"), the matching sub-attribute of all
          matching records is replaced.
        - If the target location is a multi-valued attribute for which a value selection filter ("valuePath") has been
          supplied and no record match was made, the service provider will indicate the failure by returning HTTP status
          code 400 and a `scimType` error code of "noTarget".

        **Remove operations**:

        The `remove` operation removes the value at the target location specified by the required attribute `path`.
        The operation performs the following functions, depending on the target location specified by `path`:

        - If `path` is unspecified, the operation fails with HTTP status code 400 and a "scimType" error code of
          "noTarget".
        - If the target location is a single-value attribute, the attribute and its associated value is removed, and
          the attribute will be considered unassigned.
        - If the target location is a multi-valued attribute and no filter is specified, the attribute and all values
          are removed, and the attribute SHALL be considered unassigned.
        - If the target location is a multi-valued attribute and a complex filter is specified comparing a `value`, the
          values matched by the filter are removed.  If no other values remain after the removal of the selected values,
          the multi-valued attribute will be considered unassigned.
        - If the target location is a complex multi-valued attribute and a complex filter is specified based on the
          attribute's sub-attributes, the matching records are removed.  Sub-attributes whose values have been removed
          will be considered unassigned.  If the complex multi-valued attribute has no remaining records, the attribute
          will be considered unassigned.

        :param org_id: Webex Identity assigned organization identifier for user's organization.
        :type org_id: str
        :param user_id: Webex Identity assigned user identifier.
        :type user_id: str
        :param operations: A list of patch operations.
        :type operations: list[PatchUserOperation]
        :rtype: :class:`ScimUser`
        """
        body = dict()
        body['schemas'] = ["urn:ietf:params:scim:api:messages:2.0:PatchOp"]
        body['Operations'] = TypeAdapter(list[PatchUserOperation]).dump_python(operations, mode='json', by_alias=True,
                                                                               exclude_none=True)
        url = self.ep(f'{org_id}/v2/Users/{user_id}')
        data = await super().patch(url, json=body)
        r = ScimUser.model_validate(data)
        return r

    async def delete(self, org_id: str, user_id: str):
        """
        Delete a user

        **Authorization**

        OAuth token rendered by Identity Broker.

        One of the following OAuth scopes is required:

        - `identity:people_rw`
        - `Identity:SCIM`

        The following administrators can use this API:

        - `id_full_admin`
        - `id_user_admin`

        :param org_id: Webex Identity assigned organization identifier for user's organization.
        :type org_id: str
        :param user_id: Webex Identity assigned user identifier.
        :type user_id: str
        :rtype: None
        """
        url = self.ep(f'{org_id}/v2/Users/{user_id}')
        await super().delete(url)


@dataclass(init=False, repr=False)
class AsScimV2Api(AsApiChild, base=''):
    users: AsSCIM2UsersApi
    bulk: AsSCIM2BulkApi
    groups: AsSCIM2GroupsApi

    def __init__(self, *, session: AsRestSession):
        super().__init__(session=session)
        self.users = AsSCIM2UsersApi(session=session)
        self.bulk = AsSCIM2BulkApi(session=session)
        self.groups = AsSCIM2GroupsApi(session=session)


class AsStatusAPI(AsApiChild, base='status'):
    """
    Webex Status API as described at https://status.webex.com/api
    """

    # noinspection PyMethodOverriding
    def ep(self, path: str):
        """

        :meta private:
        """
        return f'https://status.webex.com/{path}.json'

    async def summary(self) -> StatusSummary:
        """
        Get a summary of the status page, including a status indicator, component statuses, unresolved incidents,
        and any upcoming or in-progress scheduled maintenances.

        :return: Status summary
        :rtype: StatusSummary
        """
        url = self.ep('index')
        data = await self.session.rest_get(url=url)
        return StatusSummary.model_validate(data)

    async def status(self) -> str:
        """
        Get the status rollup for the whole page. This response includes an indicator - one of green (operational),
        yellow (under_maintenance/degraded_performance/partial_outage), red (major_outage).

        :return: Webex status
        :rtype: str
        """
        url = self.ep('status')
        data = await self.session.rest_get(url=url)
        return data['status']['indicator']

    async def components(self) -> list[Component]:
        """
        Get the components for the status page. Each component is listed along with its status - one of operational,
        under_maintenance,degraded_performance, partial_outage, or major_outage.

        :return: list of components
        :rtype: list[Component]
        """
        url = self.ep('components')
        data = await self.session.rest_get(url=url)
        return TypeAdapter(list[Component]).validate_python(data['components'])

    async def unresolved_incidents(self) -> list[Incident]:
        """
        Get a list of any unresolved incidents. This response will only return incidents in the Investigating,
        Identified, or Monitoring state.

        :return: list of incidents
        :rtype: list[Incident]
        """
        url = self.ep('unresolved-incidents')
        data = await self.session.rest_get(url=url)
        return TypeAdapter(list[Incident]).validate_python(data['incidents'])

    async def all_incidents(self) -> list[Incident]:
        """
        Get a list of the 50 most recent incidents. This includes all unresolved incidents as described above,
        as well as those in the Resolved state.

        :return: list of incidents
        :rtype: list[Incident]
        """
        url = self.ep('all-incidents')
        data = await self.session.rest_get(url=url)
        return TypeAdapter(list[Incident]).validate_python(data['incidents'])

    async def upcoming_scheduled_maintenances(self) -> list[Incident]:
        """
        Scheduled maintenances are planned outages, upgrades, or general notices that you're working on
        infrastructure and disruptions may occurr. A close sibling of Incidents, each usually goes through a
        progression of statuses listed below, with an impact calculated from a blend of component statuses (or an
        optional override).

        Status: Scheduled, In Progress, or Completed

        Impact: Maintenance

        :return: list of incidents
        :rtype: list[Incident]
        """
        url = self.ep('upcoming-scheduled-maintenances')
        data = await self.session.rest_get(url=url)
        return TypeAdapter(list[Incident]).validate_python(data['scheduled_maintenances'])

    async def active_scheduled_maintenances(self) -> list[Incident]:
        """
        Get a list of any active maintenances. This response will only return scheduled maintenances in the In
        Progress state.

        :return: list of incidents
        :rtype: list[Incident]
        """
        url = self.ep('active-scheduled-maintenances')
        data = await self.session.rest_get(url=url)
        return TypeAdapter(list[Incident]).validate_python(data['scheduled_maintenances'])

    async def all_scheduled_maintenances(self) -> list[Incident]:
        """
        Get a list of the 50 most recent scheduled maintenances. This includes scheduled maintenances in Scheduled ,
        In Progress or Completed state.

        :return: list of incidents
        :rtype: list[Incident]
        """
        url = self.ep('all-scheduled-maintenances')
        data = await self.session.rest_get(url=url)
        return TypeAdapter(list[Incident]).validate_python(data['scheduled_maintenances'])


class AsTeamMembershipsApi(AsApiChild, base='team/memberships'):
    """
    Team Memberships represent a person's relationship to a team. Use this API to list members of any team that you're
    in or create memberships to invite someone to a team. Team memberships can also be updated to make someone a
    moderator or deleted to remove them from the team.
    Just like in the Webex app, you must be a member of the team in order to list its memberships or invite people.
    """

    def list_gen(self, team_id: str, **params) -> AsyncGenerator[TeamMembership, None, None]:
        """
        Lists all team memberships for a given team, specified by the teamId query parameter.
        Use query parameters to filter the response.

        :param team_id: List memberships for a team, by ID.
        :type team_id: str
        """
        if team_id is not None:
            params['teamId'] = team_id
        url = self.ep()
        return self.session.follow_pagination(url=url, model=TeamMembership, params=params)

    async def list(self, team_id: str, **params) -> List[TeamMembership]:
        """
        Lists all team memberships for a given team, specified by the teamId query parameter.
        Use query parameters to filter the response.

        :param team_id: List memberships for a team, by ID.
        :type team_id: str
        """
        if team_id is not None:
            params['teamId'] = team_id
        url = self.ep()
        return [o async for o in self.session.follow_pagination(url=url, model=TeamMembership, params=params)]

    async def create(self, team_id: str, person_id: str = None, person_email: str = None,
               is_moderator: bool = None) -> TeamMembership:
        """
        Add someone to a team by Person ID or email address, optionally making them a moderator.

        :param team_id: The team ID.
        :type team_id: str
        :param person_id: The person ID.
        :type person_id: str
        :param person_email: The email address of the person.
        :type person_email: str
        :param is_moderator: Whether or not the participant is a team moderator.
        :type is_moderator: bool
        """
        body = {}
        if team_id is not None:
            body['teamId'] = team_id
        if person_id is not None:
            body['personId'] = person_id
        if person_email is not None:
            body['personEmail'] = person_email
        if is_moderator is not None:
            body['isModerator'] = is_moderator
        url = self.ep()
        data = await super().post(url=url, json=body)
        return TeamMembership.model_validate(data)

    async def details(self, membership_id: str) -> TeamMembership:
        """
        Shows details for a team membership, by ID.
        Specify the team membership ID in the membershipId URI parameter.

        :param membership_id: The unique identifier for the team membership.
        :type membership_id: str
        """
        url = self.ep(f'{membership_id}')
        data = await super().get(url=url)
        return TeamMembership.model_validate(data)

    async def membership(self, membership_id: str, is_moderator: bool) -> TeamMembership:
        """
        Updates a team membership, by ID.
        Specify the team membership ID in the membershipId URI parameter.

        :param membership_id: The unique identifier for the team membership.
        :type membership_id: str
        :param is_moderator: Whether or not the participant is a team moderator.
        :type is_moderator: bool
        """
        body = {'isModerator': is_moderator}
        url = self.ep(f'{membership_id}')
        data = await super().put(url=url, json=body)
        return TeamMembership.model_validate(data)

    async def delete(self, membership_id: str):
        """
        Deletes a team membership, by ID.
        Specify the team membership ID in the membershipId URI parameter.
        The team membership for the last moderator of a team may not be deleted; promote another user to team moderator
        first.

        :param membership_id: The unique identifier for the team membership.
        :type membership_id: str
        """
        url = self.ep(f'{membership_id}')
        await super().delete(url=url)
        return


class AsTeamsApi(AsApiChild, base='teams'):
    """
    Teams are groups of people with a set of rooms that are visible to all members of that team. This API is used to
    manage the teams themselves. Teams are created and deleted with this API. You can also update a team to change its
    name, for example.
    To manage people in a team see the Team Memberships API.
    To manage team rooms see the Rooms API.
    """

    def list_gen(self) -> AsyncGenerator[Team, None, None]:
        """
        Lists teams to which the authenticated user belongs.
        """
        url = self.ep()
        return self.session.follow_pagination(url=url, model=Team)

    async def list(self) -> List[Team]:
        """
        Lists teams to which the authenticated user belongs.
        """
        url = self.ep()
        return [o async for o in self.session.follow_pagination(url=url, model=Team)]

    async def create(self, name: str) -> Team:
        """
        Creates a team.
        The authenticated user is automatically added as a member of the team. See the Team Memberships API to learn
        how to add more people to the team.

        :param name: A user-friendly name for the team.
        :type name: str
        """
        body = {}
        if name is not None:
            body['name'] = name
        url = self.ep()
        data = await super().post(url=url, json=body)
        return Team.model_validate(data)

    async def details(self, team_id: str) -> Team:
        """
        Shows details for a team, by ID.
        Specify the team ID in the teamId parameter in the URI.

        :param team_id: The unique identifier for the team.
        :type team_id: str
        """
        url = self.ep(f'{team_id}')
        data = await super().get(url=url)
        return Team.model_validate(data)

    async def update(self, team_id: str, name: str) -> Team:
        """
        Updates details for a team, by ID.
        Specify the team ID in the teamId parameter in the URI.

        :param team_id: The unique identifier for the team.
        :type team_id: str
        :param name: A user-friendly name for the team.
        :type name: str
        """
        body = {'name': name}
        url = self.ep(f'{team_id}')
        data = await super().put(url=url, json=body)
        return Team.model_validate(data)

    async def delete(self, team_id: str):
        """
        Deletes a team, by ID.
        Specify the team ID in the teamId parameter in the URI.

        :param team_id: The unique identifier for the team.
        :type team_id: str
        """
        url = self.ep(f'{team_id}')
        await super().delete(url=url)
        return


class AsAnnouncementsRepositoryApi(AsApiChild, base='telephony/config'):
    """
    Not supported for Webex for Government (FedRAMP)

    Features: Announcement Repository support reading and writing of Webex Calling Announcement Repository settings for
    a specific organization.

    Viewing these read-only organization settings requires a full or read-only administrator auth token with a scope of
    spark-admin:telephony_config_read.
    Modifying these organization settings requires a full administrator auth token with a scope
    of spark-admin:telephony_config_write.

    A partner administrator can retrieve or change settings in a customer's organization using the optional orgId query
    parameter.
    """

    def list_gen(self, location_id: str = None, order: str = None, file_name: str = None, file_type: str = None,
             media_file_type: str = None, name: str = None, org_id: str = None,
             **params) -> AsyncGenerator[RepoAnnouncement, None, None]:
        """
        Fetch list of announcement greetings on location and organization level

        Fetch a list of binary announcement greetings at an organization as well as location level.

        An admin can upload a file at an organization level. This file will be uploaded to the announcement repository.

        This API requires a full or read-only administrator or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param location_id: Return the list of enterprise or Location announcement files. Without this parameter, the
            Enterprise level announcements are returned. Possible values: all, locations,
            Y2lzY29zcGFyazovL3VzL0xPQ0FUSU9OLzMxMTYx
        :type location_id: str
        :param order: Sort the list according to fileName or fileSize. The default sort will be in Ascending order.
        :type order: str
        :param file_name: Return the list of announcements with the given fileName.
        :type file_name: str
        :param file_type: Return the list of announcement files for this fileType.
        :type file_type: str
        :param media_file_type: Return the list of announcement files for this mediaFileType.
        :type media_file_type: str
        :param name: Return the list of announcement files for this announcement label.
        :type name: str
        :param org_id: Create an announcement in this organization.
        :type org_id: str
        :return: yields :class:`RepoAnnouncement` objects

        """
        if org_id is not None:
            params['orgId'] = org_id
        if location_id is not None:
            params['locationId'] = location_id
        if order is not None:
            params['order'] = order
        if file_name is not None:
            params['fileName'] = file_name
        if file_type is not None:
            params['fileType'] = file_type
        if media_file_type is not None:
            params['mediaFileType'] = media_file_type
        if name is not None:
            params['name'] = name
        url = self.ep('announcements')
        return self.session.follow_pagination(url=url, model=RepoAnnouncement, item_key='announcements',
                                              params=params)

    async def list(self, location_id: str = None, order: str = None, file_name: str = None, file_type: str = None,
             media_file_type: str = None, name: str = None, org_id: str = None,
             **params) -> List[RepoAnnouncement]:
        """
        Fetch list of announcement greetings on location and organization level

        Fetch a list of binary announcement greetings at an organization as well as location level.

        An admin can upload a file at an organization level. This file will be uploaded to the announcement repository.

        This API requires a full or read-only administrator or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param location_id: Return the list of enterprise or Location announcement files. Without this parameter, the
            Enterprise level announcements are returned. Possible values: all, locations,
            Y2lzY29zcGFyazovL3VzL0xPQ0FUSU9OLzMxMTYx
        :type location_id: str
        :param order: Sort the list according to fileName or fileSize. The default sort will be in Ascending order.
        :type order: str
        :param file_name: Return the list of announcements with the given fileName.
        :type file_name: str
        :param file_type: Return the list of announcement files for this fileType.
        :type file_type: str
        :param media_file_type: Return the list of announcement files for this mediaFileType.
        :type media_file_type: str
        :param name: Return the list of announcement files for this announcement label.
        :type name: str
        :param org_id: Create an announcement in this organization.
        :type org_id: str
        :return: yields :class:`RepoAnnouncement` objects

        """
        if org_id is not None:
            params['orgId'] = org_id
        if location_id is not None:
            params['locationId'] = location_id
        if order is not None:
            params['order'] = order
        if file_name is not None:
            params['fileName'] = file_name
        if file_type is not None:
            params['fileType'] = file_type
        if media_file_type is not None:
            params['mediaFileType'] = media_file_type
        if name is not None:
            params['name'] = name
        url = self.ep('announcements')
        return [o async for o in self.session.follow_pagination(url=url, model=RepoAnnouncement, item_key='announcements',
                                              params=params)]

    async def _upload_or_modify(self, *, url, name, file, upload_as, params, is_upload) -> dict:
        if isinstance(file, str):
            upload_as = upload_as or os.path.basename(file)
            file = open(file, mode='rb')
            must_close = True
        else:
            must_close = False
            # an existing reader
            if not upload_as:
                raise ValueError('upload_as is required')
        encoder = MultipartEncoder({'name': name, 'file': (upload_as, file, 'audio/wav')})
        if is_upload:
            meth = super().post
        else:
            meth = super().put
        try:
            data = await meth(url, data=encoder, headers={'Content-Type': encoder.content_type},
                        params=params)
        finally:
            if must_close:
                file.close()
        return data
        

    async def upload_announcement(self, name: str, file: Union[BufferedReader, str], upload_as: str = None,
                            location_id: str = None,
                            org_id: str = None) -> str:
        params = org_id and {'orgId': org_id} or None
        if location_id is None:
            url = self.ep('announcements')
        else:
            url = self.ep(f'locations/{location_id}/announcements')
        data = await self._upload_or_modify(url=url, name=name, file=file, upload_as=upload_as, params=params,
                                      is_upload=True)
        return data["id"]

        

    async def usage(self, location_id: str = None, org_id: str = None) -> RepositoryUsage:
        """
        Fetch repository usage for announcements for an organization or location

        Retrieves repository usage for announcements for an organization or location

        This API requires a full or read-only administrator auth token with a scope of
        spark-admin:telephony_config_read.

        :param location_id: Unique identifier of a location
        :type location_id: str
        :param org_id: Create an announcement in this organization.
        :type org_id: str

        """
        params = org_id and {'orgId': org_id} or None
        if location_id is None:
            url = self.ep('announcements/usage')
        else:
            url = self.ep(f'locations/{location_id}/announcements/usage')
        data = await super().get(url=url, params=params)
        return RepositoryUsage.model_validate(data)

    async def details(self, announcement_id: str, location_id: str = None, org_id: str = None) -> RepoAnnouncement:
        """
        Fetch details of a binary announcement greeting at the organization or location level

        Fetch details of a binary announcement greeting by its ID at an organization level.

        An admin can upload a file at an organization level. This file will be uploaded to the announcement repository.
        This API requires a full or read-only administrator auth token with a scope of
        spark-admin:telephony_config_read.

        :param location_id: Unique identifier of a location
        :type location_id: str
        :param announcement_id: Unique identifier of an announcement.
        :type announcement_id: str
        :param org_id: Get details of an announcement in this organization.
        :type org_id: str

        """
        params = org_id and {'orgId': org_id} or None
        if location_id is None:
            url = self.ep(f'announcements/{announcement_id}')
        else:
            url = self.ep(f'locations/{location_id}/announcements/{announcement_id}')
        data = await super().get(url=url, params=params)
        return RepoAnnouncement.model_validate(data)

    async def delete(self, announcement_id: str, location_id: str = None, org_id: str = None):
        """
        Delete an announcement greeting.

        This API requires a full administrator auth token with a scope of spark-admin:telephony_config_write.

        :param announcement_id: Unique identifier of an announcement.
        :type announcement_id: str
        :param location_id: Unique identifier of a location where announcement is being deleted.
        :type location_id: str
        :param org_id: Delete an announcement in this organization.
        :type org_id: str

        """
        params = org_id and {'orgId': org_id} or None

        if location_id is None:
            url = self.ep(f'announcements/{announcement_id}')
        else:
            url = self.ep(f'locations/{location_id}/announcements/{announcement_id}')
        await super().delete(url=url, params=params)

    async def modify(self, announcement_id: str, name: str, file: Union[BufferedReader, str],
               upload_as: str = None, location_id: str = None, org_id: str = None):
        params = org_id and {'orgId': org_id} or None
        if location_id is None:
            url = self.ep(f'announcements/{announcement_id}')
        else:
            url = self.ep(f'locations/{location_id}/announcements/{announcement_id}')
        data = await self._upload_or_modify(url=url, name=name, file=file, upload_as=upload_as, params=params,
                                      is_upload=False)
        return data["id"]

        


class AsFeatureSelector(str, Enum):
    queues = 'queues'
    huntgroups = 'huntGroups'
    auto_attendants = 'autoAttendants'





class AsForwardingApi(AsApiChild, base=''):
    """
    API for forwarding settings on call queues, hunt groups, and auto attendants
    """
    _session: AsRestSession
    _feature: AsFeatureSelector

    def __init__(self, session: AsRestSession, feature_selector: AsFeatureSelector):
        self._session = session
        self._feature = feature_selector
        super().__init__(session=session)

    def _endpoint(self, location_id: str, feature_id: str, path: str = None):
        """

        :meta private:
        :param location_id:
        :param feature_id:
        :param path:
        """
        path = path and f'/{path}' or ''
        ep = self._session.ep(path=f'telephony/config/locations/{location_id}/{self._feature.value}/'
                                   f'{feature_id}/callForwarding{path}')
        return ep

    async def settings(self, location_id: str, feature_id: str, org_id: str = None) -> CallForwarding:
        """
        Retrieve Call Forwarding settings for the designated feature including the list of call
        forwarding rules.

        The call forwarding feature allows you to direct all incoming calls based on specific criteria that you define.
        Below are the available options for configuring your call forwarding:
        1. Always forward calls to a designated number.
        2. Forward calls to a designated number based on certain criteria.
        3. Forward calls using different modes.

        Retrieving call forwarding settings for an auto attendant requires a full or read-only administrator or
        location administrator auth token with a scope of `spark-admin:telephony_config_read`.

        :param location_id: Location in which this feature exists.
        :type location_id: str
        :param feature_id: Retrieve the call forwarding settings for this entity
        :type feature_id: str
        :param org_id: Retrieve call forwarding settings from this organization.
        :type org_id: str
        :return: call forwarding settings
        :rtype: class:`CallForwarding`
        """
        params = org_id and {'orgId': org_id} or {}
        url = self._endpoint(location_id=location_id, feature_id=feature_id)
        data = await self._session.rest_get(url=url, params=params)
        result = CallForwarding.model_validate(data['callForwarding'])
        return result

    async def update(self, location_id: str, feature_id: str,
               forwarding: CallForwarding, org_id: str = None):
        """
        Update Call Forwarding Settings for a feature

        Update Call Forwarding settings for the designated feature.

        Updating call forwarding settings for a feature requires a full administrator auth token with a scope
        of spark-admin:telephony_config_write.

        :param location_id: Location in which this feature exists.
        :type location_id: str
        :param feature_id: Update call forwarding settings for this feature.
        :type feature_id: str
        :param forwarding: Forwarding settings
        :type forwarding: :class:`CallForwarding`
        :param org_id: Update feature forwarding settings from this organization.
        :type org_id: str
        """
        params = org_id and {'orgId': org_id} or {}
        url = self._endpoint(location_id=location_id, feature_id=feature_id)

        body = {'callForwarding': forwarding.update()}
        await self._session.rest_put(url=url, json=body, params=params)

    async def create_call_forwarding_rule(self, location_id: str, feature_id: str,
                                    forwarding_rule: ForwardingRuleDetails, org_id: str = None) -> str:
        """
        Create a Selective Call Forwarding Rule feature

        A selective call forwarding rule for feature to be forwarded or not
        forwarded to the designated number, based on the defined criteria.

        Note that the list of existing call forward rules is available feature's call
        forwarding settings.
        :param location_id: Location in which the call queue exists.
        :type location_id: str
        :param feature_id: Create the rule for this feature
        :type feature_id: str
        :param forwarding_rule: details of rule to be created
        :type forwarding_rule: :class:`ForwardingRuleDetails`
        :param org_id: Create the feature forwarding rule for this organization.
        :type org_id: str
        :return: forwarding rule id
        :rtype; str
        """
        url = self._endpoint(location_id=location_id, feature_id=feature_id, path='selectiveRules')
        params = org_id and {'orgId': org_id} or None
        body = forwarding_rule.model_dump_json()
        data = await self._session.rest_post(url=url, data=body, params=params)
        return data['id']

    async def call_forwarding_rule(self, location_id: str, feature_id: str, rule_id: str,
                             org_id: str = None) -> ForwardingRuleDetails:
        """
        Retrieve a Selective Call Forwarding Rule's settings for the designated Call Queue.

        A selective call forwarding rule for feature allows calls to be forwarded or not forwarded
        to the designated number, based on the defined criteria.

        Note that the list of existing call forward rules is available in the feature's call
        forwarding settings.
        :param location_id: Location in which the feature exists.
        :type location_id: stre
        :param feature_id: Retrieve setting for a rule for this feature.
        :type feature_id: str
        :param rule_id: feature rule you are retrieving settings for.
        :type rule_id: str
        :param org_id: Retrieve feature forwarding settings from this organization.
        :type org_id: str
        :return: call forwarding rule details
        :rtype: :class:`ForwardingRuleDetails`
        """
        url = self._endpoint(location_id=location_id, feature_id=feature_id, path=f'selectiveRules/{rule_id}')
        params = org_id and {'orgId': org_id} or None
        data = await self._session.rest_get(url=url, params=params)
        result = ForwardingRuleDetails.model_validate(data)
        return result

    async def update_call_forwarding_rule(self, location_id: str, feature_id: str, rule_id: str,
                                    forwarding_rule: ForwardingRuleDetails, org_id: str = None) -> str:
        """
        Update a Selective Call Forwarding Rule's settings for the designated feature.

        A selective call forwarding rule for feature allows calls to be forwarded or not forwarded
        to the designated number, based on the defined criteria.

        Note that the list of existing call forward rules is available in the feature's call
        forwarding settings.

        Updating a selective call forwarding rule's settings requires a full administrator or location
        administrator auth token with a scope of `spark-admin:telephony_config_write`.

        NOTE: The Call Forwarding Rule ID will change upon modification of the Call Forwarding Rule name.

        :param location_id: Location in which the feature exists.
        :type location_id: str
        :param feature_id: Update settings for a rule for this feature.
        :type feature_id: str
        :param rule_id: feature you are updating settings for.
        :type rule_id: str
        :param forwarding_rule: forwarding rule details for update
        :type forwarding_rule: :class:`ForwardingRuleDetails`
        :param org_id: Update feature rule settings for this organization.
        :type org_id: str
        :return: new call forwarding rule id
        :rtype: str
        """
        url = self._endpoint(location_id=location_id, feature_id=feature_id, path=f'selectiveRules/{rule_id}')
        params = org_id and {'orgId': org_id} or None
        body = forwarding_rule.model_dump_json(exclude={'id'})
        data = await self._session.rest_put(url=url, params=params, data=body)
        return data['id']

    async def delete_call_forwarding_rule(self, location_id: str, feature_id: str, rule_id: str, org_id: str = None):
        """
        Delete a Selective Call Forwarding Rule for the designated feature.

        A selective call forwarding rule for a feature allows calls to be forwarded or not forwarded
        to the designated number, based on the defined criteria.

        Note that the list of existing call forward rules is available in the feature's call
        forwarding
        settings.
        """
        url = self._endpoint(location_id=location_id, feature_id=feature_id, path=f'selectiveRules/{rule_id}')
        params = org_id and {'orgId': org_id} or None
        await self._session.rest_delete(url=url, params=params)

    async def switch_mode_for_call_forwarding(self, location_id: str, feature_id: str,
                                        org_id: str = None):
        """
        Switch Mode for Call Forwarding Settings for an entity

        Switches the current operating mode to the mode as per normal operations.

        Switching operating mode a full, or location administrator auth token with a scope
        of `spark-admin:telephony_config_write`.

        :param location_id: `Location` in which this `call queue` exists.
        :type location_id: str
        :param feature_id: Switch operating mode to normal operations for this entity.
        :type feature_id: str
        :param org_id: Switch operating mode as per normal operations for this entity from this organization.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self._endpoint(location_id=location_id, feature_id=feature_id, path='actions/switchMode/invoke')
        await self._session.rest_post(url, params=params)


class AsAutoAttendantApi(AsApiChild, base='telephony/config/autoAttendants'):
    """
    Features:  Auto Attendant

    Features: Auto Attendant support reading and writing of Webex Calling Auto Attendant settings for a specific
    organization.

    Viewing these read-only organization settings requires a full or read-only administrator auth token with a scope of
    `spark-admin:telephony_config_read`.

    Modifying these organization settings requires a full administrator auth token with a scope of
    `spark-admin:telephony_config_write`.

    A partner administrator can retrieve or change settings in a customer's organization using the optional `orgId`
    query parameter.
    """
    forwarding: AsForwardingApi

    def __init__(self, session: AsRestSession):
        super().__init__(session=session)
        self.forwarding = AsForwardingApi(session=session, feature_selector=AsFeatureSelector.auto_attendants)

    def _endpoint(self, *, location_id: str = None, auto_attendant_id: str = None, path: str = None) -> str:
        """
        auto attendant specific feature endpoint like /v1/telephony/config/locations/{locationId}/autoAttendants/{
        auto_attendant_id}

        :meta private:
        :param location_id: Unique identifier for the location.
        :type location_id: str
        :param auto_attendant_id: auto attendant id
        :type auto_attendant_id: str
        :return: full endpoint
        :rtype: str
        """
        if location_id is None:
            return self.session.ep('telephony/config/autoAttendants')
        else:
            ep = self.session.ep(f'telephony/config/locations/{location_id}/autoAttendants')
            if auto_attendant_id:
                ep = f'{ep}/{auto_attendant_id}'
            if path:
                ep = f'{ep}/{path}'
            return ep

    def list_gen(self, org_id: str = None, location_id: str = None, name: str = None,
             phone_number: str = None, **params) -> AsyncGenerator[AutoAttendant, None, None]:
        """
        Read the List of Auto Attendants

        List all Auto Attendants for the organization.

        Auto attendants play customized prompts and provide callers with menu options for routing their calls through
        your system.

        Retrieving this list requires a full or read-only administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param org_id: List auto attendants for this organization.
        :type org_id: str
        :param location_id: Return the list of auto attendants for this location.
        :type location_id: str
        :param name: Only return auto attendants with the matching name.
        :type name: str
        :param phone_number: Only return auto attendants with the matching phone number.
        :type phone_number: str
        :return: yields :class:`AutoAttendant` objects
        """
        params.update((to_camel(k), v)
                      for i, (k, v) in enumerate(locals().items())
                      if i and v is not None and k != 'params')
        url = self._endpoint()
        # noinspection PyTypeChecker
        return self.session.follow_pagination(url=url, model=AutoAttendant, params=params, item_key='autoAttendants')

    async def list(self, org_id: str = None, location_id: str = None, name: str = None,
             phone_number: str = None, **params) -> List[AutoAttendant]:
        """
        Read the List of Auto Attendants

        List all Auto Attendants for the organization.

        Auto attendants play customized prompts and provide callers with menu options for routing their calls through
        your system.

        Retrieving this list requires a full or read-only administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param org_id: List auto attendants for this organization.
        :type org_id: str
        :param location_id: Return the list of auto attendants for this location.
        :type location_id: str
        :param name: Only return auto attendants with the matching name.
        :type name: str
        :param phone_number: Only return auto attendants with the matching phone number.
        :type phone_number: str
        :return: yields :class:`AutoAttendant` objects
        """
        params.update((to_camel(k), v)
                      for i, (k, v) in enumerate(locals().items())
                      if i and v is not None and k != 'params')
        url = self._endpoint()
        # noinspection PyTypeChecker
        return [o async for o in self.session.follow_pagination(url=url, model=AutoAttendant, params=params, item_key='autoAttendants')]

    async def by_name(self, name: str, location_id: str = None, org_id: str = None) -> Optional[AutoAttendant]:
        """
        Get auto attendant info by name

        :param location_id:
        :param name:
        :param org_id:
        :return:
        """
        return next((hg for hg in await self.list(name=name, location_id=location_id, org_id=org_id)
                     if hg.name == name), None)

    async def details(self, location_id: str, auto_attendant_id: str, org_id: str = None) -> AutoAttendant:
        """
        Get Details for an Auto Attendant

        Retrieve an Auto Attendant details.

        Auto attendants play customized prompts and provide callers with menu options for routing their calls through
        your system.

        Retrieving an auto attendant details requires a full or read-only administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param location_id: Retrieve an auto attendant details in this location.
        :type location_id: str
        :param auto_attendant_id: Retrieve the auto attendant with the matching ID.
        :type auto_attendant_id: str
        :param org_id: Retrieve auto attendant details from this organization.
        :type org_id: str
        :return: auto attendant details
        :rtype: :class:`AutoAttendant`
        """
        url = self._endpoint(location_id=location_id, auto_attendant_id=auto_attendant_id)
        params = org_id and {'orgId': org_id} or None
        return AutoAttendant.model_validate(await self.get(url, params=params))

    async def create(self, location_id: str, settings: AutoAttendant, org_id: str = None) -> str:
        """
        Create an Auto Attendant

        Create new Auto Attendant for the given location.

        Auto attendants play customized prompts and provide callers with menu options for routing their calls through
        your system.

        Creating an auto attendant requires a full administrator or location administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        The fields
        `directLineCallerIdName.selection`, `directLineCallerIdName.customName`, and `dialByName` are not supported in
        Webex for Government (FedRAMP). Instead, administrators must use the `firstName` and `lastName` fields to
        configure and view both caller ID and dial-by-name settings


        :param location_id: Create the auto attendant for this location.
        :type location_id: str
        :param settings: auto attendant settings for new auto attendant
        :type settings: :class:`AutoAttendant`
        :param org_id: Create the auto attendant for this organization.
        :type org_id: str
        :return: ID of the newly created auto attendant.
        :rtype: str
        """
        data = settings.create_or_update()
        url = self._endpoint(location_id=location_id)
        params = org_id and {'orgId': org_id} or None
        data = await self.post(url, data=data, params=params)
        return data['id']

    async def update(self, location_id: str, auto_attendant_id: str, settings: AutoAttendant, org_id: str = None):
        """
        Update an Auto Attendant

        Update the designated Auto Attendant.

        Auto attendants play customized prompts and provide callers with menu options for routing their calls through
        your system.

        Updating an auto attendant requires a full administrator auth token with a scope
        of spark-admin:telephony_config_write.

        :param location_id: Location in which this auto attendant exists.
        :type location_id: str
        :param auto_attendant_id: Update an auto attendant with the matching ID.
        :type auto_attendant_id: str
        :param settings: auto attendant settings for the update
        :type settings: :class:`AutoAttendant`
        :param org_id: Create the auto attendant for this organization.
        :type org_id: str
        """
        data = settings.create_or_update()
        url = self._endpoint(location_id=location_id, auto_attendant_id=auto_attendant_id)
        params = org_id and {'orgId': org_id} or None
        await self.put(url, data=data, params=params)

    async def delete_auto_attendant(self, location_id: str, auto_attendant_id: str, org_id: str = None):
        """
        elete the designated Auto Attendant.

        Auto attendants play customized prompts and provide callers with menu options for routing their calls through
        your system.

        Deleting an auto attendant requires a full administrator auth token with a scope
        of spark-admin:telephony_config_write.

        :param location_id: Location from which to delete an auto attendant.
        :type location_id: str
        :param auto_attendant_id: Delete the auto attendant with the matching ID.
        :type auto_attendant_id: str
        :param org_id: Delete the auto attendant from this organization.
        :type org_id: str
        """
        url = self._endpoint(location_id=location_id, auto_attendant_id=auto_attendant_id)
        params = org_id and {'orgId': org_id} or None
        await self.delete(url, params=params)

    def primary_available_phone_numbers_gen(self, location_id: str, phone_number: List[str] = None,
                                        org_id: str = None,
                                        **params) -> AsyncGenerator[AvailableNumber, None, None]:
        """
        Get Auto Attendant Primary Available Phone Numbers

        List service and standard numbers that are available to be assigned as the auto attendant's primary phone
        number.
        These numbers are associated with the location specified in the request URL, can be active or inactive, and are
        unassigned.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full, read-only or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param location_id: Return the list of phone numbers for this location within the given organization. The
            maximum length is 36.
        :type location_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableNumber` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        url = self._endpoint(location_id=location_id, path='availableNumbers')
        return self.session.follow_pagination(url=url, model=AvailableNumber, item_key='phoneNumbers', params=params)

    async def primary_available_phone_numbers(self, location_id: str, phone_number: List[str] = None,
                                        org_id: str = None,
                                        **params) -> List[AvailableNumber]:
        """
        Get Auto Attendant Primary Available Phone Numbers

        List service and standard numbers that are available to be assigned as the auto attendant's primary phone
        number.
        These numbers are associated with the location specified in the request URL, can be active or inactive, and are
        unassigned.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full, read-only or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param location_id: Return the list of phone numbers for this location within the given organization. The
            maximum length is 36.
        :type location_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableNumber` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        url = self._endpoint(location_id=location_id, path='availableNumbers')
        return [o async for o in self.session.follow_pagination(url=url, model=AvailableNumber, item_key='phoneNumbers', params=params)]

    def alternate_available_phone_numbers_gen(self, location_id: str, phone_number: List[str] = None,
                                          org_id: str = None,
                                          **params) -> AsyncGenerator[AvailableNumber, None, None]:
        """
        Get Auto Attendant Alternate Available Phone Numbers

        List service and standard numbers that are available to be assigned as the auto attendant's alternate number.
        These numbers are associated with the location specified in the request URL, can be active or inactive, and are
        unassigned.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full, read-only or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param location_id: Return the list of phone numbers for this location within the given organization. The
            maximum length is 36.
        :type location_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableNumber` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        url = self._endpoint(location_id=location_id, path='alternate/availableNumbers')
        return self.session.follow_pagination(url=url, model=AvailableNumber, item_key='phoneNumbers', params=params)

    async def alternate_available_phone_numbers(self, location_id: str, phone_number: List[str] = None,
                                          org_id: str = None,
                                          **params) -> List[AvailableNumber]:
        """
        Get Auto Attendant Alternate Available Phone Numbers

        List service and standard numbers that are available to be assigned as the auto attendant's alternate number.
        These numbers are associated with the location specified in the request URL, can be active or inactive, and are
        unassigned.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full, read-only or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param location_id: Return the list of phone numbers for this location within the given organization. The
            maximum length is 36.
        :type location_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableNumber` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        url = self._endpoint(location_id=location_id, path='alternate/availableNumbers')
        return [o async for o in self.session.follow_pagination(url=url, model=AvailableNumber, item_key='phoneNumbers', params=params)]

    def call_forward_available_phone_numbers_gen(self, location_id: str, phone_number: List[str] = None,
                                             owner_name: str = None, extension: str = None,
                                             org_id: str = None,
                                             **params) -> AsyncGenerator[AvailableNumber, None, None]:
        """
        Get Auto Attendant Call Forward Available Phone Numbers

        List service and standard numbers that are available to be assigned as the auto attendant's call forward
        number.
        These numbers are associated with the location specified in the request URL, can be active or inactive, and are
        assigned to an owning entity.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full, read-only or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param location_id: Return the list of phone numbers for this location within the given organization. The
            maximum length is 36.
        :type location_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param owner_name: Return the list of phone numbers that are owned by the given `ownerName`. Maximum length is
            255.
        :type owner_name: str
        :param extension: Returns the list of PSTN phone numbers with the given `extension`.
        :type extension: str
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableNumber` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        if owner_name is not None:
            params['ownerName'] = owner_name
        if extension is not None:
            params['extension'] = extension
        url = self._endpoint(location_id=location_id, path='callForwarding/availableNumbers')
        return self.session.follow_pagination(url=url, model=AvailableNumber, item_key='phoneNumbers', params=params)

    async def call_forward_available_phone_numbers(self, location_id: str, phone_number: List[str] = None,
                                             owner_name: str = None, extension: str = None,
                                             org_id: str = None,
                                             **params) -> List[AvailableNumber]:
        """
        Get Auto Attendant Call Forward Available Phone Numbers

        List service and standard numbers that are available to be assigned as the auto attendant's call forward
        number.
        These numbers are associated with the location specified in the request URL, can be active or inactive, and are
        assigned to an owning entity.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full, read-only or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param location_id: Return the list of phone numbers for this location within the given organization. The
            maximum length is 36.
        :type location_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param owner_name: Return the list of phone numbers that are owned by the given `ownerName`. Maximum length is
            255.
        :type owner_name: str
        :param extension: Returns the list of PSTN phone numbers with the given `extension`.
        :type extension: str
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableNumber` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        if owner_name is not None:
            params['ownerName'] = owner_name
        if extension is not None:
            params['extension'] = extension
        url = self._endpoint(location_id=location_id, path='callForwarding/availableNumbers')
        return [o async for o in self.session.follow_pagination(url=url, model=AvailableNumber, item_key='phoneNumbers', params=params)]

    async def list_announcement_files(self, location_id: str, auto_attendant_id: str,
                                org_id: str = None) -> List[AnnAudioFile]:
        """
        Read the List of Auto Attendant Announcement Files

        List file info for all auto attendant announcement files associated with this auto attendant.

        Auto attendant announcement files contain messages and music that callers hear while waiting in the queue. A
        auto attendant can be configured to play whatever subset of these announcement files is desired.

        Retrieving this list of files requires a full or read-only administrator or location administrator auth token
        with a scope of `spark-admin:telephony_config_read`.

        Note that uploading of announcement files via API is not currently supported, but is available via Webex
        Control Hub.

        :param location_id: Location in which this auto attendant exists.
        :type location_id: str
        :param auto_attendant_id: Retrieve announcement files for the auto attendant with this identifier.
        :type auto_attendant_id: str
        :param org_id: Retrieve announcement files for an auto attendant from this organization.
        :type org_id: str
        :rtype: list[AnnAudioFile]
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.session.ep(
            f'telephony/config/locations/{location_id}/autoAttendants/{auto_attendant_id}/announcements')
        data = await super().get(url, params=params)
        r = TypeAdapter(list[AnnAudioFile]).validate_python(data['announcements'])
        return r

    async def delete_announcement_file(self, location_id: str, auto_attendant_id: str, file_name: str,
                                 org_id: str = None):
        """
        Delete an Auto Attendant Announcement File

        Delete an announcement file for the designated auto attendant.

        Auto Attendant announcement files contain messages and music that callers hear while waiting in the queue. A
        auto attendant can be configured to play whatever subset of these announcement files is desired.

        Deleting an announcement file for a auto attendant requires a full administrator or location administrator auth
        token with a scope of `spark-admin:telephony_config_write`.

        :param location_id: Delete an announcement for a auto attendant in this location.
        :type location_id: str
        :param auto_attendant_id: Delete an announcement for the auto attendant with this identifier.
        :type auto_attendant_id: str
        :type file_name: str
        :param org_id: Delete auto attendant announcement from this organization.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.session.ep(
            f'telephony/config/locations/{location_id}/autoAttendants/{auto_attendant_id}/announcements/{file_name}')
        await super().delete(url, params=params)


class AsCallParkApi(AsApiChild, base='telephony/config/callParks'):
    """
    Features:  Call Park

    Features: Call Park supports reading and writing of Webex Calling Call Park settings for a specific organization.

    Viewing these read-only organization settings requires a full or read-only administrator auth token with a scope of
    `spark-admin:telephony_config_read`.

    Modifying these organization settings requires a full administrator auth token with a scope of
    `spark-admin:telephony_config_write`.

    A partner administrator can retrieve or change settings in a customer's organization using the optional `orgId`
    query parameter.
    """

    def _endpoint(self, *, location_id: str, callpark_id: str = None, path: str = None) -> str:
        """
        call park specific feature endpoint like /v1/telephony/config/locations/{locationId}/callParks/{callpark_id}

        :meta private:
        :param location_id: Unique identifier for the location.
        :type location_id: str
        :param callpark_id: call park id
        :type callpark_id: str
        :param path: addtl. path
        :type path: str
        :return: full endpoint
        :rtype: str
        """
        call_park_id = callpark_id and f'/{callpark_id}' or ''
        path = path and f'/{path}' or ''
        ep = self.session.ep(f'telephony/config/locations/{location_id}/callParks{call_park_id}{path}')
        return ep

    def list_gen(self, location_id: str, order: str = None, name: str = None,
             org_id: str = None, **params) -> AsyncGenerator[CallPark, None, None]:
        """
        Read the List of Call Parks

        List all Call Parks for the organization.

        Call Park allows call recipients to place a call on hold so that it can be retrieved from another device.

        Retrieving this list requires a full or read-only administrator or location administrator auth token with a
        scope of `spark-admin:telephony_config_read`.

        NOTE: The Call Park ID will change upon modification of the Call Park name.

        :param location_id: Return the list of call parks for this location.
        :type location_id: str
        :param order: Sort the list of call parks by name, either ASC or DSC. Default is ASC.
        :type order: str
        :param name: Return the list of call parks that contains the given name. The maximum length is 80.
        :type name: str
        :param org_id: List call park extensions for this organization.
        :type org_id: str
        :return: yields :class:`CallPark` objects
        """
        if org_id is not None:
            params['orgId'] = org_id
        if order is not None:
            params['order'] = order
        if name is not None:
            params['name'] = name
        url = self._endpoint(location_id=location_id)
        # noinspection PyTypeChecker
        return self.session.follow_pagination(url=url, model=CallPark, params=params, item_key='callParks')

    async def list(self, location_id: str, order: str = None, name: str = None,
             org_id: str = None, **params) -> List[CallPark]:
        """
        Read the List of Call Parks

        List all Call Parks for the organization.

        Call Park allows call recipients to place a call on hold so that it can be retrieved from another device.

        Retrieving this list requires a full or read-only administrator or location administrator auth token with a
        scope of `spark-admin:telephony_config_read`.

        NOTE: The Call Park ID will change upon modification of the Call Park name.

        :param location_id: Return the list of call parks for this location.
        :type location_id: str
        :param order: Sort the list of call parks by name, either ASC or DSC. Default is ASC.
        :type order: str
        :param name: Return the list of call parks that contains the given name. The maximum length is 80.
        :type name: str
        :param org_id: List call park extensions for this organization.
        :type org_id: str
        :return: yields :class:`CallPark` objects
        """
        if org_id is not None:
            params['orgId'] = org_id
        if order is not None:
            params['order'] = order
        if name is not None:
            params['name'] = name
        url = self._endpoint(location_id=location_id)
        # noinspection PyTypeChecker
        return [o async for o in self.session.follow_pagination(url=url, model=CallPark, params=params, item_key='callParks')]

    async def create(self, location_id: str, settings: CallPark, org_id: str = None) -> str:
        """
        Create a Call Park Extension

        Create new Call Park Extensions for the given location.

        Call Park Extension enables a call recipient to park a call to an extension, so someone else within the same
        Organization can retrieve the parked call by dialing that extension. Call Park Extensions can be added as
        monitored lines by users' Cisco phones, so users can park and retrieve calls by pressing the associated phone
        line key.

        Creating a call park extension requires a full administrator or location administrator auth token with a scope
        of `spark-admin:telephony_config_write`.

        :param location_id: Create the call park for this location.
        :type location_id: str
        :param settings: settings for new call park
        :type settings: :class:`CallPark`
        :param org_id: Create the call park for this organization.
        :return: ID of the newly created call park.
        :rtype: str
        """
        params = org_id and {'orgId': org_id} or None
        url = self._endpoint(location_id=location_id)
        body = settings.create_or_update()
        data = await self.post(url, data=body, params=params)
        return data['id']

    async def delete_callpark(self, location_id: str, callpark_id: str, org_id: str = None):
        """
        Delete a Call Park

        Delete the designated Call Park.

        Call Park allows call recipients to place a call on hold so that it can be retrieved from another device.

        Deleting a call park requires a full administrator auth token with a scope
        of spark-admin:telephony_config_write.

        NOTE: The Call Park ID will change upon modification of the Call Park name.

        :param location_id: Location from which to delete a call park.
        :type location_id: str
        :param callpark_id: Delete the call park with the matching ID.
        :type callpark_id: str
        :param org_id: Delete the call park from this organization.
        :type org_id: str
        """
        url = self._endpoint(location_id=location_id, callpark_id=callpark_id)
        params = org_id and {'orgId': org_id} or None
        await self.delete(url, params=params)

    async def details(self, location_id: str, callpark_id: str, org_id: str = None) -> CallPark:
        """
        Get Details for a Call Park

        Retrieve Call Park details.

        Call Park allows call recipients to place a call on hold so that it can be retrieved from another device.

        Retrieving call park details requires a full or read-only administrator auth token with a scope
        of spark-admin:telephony_config_read.

        NOTE: The Call Park ID will change upon modification of the Call Park name.

        :param location_id: Retrieve settings for a call park in this location.
        :type location_id: str
        :param callpark_id: Retrieve settings for a call park with the matching ID.
        :type callpark_id: str
        :param org_id: Retrieve call park settings from this organization.
        :type org_id: str
        :return: call park info
        :rtype: :class:`CallPark`
        """
        url = self._endpoint(location_id=location_id, callpark_id=callpark_id)
        params = org_id and {'orgId': org_id} or None
        return CallPark.model_validate(await self.get(url, params=params))

    async def update(self, location_id: str, callpark_id: str, settings: CallPark, org_id: str = None) -> str:
        """
        Update a Call Park

        Update the designated Call Park.

        Call Park allows call recipients to place a call on hold so that it can be retrieved from another device.

        Updating a call park requires a full administrator auth token with a scope
        of spark-admin:telephony_config_write.

        NOTE: The Call Park ID will change upon modification of the Call Park name.

        :param location_id: RLocation in which this call park exists.
        :type location_id: str
        :param callpark_id: Update settings for a call park with the matching ID.
        :type callpark_id: str
        :param settings: updates
        :type settings: :class:`CallPark`
        :param org_id: Update call park settings from this organization.
        :type org_id: str
        """
        params = org_id and {'orgId': org_id} or None
        url = self._endpoint(location_id=location_id, callpark_id=callpark_id)
        body = settings.create_or_update()
        data = await self.put(url, data=body, params=params)
        return data['id']

    def available_agents_gen(self, location_id: str, call_park_name: str = None, name: str = None, phone_number: str = None,
                         order: str = None, org_id: str = None, **params) -> AsyncGenerator[PersonPlaceAgent, None, None]:
        """
        Get available agents from Call Parks

        Retrieve available agents from call parks for a given location.

        Call Park allows call recipients to place a call on hold so that it can be retrieved from another device.

        Retrieving available agents from call parks requires a full or read-only administrator or location
        administrator auth token with a scope of `spark-admin:telephony_config_read`.

        :param location_id: Return the available agents for this location.
        :type location_id: str
        :param call_park_name: Only return available agents from call parks with the matching name.
        :type call_park_name: str
        :param name: Only return available agents with the matching name.
        :type name: str
        :param phone_number: Only return available agents with the matching primary number.
        :type phone_number: str
        :param order: Order the available agents according to the designated fields. Up to three vertical bar (|)
            separated sort order fields may be specified. Available sort fields: fname, lname, number and extension.
            The maximum supported sort order value is 3.
        :type order: str
        :param org_id: Return the available agents for this organization.
        :type org_id: str
        :return: yields :class:`PersonPlaceCallPark` objects
        """
        if org_id is not None:
            params['orgId'] = org_id
        if call_park_name is not None:
            params['callParkName'] = call_park_name
        if name is not None:
            params['name'] = name
        if phone_number is not None:
            params['phoneNumber'] = phone_number
        if order is not None:
            params['order'] = order
        url = self._endpoint(location_id=location_id, path='availableUsers')
        # noinspection PyTypeChecker
        return self.session.follow_pagination(url=url, model=PersonPlaceAgent, params=params, item_key='agents')

    async def available_agents(self, location_id: str, call_park_name: str = None, name: str = None, phone_number: str = None,
                         order: str = None, org_id: str = None, **params) -> List[PersonPlaceAgent]:
        """
        Get available agents from Call Parks

        Retrieve available agents from call parks for a given location.

        Call Park allows call recipients to place a call on hold so that it can be retrieved from another device.

        Retrieving available agents from call parks requires a full or read-only administrator or location
        administrator auth token with a scope of `spark-admin:telephony_config_read`.

        :param location_id: Return the available agents for this location.
        :type location_id: str
        :param call_park_name: Only return available agents from call parks with the matching name.
        :type call_park_name: str
        :param name: Only return available agents with the matching name.
        :type name: str
        :param phone_number: Only return available agents with the matching primary number.
        :type phone_number: str
        :param order: Order the available agents according to the designated fields. Up to three vertical bar (|)
            separated sort order fields may be specified. Available sort fields: fname, lname, number and extension.
            The maximum supported sort order value is 3.
        :type order: str
        :param org_id: Return the available agents for this organization.
        :type org_id: str
        :return: yields :class:`PersonPlaceCallPark` objects
        """
        if org_id is not None:
            params['orgId'] = org_id
        if call_park_name is not None:
            params['callParkName'] = call_park_name
        if name is not None:
            params['name'] = name
        if phone_number is not None:
            params['phoneNumber'] = phone_number
        if order is not None:
            params['order'] = order
        url = self._endpoint(location_id=location_id, path='availableUsers')
        # noinspection PyTypeChecker
        return [o async for o in self.session.follow_pagination(url=url, model=PersonPlaceAgent, params=params, item_key='agents')]

    def available_recalls_gen(self, location_id: str, name: str = None, order: str = None,
                          org_id: str = None, **params) -> AsyncGenerator[AvailableRecallHuntGroup, None, None]:
        """
        Get available recall hunt groups from Call Parks

        Retrieve available recall hunt groups from call parks for a given location.

        Call Park allows call recipients to place a call on hold so that it can be retrieved from another device.

        Retrieving available recall hunt groups from call parks requires a full or read-only administrator auth
        token with a scope of spark-admin:telephony_config_read.

        :param location_id: Return the available recall hunt groups for this location.
        :type location_id: str
        :param name: Only return available recall hunt groups with the matching name.
        :type name: str
        :param order: Order the available recall hunt groups according to the designated fields. Available sort
            fields: lname.
        :param order: str
        :param org_id: Return the available recall hunt groups for this organization.
        :type org_id: str
        :return: yields :class:`AvailableRecallHuntGroup` objects
        """
        if org_id is not None:
            params['orgId'] = org_id
        if name is not None:
            params['name'] = name
        if order is not None:
            params['order'] = order
        url = self._endpoint(location_id=location_id, path='availableRecallHuntGroups')
        # noinspection PyTypeChecker
        return self.session.follow_pagination(url=url, model=AvailableRecallHuntGroup,
                                              params=params, item_key='huntGroups')

    async def available_recalls(self, location_id: str, name: str = None, order: str = None,
                          org_id: str = None, **params) -> List[AvailableRecallHuntGroup]:
        """
        Get available recall hunt groups from Call Parks

        Retrieve available recall hunt groups from call parks for a given location.

        Call Park allows call recipients to place a call on hold so that it can be retrieved from another device.

        Retrieving available recall hunt groups from call parks requires a full or read-only administrator auth
        token with a scope of spark-admin:telephony_config_read.

        :param location_id: Return the available recall hunt groups for this location.
        :type location_id: str
        :param name: Only return available recall hunt groups with the matching name.
        :type name: str
        :param order: Order the available recall hunt groups according to the designated fields. Available sort
            fields: lname.
        :param order: str
        :param org_id: Return the available recall hunt groups for this organization.
        :type org_id: str
        :return: yields :class:`AvailableRecallHuntGroup` objects
        """
        if org_id is not None:
            params['orgId'] = org_id
        if name is not None:
            params['name'] = name
        if order is not None:
            params['order'] = order
        url = self._endpoint(location_id=location_id, path='availableRecallHuntGroups')
        # noinspection PyTypeChecker
        return [o async for o in self.session.follow_pagination(url=url, model=AvailableRecallHuntGroup,
                                              params=params, item_key='huntGroups')]

    async def call_park_settings(self, location_id: str, org_id: str = None) -> LocationCallParkSettings:
        """
        Get Call Park Settings

        Retrieve Call Park Settings from call parks for a given location.

        Call Park allows call recipients to place a call on hold so that it can be retrieved from another device.

        Retrieving settings from call parks requires a full or read-only administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param location_id: Return the call park settings for this location.
        :type location_id: str
        :param org_id: Return the call park settings for this organization.
        :type org_id: str
        :return:
        """
        params = org_id and {'orgId': org_id} or None
        url = self._endpoint(location_id=location_id, path='settings')
        return LocationCallParkSettings.model_validate(await self.get(url, params=params))

    async def update_call_park_settings(self, location_id: str, settings: LocationCallParkSettings, org_id: str = None):
        """
        Update Call Park settings

        Update Call Park settings for the designated location.

        Call Park allows call recipients to place a call on hold so that it can be retrieved from another device.

        Updating call park settings requires a full administrator auth token with a scope
        of spark-admin:telephony_config_write.

        :param location_id: Location for which call park settings will be updated.
        :type location_id: str
        :param settings: update settings
        :type settings: :class:`LocationCallParkSettings`
        :param org_id: Update call park settings from this organization.
        :type org_id: str
        """
        params = org_id and {'orgId': org_id} or None
        url = self._endpoint(location_id=location_id, path='settings')
        body = settings.update()
        await self.put(url, params=params, data=body)


class AsCallPickupApi(AsApiChild, base='telephony/config/callPickups'):
    """
    Call Pickup API
    """

    def _endpoint(self, *, location_id: str, pickup_id: str = None, path: str = None) -> str:
        """
        call park specific feature endpoint like /v1/telephony/config/locations/{locationId}/callPickups/{pickup_id}

        :meta private:
        :param location_id: Unique identifier for the location.
        :type location_id: str
        :param pickup_id: call pickup id
        :type pickup_id: str
        :param path: addtl. path
        :type path: str
        :return: full endpoint
        :rtype: str
        """
        pickup_id = pickup_id and f'/{pickup_id}' or ''
        path = path and f'/{path}' or ''
        ep = self.session.ep(f'telephony/config/locations/{location_id}/callPickups{pickup_id}{path}')
        return ep

    def list_gen(self, location_id: str, order: Literal['ASC', 'DSC'] = None, name: str = None,
             org_id: str = None, **params) -> AsyncGenerator[CallPickup, None, None]:
        """
        Read the List of Call Pickups

        List all Call Pickups for the organization.

        Call Pickup enables a user(agent) to answer any ringing line within their pickup group.

        Retrieving this list requires a full, user, or read-only administrator auth token with a scope
        of spark-admin:telephony_config_read.

        NOTE: The Call Pickup ID will change upon modification of the Call Pickup name.

        :param location_id: Return the list of call pickups for this location.
        :type location_id: str
        :param order: Sort the list of call pickups by name, either ASC or DSC. Default is ASC.
        :type order: str
        :param name: Return the list of call pickups that contains the given name. The maximum length is 80.
        :type name: str
        :param org_id: List call pickups for this organization.
        :type org_id: str
        :return: yields :class:`CallPickup` objects
        """
        if org_id is not None:
            params['orgId'] = org_id
        if order is not None:
            params['order'] = order
        if name is not None:
            params['name'] = name
        url = self._endpoint(location_id=location_id)
        # noinspection PyTypeChecker
        return self.session.follow_pagination(url=url, model=CallPickup, params=params, item_key='callPickups')

    async def list(self, location_id: str, order: Literal['ASC', 'DSC'] = None, name: str = None,
             org_id: str = None, **params) -> List[CallPickup]:
        """
        Read the List of Call Pickups

        List all Call Pickups for the organization.

        Call Pickup enables a user(agent) to answer any ringing line within their pickup group.

        Retrieving this list requires a full, user, or read-only administrator auth token with a scope
        of spark-admin:telephony_config_read.

        NOTE: The Call Pickup ID will change upon modification of the Call Pickup name.

        :param location_id: Return the list of call pickups for this location.
        :type location_id: str
        :param order: Sort the list of call pickups by name, either ASC or DSC. Default is ASC.
        :type order: str
        :param name: Return the list of call pickups that contains the given name. The maximum length is 80.
        :type name: str
        :param org_id: List call pickups for this organization.
        :type org_id: str
        :return: yields :class:`CallPickup` objects
        """
        if org_id is not None:
            params['orgId'] = org_id
        if order is not None:
            params['order'] = order
        if name is not None:
            params['name'] = name
        url = self._endpoint(location_id=location_id)
        # noinspection PyTypeChecker
        return [o async for o in self.session.follow_pagination(url=url, model=CallPickup, params=params, item_key='callPickups')]

    async def create(self, location_id: str, settings: CallPickup, org_id: str = None) -> str:
        """
        Create a Call Pickup

        Create new Call Pickups for the given location.

        Call Pickup enables a user(agent) to answer any ringing line within their pickup group.

        Creating a call pickup requires a full or user administrator auth token with a scope
        of spark-admin:telephony_config_write.

        NOTE: The Call Pickup ID will change upon modification of the Call Pickup name.

        :param location_id: Create the call pickup for this location.
        :type location_id: str
        :param settings: settings for new call pickup
        :type settings: :class:`CallPickup`
        :param org_id: Create the call pickup for this organization.
        :return: ID of the newly created call pickup.
        :rtype: str
        """
        params = org_id and {'orgId': org_id} or None
        url = self._endpoint(location_id=location_id)
        body = settings.create_or_update()
        data = await self.post(url, json=body, params=params)
        return data['id']

    async def delete_pickup(self, location_id: str, pickup_id: str, org_id: str = None):
        """
        Delete a Call Pickup

        Delete the designated Call Pickup.

        Call Pickup enables a user(agent) to answer any ringing line within their pickup group.

        Deleting a call pickup requires a full or user administrator auth token with a scope
        of spark-admin:telephony_config_write.

        NOTE: The Call Pickup ID will change upon modification of the Call Pickup name.

        :param location_id: Location from which to delete a call pickup.
        :type location_id: str
        :param pickup_id: Delete the call pickup with the matching ID.
        :type pickup_id: str
        :param org_id: Delete the call pickup from this organization.
        :type org_id: str
        """
        url = self._endpoint(location_id=location_id, pickup_id=pickup_id)
        params = org_id and {'orgId': org_id} or None
        await self.delete(url, params=params)

    async def details(self, location_id: str, pickup_id: str, org_id: str = None) -> CallPickup:
        """
        Get Details for a Call Pickup

        Retrieve Call Pickup details.

        Call Pickup enables a user(agent) to answer any ringing line within their pickup group.

        Retrieving call pickup details requires a full, user, or read-only administrator auth token with a scope
        of spark-admin:telephony_config_read.

        NOTE: The Call Pickup ID will change upon modification of the Call Pickup name.

        :param location_id: Retrieve settings for a call pickup in this location.
        :type location_id: str
        :param pickup_id: Retrieve settings for a call pickup with the matching ID.
        :type pickup_id: str
        :param org_id: Retrieve call pickup settings from this organization.
        :type org_id: str
        :return: call pickup info
        :rtype: :class:`CallPickup`
        """
        url = self._endpoint(location_id=location_id, pickup_id=pickup_id)
        params = org_id and {'orgId': org_id} or None
        return CallPickup.model_validate(await self.get(url, params=params))

    async def update(self, location_id: str, pickup_id: str, settings: CallPickup, org_id: str = None) -> str:
        """
        Update a Call Pickup

        Update the designated Call Pickup.

        Call Pickup enables a user(agent) to answer any ringing line within their pickup group.

        Updating a call pickup requires a full or user administrator auth token with a scope
        of spark-admin:telephony_config_write.

        NOTE: The Call Pickup ID will change upon modification of the Call Pickup name.

        :param location_id: Location in which this call pickup exists.
        :type location_id: str
        :param pickup_id: Update settings for a call pickup with the matching ID.
        :type pickup_id: str
        :param settings: updates
        :type settings: :class:`CallPickup`
        :param org_id: Update call pickup settings from this organization.
        :type org_id: str
        """
        params = org_id and {'orgId': org_id} or None
        url = self._endpoint(location_id=location_id, pickup_id=pickup_id)
        body = settings.create_or_update()
        data = await self.put(url, json=body, params=params)
        return data['id']

    # noinspection DuplicatedCode

    def available_agents_gen(self, location_id: str, call_pickup_name: str = None, name: str = None,
                         phone_number: str = None, order: str = None,
                         org_id: str = None, **params) -> AsyncGenerator[PersonPlaceAgent, None, None]:
        """
        Get available agents from Call Pickups

        Retrieve available agents from call pickups for a given location.

        Call Pickup enables a user(agent) to answer any ringing line within their pickup group.

        Retrieving available agents from call pickups requires a full, user, or read-only administrator auth token
        with a scope of spark-admin:telephony_config_read.

        :param location_id: Return the available agents for this location.
        :type location_id: str
        :param call_pickup_name: Only return available agents from call pickups with the matching name.
        :type call_pickup_name: str
        :param name: Only return available agents with the matching name.
        :type name: str
        :param phone_number: Only return available agents with the matching primary number.
        :type phone_number: str
        :param order: Order the available agents according to the designated fields. Up to three vertical bar (|)
            separated sort order fields may be specified. Available sort fields: fname, lname, number and extension.
            The maximum supported sort order value is 3.
        :type order: str
        :param org_id: Return the available agents for this organization.
        :type org_id: str
        :return: yields :class:`PersonPlaceCallPark` objects
        """
        if org_id is not None:
            params['orgId'] = org_id
        if call_pickup_name is not None:
            params['callPickupName'] = call_pickup_name
        if name is not None:
            params['name'] = name
        if phone_number is not None:
            params['phoneNumber'] = phone_number
        if order is not None:
            params['order'] = order
        url = self._endpoint(location_id=location_id, path='availableUsers')
        # noinspection PyTypeChecker
        return self.session.follow_pagination(url=url, model=PersonPlaceAgent, params=params, item_key='agents')

    async def available_agents(self, location_id: str, call_pickup_name: str = None, name: str = None,
                         phone_number: str = None, order: str = None,
                         org_id: str = None, **params) -> List[PersonPlaceAgent]:
        """
        Get available agents from Call Pickups

        Retrieve available agents from call pickups for a given location.

        Call Pickup enables a user(agent) to answer any ringing line within their pickup group.

        Retrieving available agents from call pickups requires a full, user, or read-only administrator auth token
        with a scope of spark-admin:telephony_config_read.

        :param location_id: Return the available agents for this location.
        :type location_id: str
        :param call_pickup_name: Only return available agents from call pickups with the matching name.
        :type call_pickup_name: str
        :param name: Only return available agents with the matching name.
        :type name: str
        :param phone_number: Only return available agents with the matching primary number.
        :type phone_number: str
        :param order: Order the available agents according to the designated fields. Up to three vertical bar (|)
            separated sort order fields may be specified. Available sort fields: fname, lname, number and extension.
            The maximum supported sort order value is 3.
        :type order: str
        :param org_id: Return the available agents for this organization.
        :type org_id: str
        :return: yields :class:`PersonPlaceCallPark` objects
        """
        if org_id is not None:
            params['orgId'] = org_id
        if call_pickup_name is not None:
            params['callPickupName'] = call_pickup_name
        if name is not None:
            params['name'] = name
        if phone_number is not None:
            params['phoneNumber'] = phone_number
        if order is not None:
            params['order'] = order
        url = self._endpoint(location_id=location_id, path='availableUsers')
        # noinspection PyTypeChecker
        return [o async for o in self.session.follow_pagination(url=url, model=PersonPlaceAgent, params=params, item_key='agents')]


class AsAnnouncementApi:
    """
    API for call queue Announcements
    """

    def __init__(self, *, session: AsRestSession):
        self._session = session

    def _endpoint(self, location_id: str, queue_id: str, path: str = None):
        """

        :meta private:
        :param location_id:
        :param queue_id:
        :param path:
        :return:
        """
        path = path and f'/{path}' or ''
        ep = self._session.ep(path=f'telephony/config/locations/{location_id}/queues/{queue_id}/announcements{path}')
        return ep

    def list_gen(self, location_id: str, queue_id: str, org_id: str = None) -> AsyncGenerator[Announcement]:
        """
        Read the List of Call Queue Announcement Files

        List file info for all Call Queue announcement files associated with this Call Queue.

        Call Queue announcement files contain messages and music that callers hear while waiting in the queue. A call
        queue can be configured to play whatever subset of these announcement files is desired.

        Retrieving this list of files requires a full or read-only administrator or location administrator auth token
        with a scope of `spark-admin:telephony_config_read`.

        Note that uploading of announcement files via API is not currently supported, but is available via Webex
        Control Hub.

        :param location_id: Location in which this call queue exists.
        :type location_id: str
        :param queue_id: Retrieve announcement files for the call queue with this identifier.
        :type queue_id: str
        :param org_id: Retrieve announcement files for a call queue from this organization.
        :type org_id: str
        """
        url = self._endpoint(location_id=location_id, queue_id=queue_id)
        params = org_id and {'orgId': org_id} or dict()
        # noinspection PyTypeChecker
        return self._session.follow_pagination(url=url, model=Announcement, params=params)

    async def list(self, location_id: str, queue_id: str, org_id: str = None) -> List[Announcement]:
        """
        Read the List of Call Queue Announcement Files

        List file info for all Call Queue announcement files associated with this Call Queue.

        Call Queue announcement files contain messages and music that callers hear while waiting in the queue. A call
        queue can be configured to play whatever subset of these announcement files is desired.

        Retrieving this list of files requires a full or read-only administrator or location administrator auth token
        with a scope of `spark-admin:telephony_config_read`.

        Note that uploading of announcement files via API is not currently supported, but is available via Webex
        Control Hub.

        :param location_id: Location in which this call queue exists.
        :type location_id: str
        :param queue_id: Retrieve announcement files for the call queue with this identifier.
        :type queue_id: str
        :param org_id: Retrieve announcement files for a call queue from this organization.
        :type org_id: str
        """
        url = self._endpoint(location_id=location_id, queue_id=queue_id)
        params = org_id and {'orgId': org_id} or dict()
        # noinspection PyTypeChecker
        return [o async for o in self._session.follow_pagination(url=url, model=Announcement, params=params)]

    async def delete_announcement(self, location_id: str, queue_id: str, file_name: str, org_id: str = None):
        """
        Delete a Call Queue Announcement File

        Delete an announcement file for the designated Call Queue.

        Call Queue announcement files contain messages and music that callers hear while waiting in the queue. A call
        queue can be configured to play whatever subset of these announcement files is desired.

        Deleting an announcement file for a call queue requires a full administrator or location administrator auth
        token with a scope of `spark-admin:telephony_config_write`.

        :param location_id: Delete an announcement for a call queue in this location.
        :type location_id: str
        :param queue_id: Delete an announcement for the call queue with this identifier.
        :type queue_id: str
        :type file_name: str
        :param org_id: Delete call queue announcement from this organization.
        :type org_id: str
        :rtype: None
        """
        url = self._endpoint(location_id=location_id, queue_id=queue_id, path=file_name)
        params = org_id and {'orgId': org_id} or None
        await self._session.delete(url=url, params=params)


class AsCQPolicyApi:
    _session: AsRestSession

    def _ep(self, location_id: str, queue_id: str, path: str):
        return self._session.ep(f'telephony/config/locations/{location_id}/queues/{queue_id}/{path}')

    def __init__(self, session: AsRestSession):
        self._session = session

    async def holiday_service_details(self, location_id: str, queue_id: str, org_id: str = None) -> HolidayService:
        """
        Get Details for a Call Queue Holiday Service

        Retrieve Call Queue Holiday Service details.

        Configure the call queue to route calls differently during the holidays.

        Retrieving call queue holiday service details requires a full or read-only administrator auth token with a
        scope of spark-admin:telephony_config_read.

        :param location_id: Retrieve settings for a call queue in this location.
        :type location_id: str
        :param queue_id: Retrieve settings for the call queue with this identifier.
        :type queue_id: str
        :param org_id: Retrieve call queue settings from this organization.
        :type org_id: str
        :return: Call Queue Holiday Service details
        :rtype: HolidayService
        """
        url = self._ep(location_id, queue_id, 'holidayService')
        params = org_id and {'orgId': org_id} or None
        data = await self._session.rest_get(url=url, params=params)
        return HolidayService.model_validate(data)

    async def holiday_service_update(self, location_id: str, queue_id: str, update: HolidayService, org_id: str = None):
        """
        Update a Call Queue Holiday Service

        Update the designated Call Queue Holiday Service.

        Configure the call queue to route calls differently during the holidays.

        Updating a call queue holiday service requires a full administrator auth token with a scope
        of spark-admin:telephony_config_write.

        :param location_id: Location in which this call queue exists.
        :type location_id: str
        :param queue_id: Update setting for the call queue with the matching ID.
        :type queue_id: str
        :param update: holiday service settings.
        :type update: HolidayService
        :param org_id: Update call queue settings from this organisation.
        :type org_id: str
        """
        url = self._ep(location_id, queue_id, 'holidayService')
        params = org_id and {'orgId': org_id} or None
        body = update.model_dump_json(exclude={'holiday_schedules'})
        await self._session.rest_put(url=url, params=params, data=body)

    async def night_service_detail(self, location_id: str, queue_id: str, org_id: str = None) -> NightService:
        """
        Get Details for a Call Queue Night Service

        Retrieve Call Queue Night service details.

        Configure the call queue to route calls differently during the hours when the queue is not in service. This
        is determined by a schedule that defines the business hours of the queue.

        Retrieving call queue details requires a full or read-only administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param location_id: Retrieve settings for a call queue in this location.
        :type location_id: str
        :param queue_id: Retrieve settings for the call queue night service with this identifier.
        :type queue_id: str
        :param org_id: Retrieve call queue night service settings from this organisation
        :type org_id: str
        :return: Call Queue Night service details
        :rtype: NightService
        """
        url = self._ep(location_id, queue_id, 'nightService')
        params = org_id and {'orgId': org_id} or None
        data = await self._session.rest_get(url=url, params=params)
        return NightService.model_validate(data)

    async def night_service_update(self, location_id: str, queue_id: str, update: NightService, org_id: str = None):
        """
        Update a Call Queue Night Service

        Update Call Queue Night Service details.

        Configure the call queue to route calls differently during the hours when the queue is not in service. This
        is determined by a schedule that defines the business hours of the queue.

        Retrieving call queue night service details requires a full or read-only administrator auth token with a
        scope of spark-admin:telephony_config_read.

        :param location_id: update settings for a call queue in this location.
        :type location_id: str
        :param queue_id: update settings for the call queue night service with this identifier.
        :type queue_id: str
        :param update: new night service settings
        :type update: NightService
        :param org_id: update call queue night service settings from this organisation.
        :type org_id: str
        """
        url = self._ep(location_id, queue_id, 'nightService')
        params = org_id and {'orgId': org_id} or None
        body = update.model_dump_json(exclude={'business_hours_schedules'})
        await self._session.rest_put(url=url, params=params, data=body)

    async def stranded_calls_details(self, location_id: str, queue_id: str, org_id: str = None) -> StrandedCalls:
        """
        Get Details for a Call Queue Stranded Calls

        Allow admin to view default/configured Stranded Calls settings.

        Stranded-All agents logoff Policy: If the last agent staffing a queue “unjoins” the queue or signs out,
        then all calls in the queue become stranded. Stranded-Unavailable Policy: This policy allows for the
        configuration of the processing of calls that are in a staffed queue when all agents are unavailable.

        Retrieving call queue Stranded Calls details requires a full or read-only administrator auth token with a
        scope of spark-admin:telephony_config_read.

        :param location_id: Retrieve settings for a call queue in this location.
        :type location_id: str
        :param queue_id: Retrieve settings for the call queue with this identifier.
        :type queue_id: str
        :param org_id: Retrieve call queue settings from this organization.
        :type org_id: str
        :return: Stranded Calls settings
        :rtype: StrandedCalls
        """
        url = self._ep(location_id, queue_id, 'strandedCalls')
        params = org_id and {'orgId': org_id} or None
        data = await self._session.rest_get(url=url, params=params)
        return StrandedCalls.model_validate(data)

    async def stranded_calls_update(self, location_id: str, queue_id: str, update: StrandedCalls, org_id: str = None):
        """
        Update a Call Queue Stranded Calls service

        Update the designated Call Stranded Calls Service.

        Allow admin to modify configured Stranded Calls settings.

        Updating a call queue stranded calls requires a full administrator auth token with a scope
        of spark-admin:telephony_config_write.

        :param location_id: Location in which this call queue exists.
        :type location_id: str
        :param queue_id: Update setting for the call queue with the matching ID.
        :type queue_id: str
        :param update: Call Stranded Calls settings
        :type update: StrandedCalls
        :param org_id: Update call queue settings from this organisation.
        :type org_id: str
        """
        url = self._ep(location_id, queue_id, 'strandedCalls')
        params = org_id and {'orgId': org_id} or None
        await self._session.rest_put(url=url, params=params, data=update.model_dump_json())

    async def forced_forward_details(self, location_id: str, queue_id: str, org_id: str = None) -> ForcedForward:
        """
        Get Details for a Call Queue Forced Forward

        Retrieve Call Queue policy Forced Forward details.

        This policy allows calls to be temporarily diverted to a configured destination.

        Retrieving call queue Forced Forward details requires a full or read-only administrator auth token with a
        scope of spark-admin:telephony_config_read.

        :param location_id: Location in which this call queue exists.
        :param queue_id: Retrieve setting for the call queue with the matching ID.
        :param org_id: Retrieve call queue settings from this organisation.
        :return: Call Queue policy Forced Forward details.
        :rtype: ForcedForward
        """
        url = self._ep(location_id, queue_id, 'forcedForward')
        params = org_id and {'orgId': org_id} or None
        data = await self._session.rest_get(url=url, params=params)
        return ForcedForward.model_validate(data)

    async def forced_forward_update(self, location_id: str, queue_id: str, update: ForcedForward, org_id: str = None):
        """
        Update a Call Queue Forced Forward service

        Update the designated Forced Forward Service.

        If the option is enabled, then incoming calls to the queue are forwarded to the configured destination. Calls
        that are already in the queue remain queued. The policy can be configured to play an announcement prior to
        proceeding with the forward.

        Updating a call queue Forced Forward service requires a full administrator auth token with a scope
        of spark-admin:telephony_config_write.

        :param location_id: Location in which this call queue exists.
        :type location_id: str
        :param queue_id: Update setting for the call queue with the matching ID.
        :type queue_id: str
        :param update: new call queue Forced Forward settings
        :type update: ForcedForward
        :param org_id: Update call queue settings from this organisation.
        :type org_id: str
        """
        url = self._ep(location_id, queue_id, 'forcedForward')
        params = org_id and {'orgId': org_id} or None
        await self._session.rest_put(url=url, params=params, data=update.model_dump_json())


class AsCallQueueAgentsApi(AsApiChild, base='telephony/config/queues/agents'):
    """
    Call Queue Agents API
    """

    def list_gen(self, location_id: str = None, queue_id: str = None, name: str = None,
             phone_number: str = None, join_enabled: bool = None,
             has_cx_essentials: bool = None, order: str = None, org_id: str = None,
             **params) -> AsyncGenerator[CallQueueAgent, None, None]:
        """
        Read the List of Call Queue Agents

        List all Call Queues Agents for the organization.

        Agents can be users, workplace or virtual lines assigned to a call queue. Calls from the call queue are routed
        to agents based on configuration.
        An agent can be assigned to one or more call queues and can be managed by supervisors.

        Retrieving this list requires a full or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        **Note**: The decoded value of the agent's `id`, and the `type` returned in the response, are always returned
        as `PEOPLE`, even when the agent is a workspace or virtual line. This will be addressed in a future release.

        :param location_id: Return only the call queue agents in this location.
        :type location_id: str
        :param queue_id: Only return call queue agents with the matching queue ID.
        :type queue_id: str
        :param name: Returns only the list of call queue agents that match the given name.
        :type name: str
        :param phone_number: Returns only the list of call queue agents that match the given phone number or extension.
        :type phone_number: str
        :param join_enabled: Returns only the list of call queue agents that match the given `joinEnabled` value.
        :type join_enabled: bool
        :param has_cx_essentials: Returns only the list of call queues with Customer Experience Essentials license when
            `true`, otherwise returns the list of Customer Experience Basic call queues.
        :type has_cx_essentials: bool
        :param order: Sort results alphabetically by call queue agent's name, in ascending or descending order.
        :type order: str
        :param org_id: List call queues agents in this organization.
        :type org_id: str
        :return: Generator yielding :class:`ListCallQueueAgentObject` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if location_id is not None:
            params['locationId'] = location_id
        if queue_id is not None:
            params['queueId'] = queue_id
        if name is not None:
            params['name'] = name
        if phone_number is not None:
            params['phoneNumber'] = phone_number
        if join_enabled is not None:
            params['joinEnabled'] = str(join_enabled).lower()
        if has_cx_essentials is not None:
            params['hasCxEssentials'] = str(has_cx_essentials).lower()
        if order is not None:
            params['order'] = order
        url = self.ep()
        return self.session.follow_pagination(url=url, model=CallQueueAgent, item_key='agents', params=params)

    async def list(self, location_id: str = None, queue_id: str = None, name: str = None,
             phone_number: str = None, join_enabled: bool = None,
             has_cx_essentials: bool = None, order: str = None, org_id: str = None,
             **params) -> List[CallQueueAgent]:
        """
        Read the List of Call Queue Agents

        List all Call Queues Agents for the organization.

        Agents can be users, workplace or virtual lines assigned to a call queue. Calls from the call queue are routed
        to agents based on configuration.
        An agent can be assigned to one or more call queues and can be managed by supervisors.

        Retrieving this list requires a full or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        **Note**: The decoded value of the agent's `id`, and the `type` returned in the response, are always returned
        as `PEOPLE`, even when the agent is a workspace or virtual line. This will be addressed in a future release.

        :param location_id: Return only the call queue agents in this location.
        :type location_id: str
        :param queue_id: Only return call queue agents with the matching queue ID.
        :type queue_id: str
        :param name: Returns only the list of call queue agents that match the given name.
        :type name: str
        :param phone_number: Returns only the list of call queue agents that match the given phone number or extension.
        :type phone_number: str
        :param join_enabled: Returns only the list of call queue agents that match the given `joinEnabled` value.
        :type join_enabled: bool
        :param has_cx_essentials: Returns only the list of call queues with Customer Experience Essentials license when
            `true`, otherwise returns the list of Customer Experience Basic call queues.
        :type has_cx_essentials: bool
        :param order: Sort results alphabetically by call queue agent's name, in ascending or descending order.
        :type order: str
        :param org_id: List call queues agents in this organization.
        :type org_id: str
        :return: Generator yielding :class:`ListCallQueueAgentObject` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if location_id is not None:
            params['locationId'] = location_id
        if queue_id is not None:
            params['queueId'] = queue_id
        if name is not None:
            params['name'] = name
        if phone_number is not None:
            params['phoneNumber'] = phone_number
        if join_enabled is not None:
            params['joinEnabled'] = str(join_enabled).lower()
        if has_cx_essentials is not None:
            params['hasCxEssentials'] = str(has_cx_essentials).lower()
        if order is not None:
            params['order'] = order
        url = self.ep()
        return [o async for o in self.session.follow_pagination(url=url, model=CallQueueAgent, item_key='agents', params=params)]

    async def details(self, id: str, has_cx_essentials: bool = None,
                max_: int = 50, start: int = 0, org_id: str = None) -> CallQueueAgentDetail:
        """
        Get Details for a Call Queue Agent

        Retrieve details of a particular Call queue agent based on the agent ID.

        Agents can be users, workplace or virtual lines assigned to a call queue. Calls from the call queue are routed
        to agents based on configuration.
        An agent can be assigned to one or more call queues and can be managed by supervisors.

        Retrieving a call queue agent's details require a full or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        **Note**: The agent's `type` returned in the response and in the decoded value of the agent's `id`, is always
        of type `PEOPLE`, even if the agent is a workspace or virtual line. This` will be corrected in a future
        release.

        :param id: Retrieve call queue agents with this identifier.
        :type id: str
        :param max_: Limits the number of objects returned to this maximum count.
        :type max_: int
        :param start: Start at the zero-based offset in the list of matching objects.
        :type start: int
        :param has_cx_essentials: Must be set to `true` to view the details of an agent with Customer Experience
            Essentials license. This can otherwise be ommited or set to `false`.
        :type has_cx_essentials: bool
        :param org_id: Retrieve call queue agents from this organization.
        :type org_id: str
        :rtype: :class:`CallQueueAgentDetail`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        if has_cx_essentials is not None:
            params['hasCxEssentials'] = str(has_cx_essentials).lower()
        params['max'] = max_
        params['start'] = start
        url = self.ep(f'{id}')
        data = await super().get(url, params=params)
        r = CallQueueAgentDetail.model_validate(data)
        return r

    async def update_call_queue_settings(self, id: str, settings: List[AgentCallQueueSetting],
                                   has_cx_essentials: bool = None, org_id: str = None):
        """
        Update an Agent's Settings of One or More Call Queues

        Modify an agent's call queue settings for an organization.

        Calls from the call queue are routed to agents based on configuration.
        An agent can be assigned to one or more call queues and can be managed by supervisors.

        This operation requires a full administrator auth token with a scope of `spark-admin:telephony_config_write`.

        :param id: Identifier of the agent to be updated.
        :type id: str
        :type settings: list[ModifyAgentsForCallQueueObjectSettings]
        :param has_cx_essentials: Must be set to `true` to modify an agent that has Customer Experience Essentials
            license. This can otherwise be ommited or set to `false`.
        :type has_cx_essentials: bool
        :param org_id: Update the settings of an agent in this organization.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        if has_cx_essentials is not None:
            params['hasCxEssentials'] = str(has_cx_essentials).lower()
        body = dict()
        body['settings'] = TypeAdapter(list[AgentCallQueueSetting]).dump_python(settings, mode='json',
                                                                                by_alias=True,
                                                                                exclude_none=True)
        url = self.ep(f'{id}/settings')
        await super().put(url, params=params, json=body)


class AsCallQueueApi(AsApiChild, base=''):
    """
    Features:  Call Queue

    Features: Call Queue supports reading and writing of Webex Calling Call Queue settings for a specific organization.

    Supervisors are users who manage agents and who perform functions including monitoring, coaching, and more.

    Viewing these read-only organization settings requires a full or read-only administrator auth token with a scope
    of `spark-admin:telephony_config_read`.

    Modifying these organization settings requires a full administrator auth token with a scope
    of `spark-admin:telephony_config_write`.

    A partner administrator can retrieve or change settings in a customer's organization using the optional `orgId`
    query parameter.
    """
    agents: AsCallQueueAgentsApi
    forwarding: AsForwardingApi
    announcement: AsAnnouncementApi
    policy: AsCQPolicyApi

    def __init__(self, session: AsRestSession):
        super().__init__(session=session)
        self.agents = AsCallQueueAgentsApi(session=session)
        self.forwarding = AsForwardingApi(session=session, feature_selector=AsFeatureSelector.queues)
        self.announcement = AsAnnouncementApi(session=session)
        self.policy = AsCQPolicyApi(session=session)

    def _endpoint(self, *, location_id: str = None, queue_id: str = None, path: str = None):
        """
        Helper to get URL for API endpoints

        :meta private:
        :param location_id:
        :param queue_id:
        :return:
        """
        if location_id is None:
            return self.ep('telephony/config/queues')
        else:
            ep = self.ep(f'telephony/config/locations/{location_id}/queues')
            if queue_id:
                ep = f'{ep}/{queue_id}'
            if path:
                ep = f'{ep}/{path}'
            return ep

    def list_gen(self, location_id: str = None, name: str = None, phone_number: str = None,
             department_id: str = None, department_name: str = None,
             has_cx_essentials: bool = None, org_id: str = None,
             **params) -> AsyncGenerator[CallQueue, None, None]:
        """
        Read the List of Call Queues

        List all Call Queues for the organization.

        Call queues temporarily hold calls in the cloud, when all agents
        assigned to receive calls from the queue are unavailable. Queued calls are routed to
        an available agent, when not on an active call. Each call queue is assigned a lead number, which is a telephone
        number that external callers can dial to reach the users assigned to the call queue.
        Call queues are also assigned an internal extension, which can be dialed
        internally to reach the users assigned to the call queue.

        Retrieving this list requires a full or read-only administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param location_id: Returns the list of call queues in this location.
        :type location_id: str
        :param name: Returns only the call queues matching the given name.
        :type name: str
        :param phone_number: Returns only the call queues matching the given primary phone number or extension.
        :type phone_number: str
        :param department_id: Returns only call queues matching the given department ID.
        :type department_id: str
        :param department_name: Returns only call queues matching the given department name.
        :type department_name: str
        :param has_cx_essentials: Returns only the list of call queues with Customer Experience Essentials license when
            `true`, otherwise returns the list of Customer Experience Basic call queues.
        :type has_cx_essentials: bool
        :param org_id: Returns the list of call queues in this organization.
        :type org_id: str
        :return: yields :class:`CallQueue` objects
        """
        if org_id is not None:
            params['orgId'] = org_id
        if location_id is not None:
            params['locationId'] = location_id
        if name is not None:
            params['name'] = name
        if phone_number is not None:
            params['phoneNumber'] = phone_number
        if department_id is not None:
            params['departmentId'] = department_id
        if department_name is not None:
            params['departmentName'] = department_name
        if has_cx_essentials is not None:
            params['hasCxEssentials'] = str(has_cx_essentials).lower()
        url = self._endpoint()
        # noinspection PyTypeChecker
        return self.session.follow_pagination(url=url, model=CallQueue, params=params)

    async def list(self, location_id: str = None, name: str = None, phone_number: str = None,
             department_id: str = None, department_name: str = None,
             has_cx_essentials: bool = None, org_id: str = None,
             **params) -> List[CallQueue]:
        """
        Read the List of Call Queues

        List all Call Queues for the organization.

        Call queues temporarily hold calls in the cloud, when all agents
        assigned to receive calls from the queue are unavailable. Queued calls are routed to
        an available agent, when not on an active call. Each call queue is assigned a lead number, which is a telephone
        number that external callers can dial to reach the users assigned to the call queue.
        Call queues are also assigned an internal extension, which can be dialed
        internally to reach the users assigned to the call queue.

        Retrieving this list requires a full or read-only administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param location_id: Returns the list of call queues in this location.
        :type location_id: str
        :param name: Returns only the call queues matching the given name.
        :type name: str
        :param phone_number: Returns only the call queues matching the given primary phone number or extension.
        :type phone_number: str
        :param department_id: Returns only call queues matching the given department ID.
        :type department_id: str
        :param department_name: Returns only call queues matching the given department name.
        :type department_name: str
        :param has_cx_essentials: Returns only the list of call queues with Customer Experience Essentials license when
            `true`, otherwise returns the list of Customer Experience Basic call queues.
        :type has_cx_essentials: bool
        :param org_id: Returns the list of call queues in this organization.
        :type org_id: str
        :return: yields :class:`CallQueue` objects
        """
        if org_id is not None:
            params['orgId'] = org_id
        if location_id is not None:
            params['locationId'] = location_id
        if name is not None:
            params['name'] = name
        if phone_number is not None:
            params['phoneNumber'] = phone_number
        if department_id is not None:
            params['departmentId'] = department_id
        if department_name is not None:
            params['departmentName'] = department_name
        if has_cx_essentials is not None:
            params['hasCxEssentials'] = str(has_cx_essentials).lower()
        url = self._endpoint()
        # noinspection PyTypeChecker
        return [o async for o in self.session.follow_pagination(url=url, model=CallQueue, params=params)]

    async def by_name(self, name: str, location_id: str = None, has_cx_essentials: bool = None,
                org_id: str = None) -> Optional[CallQueue]:
        """
        Get queue info by name

        :param location_id:
        :param has_cx_essentials:
        :param name:
        :param org_id:
        :return:
        """
        return next((cq for cq in await self.list(location_id=location_id, has_cx_essentials=has_cx_essentials,
                                            org_id=org_id, name=name)
                     if cq.name == name), None)

    async def create(self, location_id: str, settings: CallQueue, has_cx_essentials: bool = None,
               org_id: str = None) -> str:
        """
        Create a Call Queue

        Create new Call Queues for the given location.

        Call queues temporarily hold calls in the cloud, when all agents assigned to receive calls from the queue are
        unavailable.
        Queued calls are routed to an available agent, when not on an active call. Each call queue is assigned a lead
        number, which is a telephone
        number that external callers can dial to reach the users assigned to the call queue. Call queues are also
        assigned an internal extension,
        which can be dialed internally to reach the users assigned to the call queue.

        Creating a call queue requires a full administrator or location administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param location_id: Create the call queue for this location.
        :type location_id: str
        :param settings: parameters for queue creation.
        :type settings: :class:`CallQueue`
        :param has_cx_essentials: Creates a Customer Experience Essentials call queue, when `true`. This requires
            Customer Experience Essentials licensed agents. If this parameter is not set, the `has_cx_essentials`
            attribute of the `settings` object is considered.
        :type has_cx_essentials: bool
        :param org_id: Create the call queue for this organization.
        :type org_id: str
        :return: queue id
        :rtype: str

        Example:

            .. code-block:: python

                settings = CallQueue(name=new_name,
                                     extension=extension,
                                     call_policies=CallQueueCallPolicies.default(),
                                     queue_settings=QueueSettings.default(queue_size=10),
                                     agents=[Agent(agent_id=user.person_id) for user in members])

                # create new queue
                queue_id = api.telephony.callqueue.create(location_id=target_location.location_id,
                                                          settings=settings)

        """
        params = org_id and {'orgId': org_id} or {}
        if has_cx_essentials is not None:
            params['hasCxEssentials'] = str(has_cx_essentials).lower()
        elif settings.has_cx_essentials:
            params['hasCxEssentials'] = 'true'
        cq_data = settings.create_or_update()
        url = self._endpoint(location_id=location_id)
        data = await self.post(url, json=cq_data, params=params)
        return data['id']

    async def delete_queue(self, location_id: str, queue_id: str, org_id: str = None):
        """
        Delete a Call Queue
        Delete the designated Call Queue.

        Call queues temporarily hold calls in the cloud when all agents, which can be users or agents, assigned to
        receive calls from the queue are unavailable. Queued calls are routed to an available agent when not on an
        active call. Each call queue is assigned a Lead Number, which is a telephone number outside callers can dial
        to reach users assigned to the call queue. Call queues are also assigned an internal extension, which can be
        dialed internally to reach users assigned to the call queue.

        Deleting a call queue requires a full administrator auth token with a scope
        of spark-admin:telephony_config_write.

        :param location_id: Location from which to delete a call queue.
        :type location_id: str
        :param queue_id: Delete the call queue with the matching ID.
        :type queue_id: str
        :param org_id: Delete the call queue from this organization.
        :type org_id: str
        """
        url = self._endpoint(location_id=location_id, queue_id=queue_id)
        params = org_id and {'orgId': org_id} or None
        await self.delete(url=url, params=params)

    async def details(self, location_id: str, queue_id: str, has_cx_essentials: bool = None,
                org_id: str = None) -> CallQueue:
        """
        Get Details for a Call Queue

        Retrieve Call Queue details.

        Call queues temporarily hold calls in the cloud, when all agents assigned to receive calls from the queue are
        unavailable.
        Queued calls are routed to an available agent, when not on an active call. Each call queue is assigned a lead
        number, which is a telephone
        number that external callers can dial to reach the users assigned to the call queue. Call queues are also
        assigned an internal extension,
        which can be dialed internally to reach the users assigned to the call queue.

        Retrieving call queue details requires a full or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param location_id: Retrieves the details of a call queue in this location.
        :type location_id: str
        :param queue_id: Retrieves the details of call queue with this identifier.
        :type queue_id: str
        :param has_cx_essentials: Must be set to `true`, to view the details of a call queue with Customer Experience
            Essentials license. This can otherwise be omited or set to `false`.
        :type has_cx_essentials: bool
        :param org_id: Retrieves the details of a call queue in this organization.
        :type org_id: str
        :return: call queue details
        :rtype: :class:`CallQueue`
        """
        url = self._endpoint(location_id=location_id, queue_id=queue_id)
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        if has_cx_essentials is not None:
            params['hasCxEssentials'] = str(has_cx_essentials).lower()
        data = await self.get(url, params=params)
        result = CallQueue.model_validate(data)
        result.location_id = location_id
        # noinspection PyTypeChecker
        return result

    async def update(self, location_id: str, queue_id: str, update: CallQueue, org_id: str = None):
        """
        Update a Call Queue

        Update the designated Call Queue.

        Call queues temporarily hold calls in the cloud when all agents, which can be users or agents, assigned to
        receive calls from the queue are unavailable. Queued calls are routed to an available agent when not on an
        active call. Each call queue is assigned a Lead Number, which is a telephone number outside callers can dial
        to reach users assigned to the call queue. Call queues are also assigned an internal extension, which can be
        dialed internally to reach users assigned to the call queue.

        Updating a call queue requires a full administrator auth token with a scope
        of spark-admin:telephony_config_write.

        :param location_id: Location in which this call queue exists.
        :type location_id: str
        :param queue_id: Update setting for the call queue with the matching ID.
        :type queue_id: str
        :param update: updates
        :type update: :class:`CallQueue`
        :param org_id: Update call queue settings from this organization.

        Examples:

        .. code-block::

            api = WebexSimpleApi()

            # shortcut
            cq = api.telephony.callqueue

            # disable a call queue
            update = CallQueue(enabled=False)
            cq.update(location_id=...,
                      queue_id=...,
                      update=update)

            # set the call routing policy to SIMULTANEOUS
            update = CallQueue(call_policies=CallPolicies(policy=Policy.simultaneous))
            cq.update(location_id=...,
                      queue_id=...,
                      update=update)
            # don't bounce calls after the set number of rings.
            update = CallQueue(
                call_policies=CallPolicies(
                    call_bounce=CallBounce(
                        enabled=False)))
            cq.update(location_id=...,
                      queue_id=...,
                      update=update)

        Alternatively you can also read call queue details, update them in place and then call update().

        .. code-block::

            details = cq.details(location_id=...,
                                 queue_id=...)
            details.call_policies.call_bounce.agent_unavailable_enabled=False
            details.call_policies.call_bounce.on_hold_enabled=False
            cq.update(location_id=...,
                      queue_id=...,
                      update=details)

        """
        params = org_id and {'orgId': org_id} or None
        if location_id is None or queue_id is None:
            raise ValueError('location_id and queue_id cannot be None')
        cq_data = update.create_or_update()
        url = self._endpoint(location_id=location_id, queue_id=queue_id)
        await self.put(url=url, json=cq_data, params=params)

    async def get_call_queue_settings(self, org_id: str = None) -> CallQueueSettings:
        """
        Get Call Queue Settings

        Retrieve Call Queue Settings for a specific organization.

        Call Queue Settings are used to enable the Simultaneous Ringing algorithm that maintains queue positions for
        customers.

        Retrieving Call Queue Settings requires a full, user, or read-only administrator auth token with a scope
        of `spark-admin:telephony_config_read`.

        :param org_id: Call Queue Settings for this organization.
        :type org_id: str
        :rtype: :class:`CallQueueSettings`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep('telephony/config/queues/settings')
        data = await self.get(url, params=params)
        r = CallQueueSettings.model_validate(data)
        return r

    async def update_call_queue_settings(self, settings: CallQueueSettings, org_id: str = None):
        """
        Update Call Queue Settings

        Update Call Queue Settings for a specific organization.

        Call Queue Settings are used to enable the Simultaneous Ringing algorithm that maintains queue positions for
        customers.

        Updating Call Queue Settings requires a full or user administrator auth token with a scope
        `spark-admin:telephony_config_write`.

        :param settings: Call Queue Settings for this organization.
        :param org_id: update Call Queue Settings for this organization.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = settings.model_dump(mode='json', by_alias=True, exclude_none=True)
        url = self.ep('telephony/config/queues/settings')
        await self.put(url, params=params, json=body)

    def primary_available_phone_numbers_gen(self, location_id: str, phone_number: List[str] = None,
                                        org_id: str = None,
                                        **params) -> AsyncGenerator[AvailableNumber, None, None]:
        """
        Get Call Queue Primary Available Phone Numbers

        List the service and standard PSTN numbers that are available to be assigned as the call queue's primary phone
        number.

        These numbers are associated with the location specified in the request URL, can be active or inactive, and are
        unassigned.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full, read-only or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param location_id: Return the list of phone numbers for this location within the given organization. The
            maximum length is 36.
        :type location_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`CallQueuePrimaryAvailableNumberObject` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        url = self._endpoint(location_id=location_id, path='availableNumbers')
        return self.session.follow_pagination(url=url, model=AvailableNumber, item_key='phoneNumbers', params=params)

    async def primary_available_phone_numbers(self, location_id: str, phone_number: List[str] = None,
                                        org_id: str = None,
                                        **params) -> List[AvailableNumber]:
        """
        Get Call Queue Primary Available Phone Numbers

        List the service and standard PSTN numbers that are available to be assigned as the call queue's primary phone
        number.

        These numbers are associated with the location specified in the request URL, can be active or inactive, and are
        unassigned.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full, read-only or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param location_id: Return the list of phone numbers for this location within the given organization. The
            maximum length is 36.
        :type location_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`CallQueuePrimaryAvailableNumberObject` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        url = self._endpoint(location_id=location_id, path='availableNumbers')
        return [o async for o in self.session.follow_pagination(url=url, model=AvailableNumber, item_key='phoneNumbers', params=params)]

    def alternate_available_phone_numbers_gen(self, location_id: str, phone_number: List[str] = None,
                                          org_id: str = None,
                                          **params) -> AsyncGenerator[AvailableNumber, None, None]:
        """
        Get Call Queue Alternate Available Phone Numbers

        List the service and standard PSTN numbers that are available to be assigned as the call queue's alternate
        phone number.

        These numbers are associated with the location specified in the request URL, can be active or inactive, and are
        unassigned.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full, read-only or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param location_id: Return the list of phone numbers for this location within the given organization. The
            maximum length is 36.
        :type location_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableNumber` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        url = self._endpoint(location_id=location_id, path='alternate/availableNumbers')
        return self.session.follow_pagination(url=url, model=AvailableNumber, item_key='phoneNumbers', params=params)

    async def alternate_available_phone_numbers(self, location_id: str, phone_number: List[str] = None,
                                          org_id: str = None,
                                          **params) -> List[AvailableNumber]:
        """
        Get Call Queue Alternate Available Phone Numbers

        List the service and standard PSTN numbers that are available to be assigned as the call queue's alternate
        phone number.

        These numbers are associated with the location specified in the request URL, can be active or inactive, and are
        unassigned.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full, read-only or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param location_id: Return the list of phone numbers for this location within the given organization. The
            maximum length is 36.
        :type location_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableNumber` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        url = self._endpoint(location_id=location_id, path='alternate/availableNumbers')
        return [o async for o in self.session.follow_pagination(url=url, model=AvailableNumber, item_key='phoneNumbers', params=params)]

    def call_forward_available_phone_numbers_gen(self, location_id: str, phone_number: List[str] = None,
                                             owner_name: str = None, extension: str = None,
                                             org_id: str = None,
                                             **params) -> AsyncGenerator[AvailableNumber, None, None]:
        """
        Get Call Queue Call Forward Available Phone Numbers

        List the service and standard PSTN numbers that are available to be assigned as the call queue's call forward
        number.

        These numbers are associated with the location specified in the request URL, can be active or inactive, and are
        assigned to an owning entity.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full, read-only or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param location_id: Return the list of phone numbers for this location within the given organization. The
            maximum length is 36.
        :type location_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param owner_name: Return the list of phone numbers that are owned by the given `ownerName`. Maximum length is
            255.
        :type owner_name: str
        :param extension: Returns the list of PSTN phone numbers with the given `extension`.
        :type extension: str
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableNumber` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        if owner_name is not None:
            params['ownerName'] = owner_name
        if extension is not None:
            params['extension'] = extension
        url = self._endpoint(location_id=location_id, path='callForwarding/availableNumbers')
        return self.session.follow_pagination(url=url, model=AvailableNumber, item_key='phoneNumbers', params=params)

    async def call_forward_available_phone_numbers(self, location_id: str, phone_number: List[str] = None,
                                             owner_name: str = None, extension: str = None,
                                             org_id: str = None,
                                             **params) -> List[AvailableNumber]:
        """
        Get Call Queue Call Forward Available Phone Numbers

        List the service and standard PSTN numbers that are available to be assigned as the call queue's call forward
        number.

        These numbers are associated with the location specified in the request URL, can be active or inactive, and are
        assigned to an owning entity.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full, read-only or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param location_id: Return the list of phone numbers for this location within the given organization. The
            maximum length is 36.
        :type location_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param owner_name: Return the list of phone numbers that are owned by the given `ownerName`. Maximum length is
            255.
        :type owner_name: str
        :param extension: Returns the list of PSTN phone numbers with the given `extension`.
        :type extension: str
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableNumber` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        if owner_name is not None:
            params['ownerName'] = owner_name
        if extension is not None:
            params['extension'] = extension
        url = self._endpoint(location_id=location_id, path='callForwarding/availableNumbers')
        return [o async for o in self.session.follow_pagination(url=url, model=AvailableNumber, item_key='phoneNumbers', params=params)]

    def available_agents_gen(self, location_id: str, name: str = None, phone_number: str = None,
                         order: str = None, org_id: str = None,
                         **params) -> AsyncGenerator[AvailableAgent, None, None]:
        """
        Get Call Queue Available Agents

        List all available users, workspaces, or virtual lines that can be assigned as call queue agents.

        Available agents are users (excluding users with Webex Calling Standard license), workspaces, or virtual lines
        that can be assigned to a call queue.
        Calls from the call queue are routed to assigned agents based on configuration.
        An agent can be assigned to one or more call queues and can be managed by supervisors.

        Retrieving this list requires a full, read-only or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param location_id: The location ID of the call queue. Temporary mandatory query parameter, used for
            performance reasons only and not a filter.
        :type location_id: str
        :param name: Search based on name (user first and last name combination).
        :type name: str
        :param phone_number: Search based on number or extension.
        :type phone_number: str
        :param order: Order the available agents according to the designated fields. Up to three comma-separated sort
            order fields may be specified. Available sort fields are: `userId`, `fname`, `firstname`, `lname`,
            `lastname`, `dn`, and `extension`. Sort order can be added together with each field using a hyphen, `-`.
            Available sort orders are: `asc`, and `desc`.
        :type order: str
        :param org_id: List available agents for this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableAgentObject` instances
        """
        params['locationId'] = location_id
        if org_id is not None:
            params['orgId'] = org_id
        if name is not None:
            params['name'] = name
        if phone_number is not None:
            params['phoneNumber'] = phone_number
        if order is not None:
            params['order'] = order
        url = self.ep('telephony/config/queues/agents/availableAgents')
        return self.session.follow_pagination(url=url, model=AvailableAgent, item_key='agents', params=params)

    async def available_agents(self, location_id: str, name: str = None, phone_number: str = None,
                         order: str = None, org_id: str = None,
                         **params) -> List[AvailableAgent]:
        """
        Get Call Queue Available Agents

        List all available users, workspaces, or virtual lines that can be assigned as call queue agents.

        Available agents are users (excluding users with Webex Calling Standard license), workspaces, or virtual lines
        that can be assigned to a call queue.
        Calls from the call queue are routed to assigned agents based on configuration.
        An agent can be assigned to one or more call queues and can be managed by supervisors.

        Retrieving this list requires a full, read-only or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param location_id: The location ID of the call queue. Temporary mandatory query parameter, used for
            performance reasons only and not a filter.
        :type location_id: str
        :param name: Search based on name (user first and last name combination).
        :type name: str
        :param phone_number: Search based on number or extension.
        :type phone_number: str
        :param order: Order the available agents according to the designated fields. Up to three comma-separated sort
            order fields may be specified. Available sort fields are: `userId`, `fname`, `firstname`, `lname`,
            `lastname`, `dn`, and `extension`. Sort order can be added together with each field using a hyphen, `-`.
            Available sort orders are: `asc`, and `desc`.
        :type order: str
        :param org_id: List available agents for this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableAgentObject` instances
        """
        params['locationId'] = location_id
        if org_id is not None:
            params['orgId'] = org_id
        if name is not None:
            params['name'] = name
        if phone_number is not None:
            params['phoneNumber'] = phone_number
        if order is not None:
            params['order'] = order
        url = self.ep('telephony/config/queues/agents/availableAgents')
        return [o async for o in self.session.follow_pagination(url=url, model=AvailableAgent, item_key='agents', params=params)]


class AsCallRecordingSettingsApi(AsApiChild, base='telephony/config'):
    """
    Features:  Call Recording

    Not supported for Webex for Government (FedRAMP)

    Features: Call Recording supports reading and writing of Webex Calling Call Recording settings for a specific
    organization.

    Viewing these read-only organization settings requires a full or read-only administrator auth token with a scope of
    `spark-admin:telephony_config_read`.

    Modifying these organization settings requires a full administrator auth token with a scope of
    `spark-admin:telephony_config_write`.

    A partner administrator can retrieve or change settings in a customer's organization using the optional `orgId`
    query parameter.
    """

    async def read(self, org_id: str = None) -> CallRecordingInfo:
        """
        Get Call Recording Settings

        Retrieve call recording settings for the organization.

        The Call Recording feature enables authorized agents to record any active call that Webex Contact Center
        manages.

        Retrieving call recording settings requires a full or read-only administrator or location administrator auth
        token with a scope of `spark-admin:telephony_config_read`.

        :param org_id: Retrieve call recording settings from this organization.
        :type org_id: str
        :rtype: :class:`CallRecordingInfo`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep('callRecording')
        data = await super().get(url, params=params)
        r = CallRecordingInfo.model_validate(data)
        return r

    async def update(self, enabled: bool, org_id: str = None):
        """
        Update Call Recording Settings

        Update call recording settings for the organization.

        The Call Recording feature enables authorized agents to record any active call that Webex Contact Center
        manages.

        Updating call recording settings requires a full administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        **NOTE**: This API is for Cisco partners only.

        :param enabled: Whether or not the call recording is enabled.
        :type enabled: bool
        :param org_id: Retrieve call recording settings from this organization.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        body['enabled'] = enabled
        url = self.ep('callRecording')
        await super().put(url, params=params, json=body)

    async def read_terms_of_service(self, vendor_id: str, org_id: str = None) -> CallRecordingTermsOfService:
        """
        Get Call Recording Terms Of Service Settings

        Retrieve call recording terms of service settings for the organization.

        The Call Recording feature enables authorized agents to record any active call that Webex Contact Center
        manages.

        Retrieving call recording terms of service settings requires a full or read-only administrator or location
        administrator auth token with a scope of `spark-admin:telephony_config_read`.

        :param vendor_id: Retrieve call recording terms of service details for the given vendor.
        :type vendor_id: str
        :param org_id: Retrieve call recording terms of service details from this organization.
        :type org_id: str
        :rtype: :class:`CallRecordingTermsOfService`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'callRecording/vendors/{vendor_id}/termsOfService')
        data = await super().get(url, params=params)
        r = CallRecordingTermsOfService.model_validate(data)
        return r

    async def update_terms_of_service(self, vendor_id: str, enabled: bool, org_id: str = None):
        """
        Update Call Recording Terms Of Service Settings

        Update call recording terms of service settings for the given vendor.

        The Call Recording feature enables authorized agents to record any active call that Webex Contact Center
        manages.

        Updating call recording terms of service settings requires a full administrator or location administrator auth
        token with a scope of `spark-admin:telephony_config_write`.

        :param vendor_id: Update call recording terms of service settings for the given vendor.
        :type vendor_id: str
        :param enabled: Whether or not the call recording terms of service are enabled.
        :type enabled: bool
        :param org_id: Update call recording terms of service settings from this organization.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        body['termsOfServiceEnabled'] = enabled
        url = self.ep(f'callRecording/vendors/{vendor_id}/termsOfService')
        await super().put(url, params=params, json=body)

    async def read_org_compliance_announcement(self, org_id: str = None) -> OrgComplianceAnnouncement:
        """
        Get details for the organization Compliance Announcement Setting


        Retrieve the organization compliance announcement settings.

        The Compliance Announcement feature interacts with the Call Recording feature, specifically with the playback
        of the start/stop announcement. When the compliance announcement is played to the PSTN party, and the PSTN
        party is connected to a party with call recording enabled, then the start/stop announcement is inhibited.

        Retrieving organization compliance announcement setting requires a full or read-only administrator auth token
        with a scope of `spark-admin:telephony_config_read`.

        :param org_id: Retrieve compliance announcement setting from this organization.
        :type org_id: str
        :rtype: :class:`OrgComplianceAnnouncement`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep('callRecording/complianceAnnouncement')
        data = await super().get(url, params=params)
        r = OrgComplianceAnnouncement.model_validate(data)
        return r

    async def update_org_compliance_announcement(self, settings: OrgComplianceAnnouncement, org_id: str = None):
        """
        Update the organization compliance announcement

        The Compliance Announcement feature interacts with the Call Recording feature, specifically with the playback
        of the start/stop announcement. When the compliance announcement is played to the PSTN party, and the PSTN
        party is connected to a party with call recording enabled, then the start/stop announcement is inhibited.

        Updating the organization compliance announcement requires a full administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param settings: new settings
        :type settings: OrgComplianceAnnouncement
        :param org_id: Update the compliance announcement setting from this organization.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = settings.model_dump(mode='json', by_alias=True, exclude_none=True)
        url = self.ep('callRecording/complianceAnnouncement')
        await super().put(url, params=params, json=body)

    async def read_location_compliance_announcement(self, location_id: str,
                                              org_id: str = None) -> LocationComplianceAnnouncement:
        """
        Get details for the Location Compliance Announcement Setting

        Retrieve the location compliance announcement settings.

        The Compliance Announcement feature interacts with the Call Recording feature, specifically with the playback
        of the start/stop announcement. When the compliance announcement is played to the PSTN party, and the PSTN
        party is connected to a party with call recording enabled, then the start/stop announcement is inhibited.

        Retrieving location compliance announcement setting requires a full or read-only administrator auth token with
        a scope of `spark-admin:telephony_config_read`.

        :param location_id: Retrieve compliance announcement settings for this location.
        :type location_id: str
        :param org_id: Retrieve compliance announcement setting from this organization.
        :type org_id: str
        :rtype: :class:`LocationComplianceAnnouncement`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'locations/{location_id}/callRecording/complianceAnnouncement')
        data = await super().get(url, params=params)
        r = LocationComplianceAnnouncement.model_validate(data)
        return r

    async def update_location_compliance_announcement(self, location_id: str, settings: LocationComplianceAnnouncement,
                                                org_id: str = None):
        """
        Update the location compliance announcement

        The Compliance Announcement feature interacts with the Call Recording feature, specifically with the playback
        of the start/stop announcement. When the compliance announcement is played to the PSTN party, and the PSTN
        party is connected to a party with call recording enabled, then the start/stop announcement is inhibited.

        Updating the location compliance announcement requires a full administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param location_id: Update the compliance announcement settings for this location.
        :type location_id: str
        :param settings: new settings
        :type settings: LocationComplianceAnnouncement
        :param org_id: Update the compliance announcement setting from this organization.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = settings.model_dump(mode='json', by_alias=True, exclude_none=True)
        url = self.ep(f'locations/{location_id}/callRecording/complianceAnnouncement')
        await super().put(url, params=params, json=body)

    async def get_call_recording_regions(self, org_id: str = None) -> list[CallRecordingRegion]:
        """
        Get Call Recording Regions

        Retrieve all the call recording regions that are available for an organization.

        The Call Recording feature supports multiple third-party call recording providers, or vendors, to capture and
        manage call recordings. An organization is configured with an overall provider, but locations can be
        configured to use a different vendor than the overall organization default.

        Requires a full or read-only administrator auth token with a scope of `spark-admin:telephony_config_read`.

        :param org_id: Retrieve call recording regions for this organization.
        :type org_id: str
        :rtype: list[Regions]
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep('callRecording/regions')
        data = await super().get(url, params=params)
        r = TypeAdapter(list[CallRecordingRegion]).validate_python(data['regions'])
        return r

    def list_org_users_gen(self, standard_user_only: bool = None,
                       org_id: str = None, **params) -> AsyncGenerator[RecordingUser, None, None]:
        """
        Get Call Recording Vendor Users

        Retrieve call recording vendor users of an organization. This API is used to get the list of users who are
        assigned to the default call-recording vendor of the organization.

        The Call Recording feature supports multiple third-party call recording providers, or vendors, to capture and
        manage call recordings. An organization is configured with an overall provider, but locations can be
        configured to use a different vendor than the overall organization default.

        Requires a full or read-only administrator auth token with a scope of `spark-admin:telephony_config_read`.

        :param standard_user_only: If true, results only include Webex Calling standard users.
        :type standard_user_only: bool
        :param org_id: Retrieve call recording vendor users for this organization.
        :type org_id: str
        """
        if org_id is not None:
            params['orgId'] = org_id
        if standard_user_only is not None:
            params['standardUserOnly'] = str(standard_user_only).lower()
        url = self.ep('callRecording/vendorUsers')
        return self.session.follow_pagination(url=url, model=RecordingUser, params=params, item_key='members')

    async def list_org_users(self, standard_user_only: bool = None,
                       org_id: str = None, **params) -> List[RecordingUser]:
        """
        Get Call Recording Vendor Users

        Retrieve call recording vendor users of an organization. This API is used to get the list of users who are
        assigned to the default call-recording vendor of the organization.

        The Call Recording feature supports multiple third-party call recording providers, or vendors, to capture and
        manage call recordings. An organization is configured with an overall provider, but locations can be
        configured to use a different vendor than the overall organization default.

        Requires a full or read-only administrator auth token with a scope of `spark-admin:telephony_config_read`.

        :param standard_user_only: If true, results only include Webex Calling standard users.
        :type standard_user_only: bool
        :param org_id: Retrieve call recording vendor users for this organization.
        :type org_id: str
        """
        if org_id is not None:
            params['orgId'] = org_id
        if standard_user_only is not None:
            params['standardUserOnly'] = str(standard_user_only).lower()
        url = self.ep('callRecording/vendorUsers')
        return [o async for o in self.session.follow_pagination(url=url, model=RecordingUser, params=params, item_key='members')]

    async def set_location_vendor(self, location_id: str, id: str = None,
                            org_default_enabled: bool = None, storage_region: str = None,
                            org_storage_region_enabled: bool = None,
                            failure_behavior: FailureBehavior = None,
                            org_failure_behavior_enabled: bool = None,
                            org_id: str = None) -> str:
        """
        Set Call Recording Vendor for a Location

        Assign a call recording vendor to a location of an organization. Response will be `204` if the changes can be
        applied immediatley otherwise `200` with a job ID is returned.

        The Call Recording feature supports multiple third-party call recording providers, or vendors, to capture and
        manage call recordings. An organization is configured with an overall provider, but locations can be
        configured to use a different vendor than the overall organization default.

        Requires a full or read-only administrator auth token with a scope of `spark-admin:telephony_config_write`.

        :param location_id: Update the call recording vendor for this location
        :type location_id: str
        :param id: Unique identifier of the call recording vendor.
        :type id: str
        :param org_default_enabled: Vendor is enabled by default.
        :type org_default_enabled: bool
        :param storage_region: Regions where call recordings are stored.
        :type storage_region: str
        :param org_storage_region_enabled: Region-based call recording storage is enabled.
        :type org_storage_region_enabled: bool
        :param failure_behavior: Type of failure behavior.
        :type failure_behavior: FailureBehavior
        :param org_failure_behavior_enabled: Failure behavior is enabled.
        :type org_failure_behavior_enabled: bool
        :param org_id: Update the call recording vendor for this organization.
        :type org_id: str
        :rtype: str
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        if id is not None:
            body['id'] = id
        if org_default_enabled is not None:
            body['orgDefaultEnabled'] = org_default_enabled
        if storage_region is not None:
            body['storageRegion'] = storage_region
        if org_storage_region_enabled is not None:
            body['orgStorageRegionEnabled'] = org_storage_region_enabled
        if failure_behavior is not None:
            body['failureBehavior'] = enum_str(failure_behavior)
        if org_failure_behavior_enabled is not None:
            body['orgFailureBehaviorEnabled'] = org_failure_behavior_enabled
        url = self.ep(f'locations/{location_id}/callRecording/vendor')
        data = await super().put(url, params=params, json=body)
        r = data['jobId']
        return r

    async def get_location_vendors(self, location_id: str,
                             org_id: str = None) -> CallRecordingLocationVendors:
        """
        Get Location Call Recording Vendors

        Retrieve details of the call recording vendor that the location is assigned and also a list of vendors.

        The Call Recording feature supports multiple third-party call recording providers, or vendors, to capture and
        manage call recordings. An organization is configured with an overall provider, but locations can be
        configured to use a different vendor than the overall organization default.

        Requires a full or read-only administrator auth token with a scope of `spark-admin:telephony_config_read`.

        :param location_id: Retrieve vendor details for this location.
        :type location_id: str
        :param org_id: Retrieve vendor details for this organization.
        :type org_id: str
        :rtype: :class:`CallRecordingLocationVendors`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'locations/{location_id}/callRecording/vendors')
        data = await super().get(url, params=params)
        r = CallRecordingLocationVendors.model_validate(data)
        return r

    def list_location_users_gen(self, location_id: str,
                            standard_user_only: bool = None,
                            org_id: str = None, **params) -> AsyncGenerator[RecordingUser, None, None]:
        """
        Get Call Recording Vendor Users for a Location

        Retrieve call recording vendor users of a location. This API is used to get the list of users assigned to the
        call recording vendor of the location.

        The Call Recording feature supports multiple third-party call recording providers, or vendors, to capture and
        manage call recordings. An organization is configured with an overall provider, but locations can be
        configured to use a different vendor than the overall organization default.

        Requires a full or read-only administrator auth token with a scope of `spark-admin:telephony_config_read`.

        :param location_id: Retrieve vendor users for this location.
        :type location_id: str
        :param standard_user_only: If true, results only include Webex Calling standard users.
        :type standard_user_only: bool
        :param org_id: Retrieve vendor users for this organization.
        :type org_id: str
        """
        if org_id is not None:
            params['orgId'] = org_id
        if standard_user_only is not None:
            params['standardUserOnly'] = str(standard_user_only).lower()
        url = self.ep(f'locations/{location_id}/callRecording/vendorUsers')
        return self.session.follow_pagination(url=url, model=RecordingUser, params=params, item_key='members')

    async def list_location_users(self, location_id: str,
                            standard_user_only: bool = None,
                            org_id: str = None, **params) -> List[RecordingUser]:
        """
        Get Call Recording Vendor Users for a Location

        Retrieve call recording vendor users of a location. This API is used to get the list of users assigned to the
        call recording vendor of the location.

        The Call Recording feature supports multiple third-party call recording providers, or vendors, to capture and
        manage call recordings. An organization is configured with an overall provider, but locations can be
        configured to use a different vendor than the overall organization default.

        Requires a full or read-only administrator auth token with a scope of `spark-admin:telephony_config_read`.

        :param location_id: Retrieve vendor users for this location.
        :type location_id: str
        :param standard_user_only: If true, results only include Webex Calling standard users.
        :type standard_user_only: bool
        :param org_id: Retrieve vendor users for this organization.
        :type org_id: str
        """
        if org_id is not None:
            params['orgId'] = org_id
        if standard_user_only is not None:
            params['standardUserOnly'] = str(standard_user_only).lower()
        url = self.ep(f'locations/{location_id}/callRecording/vendorUsers')
        return [o async for o in self.session.follow_pagination(url=url, model=RecordingUser, params=params, item_key='members')]

    async def get_org_vendors(self, org_id: str = None) -> CallRecordingVendors:
        """
        Get Organization Call Recording Vendors

        Returns what the current vendor is as well as a list of all the available vendors.

        The Call Recording feature supports multiple third-party call recording providers, or vendors, to capture and
        manage call recordings. An organization is configured with an overall provider, but locations can be
        configured to use a different vendor than the overall organization default.

        Requires a full or read-only administrator auth token with a scope of `spark-admin:telephony_config_read`.

        :param org_id: Retrieve call recording settings from this organization.
        :type org_id: str
        :rtype: :class:`CallRecordingVendors`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep('callRecording/vendors')
        data = await super().get(url, params=params)
        r = CallRecordingVendors.model_validate(data)
        return r

    async def set_org_vendor(self, vendor_id: str, storage_region: str = None,
                       failure_behavior: FailureBehavior = None, org_id: str = None) -> str:
        """
        Set Organization Call Recording Vendor

        Returns a Job ID that you can use to get the status of the job.

        The Call Recording feature supports multiple third-party call recording providers, or vendors, to capture and
        manage call recordings. An organization is configured with an overall provider, but locations can be
        configured to use a different vendor than the overall organization default.

        Requires a full administrator auth token with a scope of `spark-admin:telephony_config_write`,
        `spark-admin:telephony_config_read`, and `spark-admin:people_write`.

        :param vendor_id: Unique identifier of the vendor.
        :type vendor_id: str
        :param storage_region: Call recording storage region. Only applicable for Webex as a vendor and isn't used for
            other vendors.
        :type storage_region: str
        :param failure_behavior: Call recording failure behavior.
        :type failure_behavior: FailureBehavior
        :param org_id: Modify call recording settings from this organization.
        :type org_id: str
        :rtype: str
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        body['vendorId'] = vendor_id
        if storage_region is not None:
            body['storageRegion'] = storage_region
        if failure_behavior is not None:
            body['failureBehavior'] = enum_str(failure_behavior)
        url = self.ep('callRecording/vendor')
        data = await super().put(url, params=params, json=body)
        r = data['jobId']
        return r


class AsTranslationPatternsApi(AsApiChild, base='telephony/config/callRouting/translationPatterns'):
    """
    Translation Patterns

    A translation pattern lets you manipulate dialed digits before routing a call and applies to outbound calls only.

    Call routing supports translation patterns at the organization level.

    Viewing these translation patterns for an organization requires a full or read-only administrator auth token with a
    scope of `spark-admin:telephony_config_read`.

    Modifying these translation patterns for an organization requires a full administrator auth token with a scope
    of `spark-admin:telephony_config_write`.
    """

    def ep(self, path: str = None, location_id: str = None):
        """
        Get ep with optional location_id

        :meta private:
        :param path:
        :param location_id:
        :return:
        """
        if location_id is None:
            # org level translation pattern endpoint
            return super().ep(path)
        path = path and f'/{path}' or ''
        base = 'telephony/config/locations'
        return self.session.ep(f'{base}/{location_id}/callRouting/translationPatterns{path}')

    def list_gen(self, limit_to_location_id: str = None,
             limit_to_org_level_enabled: bool = None, order: str = None, name: str = None,
             matching_pattern: str = None, org_id: str = None,
             **params) -> AsyncGenerator[TranslationPattern, None, None]:
        """
        Retrieve a list of Translation Patterns

        A translation pattern lets you manipulate dialed digits before routing a call and applies to outbound calls
        only.

        Retrieve a list of translation patterns for a given organization.

        Requires a full or read-only administrator auth token with a scope of `spark-admin:telephony_config_read`.

        :param limit_to_location_id: When a location ID is passed, then return only the corresponding location level
            translation patterns.
        :type limit_to_location_id: str
        :param limit_to_org_level_enabled: When set to be `true`, then return only the organization-level translation
            patterns.
        :type limit_to_org_level_enabled: bool
        :param order: Sort the list of translation patterns according to translation pattern name, ascending or
            descending.
        :type order: str
        :param name: Only return translation patterns with the matching `name`.
        :type name: str
        :param matching_pattern: Only return translation patterns with the matching `matchingPattern`.
        :type matching_pattern: str
        :param org_id: ID of the organization containing the translation patterns.
        :type org_id: str
        :return: Generator yielding :class:`TranslationPatternGet` instances
        """
        if org_id:
            params['orgId'] = org_id
        if limit_to_location_id is not None:
            params['limitToLocationId'] = limit_to_location_id
        if limit_to_org_level_enabled is not None:
            params['limitToOrgLevelEnabled'] = str(limit_to_org_level_enabled).lower()
        if order is not None:
            params['order'] = order
        if name is not None:
            params['name'] = name
        if matching_pattern is not None:
            params['matchingPattern'] = matching_pattern
        url = self.ep()
        return self.session.follow_pagination(url=url, model=TranslationPattern, item_key='translationPatterns',
                                              params=params)

    async def list(self, limit_to_location_id: str = None,
             limit_to_org_level_enabled: bool = None, order: str = None, name: str = None,
             matching_pattern: str = None, org_id: str = None,
             **params) -> List[TranslationPattern]:
        """
        Retrieve a list of Translation Patterns

        A translation pattern lets you manipulate dialed digits before routing a call and applies to outbound calls
        only.

        Retrieve a list of translation patterns for a given organization.

        Requires a full or read-only administrator auth token with a scope of `spark-admin:telephony_config_read`.

        :param limit_to_location_id: When a location ID is passed, then return only the corresponding location level
            translation patterns.
        :type limit_to_location_id: str
        :param limit_to_org_level_enabled: When set to be `true`, then return only the organization-level translation
            patterns.
        :type limit_to_org_level_enabled: bool
        :param order: Sort the list of translation patterns according to translation pattern name, ascending or
            descending.
        :type order: str
        :param name: Only return translation patterns with the matching `name`.
        :type name: str
        :param matching_pattern: Only return translation patterns with the matching `matchingPattern`.
        :type matching_pattern: str
        :param org_id: ID of the organization containing the translation patterns.
        :type org_id: str
        :return: Generator yielding :class:`TranslationPatternGet` instances
        """
        if org_id:
            params['orgId'] = org_id
        if limit_to_location_id is not None:
            params['limitToLocationId'] = limit_to_location_id
        if limit_to_org_level_enabled is not None:
            params['limitToOrgLevelEnabled'] = str(limit_to_org_level_enabled).lower()
        if order is not None:
            params['order'] = order
        if name is not None:
            params['name'] = name
        if matching_pattern is not None:
            params['matchingPattern'] = matching_pattern
        url = self.ep()
        return [o async for o in self.session.follow_pagination(url=url, model=TranslationPattern, item_key='translationPatterns',
                                              params=params)]

    async def create(self, pattern: TranslationPattern, location_id: str = None,
               org_id: str = None) -> str:
        """
        Create a Translation Pattern

        A translation pattern lets you manipulate dialed digits before routing a call and applies to outbound calls
        only.

        Create a translation pattern for a given organization.

        Requires a full administrator auth token with the `spark-admin:telephony_config_write` scope.

        :param pattern: Translation pattern to create
        :type pattern: TranslationPattern
        :param location_id: Unique identifier for the location. Only used when creating location level translation
        :type location_id: str
        :param org_id: ID of the organization containing the translation pattern
        :type org_id: str
        :rtype: str
        """
        params = org_id and {'orgId': org_id} or None
        body = pattern.create_update()
        url = self.ep(location_id=location_id)
        data = await super().post(url, params=params, json=body)
        r = data['id']
        return r

    async def details(self, translation_id: str,
                location_id: str = None,
                org_id: str = None) -> TranslationPattern:
        """
        Retrieve the details of a Translation Pattern

        A translation pattern lets you manipulate dialed digits before routing a call and applies to outbound calls
        only.

        Retrieve the details of a translation pattern for a given organization.

        Requires a full or read-only administrator auth token with a scope of `spark-admin:telephony_config_read`.

        :param translation_id: Retrieve the translation pattern with the matching ID.
        :type translation_id: str
        :param location_id: Unique identifier for the location. Only used when getting details for location level
            translation patterns
        :type location_id: str
        :param org_id: ID of the organization containing the translation pattern.
        :type org_id: str
        :rtype: :class:`TranslationPattern`
        """
        params = org_id and {'orgId': org_id} or None
        url = self.ep(path=translation_id, location_id=location_id)
        data = await super().get(url, params=params)
        r = TranslationPattern.model_validate(data)
        return r

    async def update(self, pattern: TranslationPattern, location_id: str = None, org_id: str = None):
        """
        Modify a Translation Pattern

        A translation pattern lets you manipulate dialed digits before routing a call and applies to outbound calls
        only.

        Modify a translation pattern for a given organization. To update a location level translation pattern the
        location.id attribute of the pattern has to be set

        Requires a full administrator auth token with the `spark-admin:telephony_config_write` scope.

        :param pattern: Translation pattern to be updated
        :type pattern: TranslationPattern
        :param location_id: Unique identifier for the location. Only used when updating location level translation
            pattern
        :type location_id: str
        :param org_id: ID of the organization containing the translation pattern.
        :type org_id: str
        :rtype: None
        """
        params = org_id and {'orgId': org_id} or None
        body = pattern.create_update()
        url = self.ep(location_id=location_id, path=pattern.id)
        await super().put(url, params=params, json=body)

    async def delete(self, translation_id: str, location_id: str = None, org_id: str = None):
        """
        Delete a Translation Pattern

        A translation pattern lets you manipulate dialed digits before routing a call and applies to outbound calls
        only.

        Delete a translation pattern for a given organization.

        Requires a full administrator auth token with the `spark-admin:telephony_config_write` scope.

        :param translation_id: Delete a translation pattern with the matching ID.
        :type translation_id: str
        :param location_id: Unique identifier for the location. Only used when deleting location level translation
            patterns
        :param org_id: ID of the organization containing the translation pattern.
        :type org_id: str
        :rtype: None
        """
        params = org_id and {'orgId': org_id} or None
        url = self.ep(location_id=location_id, path=translation_id)
        await super().delete(url, params=params)


@dataclass(init=False, repr=False)
class AsCallRoutingApi(AsApiChild, base='telephony/config'):
    """
    Call Routing Api
    """
    #: translation patterns
    tp: AsTranslationPatternsApi

    def __init__(self, session: AsRestSession):
        super().__init__(session=session)
        self.tp = AsTranslationPatternsApi(session=session)


class AsCallparkExtensionApi(AsApiChild, base='telephony'):
    """
    Call Park Extension API
    """

    def _endpoint(self, *, location_id: str = None, cpe_id: str = None) -> str:
        """
        call park extension specific feature endpoint like
        /v1/telephony/config/locations/{locationId}/callParkExtensions/{cpe_id}

        :meta private:
        :param location_id: Unique identifier for the location.
        :type location_id: str
        :param cpe_id: call park extension id
        :type cpe_id: str
        :return: full endpoint
        :rtype: str
        """
        if location_id is None:
            return self.session.ep('telephony/config/callParkExtensions')
        else:
            ep = self.session.ep(f'telephony/config/locations/{location_id}/callParkExtensions')
            if cpe_id:
                ep = f'{ep}/{cpe_id}'
            return ep

    def list_gen(self, extension: str = None, name: str = None, location_id: str = None,
             location_name: str = None, order: str = None, org_id: str = None,
             **params) -> AsyncGenerator[CallParkExtension, None, None]:
        """
        Read the List of Call Park Extensions

        List all Call Park Extensions for the organization.

        The Call Park service, enabled for all users by default, allows a user to park a call against an available
        user's extension or to a Call Park Extension. Call Park Extensions are extensions defined within the Call
        Park service for holding parked calls.

        Retrieving this list requires a full or read-only administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param extension: Only return call park extensions with the matching extension.
        :type extension: str
        :param name: Only return call park extensions with the matching name.
        :type name: str
        :param location_id: Only return call park extensions with matching location ID.
        :type location_id: str
        :param location_name: Only return call park extensions with matching location name.
        :type location_name: str
        :param order: Order the available agents according to the designated fields. Available sort fields: groupName,
            callParkExtension, callParkExtensionName, callParkExtensionExternalId.
        :type order: str
        :param org_id: List call park extensions for this organization.
        :type org_id: str
        :param params: additional parameters
        :return: yields :class:`wxc_sdk.common.CallParkExtension` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if location_id is not None:
            params['locationId'] = location_id
        if extension is not None:
            params['extension'] = extension
        if location_name is not None:
            params['locationName'] = location_name
        if name is not None:
            params['name'] = name
        if order is not None:
            params['order'] = order
        url = self._endpoint()
        # noinspection PyTypeChecker
        return self.session.follow_pagination(url=url, model=CallParkExtension, params=params)

    async def list(self, extension: str = None, name: str = None, location_id: str = None,
             location_name: str = None, order: str = None, org_id: str = None,
             **params) -> List[CallParkExtension]:
        """
        Read the List of Call Park Extensions

        List all Call Park Extensions for the organization.

        The Call Park service, enabled for all users by default, allows a user to park a call against an available
        user's extension or to a Call Park Extension. Call Park Extensions are extensions defined within the Call
        Park service for holding parked calls.

        Retrieving this list requires a full or read-only administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param extension: Only return call park extensions with the matching extension.
        :type extension: str
        :param name: Only return call park extensions with the matching name.
        :type name: str
        :param location_id: Only return call park extensions with matching location ID.
        :type location_id: str
        :param location_name: Only return call park extensions with matching location name.
        :type location_name: str
        :param order: Order the available agents according to the designated fields. Available sort fields: groupName,
            callParkExtension, callParkExtensionName, callParkExtensionExternalId.
        :type order: str
        :param org_id: List call park extensions for this organization.
        :type org_id: str
        :param params: additional parameters
        :return: yields :class:`wxc_sdk.common.CallParkExtension` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if location_id is not None:
            params['locationId'] = location_id
        if extension is not None:
            params['extension'] = extension
        if location_name is not None:
            params['locationName'] = location_name
        if name is not None:
            params['name'] = name
        if order is not None:
            params['order'] = order
        url = self._endpoint()
        # noinspection PyTypeChecker
        return [o async for o in self.session.follow_pagination(url=url, model=CallParkExtension, params=params)]

    async def details(self, location_id: str, cpe_id: str, org_id: str = None) -> CallParkExtension:
        """
        Get Details for a Call Park Extension

        Retrieve Call Park Extension details.

        The Call Park service, enabled for all users by default, allows a user to park a call against an available
        user's extension or to a Call Park Extension. Call Park Extensions are extensions defined within the Call
        Park service for holding parked calls.

        Retrieving call park extension details requires a full or read-only administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param location_id: Retrieve details for a call park extension in this location.
        :type location_id: str
        :param cpe_id: Retrieve details for a call park extension with the matching ID.
        :type cpe_id: str
        :param org_id: Retrieve call park extension details from this organization
        :type org_id: str
        :return: call park extension details
        :rtype: :class:`wxc_sdk.common.CallParkExtension` instance (only name and extension are set)
        """
        url = self._endpoint(location_id=location_id, cpe_id=cpe_id)
        params = org_id and {'orgId': org_id} or {}
        data = await self.get(url, params=params)
        return CallParkExtension.model_validate(data)

    async def create(self, location_id: str, name: str, extension: str, org_id: str = None, ) -> str:
        """
        Create a Call Park Extension

        Create new Call Park Extensions for the given location.

        Call Park Extension enables a call recipient to park a call to an extension, so someone else within the same
        Organization can retrieve the parked call by dialing that extension. Call Park Extensions can be added as
        monitored lines by users' Cisco phones, so users can park and retrieve calls by pressing the associated phone
        line key.

        Creating a call park extension requires a full administrator or location administrator auth token with a scope
        of `spark-admin:telephony_config_write`.

        :param location_id: Create the call park extension for this location.
        :type location_id: str
        :param name: Name for the call park extension. The maximum length is 30.
        :type name: str
        :param extension: Unique extension which will be assigned to call park extension. The minimum length is 2,
            maximum length is 10.
        :type extension: str
        :param org_id: Create the call park extension for this organization.
        :type org_id: str
        :return: id of the new call park extension
        :rtype: str
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = {'name': name, 'extension': extension}
        url = self._endpoint(location_id=location_id)
        data = await super().post(url=url, params=params, json=body)
        return data["id"]

    async def delete(self, location_id: str, cpe_id: str, org_id: str = None):
        """
        Delete a Call Park Extension

        Delete the designated Call Park Extension.

        Call Park Extension enables a call recipient to park a call to an extension, so someone else within the same
        Organization can retrieve the parked call by dialing that extension. Call Park Extensions can be added as
        monitored lines by users' Cisco phones, so users can park and retrieve calls by pressing the associated phone
        line key.

        Deleting a call park extension requires a full administrator or location administrator auth token with a scope
        of `spark-admin:telephony_config_write`.

        :param location_id: Location from which to delete a call park extension.
        :type location_id: str
        :param cpe_id: Delete the call park extension with the matching ID.
        :type cpe_id: str
        :param org_id: Delete the call park extension from this organization.
        :type org_id: str
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self._endpoint(location_id=location_id, cpe_id=cpe_id)

        await super().delete(url=url, params=params)
        return

    async def update(self, location_id: str, cpe_id: str, name: str = None, extension: str = None, org_id: str = None):
        """
        Update a Call Park Extension

        Update the designated Call Park Extension.

        Call Park Extension enables a call recipient to park a call to an extension, so someone else within the same
        Organization can retrieve the parked call by dialing that extension. Call Park Extensions can be added as
        monitored lines by users' Cisco phones, so users can park and retrieve calls by pressing the associated phone
        line key.

        Updating a call park extension requires a full administrator or location administrator auth token with a scope
        of `spark-admin:telephony_config_write`.

        :param location_id: Location in which this call park extension exists.
        :type location_id: str
        :param cpe_id: Update a call park extension with the matching ID.
        :type cpe_id: str
        :param name: Name for the call park extension. The maximum length is 30.
        :type name: str
        :param extension: Unique extension which will be assigned to call park extension. The minimum length is 2,
            maximum length is 10.
        :type extension: str
        :param org_id: Update a call park extension from this organization.
        :type org_id: str
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = {}
        if name is not None:
            body['name'] = name
        if extension is not None:
            body['extension'] = extension
        url = self._endpoint(location_id=location_id, cpe_id=cpe_id)
        await super().put(url=url, params=params, json=body)
        return


class AsCallsApi(AsApiChild, base='telephony/calls'):
    """
    Call Controls

    Call Control APIs in support of Webex Calling. All `GET` commands require the `spark:calls_read` scope while all
    other commands require the `spark:calls_write` scope.

    **Notes:**

    - These APIs support 3rd Party Call Control only.
    - The Call Control APIs are only for use by Webex Calling Multi Tenant users and not applicable for users hosted on
      UCM, including Dedicated Instance users.
    """

    async def list_calls(self) -> list[TelephonyCall]:
        """
        List Calls

        Get the list of details for all active calls associated with the user.

        :rtype: list[Call]
        """
        url = self.ep()
        data = await super().get(url)
        r = TypeAdapter(list[TelephonyCall]).validate_python(data['items'])
        return r

    async def answer(self, call_id: str, endpoint_id: str = None):
        """
        Answer

        Answer an incoming call. When no endpointId is specified, the call is answered on the user's primary device.
        When an endpointId is specified, the call is answered on the device or application identified by the
        endpointId. The answer API is rejected if the device is not alerting for the call or the device does not
        support answer via API.

        :param call_id: The call identifier of the call to be answered.
        :type call_id: str
        :param endpoint_id: The ID of the device or application to answer the call on. The `endpointId` must be one of
            the endpointIds returned by the `Get Preferred Answer Endpoint API
            <https://developer.webex.com/docs/api/v1/user-call-settings-2-2/get-preferred-answer-endpoint>`_.
        :type endpoint_id: str
        :rtype: None
        """
        body = dict()
        body['callId'] = call_id
        if endpoint_id is not None:
            body['endpointId'] = endpoint_id
        url = self.ep('answer')
        await super().post(url, json=body)

    async def barge_in(self, target: str, endpoint_id: str = None) -> CallInfo:
        """
        Barge In

        Barge-in on another user's answered call. A new call is initiated to perform the barge-in in a similar manner
        to the dial command.

        :param target: Identifies the user to barge-in on. The target can be digits or a URI. Some examples for target
            include: `1234`, `2223334444`, `+12223334444`, `tel:+12223334444`, `user@company.domain`,
            `sip:user@company.domain`
        :type target: str
        :param endpoint_id: The ID of the device or application to use for the barge-in. The `endpointId` must be one
            of the endpointIds returned by the `Get Preferred Answer Endpoint API
            <https://developer.webex.com/docs/api/v1/user-call-settings-2-2/get-preferred-answer-endpoint>`_.
        :type endpoint_id: str
        :rtype: :class:`CallInfo`
        """
        body = dict()
        body['target'] = target
        if endpoint_id is not None:
            body['endpointId'] = endpoint_id
        url = self.ep('bargeIn')
        data = await super().post(url, json=body)
        r = CallInfo.model_validate(data)
        return r

    async def dial(self, destination: str, endpoint_id: str = None) -> CallInfo:
        """
        Dial

        Initiate an outbound call to a specified destination. This is also commonly referred to as Click to Call or
        Click to Dial. Alerts occur on all the devices belonging to a user unless an optional endpointId is specified
        in which case only the device or application identified by the endpointId is alerted. When a user answers an
        alerting device, an outbound call is placed from that device to the destination.

        :param destination: The destination to be dialed. The destination can be digits or a URI. Some examples for
            destination include: `1234`, `2223334444`, `+12223334444`, `*73`, `tel:+12223334444`,
            `user@company.domain`, and `sip:user@company.domain`.
        :type destination: str
        :param endpoint_id: The ID of the device or application to use for the call. The `endpointId` must be one of
            the endpointIds returned by the `Get Preferred Answer Endpoint API
            <https://developer.webex.com/docs/api/v1/user-call-settings-2-2/get-preferred-answer-endpoint>`_.
        :type endpoint_id: str
        :rtype: :class:`CallInfo`
        """
        body = dict()
        body['destination'] = destination
        if endpoint_id is not None:
            body['endpointId'] = endpoint_id
        url = self.ep('dial')
        data = await super().post(url, json=body)
        r = CallInfo.model_validate(data)
        return r

    async def divert(self, call_id: str, destination: str = None, to_voicemail: bool = None):
        """
        Divert

        Divert a call to a destination or a user's voicemail. This is also commonly referred to as a Blind Transfer.

        :param call_id: The call identifier of the call to divert.
        :type call_id: str
        :param destination: The destination to divert the call to. If toVoicemail is false, destination is required.
            The destination can be digits or a URI. Some examples for destination include: `1234`, `2223334444`,
            `+12223334444`, `*73`, `tel:+12223334444`, `user@company.domain`, `sip:user@company.domain`
        :type destination: str
        :param to_voicemail: If set to true, the call is diverted to voicemail. If no destination is specified, the
            call is diverted to the user's own voicemail. If a destination is specified, the call is diverted to the
            specified user's voicemail.
        :type to_voicemail: bool
        :rtype: None
        """
        body = dict()
        body['callId'] = call_id
        if destination is not None:
            body['destination'] = destination
        if to_voicemail is not None:
            body['toVoicemail'] = to_voicemail
        url = self.ep('divert')
        await super().post(url, json=body)

    async def hangup(self, call_id: str):
        """
        Hangup

        Hangup a call. If used on an unanswered incoming call, the call is rejected and sent to busy.

        :param call_id: The call identifier of the call to hangup.
        :type call_id: str
        :rtype: None
        """
        body = dict()
        body['callId'] = call_id
        url = self.ep('hangup')
        await super().post(url, json=body)

    async def call_history(self, history_type: HistoryType = None) -> list[CallHistoryRecord]:
        """
        List Call History

        Get the list of call history records for the user. A maximum of 20 call history records per type (`placed`,
        `missed`, `received`) are returned.

        :param history_type: The type of call history records to retrieve. If not specified, then all call history
        records are
            retrieved.
        :type history_type: CallHistoryRecordTypeEnum
        :rtype: list[CallHistoryRecord]
        """
        params = {}
        if history_type is not None:
            params['type'] = enum_str(history_type)
        url = self.ep('history')
        data = await super().get(url, params=params)
        r = TypeAdapter(list[CallHistoryRecord]).validate_python(data['items'])
        return r

    async def hold(self, call_id: str):
        """
        Hold

        Hold a connected call.

        :param call_id: The call identifier of the call to hold.
        :type call_id: str
        :rtype: None
        """
        body = dict()
        body['callId'] = call_id
        url = self.ep('hold')
        await super().post(url, json=body)

    async def mute(self, call_id: str):
        """
        Mute

        Mute a call. This API can only be used for a call that reports itself as mute capable via the muteCapable field
        in the call details.

        :param call_id: The call identifier of the call to mute.
        :type call_id: str
        :rtype: None
        """
        body = dict()
        body['callId'] = call_id
        url = self.ep('mute')
        await super().post(url, json=body)

    async def park(self, call_id: str, destination: str = None, is_group_park: bool = None) -> TelephonyParty:
        """
        Park

        Park a connected call. The number field in the response can be used as the destination for the retrieve command
        to retrieve the parked call.

        :param call_id: The call identifier of the call to park.
        :type call_id: str
        :param destination: Identifes where the call is to be parked. If not provided, the call is parked against the
            parking user. The destination can be digits or a URI. Some examples for destination include: `1234`,
            `2223334444`, `+12223334444`, `*73`, `tel:+12223334444`, `user@company.domain`, `sip:user@company.domain`
        :type destination: str
        :param is_group_park: If set to`true`, the call is parked against an automatically selected member of the
            user's call park group and the destination parameter is ignored.
        :type is_group_park: bool
        :rtype: TelephonyParty
        """
        body = dict()
        body['callId'] = call_id
        if destination is not None:
            body['destination'] = destination
        if is_group_park is not None:
            body['isGroupPark'] = is_group_park
        url = self.ep('park')
        data = await super().post(url, json=body)
        r = TelephonyParty.model_validate(data['parkedAgainst'])
        return r

    async def pause_recording(self, call_id: str = None):
        """
        Pause Recording

        Pause recording on a call. Use of this API is only valid when a call is being recorded and the user's call
        recording mode is set to "On Demand" or "Always with Pause/Resume".

        :param call_id: The call identifier of the call to pause recording.
        :type call_id: str
        :rtype: None
        """
        body = dict()
        if call_id is not None:
            body['callId'] = call_id
        url = self.ep('pauseRecording')
        await super().post(url, json=body)

    async def pickup(self, target: str = None, endpoint_id: str = None) -> CallInfo:
        """
        Pickup

        Picks up an incoming call to another user. A new call is initiated to perform the pickup in a similar manner to
        the dial command. When target is not present, the API pickups up a call from the user's call pickup group.
        When target is present, the API pickups an incoming call from the specified target user.

        :param target: Identifies the user to pickup an incoming call from. If not provided, an incoming call to the
            user's call pickup group is picked up. The target can be digits or a URI. Some examples for target
            include: `1234`, `2223334444`, `+12223334444`, `tel:+12223334444`, `user@company.domain`,
            `sip:user@company.domain`
        :type target: str
        :param endpoint_id: The ID of the device or application to use for the pickup. The `endpointId` must be one of
            the endpointIds returned by the `Get Preferred Answer Endpoint API
            <https://developer.webex.com/docs/api/v1/user-call-settings-2-2/get-preferred-answer-endpoint>`_.
        :type endpoint_id: str
        :rtype: :class:`CallInfo`
        """
        body = dict()
        if target is not None:
            body['target'] = target
        if endpoint_id is not None:
            body['endpointId'] = endpoint_id
        url = self.ep('pickup')
        data = await super().post(url, json=body)
        r = CallInfo.model_validate(data)
        return r

    async def push(self, call_id: str = None):
        """
        Push

        Pushes a call from the assistant to the executive the call is associated with. Use of this API is only valid
        when the assistant's call is associated with an executive.

        :param call_id: The call identifier of the call to push.
        :type call_id: str
        :rtype: None
        """
        body = dict()
        if call_id is not None:
            body['callId'] = call_id
        url = self.ep('push')
        await super().post(url, json=body)

    async def reject(self, call_id: str, action: RejectAction = None):
        """
        Reject

        Reject an unanswered incoming call.

        :param call_id: The call identifier of the call to be rejected.
        :type call_id: str
        :param action: The rejection action to apply to the call. The busy action is applied if no specific action is
            provided.
        :type action: RejectAction
        :rtype: None
        """
        body = dict()
        body['callId'] = call_id
        if action is not None:
            body['action'] = enum_str(action)
        url = self.ep('reject')
        await super().post(url, json=body)

    async def resume(self, call_id: str):
        """
        Resume

        Resume a held call.

        :param call_id: The call identifier of the call to resume.
        :type call_id: str
        :rtype: None
        """
        body = dict()
        body['callId'] = call_id
        url = self.ep('resume')
        await super().post(url, json=body)

    async def resume_recording(self, call_id: str = None):
        """
        Resume Recording

        Resume recording a call. Use of this API is only valid when a call's recording is paused and the user's call
        recording mode is set to "On Demand" or "Always with Pause/Resume".

        :param call_id: The call identifier of the call to resume recording.
        :type call_id: str
        :rtype: None
        """
        body = dict()
        if call_id is not None:
            body['callId'] = call_id
        url = self.ep('resumeRecording')
        await super().post(url, json=body)

    async def retrieve(self, destination: str = None, endpoint_id: str = None) -> CallInfo:
        """
        Retrieve

        Retrieve a parked call. A new call is initiated to perform the retrieval in a similar manner to the dial
        command. The number field from the park command response can be used as the destination for the retrieve
        command.

        :param destination: Identifies where the call is parked. The number field from the park command response can be
            used as the destination for the retrieve command. If not provided, the call parked against the retrieving
            user is retrieved. The destination can be digits or a URI. Some examples for destination include: `1234`,
            `2223334444`, `+12223334444`, `*73`, `tel:+12223334444`, `user@company.domain`, `sip:user@company.domain`
        :type destination: str
        :param endpoint_id: The ID of the device or application to use for the retrieval. The `endpointId` must be one
            of the endpointIds returned by the `Get Preferred Answer Endpoint API
            <https://developer.webex.com/docs/api/v1/user-call-settings-2-2/get-preferred-answer-endpoint>`_.
        :type endpoint_id: str
        :rtype: :class:`CallInfo`
        """
        body = dict()
        if destination is not None:
            body['destination'] = destination
        if endpoint_id is not None:
            body['endpointId'] = endpoint_id
        url = self.ep('retrieve')
        data = await super().post(url, json=body)
        r = CallInfo.model_validate(data)
        return r

    async def start_recording(self, call_id: str = None):
        """
        Start Recording

        Start recording a call. Use of this API is only valid when the user's call recording mode is set to "On
        Demand".

        :param call_id: The call identifier of the call to start recording.
        :type call_id: str
        :rtype: None
        """
        body = dict()
        if call_id is not None:
            body['callId'] = call_id
        url = self.ep('startRecording')
        await super().post(url, json=body)

    async def stop_recording(self, call_id: str = None):
        """
        Stop Recording

        Stop recording a call. Use of this API is only valid when a call is being recorded and the user's call
        recording mode is set to "On Demand".

        :param call_id: The call identifier of the call to stop recording.
        :type call_id: str
        :rtype: None
        """
        body = dict()
        if call_id is not None:
            body['callId'] = call_id
        url = self.ep('stopRecording')
        await super().post(url, json=body)

    async def transfer(self, call_id1: str = None, call_id2: str = None, destination: str = None) -> CallInfo:
        """
        Transfer

        Transfer two calls together.

        Unanswered incoming calls cannot be transferred but can be diverted using the divert API.

        If the user has only two calls and wants to transfer them together, the `callId1` and `callId2` parameters are
        optional and when not provided the calls are automatically selected and transferred.

        If the user has more than two calls and wants to transfer two of them together, the `callId1` and `callId2`
        parameters are mandatory to specify which calls are being transferred. Those are also commonly referred to as
        Attended Transfer, Consultative Transfer, or Supervised Transfer and will return a `204` response.

        If the user wants to transfer one call to a new destination but only when the destination responds, the
        `callId1` and destination parameters are mandatory to specify the call being transferred and the destination.

        This is referred to as a Mute Transfer and is similar to the divert API with the difference of waiting for the
        destination to respond prior to transferring the call. If the destination does not respond, the call is not
        transferred. This will return a `201` response.

        :param call_id1: The call identifier of the first call to transfer. This parameter is mandatory if either
            `callId2` or `destination` is provided.
        :type call_id1: str
        :param call_id2: The call identifier of the second call to transfer. This parameter is mandatory if `callId1`
            is provided and `destination` is not provided.
        :type call_id2: str
        :param destination: The destination to be transferred to. The destination can be digits or a URI. Some examples
            for destination include: `1234`, `2223334444`, `+12223334444`, `tel:+12223334444`, `user@company.domain`,
            `sip:user@company.domain`. This parameter is mandatory if `callId1` is provided and `callId2` is not
            provided.
        :type destination: str
        :rtype: :class:`CallInfo`
        """
        body = dict()
        if call_id1 is not None:
            body['callId1'] = call_id1
        if call_id2 is not None:
            body['callId2'] = call_id2
        if destination is not None:
            body['destination'] = destination
        url = self.ep('transfer')
        data = await super().post(url, json=body)
        r = CallInfo.model_validate(data)
        return r

    async def transmit_dtmf(self, call_id: str = None, dtmf: str = None):
        """
        Transmit DTMF

        Transmit DTMF digits to a call.

        :param call_id: The call identifier of the call to transmit DTMF digits for.
        :type call_id: str
        :param dtmf: The DTMF digits to transmit. Each digit must be part of the following set: `[0, 1, 2, 3, 4, 5, 6,
            7, 8, 9, *, #, A, B, C, D]`. A comma "," may be included to indicate a pause between digits. For the value
            “1,234”, the DTMF 1 digit is initially sent. After a pause, the DTMF 2, 3, and 4 digits are sent
            successively.
        :type dtmf: str
        :rtype: None
        """
        body = dict()
        if call_id is not None:
            body['callId'] = call_id
        if dtmf is not None:
            body['dtmf'] = dtmf
        url = self.ep('transmitDtmf')
        await super().post(url, json=body)

    async def unmute(self, call_id: str):
        """
        Unmute

        Unmute a call. This API can only be used for a call that reports itself as mute capable via the muteCapable
        field in the call details.

        :param call_id: The call identifier of the call to unmute.
        :type call_id: str
        :rtype: None
        """
        body = dict()
        body['callId'] = call_id
        url = self.ep('unmute')
        await super().post(url, json=body)

    async def call_details(self, call_id: str) -> TelephonyCall:
        """
        Get Call Details

        Get the details of the specified active call for the user.

        :param call_id: The call identifier of the call.
        :type call_id: str
        :rtype: :class:`Call`
        """
        url = self.ep(f'{call_id}')
        data = await super().get(url)
        r = TelephonyCall.model_validate(data)
        return r

    async def update_external_voicemail_mwi(self, id: str, action: ExternalVoicemailMwiAction, org_id: str = None):
        """
        Set or Clear Message Waiting Indicator (MWI) Status

        Enables an external voicemail service to SET or CLEAR the Message Waiting Indicator (MWI) for a person or
        workspace.

        Invoke the API using a bearer token from a Service App in the target organization, created by a full admin with
        the scope `spark-admin:calls_write`.

        Specify the target user or workspace with the required `id` query parameter.

        Optionally, use the orgId parameter to indicate the organization; if omitted, the Service App’s organization is
        used.

        If `orgId` is provided, it must match the Service App’s organization or be a managed organization.

        Set the desired action (SET or CLEAR) in the message body’s action field.

        Learn more about `using Webex Service Apps
        <https://developer.webex.com/messaging/docs/service-apps>`_.

        :param id: Unique identifier for the user or workspace.
        :type id: str
        :param action: Indicates whether to SET or CLEAR the MWI status.
        :type action: ExternalVoicemailMwiAction
        :param org_id: Id of the organization to which the user or workspace belongs. If not provided, the orgId of the
            Service App is used. If provided, the organization must be the same as or managed by the Service App's
            organization.
        :type org_id: str
        :rtype: None
        """
        params = {}
        params['id'] = id
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        body['action'] = enum_str(action)
        url = self.session.ep('telephony/externalVoicemail/mwi')
        await super().post(url, params=params, json=body)


class AsConferenceControlsApi(AsApiChild, base='telephony/conference'):
    """
    Conference Controls

    Conference Control APIs in support of Webex Calling.

    All `GET` commands require the `spark:calls_read` scope while all other commands require the `spark:calls_write`
    scope.
    """

    async def release_conference(self):
        """
        Release Conference

        Release the conference (the host and all participants). Note that for a 3WC (three-way call) the `Transfer API
        <https://developer.webex.com/docs/api/v1/call-controls/transfer>`_
        can be used to perform an attended transfer so that the participants remain connected.

        :rtype: None
        """
        url = self.ep()
        await super().delete(url)

    async def get_conference_details(self) -> ConferenceDetails:
        """
        Get Conference Details

        Get the details of the conference.  An empty JSON object body is returned if there is no conference.

        :rtype: :class:`ConferenceDetails`
        """
        url = self.ep()
        data = await super().get(url)
        r = ConferenceDetails.model_validate(data)
        return r

    async def start_conference(self, call_ids: list[str]):
        """
        Start Conference

        Join the user's calls into a conference.  A minimum of two call IDs are required. Each call ID identifies an
        existing call between the user
        and a participant to be added to the conference.

        :param call_ids: List of call identifiers of the participants to join into the conference. A minimum of two
            call IDs are required.
        :type call_ids: list[str]
        :rtype: None
        """
        body = dict()
        body['callIds'] = call_ids
        url = self.ep()
        await super().post(url, json=body)

    async def add_participant(self, call_id: str):
        """
        Add Participant

        Adds a participant to an existing conference.  The request body contains the participant's call ID.

        :param call_id: The call identifier of the participant to add.
        :type call_id: str
        :rtype: None
        """
        body = dict()
        body['callId'] = call_id
        url = self.ep('addParticipant')
        await super().post(url, json=body)

    async def deafen_participant(self, call_id: str):
        """
        Deafen Participant

        Deafens a participant (i.e. media stream will not be transmitted to the participant).
        The request body contains the call ID of the participant to deafen

        :param call_id: The call identifier of the participant to deafen.
        :type call_id: str
        :rtype: None
        """
        body = dict()
        body['callId'] = call_id
        url = self.ep('deafen')
        await super().post(url, json=body)

    async def hold(self):
        """
        Hold

        Hold the conference host.  There is no request body.

        :rtype: None
        """
        url = self.ep('hold')
        await super().post(url)

    async def mute(self, call_id: str = None):
        """
        Mute

        Mutes the host or a participant. Mutes the host when no request body is provided (i.e. media stream from the
        host will not be transmitted to the conference).  Mutes a participant when the request body contains the
        participant's call ID (i.e. media stream from the participant will not be transmitted to the conference).

        :param call_id: The call identifier of the participant to mute. The conference host is muted when this
            attribute is not provided.
        :type call_id: str
        :rtype: None
        """
        body = dict()
        if call_id is not None:
            body['callId'] = call_id
        url = self.ep('mute')
        await super().post(url, json=body)

    async def resume(self):
        """
        Resume

        Resumes the held conference host.  There is no request body.

        :rtype: None
        """
        url = self.ep('resume')
        await super().post(url)

    async def undeafen_participant(self, call_id: str):
        """
        Undeafen Participant

        Undeafens a participant (i.e. resume transmitting the conference media stream to the participant).
        The request body contains the call ID of the participant to undeafen.

        :param call_id: The call identifier of the participant to undeafen.
        :type call_id: str
        :rtype: None
        """
        body = dict()
        body['callId'] = call_id
        url = self.ep('undeafen')
        await super().post(url, json=body)

    async def unmute(self, call_id: str = None):
        """
        Unmute

        Unmutes the host or a participant. Unmutes the host when no request body is provided (i.e. media stream from
        the host will be transmitted to the conference).  Unmutes a participant when the request body contains the
        participant's call ID (i.e. media stream from the participant will be transmitted to the conference).

        :param call_id: The call identifier of the participant to unmute. The conference host is unmuted when this
            attribute is not provided.
        :type call_id: str
        :rtype: None
        """
        body = dict()
        if call_id is not None:
            body['callId'] = call_id
        url = self.ep('unmute')
        await super().post(url, json=body)


class AsQueueCallRecordingSettingsApi(AsApiChild, base='telephony/config/locations'):
    """
    Queue Call Recording Settings

    Queue Call Settings supports modifying Webex Calling settings for a specific queue.

    Viewing Queue recording settings requires a full, user, or read-only administrator or location administrator auth
    token with a scope of `spark-admin:people_read` or, for select APIs, a user auth token with `spark:people_read`
    scope can be used by a person to read their own settings.

    Configuring Queue recording settings requires a full or user administrator or location administrator auth token
    with the `spark-admin:people_write` scope or, for select APIs, a user auth token with `spark:people_write` scope
    can be used by a person to update their own settings.

    Call Queue Recording Settings API access can be restricted via Control Hub by a full administrator. Restricting
    access causes the APIs to throw a `403 Access Forbidden` error.

    See details about `features available by license type for Webex Calling
    <https://help.webex.com/en-us/article/n1qbbp7/Features-available-by-license-type-for-Webex-Calling>`_.
    """

    async def read(self, location_id: str, queue_id: str,
             org_id: str = None) -> CallRecordingSetting:
        """
        Read Queue Call Recording Settings for a Queue

        Retrieve a queue's Call Recording settings.

        The Call Recording feature provides a hosted mechanism to record the calls placed and received on the Carrier
        platform for replay and archival. This feature is helpful for quality assurance, security, training, and more.

        This API requires a full or user administrator or location administrator auth token with the
        `spark-admin:people_read` scope.

        A person with a Webex Calling Standard license is eligible for the Call Recording
        feature only when the Call Recording vendor is Webex.

        :param location_id: Unique identifier for the location.
        :type location_id: str
        :param queue_id: Unique identifier for the queue.
        :type queue_id: str
        :param org_id: ID of the organization in which the queue resides. Only admin users of another organization
            (such as partners) may use this parameter as the default is the same organization as the token used to
            access API.
        :type org_id: str
        :rtype: :class:`CallRecordingSetting`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'{location_id}/queues/{queue_id}/cxEssentials/callRecordings')
        data = await super().get(url, params=params)
        r = CallRecordingSetting.model_validate(data)
        return r

    async def configure(self, location_id: str, queue_id: str,
                  recording: CallRecordingSetting,
                  org_id: str = None):
        """
        Configure Queue Call Recording Settings for a Queue

        Configure a queue's Call Recording settings.

        The Call Recording feature provides a hosted mechanism to record the calls placed and received on the Carrier
        platform for replay and archival. This feature is helpful for quality assurance, security, training, and more.

        This API requires a full or user administrator or location administrator auth token with the
        `spark-admin:people_write` scope.

        <div><Callout type="warning">A person with a Webex Calling Standard license is eligible for the Call Recording
        feature only when the Call Recording vendor is Webex.</Callout></div>

        :param location_id: Unique identifier for the location.
        :type location_id: str
        :param queue_id: Unique identifier for the queue.
        :type queue_id: str
        :param recording: the new recording settings
        :type recording: CallRecordingSetting
        :param org_id: ID of the organization in which the queue resides. Only admin users of another organization
            (such as partners) may use this parameter as the default is the same organization as the token used to
            access API.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = recording.update()
        url = self.ep(f'{location_id}/queues/{queue_id}/cxEssentials/callRecordings')
        await super().put(url, params=params, json=body)


class AsWrapupReasonApi(AsApiChild, base='telephony/config'):
    """
    Wrap up reasons API
    """

    async def read_queue_settings(self, location_id: str, queue_id: str) -> QueueWrapupReasonSettings:
        """
        Read Wrap Up Reason Settings

        Return a wrap-up reason by location ID and queue ID.

        Agents handling calls use wrap-up reasons to categorize the outcome after a call ends. The control hub admin
        can configure these reasons for customers and assign them to queues.

        Upon call completion, agents select a wrap-up reason from the queue's assigned list. Each wrap-up reason
        includes a name and description, and can be set as the default for a queue.

        Admins can also configure a timer, which dictates the time agents have to select a reason post-call, with a
        default of 60 seconds. This timer can be disabled if necessary.

        Retrieving the wrap-up reason by location ID and queue ID requires a full or read-only administrator auth token
        with a scope of `spark-admin:telephony_config_read`.

        :param location_id: The location ID.
        :type location_id: str
        :param queue_id: The queue ID.
        :type queue_id: str
        :rtype: list[QueueWrapupReasonSettings]
        """
        url = self.ep(f'cxEssentials/locations/{location_id}/queues/{queue_id}/wrapup/settings')
        data = await super().get(url)
        return QueueWrapupReasonSettings.model_validate(data)

    async def update_queue_settings(self, location_id: str, queue_id: str, wrapup_reasons: list[str] = None,
                              default_wrapup_reason_id: str = None, wrapup_timer_enabled: bool = None,
                              wrapup_timer: int = None):
        """
        Update Wrap Up Reason Settings

        Modify a wrap-up reason by location ID and queue ID.

        Agents handling calls use wrap-up reasons to categorize the outcome after a call ends. The control hub admin
        can configure these reasons for customers and assign them to queues.

        Upon call completion, agents select a wrap-up reason from the queue's assigned list. Each wrap-up reason
        includes a name and description, and can be set as the default for a queue.

        Admins can also configure a timer, which dictates the time agents have to select a reason post-call, with a
        default of 60 seconds. This timer can be disabled if necessary.

        Modifying a wrap-up reason by location ID and queue ID requires a full or device administrator auth token with
        a scope of `spark-admin:telephony_config_write`.

        :param location_id: The location ID.
        :type location_id: str
        :param queue_id: The queue ID.
        :type queue_id: str
        :param wrapup_reasons: List of wrap-up reason IDs.
        :type wrapup_reasons: list[str]
        :param default_wrapup_reason_id: Unique wrap-up identifier. To clear the default wrap-up reason, set this to ''.
        :type default_wrapup_reason_id: str
        :param wrapup_timer_enabled: Denotes whether the wrap-up timer is enabled.
        :type wrapup_timer_enabled: bool
        :param wrapup_timer: Wrap up timer value in seconds.
        :type wrapup_timer: int
        :rtype: None
        """
        body = dict()
        if wrapup_reasons is not None:
            body['wrapupReasons'] = wrapup_reasons
        if default_wrapup_reason_id is not None:
            body['defaultWrapupReasonId'] = default_wrapup_reason_id or None
        if wrapup_timer_enabled is not None:
            body['wrapupTimerEnabled'] = wrapup_timer_enabled
        if wrapup_timer is not None:
            body['wrapupTimer'] = wrapup_timer
        url = self.ep(f'cxEssentials/locations/{location_id}/queues/{queue_id}/wrapup/settings')
        await super().put(url, json=body)

    async def list(self) -> List[WrapUpReason]:
        """
        List Wrap Up Reasons

        Return the list of wrap-up reasons configured for a customer.

        Agents handling calls use wrap-up reasons to categorize the outcome after a call ends. The control hub admin
        can configure these reasons for customers and assign them to queues. Upon call completion, agents select a
        wrap-up reason from the queue's assigned list. Each wrap-up reason includes a name and description, and can be
        set as the default for a queue. Admins can also configure a timer, which dictates the time agents have to
        select a reason post-call, with a default of 60 seconds. This timer can be disabled if necessary.

        Retrieving the list of wrap-up reasons requires a full or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :rtype: list[WrapUpReason]
        """
        url = self.ep('cxEssentials/wrapup/reasons')
        data = await super().get(url)
        r = TypeAdapter(list[WrapUpReason]).validate_python(data['wrapupReasons'])
        return r

    async def create(self, name: str, description: str = None, queues: List[str] = None,
               assign_all_queues_enabled: bool = None) -> str:
        """
        Create Wrap Up Reason

        Create a wrap-up reason.

        Agents handling calls use wrap-up reasons to categorize the outcome after a call ends. The control hub admin
        can configure these reasons for customers and assign them to queues.

        Upon call completion, agents select a wrap-up reason from the queue's assigned list. Each wrap-up reason
        includes a name and description, and can be set as the default for a queue.

        Admins can also configure a timer, which dictates the time agents have to select a reason post-call, with a
        default of 60 seconds. This timer can be disabled if necessary.

        Creating a wrap-up reason requires a full or device administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param name: Name of the wrap-up reason.
        :type name: str
        :param description: Description of the wrap-up reason.
        :type description: str
        :param queues: List of queue IDs assigned to the wrap-up reason.
        :type queues: list[str]
        :param assign_all_queues_enabled: Denotes whether all queues are assigned to the wrap-up reason.
        :type assign_all_queues_enabled: bool
        :return: Wrap-up reason ID.
        :rtype: str
        """
        body = dict()
        body['name'] = name
        if description is not None:
            body['description'] = description
        if queues is not None:
            body['queues'] = queues
        if assign_all_queues_enabled is not None:
            body['assignAllQueuesEnabled'] = assign_all_queues_enabled
        url = self.ep('cxEssentials/wrapup/reasons')
        data = await super().post(url, json=body)
        return data['id']

    async def validate(self, name: str):
        """
        Validate Wrap Up Reason

        Validate the wrap-up reason name.

        Agents handling calls use wrap-up reasons to categorize the outcome after a call ends. The control hub admin
        can configure these reasons for customers and assign them to queues.

        Upon call completion, agents select a wrap-up reason from the queue's assigned list. Each wrap-up reason
        includes a name and description, and can be set as the default for a queue.

        Admins can also configure a timer, which dictates the time agents have to select a reason post-call, with a
        default of 60 seconds. This timer can be disabled if necessary.

        Validating the wrap-up reason name requires a full or device administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param name: Name of the wrap-up reason.
        :type name: str
        :rtype: None
        """
        body = dict()
        body['name'] = name
        url = self.ep('cxEssentials/wrapup/reasons/actions/validateName/invoke')
        await super().post(url, json=body)

    async def delete(self, wrapup_reason_id: str):
        """
        Delete Wrap Up Reason

        Delete a wrap-up reason.

        Agents handling calls use wrap-up reasons to categorize the outcome after a call ends. The control hub admin
        can configure these reasons for customers and assign them to queues.

        Upon call completion, agents select a wrap-up reason from the queue's assigned list. Each wrap-up reason
        includes a name and description, and can be set as the default for a queue.

        Admins can also configure a timer, which dictates the time agents have to select a reason post-call, with a
        default of 60 seconds. This timer can be disabled if necessary.

        Deleting the wrap-up reason requires a full or device administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param wrapup_reason_id: Wrap-up reason ID.
        :type wrapup_reason_id: str
        :rtype: None
        """
        url = self.ep(f'cxEssentials/wrapup/reasons/{wrapup_reason_id}')
        await super().delete(url)

    async def details(self, wrapup_reason_id: str) -> WrapUpReasonDetails:
        """
        Read Wrap Up Reason

        Return the wrap-up reason by ID.

        Agents handling calls use wrap-up reasons to categorize the outcome after a call ends. The control hub admin
        can configure these reasons for customers and assign them to queues.

        Upon call completion, agents select a wrap-up reason from the queue's assigned list. Each wrap-up reason
        includes a name and description, and can be set as the default for a queue.

        Admins can also configure a timer, which dictates the time agents have to select a reason post-call, with a
        default of 60 seconds. This timer can be disabled if necessary.

        Retrieving the wrap-up reason by ID requires a full or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param wrapup_reason_id: Wrap-up reason ID.
        :type wrapup_reason_id: str
        :rtype: WrapUpReasonDetails
        """
        url = self.ep(f'cxEssentials/wrapup/reasons/{wrapup_reason_id}')
        data = await super().get(url)
        r = WrapUpReasonDetails.model_validate(data)
        return r

    async def update(self, wrapup_reason_id: str, name: str = None, description: str = None,
               queues_to_assign: List[str] = None, queues_to_unassign: List[str] = None,
               assign_all_queues_enabled: bool = None, unassign_all_queues_enabled: bool = None):
        """
        Update Wrap Up Reason

        Modify a wrap-up reason.

        Agents handling calls use wrap-up reasons to categorize the outcome after a call ends. The control hub admin
        can configure these reasons for customers and assign them to queues.

        Upon call completion, agents select a wrap-up reason from the queue's assigned list. Each wrap-up reason
        includes a name and description, and can be set as the default for a queue.

        Admins can also configure a timer, which dictates the time agents have to select a reason post-call, with a
        default of 60 seconds. This timer can be disabled if necessary.

        Modifying a wrap-up reason requires a full or device administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param wrapup_reason_id: Wrap-up reason ID.
        :type wrapup_reason_id: str
        :param name: Name of the wrap-up reason.
        :type name: str
        :param description: Description of the wrap-up reason.
        :type description: str
        :param queues_to_assign: List of queue IDs to assign to the wrap-up reason.
        :type queues_to_assign: list[str]
        :param queues_to_unassign: List of queue IDs to unassign from the wrap-up reason.
        :type queues_to_unassign: list[str]
        :param assign_all_queues_enabled: Denotes whether all queues are assigned to the wrap-up reason.
        :type assign_all_queues_enabled: bool
        :param unassign_all_queues_enabled: Denotes whether all queues are unassigned from the wrap-up reason.
        :type unassign_all_queues_enabled: bool
        :rtype: None
        """
        body = dict()
        if name is not None:
            body['name'] = name
        if description is not None:
            body['description'] = description
        if queues_to_assign is not None:
            body['queuesToAssign'] = queues_to_assign
        if queues_to_unassign is not None:
            body['queuesToUnassign'] = queues_to_unassign
        if assign_all_queues_enabled is not None:
            body['assignAllQueuesEnabled'] = assign_all_queues_enabled
        if unassign_all_queues_enabled is not None:
            body['unassignAllQueuesEnabled'] = unassign_all_queues_enabled
        url = self.ep(f'cxEssentials/wrapup/reasons/{wrapup_reason_id}')
        await super().put(url, json=body)

    async def available_queues(self, wrapup_reason_id: str) -> List[AvailableQueue]:
        """
        Read Available Queues

        Return the available queues for a wrap-up reason.

        Agents handling calls use wrap-up reasons to categorize the outcome after a call ends. The control hub admin
        can configure these reasons for customers and assign them to queues.

        Upon call completion, agents select a wrap-up reason from the queue's assigned list. Each wrap-up reason
        includes a name and description, and can be set as the default for a queue.

        Admins can also configure a timer, which dictates the time agents have to select a reason post-call, with a
        default of 60 seconds. This timer can be disabled if necessary.

        Retrieving the available queues for a wrap-up reason requires a full or read-only administrator auth token with
        a scope of `spark-admin:telephony_config_read`.

        :param wrapup_reason_id: Wrap-up reason ID.
        :type wrapup_reason_id: str
        :rtype: list[AvailableQueue]
        """
        url = self.ep(f'cxEssentials/wrapup/reasons/{wrapup_reason_id}/availableQueues')
        data = await super().get(url)
        if 'queues' not in data:
            return []
        r = TypeAdapter(list[AvailableQueue]).validate_python(data['queues'])
        return r


class AsCustomerExperienceEssentialsApi(AsApiChild, base='telephony/config'):
    """
    Customer Experience Essentials

    Webex Customer Experience Essentials APIs provide the core capabilities of the Webex Contact Center solution. These
    APIs allow you to
    manage Customer Experience Essentials features such as supervisor configuration, agent configuration, and call
    queue configuration, which are distinct from the Customer Experience Basic suite.

    `Learn more about the customer Experience Essentials suite.
    <https://help.webex.com/en-us/article/72sb3r/Webex-Customer-Experience-Essentials>`_

    Viewing the read-only customer Experience Essentials APIs requires a full, device or read-only administrator auth
    token with a scope of `spark-admin:telephony_config_read`.

    Modifying the customer Experience Essentials APIs requires a full or device administrator auth token with a scope
    of `spark-admin:telephony_config_write`.
    """
    callqueue_recording: AsQueueCallRecordingSettingsApi
    wrapup_reasons: AsWrapupReasonApi

    def __init__(self, session: AsRestSession):
        super().__init__(session=session)
        self.callqueue_recording = AsQueueCallRecordingSettingsApi(session=session)
        self.wrapup_reasons = AsWrapupReasonApi(session=session)

    async def get_screen_pop_configuration(self, location_id: str = None,
                                     queue_id: str = None, org_id: str = None) -> ScreenPopConfiguration:
        """
        Get Screen Pop configuration for a Call Queue in a Location

        Screen pop lets agents view customer-related info in a pop-up window. This API returns the screen pop
        configuration for a call queue in a location.

        :param location_id: The location ID where the call queue resides.
        :type location_id: str
        :param queue_id: The call queue ID for which screen pop configuration is modified.
        :type queue_id: str
        :param org_id: The organization ID of the customer or partner's organization.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'locations/{location_id}/queues/{queue_id}/cxEssentials/screenPop')
        data = await super().get(url, params=params)
        return ScreenPopConfiguration.model_validate(data)

    async def modify_screen_pop_configuration(self, location_id: str,
                                        queue_id: str, settings: ScreenPopConfiguration, org_id: str = None):
        """
        Modify Screen Pop configuration for a Call Queue in a Location

        Screen pop lets agents view customer-related info in a pop-up window. This API allows you to modify the screen
        pop configuration for a call queue in a location.

        :param location_id: The location ID where the call queue resides.
        :type location_id: str
        :param queue_id: The call queue ID for which screen pop configuration is modified.
        :type queue_id: str
        :param settings: The screen pop configuration settings.
        :type settings: ScreenPopConfiguration
        :param org_id: The organization ID of the customer or partner's organization.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = settings.model_dump(mode='json', by_alias=True)
        url = self.ep(f'locations/{location_id}/queues/{queue_id}/cxEssentials/screenPop')
        await super().put(url, params=params, json=body)

    def available_agents_gen(self, location_id: str,
                         has_cx_essentials: bool = None,
                         org_id: str = None) -> AsyncGenerator[AvailableAgent, None, None]:
        """
        Get List of available agents for Customer Experience Essentials

        Retrieve the list of available agents with Customer Experience Essentials license in a location.

        This operation requires a full or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param location_id: Retrieve the list of avaiilable agents in this location.
        :type location_id: str
        :param has_cx_essentials: Returns only the list of available agents with Customer Experience Essentials license
            when `true`, otherwise returns the list of available agents with Customer Experience Basic license.
        :type has_cx_essentials: bool
        :param org_id: The organization ID of the customer or partner's organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableAgent` instances
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        if has_cx_essentials is not None:
            params['hasCxEssentials'] = str(has_cx_essentials).lower()
        url = self.ep(f'locations/{location_id}/cxEssentials/agents/availableAgents')
        return self.session.follow_pagination(url=url, model=AvailableAgent, item_key='agents',
                                              params=params)

    async def available_agents(self, location_id: str,
                         has_cx_essentials: bool = None,
                         org_id: str = None) -> List[AvailableAgent]:
        """
        Get List of available agents for Customer Experience Essentials

        Retrieve the list of available agents with Customer Experience Essentials license in a location.

        This operation requires a full or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param location_id: Retrieve the list of avaiilable agents in this location.
        :type location_id: str
        :param has_cx_essentials: Returns only the list of available agents with Customer Experience Essentials license
            when `true`, otherwise returns the list of available agents with Customer Experience Basic license.
        :type has_cx_essentials: bool
        :param org_id: The organization ID of the customer or partner's organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableAgent` instances
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        if has_cx_essentials is not None:
            params['hasCxEssentials'] = str(has_cx_essentials).lower()
        url = self.ep(f'locations/{location_id}/cxEssentials/agents/availableAgents')
        return [o async for o in self.session.follow_pagination(url=url, model=AvailableAgent, item_key='agents',
                                              params=params)]


class AsDECTDevicesApi(AsApiChild, base='telephony/config'):
    """
    DECT Devices Settings

    Not supported for Webex for Government (FedRAMP)

    DECT APIs allow the admin to create a DECT network, and add base stations and handsets to the DECT network. People,
    places and virtual lines member types are supported on handset lines in the DECT network. Currently, APIs support
    Cisco DECT device models only.

    Viewing and searching  DECT settings requires a full or read-only administrator auth token with a scope
    of `spark-admin:telephony_config_read`.

    Adding and modifying these DECT settings requires a full administrator auth token with a scope
    of `spark-admin:telephony_config_write`.
    """

    async def device_type_list(self, org_id: str = None) -> list[DectDevice]:
        """
        Read the DECT device type list

        Not supported for Webex for Government (FedRAMP).

        Get DECT device type list with base stations and line ports supported count. This is a static list.

        Retrieving this list requires a full or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :type org_id: str
        :rtype: list[DectDevice]
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep('devices/dectNetworks/supportedDevices')
        data = await super().get(url, params=params)
        r = TypeAdapter(list[DectDevice]).validate_python(data['devices'])
        return r

    async def create_dect_network(self, location_id: str, name: str, model: DECTNetworkModel,
                            default_access_code_enabled: bool, default_access_code: str, display_name: str = None,
                            org_id: str = None) -> str:
        """
        Create a DECT Network

        Create a multi-cell DECT network for a given location.

        Creating a DECT network requires a full administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param location_id: Create a DECT network in this location.
        :type location_id: str
        :param name: Name of the DECT network. Min and max length supported for the DECT network name are 1 and 40
            respectively.
        :type name: str
        :param model: Select a device model type depending on the number of base stations and handset lines needed in
            the DECT network.
        :type model: DECTNetworkModel
        :param default_access_code_enabled: If set to true, need to provide a default access code that will be shared
            for all users in this network to pair their lines to the next available handset. Otherwise, each user will
            get a unique 4-digit access code that will be auto-generated. Note: There is currently no public API to
            retrieve the auto generated access codes for handsets. Use Control Hub instead.
        :type default_access_code_enabled: bool
        :param default_access_code: If `defaultAccessCodeEnabled` is set to true, then provide a default access code
            that needs to be a 4-numeric digit. The access code should be unique to the DECT network for the location.
        :type default_access_code: str
        :param display_name: Add a default name (11 characters max) to display for all handsets. If left blank, the
            default name will be an indexed number followed by the DECT network name.
        :type display_name: str
        :param org_id: Create a DECT network in this organization
        :type org_id: str
        :rtype: str
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        body['name'] = name
        if display_name is not None:
            body['displayName'] = display_name
        body['model'] = enum_str(model)
        body['defaultAccessCodeEnabled'] = default_access_code_enabled
        body['defaultAccessCode'] = default_access_code
        url = self.ep(f'locations/{location_id}/dectNetworks')
        data = await super().post(url, params=params, json=body)
        r = data['dectNetworkId']
        return r

    async def list_dect_networks(self, name: str = None, location_id: str = None,
                           org_id: str = None) -> list[DECTNetworkDetail]:
        """
        Get the List of DECT Networks for an organization

        Retrieves the list of DECT networks for an organization.

        DECT Networks provide roaming voice services via base stations and wireless handsets. A DECT network can be
        provisioned up to 1000 lines across up to 254 base stations.

        This API requires a full or read-only administrator auth token with a scope
        of `spark-admin:telephony_config_read`.

        :param name: List of DECT networks with this name.
        :type name: str
        :param location_id: List of DECT networks at this location.
        :type location_id: str
        :param org_id: List of DECT networks in this organization.
        :type org_id: str
        :rtype: list[DECTNetworkDetail]
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        if name is not None:
            params['name'] = name
        if location_id is not None:
            params['locationId'] = location_id
        url = self.ep('dectNetworks')
        data = await super().get(url, params=params)
        r = TypeAdapter(list[DECTNetworkDetail]).validate_python(data['dectNetworks'])
        return r

    async def dect_network_details(self, location_id: str, dect_network_id: str,
                             org_id: str = None) -> DECTNetworkDetail:
        """
        Get DECT Network Details

        Retrieves the details of a DECT network.

        DECT Networks provide roaming voice services via base stations and wireless handsets. A DECT network can be
        provisioned up to 1000 lines across up to 254 base stations.

        This API requires a full or read-only administrator auth token with a scope
        of `spark-admin:telephony_config_read`.

        :param location_id: Details of the DECT network at this location.
        :type location_id: str
        :param dect_network_id: Details of the specified DECT network.
        :type dect_network_id: str
        :param org_id: Details of the DECT network in this organization.
        :type org_id: str
        :rtype: :class:`DECTNetworkDetail`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'locations/{location_id}/dectNetworks/{dect_network_id}')
        data = await super().get(url, params=params)
        r = DECTNetworkDetail.model_validate(data)
        return r

    async def update_dect_network(self, location_id: str, dect_network_id: str, name: str, default_access_code_enabled: bool,
                            default_access_code: str = None, display_name: str = None, org_id: str = None):
        """
        Update DECT Network

        Update the details of a DECT network.

        DECT Networks provide roaming voice services via base stations and wireless handsets. A DECT network can be
        provisioned up to 1000 lines across up to 254 base stations.

        This API requires a full or read-only administrator auth token with a scope
        of `spark-admin:telephony_config_write`.

        :param location_id: Update DECT network details in the specified location.
        :type location_id: str
        :param dect_network_id: Update DECT network details in the specified DECT network.
        :type dect_network_id: str
        :param name: Name of the DECT network. This should be unique across the location.
        :type name: str
        :param default_access_code_enabled: Default access code is enabled. If true, the default access code is
            mandatory. If false, an auto-generated access code is used.
        :type default_access_code_enabled: bool
        :param default_access_code: Default access code for the DECT network. The default access code should be unique
            within the same location to avoid the handset accidentally registering with base stations from different
            DECT networks in range. This is mandatory when `defaultAccessCodeEnabled` is true.
        :type default_access_code: str
        :param display_name: DECT network name that will be displayed on the handset.
        :type display_name: str
        :param org_id: Update DECT network details in the specified organization.
        :type org_id: str
        :rtype: None
        """
        params = org_id and {'orgId': org_id} or None
        body = dict()
        body['name'] = name
        if display_name is not None:
            body['displayName'] = display_name
        body['defaultAccessCodeEnabled'] = default_access_code_enabled
        if default_access_code is not None:
            body['defaultAccessCode'] = default_access_code or None
        url = self.ep(f'locations/{location_id}/dectNetworks/{dect_network_id}')
        await super().put(url, params=params, json=body)

    async def update_dect_network_settings(self, settings: DECTNetworkDetail, org_id: str = None):
        """
        Update DECT Network from settings

        Update the details of a DECT network from settings.

        DECT Networks provide roaming voice services via base stations and wireless handsets. A DECT network can be
        provisioned up to 1000 lines across up to 254 base stations.

        This API requires a full or read-only administrator auth token with a scope
        of `spark-admin:telephony_config_write`.

        :param settings: DECT Network to update. location.id and id are used to address the DECT network to be
            updated. Only name, display_name, default_access_code_enabled, default_access_code are considered for the
            update

        :param org_id: Update DECT network details in the specified organization.
        :type org_id: str
        :rtype: None
        """
        params = org_id and {'orgId': org_id} or None
        body = settings.update()
        url = self.ep(f'locations/{settings.location.id}/dectNetworks/{settings.id}')
        await super().put(url, params=params, json=body)

    async def delete_dect_network(self, location_id: str, dect_network_id: str, org_id: str = None):
        """
        Delete DECT Network

        Delete a DECT network.

        DECT Networks provide roaming voice services via base stations and wireless handsets. A DECT network can be
        provisioned up to 1000 lines across up to 254 base stations.

        This API requires a full or read-only administrator auth token with a scope
        of `spark-admin:telephony_config_write`.

        :param location_id: Delete the DECT network in the specified location.
        :type location_id: str
        :param dect_network_id: Delete the specified DECT network.
        :type dect_network_id: str
        :param org_id: Delete the DECT network in the specified organization.
        :type org_id: str
        :rtype: None
        """
        params = org_id and {'orgId': org_id} or None
        url = self.ep(f'locations/{location_id}/dectNetworks/{dect_network_id}')
        await super().delete(url, params=params)

    async def create_base_stations(self, location_id: str, dect_id: str, base_station_macs: list[str],
                             org_id: str = None) -> list[BaseStationResponse]:
        """
        Create Multiple Base Stations

        This API is used to create multiple base stations in a DECT network in an organization.

        Creating base stations in a DECT network requires a full administrator auth token with a scope
        of `spark-admin:telephony_config_write`.

        :param location_id: Create a base station in this location.
        :type location_id: str
        :param dect_id: Create a base station for the DECT network.
        :type dect_id: str
        :param base_station_macs: Array of base stations.
        :type base_station_macs: list[str]
        :param org_id: Create a base station for a DECT network in this organization.
        :type org_id: str
        :rtype: list[BaseStationResponse]
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        body['baseStationMacs'] = base_station_macs
        url = self.ep(f'locations/{location_id}/dectNetworks/{dect_id}/baseStations')
        data = await super().post(url, params=params, json=body)
        r = TypeAdapter(list[BaseStationResponse]).validate_python(data['baseStations'])
        return r

    async def list_base_stations(self, location_id: str, dect_network_id: str,
                           org_id: str = None) -> list[BaseStationsResponse]:
        """
        Get a list of DECT Network Base Stations

        Retrieve a list of base stations in a DECT Network.

        A DECT network supports 2 types of base stations, DECT DBS-110 Single-Cell and DECT DBS-210 Multi-Cell.
        A DECT DBS-110 allows up to 30 lines of registration and supports 1 base station only. A DECT DBS-210 can have
        up to 254 base stations and supports up to 1000 lines of registration.

        This API requires a full or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param location_id: Location containing the DECT network.
        :type location_id: str
        :param dect_network_id: Retrieve the list of base stations in the specified DECT network ID.
        :type dect_network_id: str
        :param org_id: Organization containing the DECT network.
        :type org_id: str
        :rtype: list[BaseStationsResponse]
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'locations/{location_id}/dectNetworks/{dect_network_id}/baseStations')
        data = await super().get(url, params=params)
        r = TypeAdapter(list[BaseStationsResponse]).validate_python(data['baseStations'])
        return r

    async def base_station_details(self, location_id: str, dect_network_id: str,
                             base_station_id: str,
                             org_id: str = None) -> BaseStationDetail:
        """
        Get the details of a specific DECT Network Base Station

        Retrieve details of a specific base station in the DECT Network.

        A DECT network supports 2 types of base stations, DECT DBS-110 Single-Cell and DECT DBS-210 Multi-Cell.
        A DECT DBS-110 allows up to 30 lines of registration and supports 1 base station only. A DECT DBS-210 can have
        up to 254 base stations and supports up to 1000 lines of registration.

        This API requires a full or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param location_id: Location containing the DECT network.
        :type location_id: str
        :param dect_network_id: Retrieve details of a specific base station in the specified DECT network ID.
        :type dect_network_id: str
        :param base_station_id: Retrieve details of the specific DECT base station ID.
        :type base_station_id: str
        :param org_id: Organization containing the DECT network.
        :type org_id: str
        :rtype: :class:`BaseStationDetail`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'locations/{location_id}/dectNetworks/{dect_network_id}/baseStations/{base_station_id}')
        data = await super().get(url, params=params)
        r = BaseStationDetail.model_validate(data)
        return r

    async def delete_bulk_base_stations(self, location_id: str, dect_network_id: str, org_id: str = None):
        """
        Delete bulk DECT Network Base Stations

        Delete all the base stations in the DECT Network.

        A DECT network supports 2 types of base stations, DECT DBS-110 Single-Cell and DECT DBS-210 Multi-Cell.
        A DECT DBS-110 allows up to 30 lines of registration and supports 1 base station only. A DECT DBS-210 can have
        up to 254 base stations and supports up to 1000 lines of registration.

        This API requires a full administrator auth token with a scope of `spark-admin:telephony_config_write`.

        :param location_id: Location containing the DECT network.
        :type location_id: str
        :param dect_network_id: Delete all the base stations in the specified DECT network ID.
        :type dect_network_id: str
        :param org_id: Organization containing the DECT network.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'locations/{location_id}/dectNetworks/{dect_network_id}/baseStations')
        await super().delete(url, params=params)

    async def delete_base_station(self, location_id: str, dect_network_id: str, base_station_id: str,
                            org_id: str = None):
        """
        Delete a specific DECT Network Base Station

        Delete a specific base station in the DECT Network.

        A DECT network supports 2 types of base stations, DECT DBS-110 Single-Cell and DECT DBS-210 Multi-Cell.
        A DECT DBS-110 allows up to 30 lines of registration and supports 1 base station only. A DECT DBS-210 can have
        up to 254 base stations and supports up to 1000 lines of registration.

        This API requires a full administrator auth token with a scope of `spark-admin:telephony_config_write`.

        :param location_id: Location containing the DECT network.
        :type location_id: str
        :param dect_network_id: Delete a specific base station in the specified DECT network ID.
        :type dect_network_id: str
        :param base_station_id: Delete the specific DECT base station ID.
        :type base_station_id: str
        :param org_id: Organization containing the DECT network.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'locations/{location_id}/dectNetworks/{dect_network_id}/baseStations/{base_station_id}')
        await super().delete(url, params=params)

    async def add_a_handset(self, location_id: str, dect_network_id: str, line1_member_id: str,
                      line2_member_id: str = None, custom_display_name: str = None, org_id: str = None):
        """
        Add a Handset to a DECT Network

        Add a handset to a DECT network in a location in an organization.

        Adding a handset to a DECT network requires a full administrator auth token with a scope
        of `spark-admin:telephony_config_write`

        Adding a DECT handset to a person with a Webex Calling Standard license will
        disable Webex Calling across their Webex mobile, tablet, desktop, and browser applications.

        Adding or removing handsets to the DECT network in less than 90 seconds may result
        in base station not having the latest configuration until the base station is rebooted.

        :param location_id: Add handset in this location.
        :type location_id: str
        :param dect_network_id: A unique identifier for the DECT network.
        :type dect_network_id: str
        :param line1_member_id: ID of the member on line1 of the handset. Members can be PEOPLE or PLACE.
        :type line1_member_id: str
        :param line2_member_id: ID of the member on line2 of the handset. Members can be PEOPLE, PLACE, or
            VIRTUAL_LINE.
        :type line2_member_id: str
        :param custom_display_name: Custom display name on the handset. Min and max length supported for the custom
            display name is 1 and 16 respectively. Mandatory parameter.
        :type custom_display_name: str
        :param org_id: Add handset in this organization.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        body['line1MemberId'] = line1_member_id
        if line2_member_id is not None:
            body['line2MemberId'] = line2_member_id
        if custom_display_name is None:
            raise ValueError('custom_display_name cannot be None')
        body['customDisplayName'] = custom_display_name
        url = self.ep(f'locations/{location_id}/dectNetworks/{dect_network_id}/handsets')
        await super().post(url, params=params, json=body)

    async def add_list_of_handsets(self, location_id: str, dect_network_id: str,
                             items: list[AddDECTHandset],
                             org_id: str = None) -> list[AddDECTHandsetBulkResponse]:
        """
        Add a List of Handsets to a DECT Network

        Add a list of up to 50 handsets to a DECT network in a location.

        A DECT network acts as a container that can support up to 1,000 lines across all handsets, with each handset
        capable of handling up to two lines. Once the network is created, you can add bases, handsets, and assign
        users or lines as needed.

        Adding a list of handsets to a DECT network requires a full administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        Adding a DECT handset to a person with a Webex Calling Standard license will
        disable Webex Calling across their Webex mobile, tablet, desktop, and browser applications.

        Adding or removing handsets to the DECT network in less than 90 seconds may result
        in base station not having the latest configuration until the base station is rebooted.

        :param location_id: Add handsets in this location.
        :type location_id: str
        :param dect_network_id: A unique identifier for the DECT network.
        :type dect_network_id: str
        :param items: List of handsets that are to be added to the DECT network.
        :type items: list[AddDECTHandset]
        :param org_id: Add handsets in this organization.
        :type org_id: str
        :rtype: list[AddDECTHandsetBulkResponse]
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        body['items'] = TypeAdapter(list[AddDECTHandset]).dump_python(items, mode='json', by_alias=True,
                                                                      exclude_none=True)
        url = self.ep(f'locations/{location_id}/dectNetworks/{dect_network_id}/handsets/bulk')
        data = await super().post(url, params=params, json=body)
        r = TypeAdapter(list[AddDECTHandsetBulkResponse]).validate_python(data['items'])
        return r

    async def list_handsets(self, location_id: str, dect_network_id: str,
                      basestation_id: str = None, member_id: str = None,
                      org_id: str = None) -> DECTHandsetList:
        """
        Get List of Handsets for a DECT Network ID

        List all the handsets associated with a DECT Network ID.

        A handset can have up to two lines, and a DECT network supports a total of 120 lines across all handsets.
        A member on line1 of a DECT handset can be of type PEOPLE or PLACE while a member on line2 of a DECT handset
        can be of type PEOPLE, PLACE, or VIRTUAL_LINE.

        This API requires a full or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param location_id: Location containing the DECT network.
        :type location_id: str
        :param dect_network_id: Search handset details in the specified DECT network ID.
        :type dect_network_id: str
        :param basestation_id: Search handset details in the specified DECT base station ID.
        :type basestation_id: str
        :param member_id: ID of the member of the handset. Members can be of type PEOPLE, PLACE, or VIRTUAL_LINE.
        :type member_id: str
        :param org_id: Organization containing the DECT network.
        :type org_id: str
        :rtype: :class:`DECTHandsetList`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        if basestation_id is not None:
            params['basestationId'] = basestation_id
        if member_id is not None:
            params['memberId'] = member_id
        url = self.ep(f'locations/{location_id}/dectNetworks/{dect_network_id}/handsets')
        data = await super().get(url, params=params)
        r = DECTHandsetList.model_validate(data)
        return r

    async def handset_details(self, location_id: str, dect_network_id: str, handset_id: str,
                        org_id: str = None) -> DECTHandsetItem:
        """
        Get Specific DECT Network Handset Details

        List the specific DECT Network handset details.

        A handset can have up to two lines, and a DECT network supports a total of 120 lines across all handsets.
        A member on line1 of a DECT handset can be of type PEOPLE or PLACE while a member on line2 of a DECT handset
        can be of type PEOPLE, PLACE, or VIRTUAL_LINE.

        This API requires a full or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param location_id: Location containing the DECT network.
        :type location_id: str
        :param dect_network_id: Search handset details in the specified DECT network ID.
        :type dect_network_id: str
        :param handset_id: A unique identifier for the handset.
        :type handset_id: str
        :param org_id: Organization containing the DECT network.
        :type org_id: str
        :rtype: :class:`DECTHandsetGet`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'locations/{location_id}/dectNetworks/{dect_network_id}/handsets/{handset_id}')
        data = await super().get(url, params=params)
        r = DECTHandsetItem.model_validate(data)
        return r

    async def update_handset(self, location_id: str, dect_network_id: str, handset_id: str,
                       line1_member_id: str, custom_display_name: str, line2_member_id: str = None,
                       org_id: str = None):
        """
        Update DECT Network Handset

        Update the line assignment on a handset.

        A handset can have up to two lines, and a DECT network supports a total of 120 lines across all handsets.
        A member on line1 of a DECT handset can be of type PEOPLE or PLACE while a member on line2 of a DECT handset
        can be of type PEOPLE, PLACE, or VIRTUAL_LINE.

        Updating a DECT Network handset requires a full administrator auth token with a scope
        of `spark-admin:telephony_config_write`.

        :param location_id: Location containing the DECT network.
        :type location_id: str
        :param dect_network_id: Update handset details in the specified DECT network.
        :type dect_network_id: str
        :param handset_id: A unique identifier for the handset.
        :type handset_id: str
        :param line1_member_id: ID of the member on line1 of the handset. Members can be PEOPLE or PLACE.
        :type line1_member_id: str
        :param custom_display_name: Custom display name on the handset.
        :type custom_display_name: str
        :param line2_member_id: ID of the member on line2 of the handset. Members can be PEOPLE, PLACE, or
            VIRTUAL_LINE.
        :type line2_member_id: str
        :param org_id: Organization containing the DECT network.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        body['line1MemberId'] = line1_member_id
        body['line2MemberId'] = line2_member_id
        body['customDisplayName'] = custom_display_name
        url = self.ep(f'locations/{location_id}/dectNetworks/{dect_network_id}/handsets/{handset_id}')
        await super().put(url, params=params, json=body)

    async def delete_handset(self, location_id: str, dect_network_id: str, handset_id: str,
                       org_id: str = None):
        """
        Delete specific DECT Network Handset Details

        Delete a specific DECT Network handset.

        A handset can have up to two lines, and a DECT network supports a total of 120 lines across all handsets.
        A member on line1 of a DECT handset can be of type PEOPLE or PLACE while a member on line2 of a DECT handset
        can be of type PEOPLE, PLACE, or VIRTUAL_LINE.

        This API requires a full administrator auth token with a scope of `spark-admin:telephony_config_write`.

        :param location_id: Location containing the DECT network.
        :type location_id: str
        :param dect_network_id: Delete handset details in the specified DECT network ID.
        :type dect_network_id: str
        :param handset_id: A unique identifier for the handset.
        :type handset_id: str
        :param org_id: Organization containing the DECT network.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'locations/{location_id}/dectNetworks/{dect_network_id}/handsets/{handset_id}')
        await super().delete(url, params=params)

    async def delete_handsets(self, location_id: str, dect_network_id: str, handset_ids: list[str],
                        delete_all: bool = None, org_id: str = None):
        """
        Delete multiple handsets

        Delete multiple handsets or all of them.

        A handset can have up to two lines, and a DECT network supports a total of 120 lines across all handsets.
        A member on line1 of a DECT handset can be of type PEOPLE or PLACE while a member on line2 of a DECT handset
        can be of type PEOPLE, PLACE, or VIRTUAL_LINE.

        This API requires a full administrator auth token with a scope of `spark-admin:telephony_config_write`.

        Deleting a DECT handset from a person with a Webex Calling Standard license will
        enable Webex Calling across their Webex mobile, tablet, desktop, and browser applications.

        Adding or removing handsets to the DECT network in less than 90 seconds may result
        in base station not having the latest configuration until the base station is rebooted.

        :param location_id: Location containing the DECT network.
        :type location_id: str
        :param dect_network_id: Delete handset details in the specified DECT network ID.
        :type dect_network_id: str
        :param handset_ids: Array of the handset IDs to be deleted.
        :type handset_ids: list[str]
        :param delete_all: If present the items array is ignored and all items in the context are deleted.
        :type delete_all: bool
        :param org_id: Organization containing the DECT network.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        body['handsetIds'] = handset_ids
        if delete_all is not None:
            body['deleteAll'] = delete_all
        url = self.ep(f'locations/{location_id}/dectNetworks/{dect_network_id}/handsets')
        await super().delete(url, params=params, json=body)

    async def dect_networks_associated_with_person(self, person_id: str,
                                             org_id: str = None) -> list[AssignedDectNetwork]:
        """
        GET List of DECT networks associated with a Person

        Retrieves the list of DECT networks for a person in an organization.

        DECT Network provides roaming voice services via base stations and wireless handsets. DECT network can be
        provisioned up to 1000 lines across up to 254 base stations.

        This API requires a full or read-only administrator auth token with a scope
        of `spark-admin:telephony_config_read`.

        :param person_id: List of DECT networks associated with this person.
        :type person_id: str
        :param org_id: List of DECT networks associated with a person in this organization.
        :type org_id: str
        :rtype: list[AssignedDectNetwork]
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'people/{person_id}/dectNetworks')
        data = await super().get(url, params=params)
        r = TypeAdapter(list[AssignedDectNetwork]).validate_python(data['dectNetworks'])
        return r

    async def dect_networks_associated_with_workspace(self, workspace_id: str,
                                                org_id: str = None) -> list[AssignedDectNetwork]:
        """
        GET List of DECT networks associated with a workspace

        Retrieves the list of DECT networks for a workspace in an organization.

        DECT Network provides roaming voice services via base stations and wireless handsets. DECT network can be
        provisioned up to 1000 lines across up to 254 base stations.

        This API requires a full or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param workspace_id: List of DECT networks associated with this workspace.
        :type workspace_id: str
        :param org_id: List of DECT networks associated with a workspace in this organization.
        :type org_id: str
        :rtype: list[AssignedDectNetwork]
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'workspaces/{workspace_id}/dectNetworks')
        data = await super().get(url, params=params)
        r = TypeAdapter(list[AssignedDectNetwork]).validate_python(data['dectNetworks'])
        return r

    async def dect_networks_associated_with_virtual_line(self, virtual_line_id: str,
                                                   org_id: str = None) -> list[AssignedDectNetwork]:
        """
        Get List of Dect Networks Handsets for a Virtual Line

        Retrieve DECT Network details assigned for a virtual line.

        Virtual line is a capability in Webex Calling that allows administrators to configure multiple lines to Webex
        Calling users.

        Retrieving the assigned device detials for a virtual line requires a full or user or read-only administrator
        auth token with a scope of `spark-admin:telephony_config_read`.

        :param virtual_line_id: Retrieve settings for a virtual line with the matching ID.
        :type virtual_line_id: str
        :param org_id: Retrieve virtual line settings from this organization.
        :type org_id: str
        :rtype: list[AssignedDectNetwork]
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id

        url = self.ep(f'virtualLines/{virtual_line_id}/dectNetworks')
        data = await super().get(url, params=params)
        r = TypeAdapter(list[AssignedDectNetwork]).validate_python(data['dectNetworks'])
        return r

    def available_members_gen(self, member_name: str = None, phone_number: str = None, extension: str = None,
                          location_id: str = None, order: str = None, exclude_virtual_line: bool = None,
                          usage_type: UsageType = None, org_id: str = None,
                          **params) -> AsyncGenerator[AvailableMember, None, None]:
        """
        Search Available Members

        List the members that are available to be assigned to DECT handset lines.

        This requires a full or read-only administrator auth token with a scope of `spark-admin:telephony_config_read`.

        :param member_name: Search (Contains) numbers based on member name.
        :type member_name: str
        :param phone_number: Search (Contains) based on number.
        :type phone_number: str
        :param extension: Search (Contains) based on extension.
        :type extension: str
        :param location_id: List members for the location ID.
        :type location_id: str
        :param order: Sort the list of available members on the device in ascending order by name, using either last
            name `lname` or first name `fname`. Default sort is the last name in ascending order.
        :type order: str
        :param exclude_virtual_line: If true, search results will exclude virtual lines in the member list. NOTE:
            Virtual lines cannot be assigned as the primary line.
        :type exclude_virtual_line: bool
        :param usage_type: Search for members eligible to become the owner of the device, or share line on the device.
        :type usage_type: UsageType
        :param org_id: Search members in this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableMember` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if member_name is not None:
            params['memberName'] = member_name
        if phone_number is not None:
            params['phoneNumber'] = phone_number
        if extension is not None:
            params['extension'] = extension
        if location_id is not None:
            params['locationId'] = location_id
        if order is not None:
            params['order'] = order
        if exclude_virtual_line is not None:
            params['excludeVirtualLine'] = str(exclude_virtual_line).lower()
        if usage_type is not None:
            params['usageType'] = enum_str(usage_type)
        url = self.ep('devices/availableMembers')
        return self.session.follow_pagination(url=url, model=AvailableMember, item_key='members', params=params)

    async def available_members(self, member_name: str = None, phone_number: str = None, extension: str = None,
                          location_id: str = None, order: str = None, exclude_virtual_line: bool = None,
                          usage_type: UsageType = None, org_id: str = None,
                          **params) -> List[AvailableMember]:
        """
        Search Available Members

        List the members that are available to be assigned to DECT handset lines.

        This requires a full or read-only administrator auth token with a scope of `spark-admin:telephony_config_read`.

        :param member_name: Search (Contains) numbers based on member name.
        :type member_name: str
        :param phone_number: Search (Contains) based on number.
        :type phone_number: str
        :param extension: Search (Contains) based on extension.
        :type extension: str
        :param location_id: List members for the location ID.
        :type location_id: str
        :param order: Sort the list of available members on the device in ascending order by name, using either last
            name `lname` or first name `fname`. Default sort is the last name in ascending order.
        :type order: str
        :param exclude_virtual_line: If true, search results will exclude virtual lines in the member list. NOTE:
            Virtual lines cannot be assigned as the primary line.
        :type exclude_virtual_line: bool
        :param usage_type: Search for members eligible to become the owner of the device, or share line on the device.
        :type usage_type: UsageType
        :param org_id: Search members in this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableMember` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if member_name is not None:
            params['memberName'] = member_name
        if phone_number is not None:
            params['phoneNumber'] = phone_number
        if extension is not None:
            params['extension'] = extension
        if location_id is not None:
            params['locationId'] = location_id
        if order is not None:
            params['order'] = order
        if exclude_virtual_line is not None:
            params['excludeVirtualLine'] = str(exclude_virtual_line).lower()
        if usage_type is not None:
            params['usageType'] = enum_str(usage_type)
        url = self.ep('devices/availableMembers')
        return [o async for o in self.session.follow_pagination(url=url, model=AvailableMember, item_key='members', params=params)]

    async def generate_and_enable_dect_serviceability_password(self, location_id: str, dect_network_id: str,
                                                         org_id: str = None) -> str:
        """
        Generate and Enable DECT Serviceability Password

        Generates and enables a 16-character DECT serviceability password.

        Generating a password and transmitting it to the DECT network can reboot the
        entire network. Be sure you choose an appropriate time to generate a new password.

        The DECT serviceability password, also known as the admin override password, provides read/write access to DECT
        base stations for performing system serviceability and troubleshooting functions.

        This API requires either a full administrator auth token with the scope `spark-admin:telephony_config_write`,
        or a device administrator token with the scope of `spark-admin:devices_write`.

        :param location_id: Unique identifier for the location.
        :type location_id: str
        :param dect_network_id: Unique identifier for the DECT network.
        :type dect_network_id: str
        :param org_id: Unique identifier for the organization.
        :type org_id: str
        :rtype: str
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(
            f'locations/{location_id}/dectNetworks/{dect_network_id}/serviceabilityPassword/actions/generate/invoke')
        data = await super().post(url, params=params)
        r = data['password']
        return r

    async def get_dect_serviceability_password_status(self, location_id: str, dect_network_id: str,
                                                org_id: str = None) -> bool:
        """
        Get DECT Serviceability Password status

        Retrieves the DECT serviceability password status.

        If the serviceability password is enabled but has not been generated, the `enabled`
        status will be returned as `true` even though there is no active serviceability password.

        The DECT serviceability password, also known as the admin override password, provides read/write access to DECT
        base stations for performing system serviceability and troubleshooting functions.

        This API requires an auth token with either a full, read-only token with the scope of
        `spark-admin:telephony_config_read`, or a device administrator token with the scope of
        `spark-admin:devices_read`.

        :param location_id: Unique identifier for the location.
        :type location_id: str
        :param dect_network_id: Unique identifier for the DECT network.
        :type dect_network_id: str
        :param org_id: Unique identifier for the organization.
        :type org_id: str
        :rtype: bool
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'locations/{location_id}/dectNetworks/{dect_network_id}/serviceabilityPassword')
        data = await super().get(url, params=params)
        r = data['enabled']
        return r

    async def update_dect_serviceability_password_status(self, location_id: str, dect_network_id: str, enabled: bool,
                                                   org_id: str = None):
        """
        Update DECT Serviceability Password status

        Enables or disables the DECT serviceability password.

        Enabling or disabling the password and transmitting it to the DECT network can
        reboot the entire network. Be sure you choose an appropriate time for this action.

        If enabling is requested, but the serviceability password has not been generated, we
        will not actively reject the request even though there is no serviceability password.

        The DECT serviceability password, also known as the admin override password, provides read/write access to DECT
        base stations for performing system serviceability and troubleshooting functions.

        This API requires either a full administrator auth token with the scope `spark-admin:telephony_config_write`,
        or a device administrator token with the scope of `spark-admin:devices_write`.

        :param location_id: Unique identifier for the location.
        :type location_id: str
        :param dect_network_id: Unique identifier for the DECT network.
        :type dect_network_id: str
        :param enabled: DECT serviceability password status. When `enabled` is set to `true`, the serviceability
            password can be used to manage DECT. When `enabled` is set to `false`, the serviceability password is
            disabled and the password owned and known by Cisco is required to perform serviceability and
            troubleshooting.
        :type enabled: bool
        :param org_id: Unique identifier for the organization.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        body['enabled'] = enabled
        url = self.ep(f'locations/{location_id}/dectNetworks/{dect_network_id}/serviceabilityPassword')
        await super().put(url, params=params, json=body)


class AsEmergencyAddressApi(AsApiChild, base='telephony/pstn'):
    """
    API to handle emergency address settings for a location

    """

    async def add_to_location(self, location_id: str, address: Union[EmergencyAddress, SuggestedEmergencyAddress],
                        org_id: str = None) -> str:
        """
        Add an Emergency Address to a Location

        Adds a new emergency address to the specified location. On success, returns the unique identifier of the newly
        created emergency address.

        Emergency address settings allow the admin to configure or update the physical address associated with a phone
        number or a location.

        Adding emergency address to a location requires a full administrator auth token with scope of
        `spark-admin:telephony_pstn_write`.

        :param location_id: Location to which the emergency address will be added.
        :type location_id: str
        :param address: Emergency address to add.
        :type address: Union[EmergencyAddress, SuggestedEmergencyAddress]
        :param org_id: Adding emergency address for a location in this organization.
        :type org_id: str
        :rtype: str
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = address.update()
        url = self.ep(f'locations/{location_id}/emergencyAddress')
        data = await super().post(url, params=params, json=body)
        r = data['id']
        return r

    async def lookup_for_location(self, location_id: str, address: Union[EmergencyAddress, SuggestedEmergencyAddress],
                            org_id: str = None) -> list[SuggestedEmergencyAddress]:
        """
        Emergency Address Lookup to Verify if Address is Valid

        Returns a suggested address. If the input address is valid and unchanged, no errors are returned. If the input
        address requires corrections, the response includes a suggested address along with error details.

        Emergency address settings allow the admin to configure or update the physical address associated with a phone
        number or a location.

        Emergency address lookup to verify if address is valid requires a full administrator auth token with scope of
        `spark-admin:telephony_pstn_read`.

        :param location_id: Emergency address lookup for this location.
        :type location_id: str
        :param address: Emergency address to lookup.
        :type address: Union[EmergencyAddress, SuggestedEmergencyAddress]
        :param org_id: Emergency address lookup for this organization.
        :type org_id: str
        :rtype: list[SuggestedEmergencyAddress]
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = address.update()
        url = self.ep(f'locations/{location_id}/emergencyAddress/lookup')
        data = await super().post(url, params=params, json=body)
        r = TypeAdapter(list[SuggestedEmergencyAddress]).validate_python(data['addresses'])
        return r

    async def update_for_location(self, location_id: str, address_id: str,
                            address: Union[EmergencyAddress, SuggestedEmergencyAddress], org_id: str = None):
        """
        Update the Emergency Address of a Location

        Updates the emergency address of the specified location.

        Emergency address settings allow the admin to configure or update the physical address associated with a phone
        number or a location.

        Updating the emergency address of a location requires a full administrator auth token with scope of
        `spark-admin:telephony_pstn_write`.

        :param location_id: Location for which the emergency address will be updated.
        :type location_id: str
        :param address_id: Unique identifier for the emergency address that will be updated.
        :type address_id: str
        :param address: Emergency address to update.
        :type address: Union[EmergencyAddress, SuggestedEmergencyAddress]
        :param org_id: Updating the emergency address of a location in this organization.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = address.update()
        url = self.ep(f'locations/{location_id}/emergencyAddresses/{address_id}')
        await super().put(url, params=params, json=body)

    async def update_for_phone_number(self, phone_number: str,
                                emergency_address: Union[EmergencyAddress, SuggestedEmergencyAddress] = None,
                                org_id: str = None):
        """
        Update the emergency address for a phone number.

        Emergency address settings allow the admin to configure or update the physical address associated with a phone
        number or a location.

        Updating the emergency address for a phone number requires a full administrator auth token with scope of
        `spark-admin:telephony_pstn_write`.

        :param phone_number: Update the emergency address for this phone number.
        :type phone_number: str
        :param emergency_address: Emergency address to update. Using an empty JSON object deletes the custom emergency
            address for the number and replaces it with the location's default emergency address.
        :type emergency_address: Union[EmergencyAddress, SuggestedEmergencyAddress]
        :param org_id: Update the emergency address of phone number in this organization.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        if emergency_address is not None:
            body['emergencyAddress'] = emergency_address.update()
        url = self.ep(f'numbers/{phone_number}/emergencyAddress')
        await super().put(url, params=params, json=body)


class AsGuestCallingApi(AsApiChild, base='telephony/config/guestCalling'):
    """
    Guest Calling Settings with Click-to-call

    Calling Service Settings supports reading and writing of Webex Calling service settings for a specific
    organization.

    Viewing these read-only organization settings requires a full or read-only administrator auth token with a scope of
    `spark-admin:telephony_config_read`.

    Modifying these organization settings requires a full administrator auth token with a scope of
    `spark-admin:telephony_config_write`.

    A partner administrator can retrieve or change settings in a customer's organization using the optional `orgId`
    query parameter.
    """

    async def read(self, org_id: str = None) -> GuestCallingSettings:
        """
        Read the Click-to-call Settings

        Retrieve the organization's click-to-call calling settings.

        Click-to-call settings determine whether click-to-call is enabled and whether privacy mode is enabled.

        Retrieving click-to-call settings requires a full or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param org_id: ID of the organization. Only admin users of another organization (such as partners) may use this
            parameter as the default is the same organization as the token used to access the API.
        :type org_id: str
        :rtype: :class:`GuestCallingSettings`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep()
        data = await super().get(url, params=params)
        r = GuestCallingSettings.model_validate(data)
        return r

    async def update(self, enabled: bool, privacy_enabled: bool, destination_members: list[str],
               org_id: str = None):
        """
        Update the Click-to-call Settings

        Update the organization's click-to-call settings.

        Click-to-call settings determine whether click-to-call is enabled and whether privacy mode is enabled.

        Updating an organization's click-to-call settings requires a full administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param enabled: Set to `true` to allow click-to-call calling.
        :type enabled: bool
        :param privacy_enabled: Set to `true` to enable privacy mode.
        :type privacy_enabled: bool
        :param destination_members: List of destination member IDs. Supported destination types are Auto Attendant,
            Call Queue, Hunt Group, and Virtual Line.
        :type destination_members: list[str]
        :param org_id: ID of the organization. Only admin users of another organization (such as partners) may use this
            parameter as the default is the same organization as the token used to access the API.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        body['enabled'] = enabled
        body['privacyEnabled'] = privacy_enabled
        body['destinationMembers'] = destination_members
        url = self.ep()
        await super().put(url, params=params, json=body)

    def members_gen(self, member_name: str = None, phone_number: str = None, extension: str = None,
                org_id: str = None, **params) -> AsyncGenerator[DestinationMember, None, None]:
        """
        Read the Click-to-call Members

        Retrieve the organization's click-to-call members.

        Click-to-call members are the destination members that click-to-call callers can call.

        Retrieving click-to-call members requires a full or read-only administrator or location administrator auth
        token with a scope of `spark-admin:telephony_config_read`.

        :param member_name: Search (Contains) numbers based on member name.
        :type member_name: str
        :param phone_number: Search (Contains) based on number.
        :type phone_number: str
        :param extension: Search (Contains) based on extension.
        :type extension: str
        :param org_id: ID of the organization. Only admin users of another organization (such as partners) may use this
            parameter as the default is the same organization as the token used to access the API.
        :type org_id: str
        :return: Generator yielding :class:`DestinationMember` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if member_name is not None:
            params['memberName'] = member_name
        if phone_number is not None:
            params['phoneNumber'] = phone_number
        if extension is not None:
            params['extension'] = extension
        url = self.ep('members')
        return self.session.follow_pagination(url=url, model=DestinationMember, item_key='destinationMembers',
                                              params=params)

    async def members(self, member_name: str = None, phone_number: str = None, extension: str = None,
                org_id: str = None, **params) -> List[DestinationMember]:
        """
        Read the Click-to-call Members

        Retrieve the organization's click-to-call members.

        Click-to-call members are the destination members that click-to-call callers can call.

        Retrieving click-to-call members requires a full or read-only administrator or location administrator auth
        token with a scope of `spark-admin:telephony_config_read`.

        :param member_name: Search (Contains) numbers based on member name.
        :type member_name: str
        :param phone_number: Search (Contains) based on number.
        :type phone_number: str
        :param extension: Search (Contains) based on extension.
        :type extension: str
        :param org_id: ID of the organization. Only admin users of another organization (such as partners) may use this
            parameter as the default is the same organization as the token used to access the API.
        :type org_id: str
        :return: Generator yielding :class:`DestinationMember` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if member_name is not None:
            params['memberName'] = member_name
        if phone_number is not None:
            params['phoneNumber'] = phone_number
        if extension is not None:
            params['extension'] = extension
        url = self.ep('members')
        return [o async for o in self.session.follow_pagination(url=url, model=DestinationMember, item_key='destinationMembers',
                                              params=params)]

    def available_members_gen(self, member_name: str = None, phone_number: str = None,
                          extension: str = None, org_id: str = None,
                          **params) -> AsyncGenerator[DestinationMember, None, None]:
        """
        Read the Click-to-call Available Members

        Retrieve the organization's click-to-call available members.

        Click-to-call available members are the members that can be added as destination members for click-to-call
        callers.

        Retrieving click-to-call available members requires a full or read-only administrator or location administrator
        auth token with a scope of `spark-admin:telephony_config_read`.

        :param member_name: Search (Contains) numbers based on member name.
        :type member_name: str
        :param phone_number: Search (Contains) based on number.
        :type phone_number: str
        :param extension: Search (Contains) based on extension.
        :type extension: str
        :param org_id: ID of the organization. Only admin users of another organization (such as partners) may use this
            parameter as the default is the same organization as the token used to access the API.
        :type org_id: str
        :return: Generator yielding :class:`DestinationMember` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if member_name is not None:
            params['memberName'] = member_name
        if phone_number is not None:
            params['phoneNumber'] = phone_number
        if extension is not None:
            params['extension'] = extension
        url = self.ep('availableMembers')
        return self.session.follow_pagination(url=url, model=DestinationMember, item_key='availableDestinationMembers',
                                              params=params)

    async def available_members(self, member_name: str = None, phone_number: str = None,
                          extension: str = None, org_id: str = None,
                          **params) -> List[DestinationMember]:
        """
        Read the Click-to-call Available Members

        Retrieve the organization's click-to-call available members.

        Click-to-call available members are the members that can be added as destination members for click-to-call
        callers.

        Retrieving click-to-call available members requires a full or read-only administrator or location administrator
        auth token with a scope of `spark-admin:telephony_config_read`.

        :param member_name: Search (Contains) numbers based on member name.
        :type member_name: str
        :param phone_number: Search (Contains) based on number.
        :type phone_number: str
        :param extension: Search (Contains) based on extension.
        :type extension: str
        :param org_id: ID of the organization. Only admin users of another organization (such as partners) may use this
            parameter as the default is the same organization as the token used to access the API.
        :type org_id: str
        :return: Generator yielding :class:`DestinationMember` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if member_name is not None:
            params['memberName'] = member_name
        if phone_number is not None:
            params['phoneNumber'] = phone_number
        if extension is not None:
            params['extension'] = extension
        url = self.ep('availableMembers')
        return [o async for o in self.session.follow_pagination(url=url, model=DestinationMember, item_key='availableDestinationMembers',
                                              params=params)]


class AsHotDeskApi(AsApiChild, base='hotdesk/sessions'):
    """
    Hot Desk

    """

    async def list_sessions(self, person_id: str = None, workspace_id: str = None, org_id: str = None) -> list[HotDesk]:
        """
        List Sessions

        List hot desk sessions.

        Use query parameters to filter the response.
        The `orgId` parameter is for use by partner administrators acting on a managed organization.
        The `personId` and `workspaceId` parameters are optional and are used to filter the response to only include
        sessions for a specific person or workspace.
        When used together they are used as an AND filter.

        :param person_id: List sessions for this person.
        :type person_id: str
        :param workspace_id: List sessions for this workspace.
        :type workspace_id: str
        :param org_id: List sessions in this organization. Only admin users of another organization (such as partners)
            may use this parameter.
        :type org_id: str
        :rtype: list[HotDesk]
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        if person_id is not None:
            params['personId'] = person_id
        if workspace_id is not None:
            params['workspaceId'] = workspace_id
        url = self.ep()
        data = await super().get(url, params=params)
        r = TypeAdapter(list[HotDesk]).validate_python(data['items'])
        return r

    async def delete_session(self, session_id: str):
        """
        Delete Session

        Delete a hot desk session.

        :param session_id: The unique identifier for the hot desk session.
        :type session_id: str
        :rtype: None
        """
        url = self.ep(f'{session_id}')
        await super().delete(url)


class AsHotDeskingSigninViaVoicePortalApi(AsApiChild, base='telephony/config'):
    """
    Features: Hot Desking Sign-in via Voice Portal

    Features: Hot desking is a feature that allows users to sign in to a shared phone and make calls using their own
    phone number.

    Viewing these read-only organization settings requires a full or read-only administrator auth token with a scope of
    `spark-admin:telephony_config_read`.

    Modifying these organization settings requires a full administrator auth token with a scope of
    `spark-admin:telephony_config_write`.

    A partner administrator can retrieve or change settings in a customer's organization using the optional `orgId`
    query parameter.
    """

    async def location_get(self, location_id: str, org_id: str = None) -> HotDeskingVoicePortalSetting:
        """
        Voice Portal Hot desking sign in details for a location

        Get the Hot desking sign in details for a location.

        This requires a full or read-only administrator auth token with a scope of `spark-admin:telephony_config_read`.

        :param location_id: Location in which the hot desking sign in resides.
        :type location_id: str
        :param org_id: ID of the organization. Only admin users of another organization (such as partners) may use this
            parameter as the default is the same organization as the token used to access the API.
        :type org_id: str
        :rtype: HotDeskingVoicePortalSetting
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'locations/{location_id}/features/hotDesking')
        data = await super().get(url, params=params)
        return HotDeskingVoicePortalSetting.model_validate(data)

    async def location_update(self, location_id: str,
                        setting: HotDeskingVoicePortalSetting,
                        org_id: str = None):
        """
        Update Voice Portal Hot desking sign in details for a location

        Update the Hot desking sign in details for a location.

        This requires a full administrator auth token with a scope of `spark-admin:telephony_config_write`.

        :param location_id: Location in which the hot desking sign in resides.
        :type location_id: str
        :param setting: Hot desking sign in details for a location.
        :type setting: HotDeskingVoicePortalSetting
        :param org_id: ID of the organization. Only admin users of another organization (such as partners) may use this
            parameter as the default is the same organization as the token used to access the API.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = setting.model_dump(mode='json', by_alias=True, exclude_unset=True)
        url = self.ep(f'locations/{location_id}/features/hotDesking')
        await super().put(url, params=params, json=body)

    async def user_get(self, person_id: str, org_id: str = None) -> HotDeskingVoicePortalSetting:
        """
        Voice Portal Hot desking sign in details for a user

        Get the Hot desking sign in details for a user.

        This requires a full or read-only administrator auth token with a scope of `spark-admin:telephony_config_read`.

        :param person_id: ID of the person associated with the hot desking details.
        :type person_id: str
        :param org_id: ID of the organization. Only admin users of another organization (such as partners) may use this
            parameter as the default is the same organization as the token used to access the API.
        :type org_id: str
        :rtype: HotDeskingVoicePortalSetting
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'people/{person_id}/features/hotDesking/guest')
        data = await super().get(url, params=params)
        return HotDeskingVoicePortalSetting.model_validate(data)

    async def user_update(self, person_id: str,
                    setting: HotDeskingVoicePortalSetting,
                    org_id: str = None):
        """
        Update Voice Portal Hot desking sign in details for a user

        Update the Hot desking sign in details for a user.

        This requires a full administrator auth token with a scope of `spark-admin:telephony_config_write`.

        :param person_id: ID of the person associated with the hot desking details.
        :type person_id: str
        :param setting: Hot desking sign in details for a user.
        :type setting: HotDeskingVoicePortalSetting
        :param org_id: ID of the organization. Only admin users of another organization (such as partners) may use this
            parameter as the default is the same organization as the token used to access the API.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = setting.model_dump(mode='json', by_alias=True, exclude_unset=True)
        url = self.ep(f'people/{person_id}/features/hotDesking/guest')
        await super().put(url, params=params, json=body)


class AsHuntGroupApi(AsApiChild, base='telephony/config/huntGroups'):
    """
    Hunt Group API
    """
    forwarding: AsForwardingApi

    def __init__(self, session: AsRestSession):
        super().__init__(session=session)
        self.forwarding = AsForwardingApi(session=session, feature_selector=AsFeatureSelector.huntgroups)

    def _endpoint(self, *, location_id: str = None, huntgroup_id: str = None, path: str = None) -> str:
        """
        hunt group specific feature endpoint like /v1/telephony/config/locations/{locationId}/huntGroups/{huntGroupId}

        :meta private:
        :param location_id: Unique identifier for the location.
        :type location_id: str
        :param huntgroup_id: schedule id
        :type huntgroup_id: str
        :return: full endpoint
        :rtype: str
        """
        if location_id is None:
            return self.session.ep('telephony/config/huntGroups')
        else:
            ep = self.session.ep(f'telephony/config/locations/{location_id}/huntGroups')
            if huntgroup_id:
                ep = f'{ep}/{huntgroup_id}'
            if path:
                ep = f'{ep}/{path}'
            return ep

    def list_gen(self, org_id: str = None, location_id: str = None, name: str = None,
             phone_number: str = None, **params) -> AsyncGenerator[HuntGroup, None, None]:
        """
        Read the List of Hunt Groups

        List all calling Hunt Groups for the organization.

        Hunt groups can route incoming calls to a group of people or workspaces. You can even configure a pattern to
        route to a whole group.

        Retrieving this list requires a full or read-only administrator auth token with a scope of
        spark-admin:telephony_config_read.

        :param org_id: List hunt groups for this organization.
        :param location_id: Only return hunt groups with matching location ID.
        :param name: Only return hunt groups with the matching name.
        :param phone_number: Only return hunt groups with the matching primary phone number or extension.
        :return: yields :class:`HuntGroup` instances
        """
        params.update((to_camel(k), v)
                      for i, (k, v) in enumerate(locals().items())
                      if i and v is not None and k != 'params')
        url = self._endpoint()
        # noinspection PyTypeChecker
        return self.session.follow_pagination(url=url, model=HuntGroup, params=params)

    async def list(self, org_id: str = None, location_id: str = None, name: str = None,
             phone_number: str = None, **params) -> List[HuntGroup]:
        """
        Read the List of Hunt Groups

        List all calling Hunt Groups for the organization.

        Hunt groups can route incoming calls to a group of people or workspaces. You can even configure a pattern to
        route to a whole group.

        Retrieving this list requires a full or read-only administrator auth token with a scope of
        spark-admin:telephony_config_read.

        :param org_id: List hunt groups for this organization.
        :param location_id: Only return hunt groups with matching location ID.
        :param name: Only return hunt groups with the matching name.
        :param phone_number: Only return hunt groups with the matching primary phone number or extension.
        :return: yields :class:`HuntGroup` instances
        """
        params.update((to_camel(k), v)
                      for i, (k, v) in enumerate(locals().items())
                      if i and v is not None and k != 'params')
        url = self._endpoint()
        # noinspection PyTypeChecker
        return [o async for o in self.session.follow_pagination(url=url, model=HuntGroup, params=params)]

    async def by_name(self, name: str, location_id: str = None, org_id: str = None) -> Optional[HuntGroup]:
        """
        Get hunt group info by name
        :param location_id:
        :param name:
        :param org_id:
        :return:
        """
        return next((hg for hg in await self.list(name=name, location_id=location_id, org_id=org_id)
                     if hg.name == name), None)

    async def create(self, location_id: str, settings: HuntGroup, org_id: str = None) -> str:
        """
        Create a Hunt Group

        Create new Hunt Groups for the given location.

        Hunt groups can route incoming calls to a group of people or workspaces. You can even configure a pattern to
        route to a whole group.

        Creating a hunt group requires a full administrator auth token with a scope of
        spark-admin:telephony_config_write.

        :param location_id: Create the hunt group for the given location.
        :type location_id: str
        :param settings: hunt group details
        :type settings: :class:`HuntGroup`
        :param org_id: Create the hunt group for this organization.
        :type org_id: str
        :return: ID of the newly created hunt group.
        :rtype: str
        """
        params = org_id and {'orgId': org_id} or {}
        settings.call_policies = settings.call_policies or HGCallPolicies().default()
        data = settings.create_or_update()
        url = self._endpoint(location_id=location_id)
        data = await self.post(url, json=data, params=params)
        return data['id']

    async def delete_huntgroup(self, location_id: str, huntgroup_id: str, org_id: str = None):
        """
        Delete a Hunt Group

        Delete the designated Hunt Group.

        Hunt groups can route incoming calls to a group of people or workspaces. You can even configure a pattern to
        route to a whole group.

        Deleting a hunt group requires a full administrator auth token with a scope of
        spark-admin:telephony_config_write.

        :param location_id: Location from which to delete a hunt group.
        :type location_id: str
        :param huntgroup_id: Delete the hunt group with the matching ID.
        :type huntgroup_id: str
        :param org_id: Delete the hunt group with the matching ID.
        :type org_id: str
        """
        params = org_id and {'orgId': org_id} or None
        url = self._endpoint(location_id=location_id, huntgroup_id=huntgroup_id)
        await self.delete(url, params=params)

    async def details(self, location_id: str, huntgroup_id: str, org_id: str = None) -> HuntGroup:
        """
        Get Details for a Hunt Group

        Retrieve Hunt Group details.

        Hunt groups can route incoming calls to a group of people or workspaces. You can even configure a pattern to
        route to a whole group.

        Retrieving hunt group details requires a full or read-only administrator auth token with a scope of
        spark-admin:telephony_config_read.

        :param location_id: Retrieve settings for a hunt group in this location.
        :type location_id: str
        :param huntgroup_id: Retrieve settings for the hunt group with this identifier.
        :type huntgroup_id: str
        :param org_id: Retrieve hunt group settings from this organization.
        :type org_id: str
        :return: hunt group details
        """
        url = self._endpoint(location_id=location_id, huntgroup_id=huntgroup_id)
        params = org_id and {'orgId': org_id} or {}
        data = await self.get(url, params=params)
        result = HuntGroup.model_validate(data)
        return result

    async def update(self, location_id: str, huntgroup_id: str, update: HuntGroup,
               org_id: str = None):
        """
        Update a Hunt Group

        Update the designated Hunt Group.

        Hunt groups can route incoming calls to a group of people or workspaces. You can even configure a pattern to
        route to a whole group.

        Updating a hunt group requires a full administrator auth token with a scope
        of spark-admin:telephony_config_write.

        :param location_id: Update the hunt group for this location.
        :type location_id: str
        :param huntgroup_id: Update setting for the hunt group with the matching ID.
        :type huntgroup_id: str
        :param update: hunt group settings
        :type update: :class:`HuntGroup`
        :param org_id: Update hunt group settings from this organization.
        """
        params = org_id and {'orgId': org_id} or None
        data = update.create_or_update()
        url = self._endpoint(location_id=location_id, huntgroup_id=huntgroup_id)
        await self.put(url, json=data, params=params)

    def primary_available_phone_numbers_gen(self, location_id: str, phone_number: List[str] = None,
                                        org_id: str = None,
                                        **params) -> AsyncGenerator[AvailableNumber, None, None]:
        """
        Get Hunt Group Primary Available Phone Numbers

        List service and standard numbers that are available to be assigned as the hunt group's primary phone number.
        These numbers are associated with the location specified in the request URL, can be active or inactive, and are
        unassigned.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full, read-only or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param location_id: Return the list of phone numbers for this location within the given organization. The
            maximum length is 36.
        :type location_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableNumber` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        url = self._endpoint(location_id=location_id, path='availableNumbers')
        return self.session.follow_pagination(url=url, model=AvailableNumber, item_key='phoneNumbers', params=params)

    async def primary_available_phone_numbers(self, location_id: str, phone_number: List[str] = None,
                                        org_id: str = None,
                                        **params) -> List[AvailableNumber]:
        """
        Get Hunt Group Primary Available Phone Numbers

        List service and standard numbers that are available to be assigned as the hunt group's primary phone number.
        These numbers are associated with the location specified in the request URL, can be active or inactive, and are
        unassigned.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full, read-only or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param location_id: Return the list of phone numbers for this location within the given organization. The
            maximum length is 36.
        :type location_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableNumber` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        url = self._endpoint(location_id=location_id, path='availableNumbers')
        return [o async for o in self.session.follow_pagination(url=url, model=AvailableNumber, item_key='phoneNumbers', params=params)]

    def alternate_available_phone_numbers_gen(self, location_id: str, phone_number: List[str] = None,
                                          org_id: str = None,
                                          **params) -> AsyncGenerator[AvailableNumber, None, None]:
        """
        Get Hunt Group Alternate Available Phone Numbers

        List service and standard numbers that are available to be assigned as the hunt group's alternate phone number.
        These numbers are associated with the location specified in the request URL, can be active or inactive, and are
        unassigned.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full, read-only or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param location_id: Return the list of phone numbers for this location within the given organization. The
            maximum length is 36.
        :type location_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableNumber` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        url = self._endpoint(location_id=location_id, path='alternate/availableNumbers')
        return self.session.follow_pagination(url=url, model=AvailableNumber, item_key='phoneNumbers', params=params)

    async def alternate_available_phone_numbers(self, location_id: str, phone_number: List[str] = None,
                                          org_id: str = None,
                                          **params) -> List[AvailableNumber]:
        """
        Get Hunt Group Alternate Available Phone Numbers

        List service and standard numbers that are available to be assigned as the hunt group's alternate phone number.
        These numbers are associated with the location specified in the request URL, can be active or inactive, and are
        unassigned.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full, read-only or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param location_id: Return the list of phone numbers for this location within the given organization. The
            maximum length is 36.
        :type location_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableNumber` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        url = self._endpoint(location_id=location_id, path='alternate/availableNumbers')
        return [o async for o in self.session.follow_pagination(url=url, model=AvailableNumber, item_key='phoneNumbers', params=params)]

    def forward_available_phone_numbers_gen(self, location_id: str, phone_number: List[str] = None,
                                        owner_name: str = None, extension: str = None,
                                        org_id: str = None,
                                        **params) -> AsyncGenerator[AvailableNumber, None, None]:
        """
        Get Hunt Group Call Forward Available Phone Numbers

        List service and standard numbers that are available to be assigned as the hunt group's call forward number.
        These numbers are associated with the location specified in the request URL, can be active or inactive, and are
        assigned to an owning entity.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full, read-only or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param location_id: Return the list of phone numbers for this location within the given organization. The
            maximum length is 36.
        :type location_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param owner_name: Return the list of phone numbers that are owned by the given `ownerName`. Maximum length is
            255.
        :type owner_name: str
        :param extension: Returns the list of PSTN phone numbers with the given `extension`.
        :type extension: str
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableNumber` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        if owner_name is not None:
            params['ownerName'] = owner_name
        if extension is not None:
            params['extension'] = extension
        url = self._endpoint(location_id=location_id, path='callForwarding/availableNumbers')
        return self.session.follow_pagination(url=url, model=AvailableNumber, item_key='phoneNumbers', params=params)

    async def forward_available_phone_numbers(self, location_id: str, phone_number: List[str] = None,
                                        owner_name: str = None, extension: str = None,
                                        org_id: str = None,
                                        **params) -> List[AvailableNumber]:
        """
        Get Hunt Group Call Forward Available Phone Numbers

        List service and standard numbers that are available to be assigned as the hunt group's call forward number.
        These numbers are associated with the location specified in the request URL, can be active or inactive, and are
        assigned to an owning entity.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full, read-only or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param location_id: Return the list of phone numbers for this location within the given organization. The
            maximum length is 36.
        :type location_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param owner_name: Return the list of phone numbers that are owned by the given `ownerName`. Maximum length is
            255.
        :type owner_name: str
        :param extension: Returns the list of PSTN phone numbers with the given `extension`.
        :type extension: str
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableNumber` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        if owner_name is not None:
            params['ownerName'] = owner_name
        if extension is not None:
            params['extension'] = extension
        url = self._endpoint(location_id=location_id, path='callForwarding/availableNumbers')
        return [o async for o in self.session.follow_pagination(url=url, model=AvailableNumber, item_key='phoneNumbers', params=params)]


class AsLocationAccessCodesApi(AsApiChild, base='telephony/config/locations'):
    """
    Access codes API
    """

    def _endpoint(self, *, location_id: str, path: str = None) -> str:
        """
        location specific feature endpoint like
        /v1/telephony/config/locations/{locationId}/outgoingPermission/accessCodes}

        :meta private:
        :param location_id: Unique identifier for the location.
        :type location_id: str
        :param path: additional path
        :type: path: str
        :return: full endpoint
        :rtype: str
        """
        path = path and f'/{path}' or ''
        ep = self.session.ep(f'telephony/config/locations/{location_id}/outgoingPermission/accessCodes{path}')
        return ep

    async def read(self, location_id: str, org_id: str = None) -> list[AuthCode]:
        """
        Get Outgoing Permission Location Access Code

        Retrieve access codes details for a customer location.

        Use Access Codes to bypass the set permissions for all persons/workspaces at this location.

        Retrieving access codes details requires a full, user or read-only administrator or location administrator auth
        token with a scope of `spark-admin:telephony_config_read`.

        :param location_id: Retrieve access codes details for this location.
        :type location_id: str
        :param org_id: Retrieve access codes details for a customer location in this organization
        :type org_id: str
        :return: list of :class:`wxc_sdk.common.CallPark`
        """
        params = org_id and {'orgId': org_id} or None
        url = self._endpoint(location_id=location_id)
        data = await self.get(url, params=params)
        return TypeAdapter(list[AuthCode]).validate_python(data['accessCodes'])

    async def create(self, location_id: str, access_codes: list[AuthCode], org_id: str = None) -> list[AuthCode]:
        """
        Create Outgoing Permission a new access code for a customer location

        Add a new access code for the given location for a customer.

        Use Access Codes to bypass the set permissions for all persons/workspaces at this location.

        Creating an access code for the given location requires a full or user administrator or location administrator
        auth token with a scope of spark-admin:telephony_config_write.

        :param location_id: Add new access code for this location.
        :type location_id: str
        :param access_codes: Access code details
        :type access_codes: list of :class:`wxc_sdk.common.AuthCode`
        :param org_id: Add new access code for this organization.
        :type org_id: str
        """
        params = org_id and {'orgId': org_id} or None
        url = self._endpoint(location_id=location_id)
        body = {'accessCodes': [ac.model_dump(mode='json', by_alias=True, exclude_none=True) for ac in access_codes]}
        await self.post(url, json=body, params=params)

    async def delete_codes(self, location_id: str, access_codes: list[Union[str, AuthCode]],
                     org_id: str = None) -> list[AuthCode]:
        """
        Delete Access Code Location

        Deletes the access code details for a particular location for a customer.

        Use Access Codes to bypass the set permissions for all persons/workspaces at this location.

        Modifying the access code location details requires a full administrator auth token with a scope
        of spark-admin:telephony_config_write.

        :param location_id: Deletes the access code details for this location.
        :type location_id: str
        :param access_codes: access codes to delete
        :type access_codes: list of :class:`wxc_sdk.common.AuthCode` or str
        :param org_id: Delete access codes from this organization.
        :type org_id: str
        """
        params = org_id and {'orgId': org_id} or None
        url = self._endpoint(location_id=location_id)
        body = {'deleteCodes': [ac.code if isinstance(ac, AuthCode) else ac
                                for ac in access_codes]}
        await self.put(url, json=body, params=params)

    async def delete_all(self, location_id: str, org_id: str = None):
        """
        Delete Outgoing Permission Location Access Codes

        Deletes all the access codes for a particular location for a customer.

        Use Access Codes to bypass the set permissions for all persons/workspaces at this location.

        Deleting the access codes requires a full or user administrator or location administrator auth token with
        the `spark-admin:telephony_config_write` scope.

        :param location_id: Deletes all the access codes for this location.
        :type location_id: str
        :param org_id: Deletes the access codes for a customer location.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self._endpoint(location_id=location_id)
        await super().delete(url, params=params)


class AsLocationInterceptApi(AsApiChild, base='telephony/config/locations'):
    """
    API for location's call intercept settings
    """

    def _endpoint(self, *, location_id: str, path: str = None) -> str:
        """
        location specific
        telephony/config/locations/{locationId}/intercept

        :meta private:
        :param location_id: Unique identifier for the location.
        :type location_id: str
        :param path: additional path
        :type: path: str
        :return: full endpoint
        :rtype: str
        """
        path = path and f'/{path}' or ''
        ep = self.session.ep(f'telephony/config/locations/{location_id}/intercept{path}')
        return ep

    async def read(self, location_id: str, org_id: str = None) -> InterceptSetting:
        """
        Get Location Intercept

        Retrieve intercept location details for a customer location.

        Intercept incoming or outgoing calls for persons in your organization. If this is enabled, calls are either
        routed to a designated number the person chooses, or to the person's voicemail.

        Retrieving intercept location details requires a full, user or read-only administrator auth token with a
        scope of spark-admin:telephony_config_read.

        :param location_id: Retrieve intercept details for this location.
        :type location_id: str
        :param org_id: Retrieve intercept location details for a customer location.
        :type org_id: str
        :return: user's call intercept settings
        :rtype: :class:`wxc_sdk.person_settings.call_intercept.InterceptSetting`
        """
        ep = self._endpoint(location_id=location_id)
        params = org_id and {'orgId': org_id} or None
        return InterceptSetting.model_validate(await self.get(ep, params=params))

    async def configure(self, location_id: str, settings: InterceptSetting, org_id: str = None):
        """
        Put Location Intercept

        Modifies the intercept location details for a customer location.

        Intercept incoming or outgoing calls for users in your organization. If this is enabled, calls are either
        routed to a designated number the user chooses, or to the user's voicemail.

        Modifying the intercept location details requires a full, user administrator auth token with a scope
        of spark-admin:telephony_config_write.

        :param location_id: Unique identifier for the person.
        :type location_id: str
        :param settings: new intercept settings
        :type settings: InterceptSetting
        :param org_id: Person is in this organization. Only admin users of another organization (such as partners)
            may use this parameter as the default is the same organization as the token used to access API.
        :type org_id: str
        """
        ep = self._endpoint(location_id=location_id)
        params = org_id and {'orgId': org_id} or None
        data = settings.model_dump_json()
        await self.put(ep, params=params, data=data)


class AsOperatingModesApi(AsApiChild, base='telephony/config'):
    """
    Features: Operating Modes

    Features: `Operating modes` help manage calls more efficiently by routing them based on predefined settings.
    Authorized users can adjust these modes to reduce wait times for clients.
    `Operating modes` are used by mode-based forwarding for the `Auto Attendant`, `Call Queue`, and `Hunt Group`
    features.

    Viewing these read-only organization settings requires a full, read-only, or location administrator auth token with
    a scope of `spark-admin:telephony_config_read`.

    Modifying these organization settings requires a full, or location administrator auth token with a scope of
    `spark-admin:telephony_config_write`.

    A partner administrator can retrieve, or change settings in a customer's organization using the optional `orgId`
    query parameter.
    """

    def list_gen(self, limit_to_location_id: str = None, name: str = None,
             limit_to_org_level_enabled: bool = None, order: str = None,
             org_id: str = None,
             **params) -> AsyncGenerator[OperatingMode, None, None]:
        """
        Read the List of Operating Modes.

        Retrieve `Operating Modes` list defined at location, or organization level. Use query parameters to filter the
        result set by location or level. The list returned is sorted in ascending order by operating mode name. Long
        result sets are split into `pages
        <https://developer.webex.com/docs/basics#pagination>`_.

        `Operating modes` help manage calls more efficiently by routing them based on predefined settings.

        Retrieving this list requires a full, read-only, or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param limit_to_location_id: Location query parameter to filter the `operating modes` from that location only.
        :type limit_to_location_id: str
        :param name: List `operating modes` whose name contains this string.
        :type name: str
        :param limit_to_org_level_enabled: If true, only return `operating modes` defined at the organization level.
        :type limit_to_org_level_enabled: bool
        :param order: Sort the list of `operating modes` based on `name`, either asc, or desc.
        :type order: str
        :param org_id: Retrieve `operating modes` list from this organization.
        :type org_id: str
        :return: Generator yielding :class:`OperatingMode` instances
        """
        if name is not None:
            params['name'] = name
        if limit_to_location_id is not None:
            params['limitToLocationId'] = limit_to_location_id
        if limit_to_org_level_enabled is not None:
            params['limitToOrgLevelEnabled'] = str(limit_to_org_level_enabled).lower()
        if order is not None:
            params['order'] = order
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep('operatingModes')
        return self.session.follow_pagination(url=url, model=OperatingMode, item_key='operatingModes',
                                              params=params)

    async def list(self, limit_to_location_id: str = None, name: str = None,
             limit_to_org_level_enabled: bool = None, order: str = None,
             org_id: str = None,
             **params) -> List[OperatingMode]:
        """
        Read the List of Operating Modes.

        Retrieve `Operating Modes` list defined at location, or organization level. Use query parameters to filter the
        result set by location or level. The list returned is sorted in ascending order by operating mode name. Long
        result sets are split into `pages
        <https://developer.webex.com/docs/basics#pagination>`_.

        `Operating modes` help manage calls more efficiently by routing them based on predefined settings.

        Retrieving this list requires a full, read-only, or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param limit_to_location_id: Location query parameter to filter the `operating modes` from that location only.
        :type limit_to_location_id: str
        :param name: List `operating modes` whose name contains this string.
        :type name: str
        :param limit_to_org_level_enabled: If true, only return `operating modes` defined at the organization level.
        :type limit_to_org_level_enabled: bool
        :param order: Sort the list of `operating modes` based on `name`, either asc, or desc.
        :type order: str
        :param org_id: Retrieve `operating modes` list from this organization.
        :type org_id: str
        :return: Generator yielding :class:`OperatingMode` instances
        """
        if name is not None:
            params['name'] = name
        if limit_to_location_id is not None:
            params['limitToLocationId'] = limit_to_location_id
        if limit_to_org_level_enabled is not None:
            params['limitToOrgLevelEnabled'] = str(limit_to_org_level_enabled).lower()
        if order is not None:
            params['order'] = order
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep('operatingModes')
        return [o async for o in self.session.follow_pagination(url=url, model=OperatingMode, item_key='operatingModes',
                                              params=params)]

    async def details(self, mode_id: str, org_id: str = None) -> OperatingMode:
        """
        Get Details for an Operating Mode.

        Retrieve an `Operating Mode` by `Operating Mode ID`.

        `Operating modes` can be used to define call routing rules for different scenarios like business hours, after
        hours, holidays, etc.

        Retrieving an `operating mode` requires a full, read-only, or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param mode_id: Get the `operating mode` with the matching ID.
        :type mode_id: str
        :param org_id: Get the `operating mode` from this organization.
        :type org_id: str
        :rtype: :class:`OperatingMode`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'operatingModes/{mode_id}')
        data = await super().get(url, params=params)
        r = OperatingMode.model_validate(data)
        return r

    async def create(self, settings: OperatingMode, org_id: str = None) -> str:
        """
        Create an Operating Mode.

        Create an `Operating Mode` at an organization, or a location level.

        `Operating modes` can be used to define call routing rules for different scenarios like business hours, after
        hours, holidays, etc.

        Creating an `Operating Mode` requires a full, or location administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param settings: Create the `operating mode` with these settings. At least name, type and level must be set.
        :type settings: OperatingMode
        :param org_id: Create the `operating mode` for this organization.
        :type org_id: str
        :rtype: str
        """
        params = {'orgId': org_id} if org_id else None
        body = settings.create()
        url = self.ep('operatingModes')
        data = await super().post(url, params=params, json=body)
        r = data['id']
        return r

    async def update(self, mode_id: str, settings: OperatingMode, org_id: str = None):
        """
        Modify an Operating Mode.

        Modify the designated `Operating Mode's` configuration.

        `Operating modes` can be used to define call routing rules for different scenarios like business hours, after
        hours, holidays, etc.

        Modifying an `Operating Mode` requires a full, or location administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param mode_id: Modify the `operating mode` with the matching ID.
        :type mode_id: str
        :param settings: Modify the `operating mode` with these settings.
        :type settings: OperatingMode
        :param org_id: Modify the `operating mode` from this organization.
        :type org_id: str
        :rtype: None
        """
        params = {'orgId': org_id} if org_id else None
        body = settings.update()
        mode_id = mode_id or settings.id
        url = self.ep(f'operatingModes/{mode_id}')
        await super().put(url, params=params, json=body)

    async def delete(self, mode_id: str, org_id: str = None):
        """
        Delete an Operating Mode.

        Delete the designated `Operating Mode`.

        Deleting an `Operating Mode` requires a full, or location administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param mode_id: Delete the `operating mode` with the matching ID.
        :type mode_id: str
        :param org_id: Delete the `operating mode` from this organization.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'operatingModes/{mode_id}')
        await super().delete(url, params=params)

    async def holiday_details(self, mode_id: str, holiday_id: str,
                        org_id: str = None) -> OperatingModeHoliday:
        """
        Get details for an Operating Mode Holiday.

        Retrieve an `Operating Mode Holiday` by ID.

        Holidays define a recurring schedule for the `Operating Modes`.

        Retrieving an `Operating Mode Holiday` requires a full, read-only, or location administrator auth token with a
        scope of `spark-admin:telephony_config_read`.

        :param mode_id: Get the holiday from this `operating mode` matching ID.
        :type mode_id: str
        :param holiday_id: Get the `operating mode Holiday` with the matching ID.
        :type holiday_id: str
        :param org_id: Get the `operating mode` from this organization.
        :type org_id: str
        :rtype: :class:`OperatingModeHoliday`
        """
        params = {'orgId': org_id} if org_id else None
        url = self.ep(f'operatingModes/{mode_id}/holidays/{holiday_id}')
        data = await super().get(url, params=params)
        r = OperatingModeHoliday.model_validate(data)
        return r

    async def holiday_create(self, mode_id: str, settings: OperatingModeHoliday, org_id: str = None) -> str:
        """
        Create an Operating Mode Holiday.

        Create a holiday schedule event for the designated `Operating Mode`.

        Holidays define a recurring schedule for the `Operating Modes`. An `Operating Mode` can have a max of 150
        holidays.

        Creating an `Operating Mode Holiday` requires a full, or location administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param mode_id: Create the holiday for this `operating mode`.
        :type mode_id: str
        :param settings: Create the `operating mode holiday` with these settings.
        :type settings: OperatingModeHoliday
        :param org_id: Create the `operating mode holiday` for this organization.
        :type org_id: str
        :rtype: str
        """
        params = {'orgId': org_id} if org_id else None
        body = settings.create_update()
        url = self.ep(f'operatingModes/{mode_id}/holidays')
        data = await super().post(url, params=params, json=body)
        r = data['id']
        return r

    async def holiday_update(self, mode_id: str, holiday_id: str, settings: OperatingModeHoliday, org_id: str = None):
        """
        Modify an Operating Mode Holiday.

        Modify the designated `Operating Mode Holiday's` configuration.

        Modifying an `Operating Mode Holiday` requires a full, or location administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param mode_id: Modify the holiday from this `operating mode` matching ID.
        :type mode_id: str
        :param holiday_id: Modify the `Holiday` with the matching ID.
        :type holiday_id: str
        :param settings: Modify the `operating mode holiday` with these settings.
        :type settings: OperatingModeHoliday
        :param org_id: Modify the `operating mode` from this organization.
        :type org_id: str
        :rtype: None
        """
        params = {'orgId': org_id} if org_id else None
        holiday_id = holiday_id or settings.id
        body = settings.create_update()
        url = self.ep(f'operatingModes/{mode_id}/holidays/{holiday_id}')
        await super().put(url, params=params, json=body)

    async def holiday_delete(self, mode_id: str, holiday_id: str = None, org_id: str = None):
        """
        Delete an Operating Mode Holiday.

        Delete the designated `Operating Mode Holiday`.

        Deleting an `Operating Mode Holiday` requires a full, or location administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param mode_id: Delete the holiday from this `operating mode` matching ID.
        :type mode_id: str
        :param holiday_id: Delete the holiday with the matching ID.
        :type holiday_id: str
        :param org_id: Delete the `operating mode` from this organization.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'operatingModes/{mode_id}/holidays/{holiday_id}')
        await super().delete(url, params=params)

    async def available_operating_modes(self, location_id: str,
                                  org_id: str = None) -> List[IdAndName]:
        """
        Retrieve the List of Available Operating Modes in a Location.

        Retrieve list of `Operating Modes` which are available to be assigned to a location level feature (`Auto
        Attendant`, `Call Queue`, or `Hunt Group`). Since each location and an org can have a max of 100 `Operating
        Modes` defined. The max number of `operating modes` that can be returned is 200.

        `Operating modes` can be used to define call routing rules for different scenarios like business hours, after
        hours, holidays, etc. for the `Auto Attendant`, `Call Queue`, and `Hunt Group` features.

        Retrieving this list requires a full, read-only, or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param location_id: Retrieve `operating modes` list from this location.
        :type location_id: str
        :param org_id: Retrieve `operating modes` list from this organization.
        :type org_id: str
        :rtype: list[IdAndName]
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'locations/{location_id}/operatingModes/availableOperatingModes')
        data = await super().get(url, params=params)
        r = TypeAdapter(list[IdAndName]).validate_python(data['operatingModes'])
        return r

    def call_forward_available_phone_numbers_gen(self, location_id: str, phone_number: List[str] = None,
                                             owner_name: str = None, extension: str = None,
                                             org_id: str = None,
                                             **params) -> AsyncGenerator[AvailableNumber, None, None]:
        """
        Get Operating Mode Call Forward Available Phone Numbers

        List the service and standard PSTN numbers that are available to be assigned as a operating mode's call forward
        number.

        These numbers are associated with the location specified in the request URL, can be active or inactive, and are
        assigned to an owning entity.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full, read-only or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param location_id: Return the list of phone numbers for this location within the given organization. The
            maximum length is 36.
        :type location_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param owner_name: Return the list of phone numbers that are owned by the given `ownerName`. Maximum length is
            255.
        :type owner_name: str
        :param extension: Returns the list of PSTN phone numbers with the given `extension`.
        :type extension: str
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableNumber` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        if owner_name is not None:
            params['ownerName'] = owner_name
        if extension is not None:
            params['extension'] = extension
        url = self.ep(f'locations/{location_id}/operatingModes/callForwarding/availableNumbers')
        return self.session.follow_pagination(url=url, model=AvailableNumber,
                                              item_key='phoneNumbers', params=params)

    async def call_forward_available_phone_numbers(self, location_id: str, phone_number: List[str] = None,
                                             owner_name: str = None, extension: str = None,
                                             org_id: str = None,
                                             **params) -> List[AvailableNumber]:
        """
        Get Operating Mode Call Forward Available Phone Numbers

        List the service and standard PSTN numbers that are available to be assigned as a operating mode's call forward
        number.

        These numbers are associated with the location specified in the request URL, can be active or inactive, and are
        assigned to an owning entity.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full, read-only or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param location_id: Return the list of phone numbers for this location within the given organization. The
            maximum length is 36.
        :type location_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param owner_name: Return the list of phone numbers that are owned by the given `ownerName`. Maximum length is
            255.
        :type owner_name: str
        :param extension: Returns the list of PSTN phone numbers with the given `extension`.
        :type extension: str
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableNumber` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        if owner_name is not None:
            params['ownerName'] = owner_name
        if extension is not None:
            params['extension'] = extension
        url = self.ep(f'locations/{location_id}/operatingModes/callForwarding/availableNumbers')
        return [o async for o in self.session.follow_pagination(url=url, model=AvailableNumber,
                                              item_key='phoneNumbers', params=params)]


class AsOrgEmergencyServicesApi(AsApiChild, base='telephony/config/emergencyCallNotification'):
    """
    Organization Call Settings with Emergency Services

    Emergency Call Notifications can be enabled at the organization level, allowing specified email addresses to
    receive email notifications when an emergency call is made. To comply with U.S. Public Law 115-127, also known as
    Kari’s Law, any call that's made from within your organization to emergency services must generate an email
    notification.

    Viewing these organization settings requires a full or read-only administrator auth token with a scope of
    `spark-admin:telephony_config_read`. Modifying these organization settings requires a full administrator auth
    token with a scope of `spark-admin:telephony_config_write`.
    """

    async def read_emergency_call_notification(self, org_id: str = None) -> OrgEmergencyCallNotification:
        """
        Get an Organization Emergency Call Notification

        Get organization emergency call notification.

        Emergency Call Notifications can be enabled at the organization level, allowing specified email addresses to
        receive email notifications when an emergency call is made. To comply with U.S. Public Law 115-127, also known
        as Kari’s Law, any call that's made from within your organization to emergency services must generate an email
        notification.

        To retrieve organization call notifications requires a full, user or read-only administrator auth token with a
        scope of `spark-admin:telephony_config_read`.

        :param org_id: Retrieve Emergency Call Notification attributes for the organization.
        :type org_id: str
        :rtype: :class:`OrgEmergencyCallNotification`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep()
        data = await super().get(url, params=params)
        r = OrgEmergencyCallNotification.model_validate(data)
        return r

    async def update_emergency_call_notification(self, setting: OrgEmergencyCallNotification, org_id: str = None):
        """
        Update an organization emergency call notification.

        Once settings are enabled at the organization level, the configured email address will receive emergency call
        notifications for all locations.

        Emergency Call Notifications can be enabled at the organization level, allowing specified email addresses to
        receive email notifications when an emergency call is made. To comply with U.S. Public Law 115-127, also known
        as Kari’s Law, any call that's made from within your organization to emergency services must generate an email
        notification.

        To update organization call notification requires a full or user administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param setting: updated settings
        :type setting: OrgEmergencyCallNotification
        :param org_id: Update Emergency Call Notification attributes for the organization.
        :type org_id: str
        :rtype: None
        """
        params = org_id and {'orgId': org_id} or None
        body = setting.update()
        url = self.ep()
        await super().put(url, params=params, json=body)


class AsOrgMSTeamsSettingApi(AsApiChild, base='telephony/config/settings/msTeams'):
    """
    Client Call Settings

    Client Call Settings supports reading and writing of Webex Calling client settings for a specific organization.

    Viewing these read-only organization settings requires a full or read-only administrator auth token with a scope of
    `spark-admin:telephony_config_read`.

    Modifying these organization settings requires a full administrator auth token with a scope of
    `spark-admin:telephony_config_write`.

    A partner administrator can retrieve or change settings in a customer's organization using the optional `orgId`
    query parameter.
    """

    async def read(self, org_id: str = None) -> OrgMSTeamsSettings:
        """
        Get an Organization's MS Teams Settings

        Not supported for Webex for Government (FedRAMP)

        Get organization MS Teams settings.

        At an organization level, MS Teams settings allow access to viewing the `HIDE WEBEX APP` and `PRESENCE SYNC`
        settings.

        To retrieve an organization's MS Teams settings requires a full or read-only administrator auth token with a
        scope of `spark-admin:telephony_config_read`.

        :param org_id: Retrieve MS Teams settings for the organization.
        :type org_id: str
        :rtype: :class:`OrgMSTeamsSettings`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep()
        data = await super().get(url, params=params)
        r = OrgMSTeamsSettings.model_validate(data)
        return r

    async def configure(self, setting_name: str, value: bool,
                  org_id: str = None):
        """
        Update an Organization's MS Teams Setting

        Not supported for Webex for Government (FedRAMP)

        Update an MS Teams setting.

        MS Teams setting can be updated at the organization level.

        Requires a full administrator auth token with a scope of `spark-admin:telephony_config_write`.

        :param setting_name: The enum value, either `HIDE_WEBEX_APP` or `PRESENCE_SYNC`, for the respective
            `settingName` to be updated.
        :type setting_name: SettingsObjectSettingName
        :param value: The boolean value, either `true` or `false`, for the respective `settingName` to be updated.
        :type value: bool
        :param org_id: Update MS Teams setting value for the organization.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        body['settingName'] = setting_name
        body['value'] = value
        url = self.ep()
        await super().put(url, params=params, json=body)


class AsOrganisationAccessCodesApi(AsApiChild, base='telephony/config/outgoingPermission/accessCodes'):
    """
    Viewing an organisation requires a full, user or read-only administrator auth token with a scope
    of `spark-admin:telephony_config_read.
    """

    def list_gen(self, code: list[str] = None, description: list[str] = None, org_id: str = None,
             **params) -> AsyncGenerator[AuthCode, None, None]:
        """
        Retrieve the organisation's access codes.

        Access codes, also known as authorization codes, provide a mechanism to allow authorized users to enter a code
        to bypass outgoing or incoming calling permissions.

        This API requires a full, user or read-only administrator auth token with a scope
        of spark-admin:telephony_config_read

        :param code: Filter access code based on the comma-separated list provided in the `code` array.
        :type code: list[str]
        :param description: Filter access code based on the comma-separated list provided in the `description` array.
        :type description: list[str]
        :param org_id: ID of the organisation. Only admin users of another organisation (such as partners) may use this
            parameter as the default is the same organisation as the token used to access the API.
        :type org_id: str
        :return: Generator yielding :class:`AuthCode` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if code is not None:
            params['code'] = ','.join(code)
        if description is not None:
            params['description'] = ','.join(description)
        url = self.ep()
        return self.session.follow_pagination(url=url, model=AuthCode, params=params,
                                              item_key='accessCodes')

    async def list(self, code: list[str] = None, description: list[str] = None, org_id: str = None,
             **params) -> List[AuthCode]:
        """
        Retrieve the organisation's access codes.

        Access codes, also known as authorization codes, provide a mechanism to allow authorized users to enter a code
        to bypass outgoing or incoming calling permissions.

        This API requires a full, user or read-only administrator auth token with a scope
        of spark-admin:telephony_config_read

        :param code: Filter access code based on the comma-separated list provided in the `code` array.
        :type code: list[str]
        :param description: Filter access code based on the comma-separated list provided in the `description` array.
        :type description: list[str]
        :param org_id: ID of the organisation. Only admin users of another organisation (such as partners) may use this
            parameter as the default is the same organisation as the token used to access the API.
        :type org_id: str
        :return: Generator yielding :class:`AuthCode` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if code is not None:
            params['code'] = ','.join(code)
        if description is not None:
            params['description'] = ','.join(description)
        url = self.ep()
        return [o async for o in self.session.follow_pagination(url=url, model=AuthCode, params=params,
                                              item_key='accessCodes')]

    async def delete(self, delete_codes: List[str] = None,
               org_id: str = None):
        """
        Delete Outgoing Permission Access Code for an Organisation

        Deletes the access code details for a particular organisation and max limit is 10k per request.

        Access codes, also known as authorization codes, provide a mechanism to allow authorized users to enter a code
        to bypass outgoing or incoming calling permissions.

        This API requires a full or user administrator auth token with the `spark-admin:telephony_config_write` scope.

        :param delete_codes: Indicates access codes to delete.
        :type delete_codes: list[str]
        :param org_id: ID of the organisation. Only admin users of another organisation (such as partners) may use this
            parameter as the default is the same organisation as the token used to access the API.
        :type org_id: str
        :rtype: None
        """
        params = org_id and {'orgId': org_id} or None
        body = dict()
        if delete_codes is not None:
            body['deleteCodes'] = delete_codes
        url = self.ep()
        await super().put(url, params=params, json=body)

    async def create(self, access_codes: List[AuthCode], org_id: str = None):
        """
        Create Access Codes for an Organisation

        Create new access codes for the organisation and max limit is 10k per request.

        Access codes, also known as authorization codes, provide a mechanism to allow authorized users to enter a code
        to bypass outgoing or incoming calling permissions.

        This API requires a full or user administrator auth token with the `spark-admin:telephony_config_write` scope.

        :param access_codes: Indicates the set of activation codes and description.
        :type access_codes: list[AuthCode]
        :param org_id: ID of the organisation. Only admin users of another organisation (such as partners) may use this
            parameter as the default is the same organisation as the token used to access the API.
        :type org_id: str
        :rtype: None
        """
        params = org_id and {'orgId': org_id} or None
        body = dict()
        body['accessCodes'] = [ac.create() for ac in access_codes]
        url = self.ep()
        await super().post(url, params=params, json=body)


class AsOrganisationVoicemailSettingsAPI(AsApiChild, base='telephony/config/voicemail/settings'):
    """
    API for Organisation voicemail settings
    """

    async def read(self, org_id: str = None) -> OrganisationVoicemailSettings:
        """
        Get Voicemail Settings

        Retrieve the organization's voicemail settings.

        Organizational voicemail settings determines what voicemail features a person can configure and automatic
        message expiration.

        Retrieving organization's voicemail settings requires a full, user or read-only administrator auth token with
        a scope of spark-admin:telephony_config_read.

        :param org_id: Retrieve voicemail settings for this organization.
        :type org_id: str
        :return: VM settings
        :rtype: OrganisationVoicemailSettings
        """
        params = org_id and {'orgId': org_id} or None
        url = self.ep()
        return OrganisationVoicemailSettings.model_validate(await self.get(url, params=params))

    async def update(self, settings: OrganisationVoicemailSettings, org_id: str = None):
        """
        Update the organization's voicemail settings.

        Organizational voicemail settings determines what voicemail features a person can configure and automatic
        message expiration.

        Updating organization's voicemail settings requires a full administrator auth token with a scope
        of spark-admin:telephony_config_write.

        :param settings: new settings
        :type settings: OrganisationVoicemailSettings
        :param org_id: Update voicemail settings for this organization.
        :type org_id: str
        """
        params = org_id and {'orgId': org_id} or None
        url = self.ep()
        data = settings.model_dump_json()
        await self.put(url, data=data, params=params)


class AsPSTNApi(AsApiChild, base='telephony/pstn/locations'):
    """
    PSTN

    PSTN Location Connection Settings supports PSTN selection when creating a location or changing a PSTN type for a
    location. This is only supported for Local Gateway and Non-integrated CCP.

    Viewing these read-only organization settings requires a full or read-only administrator auth token with a scope of
    `spark-admin:telephony_pstn_read`.

    Modifying these organization settings requires a full administrator auth token with a scope of
    `spark-admin:telephony_pstn_write`.

    A partner administrator can retrieve or change settings in a customer's organization using the optional `orgId`
    query parameter.
    """

    async def list(self, location_id: str,
             service_types: list[PSTNServiceType] = None,
             org_id: str = None) -> list[PSTNConnectionOption]:
        """
        Retrieve PSTN Connection Options for a Location

        Retrieve the list of PSTN connection options available for a location.

        PSTN location connection settings enables the admin to configure or change the PSTN provider for a location.

        Retrieving this list requires a full or read-only administrator auth token with a scope of
        `spark-admin:telephony_pstn_read`.

        :param location_id: Return the list of List PSTN location connection options for this location.
        :type location_id: str
        :param service_types: Use the `serviceTypes` parameter to fetch connections for the following services

            * `MOBILE_NUMBERS`
        :type service_types: list[PSTNServiceType]
        :param org_id: List PSTN location connection options for this organization.
        :type org_id: str
        :rtype: list[PSTNConnectionOption]
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        if service_types is not None:
            params['serviceTypes'] = [enum_str(st) for st in service_types]
        url = self.ep(f'{location_id}/connectionOptions')
        data = await super().get(url, params=params)
        r = TypeAdapter(list[PSTNConnectionOption]).validate_python(data['items'])
        return r

    async def configure(self, location_id: str, id: str = None, premise_route_type: str = None,
                  premise_route_id: str = None, org_id: str = None):
        """
        Setup PSTN Connection for a Location

        Set up or update the PSTN connection details for a location.

        PSTN location connection settings enables the admin to configure or change the PSTN provider for a location.

        Setting up PSTN connection on a location requires a full administrator auth token with a scope of
        `spark-admin:telephony_pstn_write`.

        :param location_id: Setup PSTN location connection options for this location.
        :type location_id: str
        :param id: A unique identifier for the connection. This is required for non-integrated CCP.
        :type id: str
        :param premise_route_type: Premise route type. The possible types are TRUNK and ROUTE_GROUP. This is required
            for the local gateway.
        :type premise_route_type: str
        :param premise_route_id: Premise route ID. This refers to either a Trunk ID or a Route Group ID and is required
            for the local gateway.
        :type premise_route_id: str
        :param org_id: Setup PSTN location connection for this organization.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        if id is not None:
            body['id'] = id
        if premise_route_type is not None:
            body['premiseRouteType'] = premise_route_type
        if premise_route_id is not None:
            body['premiseRouteId'] = premise_route_id
        url = self.ep(f'{location_id}/connection')
        await super().put(url, params=params, json=body)

    async def read(self, location_id: str,
             org_id: str = None) -> PSTNConnectionOption:
        """
        Retrieve PSTN Connection for a Location

        Retrieves the current configured PSTN connection details for a location.

        PSTN location connection settings enables the admin to configure or change the PSTN provider for a location.

        Retrieving the PSTN connection details for a location requires a full or read-only administrator auth token
        with a scope of `spark-admin:telephony_pstn_read`.

        :param location_id: Retrieve PSTN location connection details for this location.
        :type location_id: str
        :param org_id: Retrieve PSTN location connection details for this organization.
        :type org_id: str
        :rtype: :class:`PSTNConnectionOption`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'{location_id}/connection')
        data = await super().get(url, params=params)
        r = PSTNConnectionOption.model_validate(data)
        return r


class AsPagingApi(AsApiChild, base='telephony/config'):

    def _endpoint(self, *, location_id: str = None, paging_id: str = None, path: str = None) -> str:
        """
        endpoint for paging group operation

        :meta private:
        :param location_id:
        :type location_id: str
        :param paging_id:
        :type paging_id: str
        """
        if location_id is None:
            return super().ep('paging')
        paging_id = paging_id and f'/{paging_id}' or ''
        path = path and f'/{path}' or ''
        return super().ep(f'locations/{location_id}/paging{paging_id}{path}')

    def list_gen(self, location_id: str = None, name: str = None, phone_number: str = None,
             org_id: str = None, **params) -> AsyncGenerator[Paging, None, None]:
        """
        Read the List of Paging Groups

        List all Paging Groups for the organization.

        Group Paging allows a person to place a one-way call or group page to up to 75 people and/or workspaces by
        dialing a number or extension assigned to a specific paging group. The Group Paging service makes a
        simultaneous call to all the assigned targets.

        Retrieving this list requires a full or read-only administrator auth token with a scope of
        spark-admin:telephony_config_read.

        :param location_id: Return only paging groups with matching location ID. Default is all locations
        :type location_id: str
        :param name: Return only paging groups with the matching name.
        :type name: str
        :param phone_number: Return only paging groups with matching primary phone number or extension.
        :type phone_number: str
        :param org_id: List paging groups for this organization.
        :type org_id: str
        :return: generator of class:`Paging` objects
        """
        params.update((to_camel(k), v)
                      for i, (k, v) in enumerate(locals().items())
                      if i and v is not None and k != 'params')
        url = self._endpoint()
        # noinspection PyTypeChecker
        return self.session.follow_pagination(url=url, model=Paging, params=params, item_key='locationPaging')
        pass

    async def list(self, location_id: str = None, name: str = None, phone_number: str = None,
             org_id: str = None, **params) -> List[Paging]:
        """
        Read the List of Paging Groups

        List all Paging Groups for the organization.

        Group Paging allows a person to place a one-way call or group page to up to 75 people and/or workspaces by
        dialing a number or extension assigned to a specific paging group. The Group Paging service makes a
        simultaneous call to all the assigned targets.

        Retrieving this list requires a full or read-only administrator auth token with a scope of
        spark-admin:telephony_config_read.

        :param location_id: Return only paging groups with matching location ID. Default is all locations
        :type location_id: str
        :param name: Return only paging groups with the matching name.
        :type name: str
        :param phone_number: Return only paging groups with matching primary phone number or extension.
        :type phone_number: str
        :param org_id: List paging groups for this organization.
        :type org_id: str
        :return: generator of class:`Paging` objects
        """
        params.update((to_camel(k), v)
                      for i, (k, v) in enumerate(locals().items())
                      if i and v is not None and k != 'params')
        url = self._endpoint()
        # noinspection PyTypeChecker
        return [o async for o in self.session.follow_pagination(url=url, model=Paging, params=params, item_key='locationPaging')]
        pass

    async def create(self, location_id: str, settings: Paging, org_id: str = None) -> str:
        """
        Create a new Paging Group

        Create a new Paging Group for the given location.

        Group Paging allows a person to place a one-way call or group page to up to 75 people and/or workspaces by
        dialing a number or extension assigned to a specific paging group. The Group Paging service makes a
        simultaneous call to all the assigned targets.

        Creating a paging group requires a full administrator auth token with a scope of
        spark-admin:telephony_config_write.

        :param location_id: Create the paging group for this location.
        :type location_id: str
        :param settings: new paging group
        :type settings: Paging
        :param org_id: Create the paging group for this organization.
        :type org_id: str
        :return: ID of the newly created paging group.
        :rtype: str
        """
        params = org_id and {'orgId': org_id} or None
        if settings.originators and settings.originator_caller_id_enabled is None:
            raise TypeError('originator_caller_id_enabled required if originators are provided')
        url = self._endpoint(location_id=location_id)
        data = settings.create_or_update()
        data = await self.post(url, json=data, params=params)
        return data['id']

    async def delete_paging(self, location_id: str, paging_id: str, org_id: str = None):
        """
        Delete a Paging Group

        Delete the designated Paging Group.

        Group Paging allows a person to place a one-way call or group page to up to 75 people and/or workspaces by
        dialing a number or extension assigned to a specific paging group. The Group Paging service makes a
        simultaneous call to all the assigned targets.

        Deleting a paging group requires a full administrator auth token with a scope of
        spark-admin:telephony_config_write.

        :param location_id: Location from which to delete a paging group.
        :type location_id: str
        :param paging_id: Delete the paging group with the matching ID.
        :param org_id: Delete the paging group from this organization.
        """
        params = org_id and {'orgId': org_id} or None
        url = self._endpoint(location_id=location_id, paging_id=paging_id)
        await self.delete(url, params=params)

    async def details(self, location_id: str, paging_id: str, org_id: str = None) -> Paging:
        """
        Get Details for a Paging Group

        Retrieve Paging Group details.

        Group Paging allows a person to place a one-way call or group page to up to 75 people and/or workspaces by
        dialing a number or extension assigned to a specific paging group. The Group Paging service makes a
        simultaneous call to all the assigned targets.

        Retrieving paging group details requires a full or read-only administrator auth token with a scope of
        spark-admin:telephony_config_read.
        :param location_id: Retrieve settings for a paging group in this location.
        :param paging_id: Retrieve settings for the paging group with this identifier.
        :param org_id: Retrieve paging group settings from this organization.
        :return: :class:`Paging` object
        """
        params = org_id and {'orgId': org_id} or None
        url = self._endpoint(location_id=location_id, paging_id=paging_id)
        return Paging.model_validate(await self.get(url, params=params))

    async def update(self, location_id: str, update: Paging, paging_id: str, org_id: str = None):
        """
        Update a Paging Group

        Update the designated Paging Group.

        Group Paging allows a person to place a one-way call or group page to up to 75 people and/or workspaces by
        dialing a number or extension assigned to a specific paging group. The Group Paging service makes a
        simultaneous call to all the assigned targets.

        Updating a paging group requires a full administrator auth token with a scope of
        spark-admin:telephony_config_write.

        :param location_id: Update settings for a paging group in this location.
        :type location_id: str
        :param update: update parameters
        :type update: Paging
        :param paging_id: Update settings for the paging group with this identifier.
        :type paging_id: str
        :param org_id: Update paging group settings from this organization.
        :type org_id: str
        """
        params = org_id and {'orgId': org_id} or None
        url = self._endpoint(location_id=location_id, paging_id=paging_id)
        data = update.create_or_update()
        await self.put(url, json=data, params=params)

    def primary_available_phone_numbers_gen(self, location_id: str, phone_number: List[str] = None,
                                        org_id: str = None,
                                        **params) -> AsyncGenerator[AvailableNumber, None, None]:
        """
        Get Paging Group Primary Available Phone Numbers

        List service and standard numbers that are available to be assigned as the paging group's primary phone number.
        These numbers are associated with the location specified in the request URL, can be active or inactive, and are
        unassigned.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full, read-only or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param location_id: Return the list of phone numbers for this location within the given organization. The
            maximum length is 36.
        :type location_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableNumber` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        url = self._endpoint(location_id=location_id, path='availableNumbers')
        return self.session.follow_pagination(url=url, model=AvailableNumber, item_key='phoneNumbers', params=params)

    async def primary_available_phone_numbers(self, location_id: str, phone_number: List[str] = None,
                                        org_id: str = None,
                                        **params) -> List[AvailableNumber]:
        """
        Get Paging Group Primary Available Phone Numbers

        List service and standard numbers that are available to be assigned as the paging group's primary phone number.
        These numbers are associated with the location specified in the request URL, can be active or inactive, and are
        unassigned.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full, read-only or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param location_id: Return the list of phone numbers for this location within the given organization. The
            maximum length is 36.
        :type location_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableNumber` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        url = self._endpoint(location_id=location_id, path='availableNumbers')
        return [o async for o in self.session.follow_pagination(url=url, model=AvailableNumber, item_key='phoneNumbers', params=params)]


class AsPlayListApi(AsApiChild, base='telephony/config/announcements/playlists'):
    """
    Features:  Announcement PlayList

    Features: Announcement PlayList support reading and writing of Webex Calling Announcement PlayList settings for a
    specific organization. The playlist has multiple announcement files which will be played where the announcement
    playlist is selected.

    Viewing these read-only organization settings requires a full or read-only administrator auth token with a scope
    of `spark-admin:telephony_config_read`.

    Modifying these organization settings requires a full administrator auth token with a scope
    of `spark-admin:telephony_config_write`.

    A partner administrator can retrieve or change settings in a customer's organization using the optional `orgId`
    query parameter.
    """

    async def list(self, org_id: str = None) -> list[PlayList]:
        """
        Fetch list of announcement playlist on organization level

        Fetch a list of announcement playlist at an organization.

        This API requires a full or read-only administrator or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param org_id: Get announcements playlist in this organization.
        :type org_id: str
        :rtype: list[:class:`PlayList`]
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep()
        data = await super().get(url, params=params)
        r = TypeAdapter(list[PlayList]).validate_python(data['playlists'])
        return r

    async def create(self, name: str, announcement_ids: List[str], org_id: str = None) -> str:
        """
        Create announcement Playlist at organization level

        Create announcement Playlist at an organization level. A maximum of 25 announcement files can be included in a
        single playlist.

        This API requires a full administrator auth token with a scope of `spark-admin:telephony_config_write`.

        :param name: Unique name for the announcement playlist.
        :type name: str
        :param announcement_ids: Array of `announcementIds` associated with the playlist.
        :type announcement_ids: list[str]
        :param org_id: Create an announcement playlist in this organization.
        :type org_id: str
        :rtype: str
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        body['name'] = name
        body['announcementIds'] = announcement_ids
        url = self.ep()
        data = await super().post(url, params=params, json=body)
        r = data['id']
        return r

    async def details(self, play_list_id: str, org_id: str = None) -> PlayList:
        """
        Fetch details of announcement playlist at the organization level

        Fetch details of announcement playlist by its ID at an organization level.

        This API requires a full or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param play_list_id: Unique identifier of an announcement playlist.
        :type play_list_id: str
        :param org_id: Get an announcement playlist in this organization.
        :type org_id: str
        :rtype: :class:`PlayList`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'{play_list_id}')
        data = await super().get(url, params=params)
        r = PlayList.model_validate(data)
        return r

    async def modify(self, play_list_id: str, name: str = None, announcement_ids: List[str] = None, org_id: str = None):
        """
        Modify announcement playlist at organization level

        Modify an existing announcement playlist at an organization level.

        This API requires a full administrator auth token with a scope of `spark-admin:telephony_config_write`.

        :param play_list_id: Unique identifier of an announcement playlist.
        :type play_list_id: str
        :param name: Unique name for the announcement playlist.
        :type name: str
        :param announcement_ids: Array of `announcementIds` associated with the playlist.
        :type announcement_ids: list[str]
        :param org_id: Modify an announcement playlist in this organization.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        if name is not None:
            body['name'] = name
        if announcement_ids is not None:
            body['announcementIds'] = announcement_ids
        url = self.ep(f'{play_list_id}')
        await super().put(url, params=params, json=body)

    async def delete(self, play_list_id: str, org_id: str = None):
        """
        Delete an announcement playlist for an organization.

        This API requires a full administrator auth token with a scope of `spark-admin:telephony_config_write`.

        :param play_list_id: Unique identifier of an announcement playlist.
        :type play_list_id: str
        :param org_id: Delete an announcement playlist in this organization.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'{play_list_id}')
        await super().delete(url, params=params)

    async def assigned_locations(self, play_list_id: str, org_id: str = None) -> List[IdAndName]:
        """
        Fetch list of locations which are assigned to the announcement playlist

        Fetch list of locations which are assigned to the given announcement playlist

        This API requires a full or read-only administrator or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param play_list_id: Unique identifier of playlist.
        :type play_list_id: str
        :param org_id: Get location associated to a playlist in this organization.
        :type org_id: str
        :rtype: :class:`PlayListAssignedLocations`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'{play_list_id}/locations')
        data = await super().get(url, params=params)
        r = TypeAdapter(list[IdAndName]).validate_python(data['locations'])
        return r

    async def modify_assigned_locations(self, play_list_id: str, location_ids: List[str], org_id: str = None):
        """
        Modify list of assigned locations to the announcement playlist

        Modify list of assigned locations or add new locations to the announcement playlist. This will assing the
        playlist to the location's music on hold.

        This API requires a full administrator auth token with a scope of `spark-admin:telephony_config_write`.

        :param play_list_id: Unique identifier of an announcement playlist.
        :type play_list_id: str
        :param location_ids: Array of location IDs with which the playlist is associated.
        :type location_ids: list[str]
        :param org_id: Modify an assign location for announcement playlist for organization.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        body['locationIds'] = location_ids
        url = self.ep(f'{play_list_id}/locations')
        await super().put(url, params=params, json=body)


class AsDialPlanApi(AsApiChild, base='telephony/config/premisePstn/dialPlans'):

    def list_gen(self, dial_plan_name: str = None, route_group_name: str = None, trunk_name: str = None,
             order: str = None, org_id: str = None, **params) -> AsyncGenerator[DialPlan, None, None]:
        """
        List all Dial Plans for the organization.

        Dial plans route calls to on-premises destinations by use of the trunks or route groups with which the dial
        plan is associated. Multiple dial patterns can be defined as part of your dial plan. Dial plans are configured
        globally for an enterprise and apply to all users, regardless of location.

        Retrieving this list requires a full or read-only administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param dial_plan_name: Return the list of dial plans matching the dial plan name.
        :type dial_plan_name: str
        :param route_group_name: Return the list of dial plans matching the route group name.
        :type route_group_name: str
        :param trunk_name: Return the list of dial plans matching the trunk name.
        :type trunk_name: str
        :param order: Order the dial plans according to the designated fields. Available sort fields: name, routeName,
            routeType. Sort order is ascending by default
        :type order: str
        :param org_id: List dial plans for this organization.
        :type org_id: str
        :return:
        """
        params.update((to_camel(p), v) for i, (p, v) in enumerate(locals().items())
                      if i and v is not None and p != 'params')
        url = self.ep()
        # noinspection PyTypeChecker
        return self.session.follow_pagination(url=url, model=DialPlan, params=params, item_key='dialPlans')

    async def list(self, dial_plan_name: str = None, route_group_name: str = None, trunk_name: str = None,
             order: str = None, org_id: str = None, **params) -> List[DialPlan]:
        """
        List all Dial Plans for the organization.

        Dial plans route calls to on-premises destinations by use of the trunks or route groups with which the dial
        plan is associated. Multiple dial patterns can be defined as part of your dial plan. Dial plans are configured
        globally for an enterprise and apply to all users, regardless of location.

        Retrieving this list requires a full or read-only administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param dial_plan_name: Return the list of dial plans matching the dial plan name.
        :type dial_plan_name: str
        :param route_group_name: Return the list of dial plans matching the route group name.
        :type route_group_name: str
        :param trunk_name: Return the list of dial plans matching the trunk name.
        :type trunk_name: str
        :param order: Order the dial plans according to the designated fields. Available sort fields: name, routeName,
            routeType. Sort order is ascending by default
        :type order: str
        :param org_id: List dial plans for this organization.
        :type org_id: str
        :return:
        """
        params.update((to_camel(p), v) for i, (p, v) in enumerate(locals().items())
                      if i and v is not None and p != 'params')
        url = self.ep()
        # noinspection PyTypeChecker
        return [o async for o in self.session.follow_pagination(url=url, model=DialPlan, params=params, item_key='dialPlans')]

    async def create(self, name: str, route_id: str, route_type: RouteType, dial_patterns: List[str] = None,
               org_id: str = None) -> CreateResponse:
        """
        Create a Dial Plan for the organization.

        Dial plans route calls to on-premises destinations by use of trunks or route groups. They are configured
        globally for an enterprise and apply to all users, regardless of location. A dial plan also specifies the
        routing choice (trunk or route group) for calls that match any of its dial patterns. Specific dial patterns
        can be defined as part of your dial plan.

        Creating a dial plan requires a full administrator auth token with a scope
        of spark-admin:telephony_config_write.

        :param name: A unique name for the dial plan.
        :type name: str
        :param route_id: ID of route type associated with the dial plan.
        :type route_id: str
        :param route_type: Route Type associated with the dial plan.
        :type route_type: :class:`wxc_sdk.common.RouteType`
        :param dial_patterns: An Array of dial patterns
        :type dial_patterns: list[str]
        :param org_id: Organization to which dial plan belongs.
        :type org_id: str
        :return: result of dial plan creation
        :rtype: :class:`CreateResponse`
        """
        url = self.ep()
        params = org_id and {'orgId': org_id} or None
        body = {
            'name': name,
            'routeId': route_id,
            'routeType': route_type.value if isinstance(route_type, RouteType) else route_type,
            'dialPatterns': dial_patterns or []
        }
        data = await self.post(url=url, params=params, json=body)
        return CreateResponse.model_validate(data)

    async def details(self, dial_plan_id: str, org_id: str = None) -> DialPlan:
        """
        Get a Dial Plan for the organization.

        Dial plans route calls to on-premises destinations by use of trunks or route groups. They are configured
        globally for an enterprise and apply to all users, regardless of location. A dial plan also specifies the
        routing choice (trunk or route group) for calls that match any of its dial patterns. Specific dial patterns
        can be defined as part of your dial plan.

        Retrieving a dial plan requires a full or read-only administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param dial_plan_id: ID of the dial plan.
        :type dial_plan_id: str
        :param org_id: Organization to which dial plan belongs.
        :type org_id: str
        :return: dial plan details
        :rtype: :class:`DialPlan`
        """
        url = self.ep(dial_plan_id)
        params = org_id and {'orgId': org_id} or None
        data = await self.get(url=url, params=params)
        dp: DialPlan = DialPlan.model_validate(data)
        dp.dial_plan_id = dial_plan_id
        return dp

    async def update(self, update: DialPlan, org_id: str = None):
        """
        Modify a Dial Plan for the organization.

        Dial plans route calls to on-premises destinations by use of trunks or route groups. They are configured
        globally for an enterprise and apply to all users, regardless of location. A dial plan also specifies the
        routing choice (trunk or route group) for calls that match any of its dial patterns. Specific dial patterns
        can be defined as part of your dial plan.

        Modifying a dial plan requires a full administrator auth token with a scope
        of spark-admin:telephony_config_write.

        :param update: DialPlan objects with updated settings. Only name, route_id and route_type are considered. All
            three need to be set
        :param org_id: Organization to which dial plan belongs.
        :type org_id: str
        """
        url = self.ep(update.dial_plan_id)
        params = org_id and {'orgId': org_id} or None
        body = update.model_dump_json(include={'name', 'route_id', 'route_type'})
        await self.put(url=url, params=params, data=body)

    async def delete_dial_plan(self, dial_plan_id: str, org_id: str = None):
        """
        Delete a Dial Plan for the organization.

        Dial plans route calls to on-premises destinations by use of trunks or route groups. They are configured
        globally for an enterprise and apply to all users, regardless of location. A dial plan also specifies the
        routing choice (trunk or route group) for calls that match any of its dial patterns. Specific dial patterns
        can be defined as part of your dial plan.

        Deleting a dial plan requires a full administrator auth token with a scope
        of spark-admin:telephony_config_write.

        :param dial_plan_id: ID of the dial plan.
        :type dial_plan_id: str
        :param org_id: Organization to which dial plan belongs.
        :type org_id: str
        """
        url = self.ep(dial_plan_id)
        params = org_id and {'orgId': org_id} or None
        await self.delete(url=url, params=params)

    def patterns_gen(self, dial_plan_id: str, org_id: str = None,
                 dial_pattern: str = None, **params) -> AsyncGenerator[str, None, None]:
        """
        Read the List of Dial Patterns

        List all Dial Patterns for the organization.

        Dial plans route calls to on-premises destinations by use of trunks or route groups. They are configured
        globally for an enterprise and apply to all users, regardless of location. A dial plan also specifies the
        routing choice (trunk or route group) for calls that match any of its dial patterns. Specific dial patterns
        can be defined as part of your dial plan.

        Retrieving this list requires a full or read-only administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param dial_plan_id: ID of the dial plan.
        :type dial_plan_id: str
        :param org_id: List dial patterns associated with a dial plan.
        :type org_id: str
        :param dial_pattern: An enterprise dial pattern is represented by a sequence of digits (1-9), followed by
            optional wildcard characters. Valid wildcard characters are ! (matches any sequence of digits) and
            X (matches a single digit, 0-9).
            The ! wildcard can only occur once at the end and only in an E.164 pattern
        :return: list of patterns
        :rtype: list[str]
        """
        params.update((to_camel(p), v) for i, (p, v) in enumerate(locals().items())
                      if i > 1 and v is not None and p != 'params')
        url = self.ep(f'{dial_plan_id}/dialPatterns')

        return self.session.follow_pagination(url=url, params=params, item_key='dialPatterns')

    async def patterns(self, dial_plan_id: str, org_id: str = None,
                 dial_pattern: str = None, **params) -> List[str]:
        """
        Read the List of Dial Patterns

        List all Dial Patterns for the organization.

        Dial plans route calls to on-premises destinations by use of trunks or route groups. They are configured
        globally for an enterprise and apply to all users, regardless of location. A dial plan also specifies the
        routing choice (trunk or route group) for calls that match any of its dial patterns. Specific dial patterns
        can be defined as part of your dial plan.

        Retrieving this list requires a full or read-only administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param dial_plan_id: ID of the dial plan.
        :type dial_plan_id: str
        :param org_id: List dial patterns associated with a dial plan.
        :type org_id: str
        :param dial_pattern: An enterprise dial pattern is represented by a sequence of digits (1-9), followed by
            optional wildcard characters. Valid wildcard characters are ! (matches any sequence of digits) and
            X (matches a single digit, 0-9).
            The ! wildcard can only occur once at the end and only in an E.164 pattern
        :return: list of patterns
        :rtype: list[str]
        """
        params.update((to_camel(p), v) for i, (p, v) in enumerate(locals().items())
                      if i > 1 and v is not None and p != 'params')
        url = self.ep(f'{dial_plan_id}/dialPatterns')

        return [o async for o in self.session.follow_pagination(url=url, params=params, item_key='dialPatterns')]

    async def modify_patterns(self, dial_plan_id: str, dial_patterns: List[PatternAndAction], org_id: str = None):
        """
        Modify dial patterns for the Dial Plan.

        Dial plans route calls to on-premises destinations by use of trunks or route groups. They are configured
        globally for an enterprise and apply to all users, regardless of location. A dial plan also specifies the
        routing choice (trunk or route group) for calls that match any of its dial patterns. Specific dial patterns
        can be defined as part of your dial plan.

        Modifying a dial pattern requires a full administrator auth token with a scope
        of spark-admin:telephony_config_write.

        :param dial_plan_id: ID of the dial plan being modified.
        :type dial_plan_id: str
        :param dial_patterns: Array of dial patterns to add or delete. Dial Pattern that is not present in the
            request is not modified.
        :type dial_patterns: :class:`PatternAndAction`
        :param org_id: Organization to which dial plan belongs.
        :type org_id: str
        """
        url = self.ep(f'{dial_plan_id}/dialPatterns')
        params = org_id and {'orgId': org_id} or None
        body = dict()
        body['dialPatterns'] = TypeAdapter(list[PatternAndAction]).dump_python(dial_patterns, mode='json',
                                                                               by_alias=True,
                                                                               exclude_none=True)
        await self.put(url=url, params=params, json=body)

    async def delete_all_patterns(self, dial_plan_id: str, org_id: str = None):
        """
        Delete all dial patterns from the Dial Plan.

        Dial plans route calls to on-premises destinations by use of trunks or route groups. They are configured
        globally for an enterprise and apply to all users, regardless of location. A dial plan also specifies the
        routing choice (trunk or route group) for calls that match any of its dial patterns. Specific dial patterns
        can be defined as part of your dial plan.

        Deleting dial pattern requires a full administrator auth token with a scope
        of spark-admin:telephony_config_write.

        :param dial_plan_id: ID of the dial plan being modified.
        :type dial_plan_id: str
        :param org_id: Organization to which dial plan belongs.
        :type org_id: str
        """
        url = self.ep(f'{dial_plan_id}/dialPatterns')
        params = org_id and {'orgId': org_id} or None
        body = {'deleteAllDialPatterns': True}
        await self.put(url=url, params=params, json=body)


class AsRouteGroupApi(AsApiChild, base='telephony/config/premisePstn/routeGroups'):
    """
    API for everything route groups
    """

    def list_gen(self, name: str = None, order: str = None,
             org_id: str = None, **params) -> AsyncGenerator[RouteGroup, None, None]:
        """
        List all Route Groups for an organization. A Route Group is a group of trunks that allows further scale and
        redundancy with the connection to the premises.

        Retrieving this route group list requires a full or read-only administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param name: Return the list of route groups matching the route group name.
        :type name: st
        :param order: Order the route groups according to designated fields. Available sort orders: asc, desc.
        :type order: str
        :param org_id: List route groups for this organization.
        :type org_id: str
        :return: generator of :class:`RouteGroup` instances
        """
        params.update((to_camel(p), v) for p, v in locals().items()
                      if v is not None and p not in {'self', 'params'})
        url = self.ep()
        # noinspection PyTypeChecker
        return self.session.follow_pagination(url=url, params=params, model=RouteGroup)

    async def list(self, name: str = None, order: str = None,
             org_id: str = None, **params) -> List[RouteGroup]:
        """
        List all Route Groups for an organization. A Route Group is a group of trunks that allows further scale and
        redundancy with the connection to the premises.

        Retrieving this route group list requires a full or read-only administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param name: Return the list of route groups matching the route group name.
        :type name: st
        :param order: Order the route groups according to designated fields. Available sort orders: asc, desc.
        :type order: str
        :param org_id: List route groups for this organization.
        :type org_id: str
        :return: generator of :class:`RouteGroup` instances
        """
        params.update((to_camel(p), v) for p, v in locals().items()
                      if v is not None and p not in {'self', 'params'})
        url = self.ep()
        # noinspection PyTypeChecker
        return [o async for o in self.session.follow_pagination(url=url, params=params, model=RouteGroup)]

    async def create(self, route_group: RouteGroup, org_id: str = None) -> str:
        """
        Creates a Route Group for the organization.

        A Route Group is a collection of trunks that allows further scale and redundancy with the connection to the
        premises. Route groups can include up to 10 trunks from different locations.

        Creating a Route Group requires a full administrator auth token with a scope
        of spark-admin:telephony_config_write.

        :param route_group: settings for new route group. name and local_gateways need to be set. For each LGW
            id and priority need to be set.
            Example:

            .. code-block:: python

                rg = RouteGroup(name=rg_name,
                        local_gateways=[RGTrunk(trunk_id=trunk.trunk_id,
                                                priority=1)])
                rg_id = api.telephony.prem_pstn.route_group.create(route_group=rg)
        :type route_group: :class:`RouteGroup`
        :param org_id:
        :type org_id: str
        :return: id of new route group
        :rtype: str
        """
        params = org_id and {'orgId': org_id} or None
        data = route_group.create_or_update()
        url = self.ep()
        data = await self.post(url=url, params=params, json=data)
        return data['id']

    async def details(self, rg_id: str, org_id: str = None) -> RouteGroup:
        """
        Reads a Route Group for the organization based on id.

        A Route Group is a collection of trunks that allows further scale and redundancy with the connection to the
        premises. Route groups can include up to 10 trunks from different locations.

        Reading a Route Group requires a full or read-only administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param rg_id: Route Group for which details are being requested.
        :type rg_id: str
        :param org_id: Organization of the Route Group.
        :type org_id: str
        :return: route group details
        :rtype: :class:`RouteGroup`
        """
        params = org_id and {'orgId': org_id} or None
        url = self.ep(rg_id)
        data = await self.get(url=url, params=params)
        rg = RouteGroup.model_validate(data)
        # rg id is not set in response
        rg.rg_id = rg_id
        return rg

    async def update(self, rg_id: str, update: RouteGroup, org_id: str = None):
        """
        Modifies an existing Route Group for an organization based on id.

        A Route Group is a collection of trunks that allows further scale and redundancy with the connection to the
        premises. Route groups can include up to 10 trunks from different locations.

        Modifying a Route Group requires a full administrator auth token with a scope
        of spark-admin:telephony_config_write.

        :param rg_id: route group to be modified
        :type rg_id: str
        :param update: new settings
        :type update: :class:`RouteGroup`
        :param org_id: Organization of the Route Group.
        :type org_id: str
        """
        params = org_id and {'orgId': org_id} or None
        data = update.create_or_update()
        url = self.ep(rg_id)
        await self.put(url=url, params=params, json=data)

    async def delete_route_group(self, rg_id: str, org_id: str = None):
        """
        Remove a Route Group from an Organization based on id.

        A Route Group is a collection of trunks that allows further scale and redundancy with the connection to the
        premises. Route groups can include up to 10 trunks from different locations.

        Removing a Route Group requires a full administrator auth token with a scope
        of spark-admin:telephony_config_write.

        :param rg_id: Route Group to be deleted
        :type rg_id: str
        :param org_id: Organization of the Route Group.
        :type org_id: str
        """
        params = org_id and {'orgId': org_id} or None
        url = self.ep(rg_id)
        await self.delete(url=url, params=params)

    async def usage(self, rg_id: str, org_id: str = None) -> RouteGroupUsage:
        """
        Read the Usage of a Routing Group

        List the number of "Call to" on-premises Extensions, Dial Plans, PSTN Connections, and Route Lists used by a
        specific Route Group.
        Users within Call to Extension locations are registered to a PBX which allows you to route unknown extensions
        (calling number length of 2-6 digits) to the PBX using an existing Trunk or Route Group.
        PSTN Connections may be a Cisco PSTN, a cloud-connected PSTN, or a premises-based PSTN (local gateway).
        Dial Plans allow you to route calls to on-premises extensions via your trunk or route group.
        Route Lists are a list of numbers that can be reached via a route group and can be used to provide cloud PSTN
        connectivity to Webex Calling Dedicated Instance.

        Retrieving usage information requires a full or read-only administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param rg_id: Route group requested for information.
        :type rg_id: str
        :param org_id: Organization associated with specific route group
        :type org_id: str
        :return: usage information
        :rtype: :class:`RouteGroupUsage`
        """
        params = org_id and {'orgId': org_id} or None
        url = self.ep(f'{rg_id}/usage')
        data = await self.get(url=url, params=params)
        return RouteGroupUsage.model_validate(data)

    def usage_call_to_extension_gen(self, rg_id: str, location_name: str = None, order: str = None,
                                org_id: str = None, **params) -> AsyncGenerator[IdAndName, None, None]:
        """
        List "Call to" on-premises Extension Locations for a specific route group. Users within these locations are
        registered to a PBX which allows you to route unknown extensions (calling number length of 2-6 digits) to
        the PBX using an existing trunk or route group.

        Retrieving this location list requires a full or read-only administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param rg_id: Route group requested for information.
        :param location_name: Return the list of locations matching the location name.
        :type location_name: str
        :param order: Order the locations according to designated fields.  Available sort orders are `asc`, and `desc`.
        :type order: str
        :param org_id: Organization associated with specific route group.
        :return: generator of instances
        :rtype: :class:`wxc_sdk.common.IdAndName`
        """
        if org_id is not None:
            params['orgId'] = org_id
        if location_name is not None:
            params['locationName'] = location_name
        if order is not None:
            params['order'] = order
        url = self.ep(f'{rg_id}/usageCallToExtension')
        # noinspection PyTypeChecker
        return self.session.follow_pagination(url=url, model=IdAndName, params=params)

    async def usage_call_to_extension(self, rg_id: str, location_name: str = None, order: str = None,
                                org_id: str = None, **params) -> List[IdAndName]:
        """
        List "Call to" on-premises Extension Locations for a specific route group. Users within these locations are
        registered to a PBX which allows you to route unknown extensions (calling number length of 2-6 digits) to
        the PBX using an existing trunk or route group.

        Retrieving this location list requires a full or read-only administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param rg_id: Route group requested for information.
        :param location_name: Return the list of locations matching the location name.
        :type location_name: str
        :param order: Order the locations according to designated fields.  Available sort orders are `asc`, and `desc`.
        :type order: str
        :param org_id: Organization associated with specific route group.
        :return: generator of instances
        :rtype: :class:`wxc_sdk.common.IdAndName`
        """
        if org_id is not None:
            params['orgId'] = org_id
        if location_name is not None:
            params['locationName'] = location_name
        if order is not None:
            params['order'] = order
        url = self.ep(f'{rg_id}/usageCallToExtension')
        # noinspection PyTypeChecker
        return [o async for o in self.session.follow_pagination(url=url, model=IdAndName, params=params)]

    def usage_dial_plan_gen(self, rg_id: str, location_name: str = None, order: str = None,
                        org_id: str = None, **params) -> AsyncGenerator[IdAndName, None, None]:
        """
        List Dial Plan Locations for a specific route group.

        Dial Plans allow you to route calls to on-premises destinations by use of trunks or route groups. They are
        configured globally for an enterprise and apply to all users, regardless of location. A Dial Plan also
        specifies the routing choice (trunk or route group) for calls that match any of its dial patterns.
        Specific dial patterns can be defined as part of your dial plan.

        Retrieving this location list requires a full or read-only administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param rg_id: Route group requested for information.
        :param location_name: Return the list of locations matching the location name.
        :type location_name: str
        :param order: Order the locations according to designated fields.  Available sort orders are `asc`, and `desc`.
        :type order: str
        :param org_id: Organization associated with specific route group.
        :return: generator of instances
        :rtype: :class:`wxc_sdk.common.IdAndName`
        """
        if org_id is not None:
            params['orgId'] = org_id
        if location_name is not None:
            params['locationName'] = location_name
        if order is not None:
            params['order'] = order
        url = self.ep(f'{rg_id}/usageDialPlan')
        # noinspection PyTypeChecker
        return self.session.follow_pagination(url=url, model=IdAndName, params=params)

    async def usage_dial_plan(self, rg_id: str, location_name: str = None, order: str = None,
                        org_id: str = None, **params) -> List[IdAndName]:
        """
        List Dial Plan Locations for a specific route group.

        Dial Plans allow you to route calls to on-premises destinations by use of trunks or route groups. They are
        configured globally for an enterprise and apply to all users, regardless of location. A Dial Plan also
        specifies the routing choice (trunk or route group) for calls that match any of its dial patterns.
        Specific dial patterns can be defined as part of your dial plan.

        Retrieving this location list requires a full or read-only administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param rg_id: Route group requested for information.
        :param location_name: Return the list of locations matching the location name.
        :type location_name: str
        :param order: Order the locations according to designated fields.  Available sort orders are `asc`, and `desc`.
        :type order: str
        :param org_id: Organization associated with specific route group.
        :return: generator of instances
        :rtype: :class:`wxc_sdk.common.IdAndName`
        """
        if org_id is not None:
            params['orgId'] = org_id
        if location_name is not None:
            params['locationName'] = location_name
        if order is not None:
            params['order'] = order
        url = self.ep(f'{rg_id}/usageDialPlan')
        # noinspection PyTypeChecker
        return [o async for o in self.session.follow_pagination(url=url, model=IdAndName, params=params)]

    def usage_location_pstn_gen(self, rg_id: str, location_name: str = None,
                            order: str = None, org_id: str = None, **params) -> AsyncGenerator[IdAndName, None, None]:
        """
        List PSTN Connection Locations for a specific route group. This solution lets you configure users to use Cloud
        PSTN (CCP or Cisco PSTN) or Premises-based PSTN.

        Retrieving this Location list requires a full or read-only administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param rg_id: Route group requested for information.
        :param location_name: Return the list of locations matching the location name.
        :type location_name: str
        :param order: Order the locations according to designated fields.  Available sort orders are `asc`, and `desc`.
        :type order: str
        :param org_id: Organization associated with specific route group.
        :return: generator of instances
        :rtype: :class:`wxc_sdk.common.IdAndName`
        """
        if org_id is not None:
            params['orgId'] = org_id
        if location_name is not None:
            params['locationName'] = location_name
        if order is not None:
            params['order'] = order
        url = self.ep(f'{rg_id}/usagePstnConnection')
        # noinspection PyTypeChecker
        return self.session.follow_pagination(url=url, model=IdAndName, params=params)

    async def usage_location_pstn(self, rg_id: str, location_name: str = None,
                            order: str = None, org_id: str = None, **params) -> List[IdAndName]:
        """
        List PSTN Connection Locations for a specific route group. This solution lets you configure users to use Cloud
        PSTN (CCP or Cisco PSTN) or Premises-based PSTN.

        Retrieving this Location list requires a full or read-only administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param rg_id: Route group requested for information.
        :param location_name: Return the list of locations matching the location name.
        :type location_name: str
        :param order: Order the locations according to designated fields.  Available sort orders are `asc`, and `desc`.
        :type order: str
        :param org_id: Organization associated with specific route group.
        :return: generator of instances
        :rtype: :class:`wxc_sdk.common.IdAndName`
        """
        if org_id is not None:
            params['orgId'] = org_id
        if location_name is not None:
            params['locationName'] = location_name
        if order is not None:
            params['order'] = order
        url = self.ep(f'{rg_id}/usagePstnConnection')
        # noinspection PyTypeChecker
        return [o async for o in self.session.follow_pagination(url=url, model=IdAndName, params=params)]

    def usage_route_lists_gen(self, rg_id: str, name: str = None, order: str = None, org_id: str = None,
                          **params) -> AsyncGenerator[UsageRouteLists, None, None]:
        """
        Read the Route Lists of a Routing Group

        List Route Lists for a specific route group. Route Lists are a list of numbers that can be reached via a Route
        Group. It can be used to provide cloud PSTN connectivity to Webex Calling Dedicated Instance.

        Retrieving this list of Route Lists requires a full or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param rg_id: Route group requested for information.
        :param name: Return the list of locations matching the location name.
        :type name: str
        :param order: Order the locations according to designated fields.  Available sort orders are `asc`, and `desc`.
        :type order: str
        :param org_id: Organization associated with specific route group.
        :return: generator of instances
        :rtype: :class:`wxc_sdk.common.IdAndName`
        """
        if org_id is not None:
            params['orgId'] = org_id
        if name is not None:
            params['name'] = name
        if order is not None:
            params['order'] = order
        url = self.ep(f'{rg_id}/usageRouteList')
        # noinspection PyTypeChecker
        return self.session.follow_pagination(url=url, model=UsageRouteLists, params=params)

    async def usage_route_lists(self, rg_id: str, name: str = None, order: str = None, org_id: str = None,
                          **params) -> List[UsageRouteLists]:
        """
        Read the Route Lists of a Routing Group

        List Route Lists for a specific route group. Route Lists are a list of numbers that can be reached via a Route
        Group. It can be used to provide cloud PSTN connectivity to Webex Calling Dedicated Instance.

        Retrieving this list of Route Lists requires a full or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param rg_id: Route group requested for information.
        :param name: Return the list of locations matching the location name.
        :type name: str
        :param order: Order the locations according to designated fields.  Available sort orders are `asc`, and `desc`.
        :type order: str
        :param org_id: Organization associated with specific route group.
        :return: generator of instances
        :rtype: :class:`wxc_sdk.common.IdAndName`
        """
        if org_id is not None:
            params['orgId'] = org_id
        if name is not None:
            params['name'] = name
        if order is not None:
            params['order'] = order
        url = self.ep(f'{rg_id}/usageRouteList')
        # noinspection PyTypeChecker
        return [o async for o in self.session.follow_pagination(url=url, model=UsageRouteLists, params=params)]


class AsRouteListApi(AsApiChild, base='telephony/config/premisePstn/routeLists'):
    """
    API for everything route lists
    """

    def list_gen(self, name: list[str] = None, location_id: list[str] = None, order: str = None,
             org_id: str = None, **params) -> AsyncGenerator[RouteList, None, None]:
        """
        List all Route Lists for the organization.

        A Route List is a list of numbers that can be reached via a Route Group. It can be used to provide cloud PSTN
        connectivity to Webex Calling Dedicated Instance.

        Retrieving the Route List requires a full or read-only administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param name: Return the list of Route List matching the route list name.
        :type name: str
        :param location_id: Return the list of Route Lists matching the location id.
        :type location_id: str
        :param order: Order the Route List according to the designated fields.Available sort fields: name, locationId.
            Sort order is ascending by default
        :type order: str
        :param org_id: List all Route List for this organization.
        :type org_id: str
        :return: generator yielding :class:`RouteList` instances
        """
        params.update((to_camel(p), v) for p, v in locals().items()
                      if v is not None and p not in {'self', 'params'})
        url = self.ep()
        # noinspection PyTypeChecker
        return self.session.follow_pagination(url=url, params=params, model=RouteList)

    async def list(self, name: list[str] = None, location_id: list[str] = None, order: str = None,
             org_id: str = None, **params) -> List[RouteList]:
        """
        List all Route Lists for the organization.

        A Route List is a list of numbers that can be reached via a Route Group. It can be used to provide cloud PSTN
        connectivity to Webex Calling Dedicated Instance.

        Retrieving the Route List requires a full or read-only administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param name: Return the list of Route List matching the route list name.
        :type name: str
        :param location_id: Return the list of Route Lists matching the location id.
        :type location_id: str
        :param order: Order the Route List according to the designated fields.Available sort fields: name, locationId.
            Sort order is ascending by default
        :type order: str
        :param org_id: List all Route List for this organization.
        :type org_id: str
        :return: generator yielding :class:`RouteList` instances
        """
        params.update((to_camel(p), v) for p, v in locals().items()
                      if v is not None and p not in {'self', 'params'})
        url = self.ep()
        # noinspection PyTypeChecker
        return [o async for o in self.session.follow_pagination(url=url, params=params, model=RouteList)]

    async def create(self, name: str, location_id: str, rg_id: str, org_id: str = None) -> str:
        """
        Create a Route List for the organization.

        A Route List is a list of numbers that can be reached via a Route Group. It can be used to provide cloud PSTN
        connectivity to Webex Calling Dedicated Instance.

        Creating a Route List requires a full administrator auth token with a scope
        of spark-admin:telephony_config_write.

        :param name: Name of the Route List
        :type name: str
        :param location_id: Location associated with the Route List.
        :type location_id: str
        :param rg_id: UUID of the route group associated with Route List.
        :type rg_id: str
        :param org_id: Organization to which Route List belongs.
        :type org_id: str
        :return: ID of the newly route list created.
        :rtype: str
        """
        params = org_id and {'orgId': org_id} or None
        body = {'name': name,
                'locationId': location_id,
                'routeGroupId': rg_id}
        url = self.ep()
        data = await self.post(url=url, params=params, json=body)
        return data['id']

    async def details(self, rl_id: str, org_id: str = None) -> RouteListDetail:
        """
        Get Route List Details.

        A Route List is a list of numbers that can be reached via a Route Group. It can be used to provide cloud PSTN
        connectivity to Webex Calling Dedicated Instance.

        Retrieving a Route List requires a full or read-only administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param rl_id: ID of the Route List.
        :type rl_id: str
        :param org_id: Organization to which Route List belongs.
        :type org_id: str
        :return: route list details
        :rtype: :class:`RouteListDetail`
        """
        params = org_id and {'orgId': org_id} or None
        url = self.ep(rl_id)
        data = await self.get(url=url, params=params)
        return RouteListDetail.model_validate(data)

    async def update(self, rl_id: str, name: str = None, rg_id: str = None, org_id: str = None):
        """
        Modify the details for a Route List.

        A Route List is a list of numbers that can be reached via a Route Group. It can be used to provide cloud PSTN
        connectivity to Webex Calling Dedicated Instance.

        Retrieving a Route List requires a full or read-only administrator auth token with a scope
        of spark-admin:telephony_config_write.

        :param rl_id: ID of the Route List.
        :type rl_id: str
        :param name: Route List new name.
        :type name: str
        :param rg_id: New route group id.
        :type rg_id: str
        :param org_id: Organization to which Route List belongs.
        :type org_id: str
        """
        params = org_id and {'orgId': org_id} or None
        body = dict()
        if name is not None:
            body['name'] = name
        if rg_id is not None:
            body['routeGroupId'] = rg_id
        url = self.ep(rl_id)
        await self.put(url=url, params=params, json=body)

    async def delete_route_list(self, rl_id: str, org_id: str = None):
        """
        Delete Route List for a Customer

        A Route List is a list of numbers that can be reached via a Route Group. It can be used to provide cloud PSTN
        connectivity to Webex Calling Dedicated Instance.

        Deleting a Route List requires a full administrator auth token with a scope
        of spark-admin:telephony_config_write.

        :param rl_id: ID of the Route List.
        :type rl_id: str
        :param org_id: Organization to which Route List belongs.
        :type org_id: str
        """
        params = org_id and {'orgId': org_id} or None
        url = self.ep(rl_id)
        await self.delete(url=url, params=params)

    def numbers_gen(self, rl_id: str, order: str = None, number: str = None,
                org_id: str = None, **params) -> AsyncGenerator[str, None, None]:
        """
        Get numbers assigned to a Route List

        A Route List is a list of numbers that can be reached via a Route Group. It can be used to provide cloud PSTN
        connectivity to Webex Calling Dedicated Instance.

        Retrieving a Route List requires a full or read-only administrator auth token with a scope
        of spark-admin:telephony_config_write.

        :param rl_id: ID of the Route List.
        :type rl_id: str
        :param order: Order the Route Lists according to number.
        :type order: str
        :param number: Number assigned to the route list.
        :type number: str
        :param org_id: Organization to which Route List belongs.
        :type org_id: str
        :return: generator yielding str
        """
        params.update((to_camel(p), v) for p, v in locals().items()
                      if v is not None and p not in {'self', 'params', 'rl_id'})
        url = self.ep(f'{rl_id}/numbers')
        # noinspection PyTypeChecker
        return self.session.follow_pagination(url=url, params=params)

    async def numbers(self, rl_id: str, order: str = None, number: str = None,
                org_id: str = None, **params) -> List[str]:
        """
        Get numbers assigned to a Route List

        A Route List is a list of numbers that can be reached via a Route Group. It can be used to provide cloud PSTN
        connectivity to Webex Calling Dedicated Instance.

        Retrieving a Route List requires a full or read-only administrator auth token with a scope
        of spark-admin:telephony_config_write.

        :param rl_id: ID of the Route List.
        :type rl_id: str
        :param order: Order the Route Lists according to number.
        :type order: str
        :param number: Number assigned to the route list.
        :type number: str
        :param org_id: Organization to which Route List belongs.
        :type org_id: str
        :return: generator yielding str
        """
        params.update((to_camel(p), v) for p, v in locals().items()
                      if v is not None and p not in {'self', 'params', 'rl_id'})
        url = self.ep(f'{rl_id}/numbers')
        # noinspection PyTypeChecker
        return [o async for o in self.session.follow_pagination(url=url, params=params)]

    async def update_numbers(self, rl_id: str, numbers: List[NumberAndAction] = None,
                       delete_all_numbers: bool = None,
                       org_id: str = None) -> List[UpdateNumbersResponse]:
        """
        Modify Numbers for Route List

        Modify numbers for a specific Route List of a Customer.

        A Route List is a list of numbers that can be reached via a Route Group. It can be used to provide cloud PSTN
        connectivity to Webex Calling Dedicated Instance.

        Retrieving a Route List requires a full or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param rl_id: ID of the Route List.
        :type rl_id: str
        :param numbers: Array of the numbers to be deleted/added.
        :type numbers: list[:class:`NumberAndAction`]
        :param delete_all_numbers: If present, the numbers array is ignored and all numbers in the route list are
            deleted.
        :type delete_all_numbers: bool
        :param org_id: Organization to which Route List belongs.
        :type org_id: str
        :return: list of update number status
        :rtype: list[:class:`UpdateNumbersResponse`]
        """
        url = self.ep(f'{rl_id}/numbers')
        params = org_id and {'orgId': org_id} or None

        body = dict()
        if numbers is not None:
            body['numbers'] = TypeAdapter(list[NumberAndAction]).dump_python(numbers, mode='json', by_alias=True,
                                                                             exclude_none=True)
        if delete_all_numbers is not None:
            body['deleteAllNumbers'] = delete_all_numbers
        data = await self.put(url=url, params=params, json=body)
        if data:
            return TypeAdapter(list[UpdateNumbersResponse]).validate_python(data['numberStatus'])
        else:
            return []

    async def delete_all_numbers(self, rl_id: str, org_id: str = None):
        url = self.ep(f'{rl_id}/numbers')
        params = org_id and {'orgId': org_id} or None
        body = {'deleteAllNumbers': True}
        await self.put(url=url, params=params, json=body)


class AsTrunkApi(AsApiChild, base='telephony/config/premisePstn/trunks'):
    """
    API for everything trunks
    """

    def list_gen(self, name: str = None, location_name: str = None, trunk_type: str = None, order: str = None,
             org_id: str = None, **params) -> AsyncGenerator[Trunk, None, None]:
        """
        List all Trunks for the organization.

        A Trunk is a connection between Webex Calling and the premises, which terminates on the premises with a local
        gateway or other supported device. The trunk can be assigned to a Route Group - a group of trunks that allow
        ebex Calling to distribute calls over multiple trunks or to provide redundancy.

        Retrieving this list requires a full or read-only administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param name: Return the list of trunks matching the local gateway names.
        :type name: str
        :param location_name: Return the list of trunks matching the location names.
        :type location_name: str
        :param trunk_type: Return the list of trunks matching the trunk type.
        :type trunk_type: str
        :param order: Order the trunks according to the designated fields. Available sort fields: name, locationName.
            Sort order is ascending by default
        :type order: str
        :param org_id:
        :type org_id: str
        :return: generator of Trunk instances
        :rtype: :class:`Trunk`
        """
        params.update((to_camel(p), v) for p, v in locals().items()
                      if v is not None and p not in {'self', 'params'})
        url = self.ep()
        # noinspection PyTypeChecker
        return self.session.follow_pagination(url=url, params=params, model=Trunk, item_key='trunks')

    async def list(self, name: str = None, location_name: str = None, trunk_type: str = None, order: str = None,
             org_id: str = None, **params) -> List[Trunk]:
        """
        List all Trunks for the organization.

        A Trunk is a connection between Webex Calling and the premises, which terminates on the premises with a local
        gateway or other supported device. The trunk can be assigned to a Route Group - a group of trunks that allow
        ebex Calling to distribute calls over multiple trunks or to provide redundancy.

        Retrieving this list requires a full or read-only administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param name: Return the list of trunks matching the local gateway names.
        :type name: str
        :param location_name: Return the list of trunks matching the location names.
        :type location_name: str
        :param trunk_type: Return the list of trunks matching the trunk type.
        :type trunk_type: str
        :param order: Order the trunks according to the designated fields. Available sort fields: name, locationName.
            Sort order is ascending by default
        :type order: str
        :param org_id:
        :type org_id: str
        :return: generator of Trunk instances
        :rtype: :class:`Trunk`
        """
        params.update((to_camel(p), v) for p, v in locals().items()
                      if v is not None and p not in {'self', 'params'})
        url = self.ep()
        # noinspection PyTypeChecker
        return [o async for o in self.session.follow_pagination(url=url, params=params, model=Trunk, item_key='trunks')]

    async def create(self, name: str, location_id: str, password: str, trunk_type: TrunkType = TrunkType.registering,
               dual_identity_support_enabled: bool = None, device_type: TrunkDeviceType = None, address: str = None,
               domain: str = None, port: int = None, max_concurrent_calls: int = None,
               p_charge_info_support_policy: PChargeInfoSupportPolicy = None, org_id: str = None) -> str:
        """
        Create a Trunk for the organization.

        A Trunk is a connection between Webex Calling and the premises, which terminates on the premises with a local
        gateway or other supported device. The trunk can be assigned to a Route Group - a group of trunks that allow
        Webex Calling to distribute calls over multiple trunks or to provide redundancy.

        Creating a trunk requires a full administrator auth token with a scope of spark-admin:telephony_config_write.

        :param name: A unique name for the trunk.
        :type name: str
        :param location_id: ID of location associated with the trunk.
        :type location_id: str
        :param password: A password to use on the trunk.
        :type password: str
        :param trunk_type: Trunk Type associated with the trunk.
        :type trunk_type: :class:`TrunkType`
        :param dual_identity_support_enabled: Dual Identity Support setting impacts the handling of the From header
            and P-Asserted-Identity header when sending an initial SIP INVITE to the trunk for an outbound call.
        :type dual_identity_support_enabled: bool
        :param device_type: Device type associated with trunk.
        :type device_type: :class:`TrunkDeviceType`
        :param address: FQDN or SRV address. Required to create a static certificate-based trunk.
        :type address: str
        :param domain: Domain name. Required to create a static certificate based trunk.
        :type domain: str
        :param port: FQDN port. Required to create a static certificate-based trunk.
        :type port: int
        :param max_concurrent_calls: Max Concurrent call. Required to create a static certificate based trunk.
        :type max_concurrent_calls: int
        :param p_charge_info_support_policy: P-Charge Info Support policy.
        :type p_charge_info_support_policy: PChargeInfoSupportPolicy
        :param org_id: Organization to which trunk belongs.
        :type org_id: str
        :return: id of new trunk
        :rtype: str
        """
        params = org_id and {'orgId': org_id} or None
        body = dict()
        body['name'] = name
        body['locationId'] = location_id
        body['password'] = password
        if dual_identity_support_enabled is not None:
            body['dualIdentitySupportEnabled'] = dual_identity_support_enabled
        body['trunkType'] = enum_str(trunk_type)
        if device_type is not None:
            body['deviceType'] = device_type
        if address is not None:
            body['address'] = address
        if domain is not None:
            body['domain'] = domain
        if port is not None:
            body['port'] = port
        if max_concurrent_calls is not None:
            body['maxConcurrentCalls'] = max_concurrent_calls
        if p_charge_info_support_policy is not None:
            body['pChargeInfoSupportPolicy'] = enum_str(p_charge_info_support_policy)
        url = self.ep()
        data = await self.post(url=url, params=params, json=body)
        return data['id']

    async def details(self, trunk_id: str, org_id: str = None) -> TrunkDetail:
        """
        Get a Trunk for the organization.

        A Trunk is a connection between Webex Calling and the premises, which terminates on the premises with a local
        gateway or other supported device. The trunk can be assigned to a Route Group - a group of trunks that allow
        Webex Calling to distribute calls over multiple trunks or to provide redundancy.

        Retrieving a trunk requires a full or read-only administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param trunk_id: ID of the trunk.
        :type trunk_id: str
        :param org_id: Organization to which trunk belongs.
        :type org_id: str
        :return: trunk details
        :rtype: :class:`TrunkDetail`
        """
        url = self.ep(trunk_id)
        params = org_id and {'orgId': org_id} or None
        data = await self.get(url=url, params=params)
        return TrunkDetail.model_validate(data)

    async def update(self, trunk_id: str, name: str, password: str, dual_identity_support_enabled: bool = None,
               max_concurrent_calls: int = None, p_charge_info_support_policy: PChargeInfoSupportPolicy = None,
               org_id: str = None):
        """
        Modify a Trunk for the organization.

        A Trunk is a connection between Webex Calling and the premises, which terminates on the premises with a local
        gateway or other supported device. The trunk can be assigned to a Route Group - a group of trunks that allow
        Webex Calling to distribute calls over multiple trunks or to provide redundancy.

        Modifying a trunk requires a full administrator auth token with a scope of spark-admin:telephony_config_write.

        :param trunk_id:
        :type name: str
        :param password: A password to use on the trunk.
        :type password: str
        :param dual_identity_support_enabled: Dual Identity Support setting impacts the handling of the From header
            and P-Asserted-Identity header when sending an initial SIP INVITE to the trunk for an outbound call.
        :type dual_identity_support_enabled: bool
        :param max_concurrent_calls: Max Concurrent call. Required to create a static certificate based trunk.
        :type max_concurrent_calls: int
        :param p_charge_info_support_policy: P-Charge Info Support policy.
        :type p_charge_info_support_policy: PChargeInfoSupportPolicy
        :param org_id: Organization to which trunk belongs.
        :type org_id: str:return:
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        body['name'] = name
        body['password'] = password
        if dual_identity_support_enabled is not None:
            body['dualIdentitySupportEnabled'] = dual_identity_support_enabled
        if max_concurrent_calls is not None:
            body['maxConcurrentCalls'] = max_concurrent_calls
        if p_charge_info_support_policy is not None:
            body['pChargeInfoSupportPolicy'] = enum_str(p_charge_info_support_policy)
        url = self.ep(trunk_id)
        await self.put(url=url, params=params, json=body)

    async def delete_trunk(self, trunk_id: str, org_id: str = None):
        """
        Delete a Trunk for the organization.

        A Trunk is a connection between Webex Calling and the premises, which terminates on the premises with a local
        gateway or other supported device. The trunk can be assigned to a Route Group - a group of trunks that allow
        Webex Calling to distribute calls over multiple trunks or to provide redundancy.

        Deleting a trunk requires a full administrator auth token with a scope of spark-admin:telephony_config_write.

        :param trunk_id: ID of the trunk.
        :type trunk_id: str
        :param org_id: Organization to which trunk belongs.
        :type org_id: str
        """
        url = self.ep(trunk_id)
        params = org_id and {'orgId': org_id} or None
        await self.delete(url=url, params=params)

    async def trunk_types(self, org_id: str = None) -> List[TrunkTypeWithDeviceType]:
        """
        List all TrunkTypes with DeviceTypes for the organization.

        A Trunk is a connection between Webex Calling and the premises, which terminates on the premises with a local
        gateway or other supported device. The trunk can be assigned to a Route Group - a group of trunks that allow
        Webex Calling to distribute calls over multiple trunks or to provide redundancy. Trunk Types are Registering
        or Certificate Based and are configured in CallManager.

        Retrieving trunk types requires a full or read-only administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param org_id:
        :return: trunk types
        :rtype: list[:class:`TrunkTypeWithDeviceType`]
        """
        params = org_id and {'orgId': org_id} or None
        ep = self.ep('trunkTypes')
        data = await self.get(url=ep, params=params)
        return TypeAdapter(list[TrunkTypeWithDeviceType]).validate_python(data['trunkTypes'])

    async def usage(self, trunk_id: str, org_id: str = None) -> TrunkUsage:
        """
        Get Local Gateway Usage Count

        A trunk is a connection between Webex Calling and the premises, which terminates on the premises with a local
        gateway or other supported device. The trunk can be assigned to a Route Group - a group of trunks that allow
        Webex Calling to distribute calls over multiple trunks or to provide redundancy.

        Retrieving this information requires a full administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param trunk_id: ID of the trunk.
        :type trunk_id: str
        :param org_id: Organization to which trunk belongs.
        :type org_id: str
        :return: usage counts
        :rtype: :class:`TrunkUsage`
        """
        url = self.ep(f'{trunk_id}/usage')
        params = org_id and {'orgId': org_id} or None
        data = await self.get(url=url, params=params)
        return TrunkUsage.model_validate(data)

    def usage_call_to_extension_gen(self, trunk_id: str, order: str = None, name: List[str] = None, org_id: str = None,
                                **params) -> AsyncGenerator[IdAndName, None, None]:
        """
        Get local gateway call to on-premises extension usage for a trunk.

        A trunk is a connection between Webex Calling and the premises, which terminates on the premises with a local
        gateway or other supported device. The trunk can be assigned to a Route Group which is a group of trunks that
        allow Webex Calling to distribute calls over multiple trunks or to provide redundancy.

        Retrieving this information requires a full administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param trunk_id: ID of the trunk.
        :type trunk_id: str
        :param order: Order the trunks according to the designated fields.  Available sort fields are `name`, and
            `locationName`. Sort order is ascending by default
        :type order: str
        :param name: Return the list of trunks matching the local gateway names
        :type name: list[str]
        :param org_id: Organization to which the trunk belongs.
        :type org_id: str
        :return: generator of :class:`wxc_sdk.common.IdAndName` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if order is not None:
            params['order'] = order
        if name is not None:
            params['name'] = ','.join(name)
        url = self.ep(f'{trunk_id}/usageCallToExtension')
        # noinspection PyTypeChecker
        return self.session.follow_pagination(url=url, model=IdAndName, params=params)

    async def usage_call_to_extension(self, trunk_id: str, order: str = None, name: List[str] = None, org_id: str = None,
                                **params) -> List[IdAndName]:
        """
        Get local gateway call to on-premises extension usage for a trunk.

        A trunk is a connection between Webex Calling and the premises, which terminates on the premises with a local
        gateway or other supported device. The trunk can be assigned to a Route Group which is a group of trunks that
        allow Webex Calling to distribute calls over multiple trunks or to provide redundancy.

        Retrieving this information requires a full administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param trunk_id: ID of the trunk.
        :type trunk_id: str
        :param order: Order the trunks according to the designated fields.  Available sort fields are `name`, and
            `locationName`. Sort order is ascending by default
        :type order: str
        :param name: Return the list of trunks matching the local gateway names
        :type name: list[str]
        :param org_id: Organization to which the trunk belongs.
        :type org_id: str
        :return: generator of :class:`wxc_sdk.common.IdAndName` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if order is not None:
            params['order'] = order
        if name is not None:
            params['name'] = ','.join(name)
        url = self.ep(f'{trunk_id}/usageCallToExtension')
        # noinspection PyTypeChecker
        return [o async for o in self.session.follow_pagination(url=url, model=IdAndName, params=params)]

    def usage_dial_plan_gen(self, trunk_id: str, order: str = None, name: List[str] = None,
                        org_id: str = None, **params) -> AsyncGenerator[IdAndName, None, None]:
        """
        Get Local Gateway Dial Plan Usage for a Trunk.

        A Trunk is a connection between Webex Calling and the premises, which terminates on the premises with a local
        gateway or other supported device. The trunk can be assigned to a Route Group - a group of trunks that allow
        Webex Calling to distribute calls over multiple trunks or to provide redundancy.

        Retrieving this information requires a full administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param trunk_id: ID of the trunk.
        :type trunk_id: str
        :param order: Order the trunks according to the designated fields.  Available sort fields are `name`, and
            `locationName`. Sort order is ascending by default
        :type order: str
        :param name: Return the list of trunks matching the local gateway names
        :type name: list[str]
        :param org_id: Organization to which trunk belongs.
        :type org_id: str
        :return: id and name objects
        """
        if org_id is not None:
            params['orgId'] = org_id
        if order is not None:
            params['order'] = order
        if name is not None:
            params['name'] = ','.join(name)
        url = self.ep(f'{trunk_id}/usageDialPlan')
        # noinspection PyTypeChecker
        return self.session.follow_pagination(url=url, model=IdAndName, params=params)

    async def usage_dial_plan(self, trunk_id: str, order: str = None, name: List[str] = None,
                        org_id: str = None, **params) -> List[IdAndName]:
        """
        Get Local Gateway Dial Plan Usage for a Trunk.

        A Trunk is a connection between Webex Calling and the premises, which terminates on the premises with a local
        gateway or other supported device. The trunk can be assigned to a Route Group - a group of trunks that allow
        Webex Calling to distribute calls over multiple trunks or to provide redundancy.

        Retrieving this information requires a full administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param trunk_id: ID of the trunk.
        :type trunk_id: str
        :param order: Order the trunks according to the designated fields.  Available sort fields are `name`, and
            `locationName`. Sort order is ascending by default
        :type order: str
        :param name: Return the list of trunks matching the local gateway names
        :type name: list[str]
        :param org_id: Organization to which trunk belongs.
        :type org_id: str
        :return: id and name objects
        """
        if org_id is not None:
            params['orgId'] = org_id
        if order is not None:
            params['order'] = order
        if name is not None:
            params['name'] = ','.join(name)
        url = self.ep(f'{trunk_id}/usageDialPlan')
        # noinspection PyTypeChecker
        return [o async for o in self.session.follow_pagination(url=url, model=IdAndName, params=params)]

    def usage_location_pstn_gen(self, trunk_id: str, org_id: str = None) -> AsyncGenerator[IdAndName, None, None]:
        """
        Get Local Gateway Dial Plan Usage for a Trunk.

        A Trunk is a connection between Webex Calling and the premises, which terminates on the premises with
        a local
        gateway or other supported device. The trunk can be assigned to a Route Group - a group of trunks
        that allow
        Webex Calling to distribute calls over multiple trunks or to provide redundancy.

        Retrieving this information requires a full administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param trunk_id: ID of the trunk.
        :type trunk_id: str
        :param org_id: Organization to which trunk belongs.
        :type org_id: str
        :return: id and name objects
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'{trunk_id}/usagePstnConnection')
        # noinspection PyTypeChecker
        return self.session.follow_pagination(url=url, model=IdAndName, params=params)

    async def usage_location_pstn(self, trunk_id: str, org_id: str = None) -> List[IdAndName]:
        """
        Get Local Gateway Dial Plan Usage for a Trunk.

        A Trunk is a connection between Webex Calling and the premises, which terminates on the premises with
        a local
        gateway or other supported device. The trunk can be assigned to a Route Group - a group of trunks
        that allow
        Webex Calling to distribute calls over multiple trunks or to provide redundancy.

        Retrieving this information requires a full administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param trunk_id: ID of the trunk.
        :type trunk_id: str
        :param org_id: Organization to which trunk belongs.
        :type org_id: str
        :return: id and name objects
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'{trunk_id}/usagePstnConnection')
        # noinspection PyTypeChecker
        return [o async for o in self.session.follow_pagination(url=url, model=IdAndName, params=params)]

    def usage_route_group_gen(self, trunk_id: str, org_id: str = None) -> AsyncGenerator[IdAndName, None, None]:
        """
        Get Local Gateway Dial Plan Usage for a Trunk.

        A Trunk is a connection between Webex Calling and the premises, which terminates on the premises with a local
        gateway or other supported device. The trunk can be assigned to a Route Group - a group of trunks that allow
        Webex Calling to distribute calls over multiple trunks or to provide redundancy.

        Retrieving this information requires a full administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param trunk_id: ID of the trunk.
        :type trunk_id: str
        :param org_id: Organization to which trunk belongs.
        :type org_id: str
        :return: id and name objects
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'{trunk_id}/usageRouteGroup')
        # noinspection PyTypeChecker
        return self.session.follow_pagination(url=url, model=IdAndName, params=params)

    async def usage_route_group(self, trunk_id: str, org_id: str = None) -> List[IdAndName]:
        """
        Get Local Gateway Dial Plan Usage for a Trunk.

        A Trunk is a connection between Webex Calling and the premises, which terminates on the premises with a local
        gateway or other supported device. The trunk can be assigned to a Route Group - a group of trunks that allow
        Webex Calling to distribute calls over multiple trunks or to provide redundancy.

        Retrieving this information requires a full administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param trunk_id: ID of the trunk.
        :type trunk_id: str
        :param org_id: Organization to which trunk belongs.
        :type org_id: str
        :return: id and name objects
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'{trunk_id}/usageRouteGroup')
        # noinspection PyTypeChecker
        return [o async for o in self.session.follow_pagination(url=url, model=IdAndName, params=params)]

    async def validate_fqdn_and_domain(self, address: str, domain: str, port: int = None, org_id: str = None):
        """
        Validate Local Gateway FQDN and Domain for the organization trunks.

        A Trunk is a connection between Webex Calling and the premises, which terminates on the premises with a local
        gateway or other supported device. The trunk can be assigned to a Route Group - a group of trunks that allow
        Webex Calling to distribute calls over multiple trunks or to provide redundancy.

        Validating Local Gateway FQDN and Domain requires a full administrator auth token with a scope
        of spark-admin:telephony_config_write.

        :param address: FQDN or SRV address of the trunk.
        :type address: str
        :param domain: Domain name of the trunk.
        :type domain: str
        :param port: FQDN port of the trunk
        :type port: int
        :param org_id: Organization to which trunk types belongs.
        :type org_id: str
        """
        body = {p: v for p, v in locals().items()
                if p not in {'self', 'org_id'} and v is not None}
        url = self.ep('actions/fqdnValidation/invoke')
        params = org_id and {'orgId': org_id} or None
        await self.post(url=url, params=params, json=body)


class AsPremisePstnApi(AsApiChild, base='telephony/config/premisePstn'):
    """
    Premises PSTN API
    """
    #: dial plan configuration
    dial_plan: AsDialPlanApi
    #: trunk configuration
    trunk: AsTrunkApi
    #: route group configuration
    route_group: AsRouteGroupApi
    #: route list configuration
    route_list: AsRouteListApi

    def __init__(self, session: AsRestSession):
        super().__init__(session=session)
        self.dial_plan = AsDialPlanApi(session=session)
        self.trunk = AsTrunkApi(session=session)
        self.route_group = AsRouteGroupApi(session=session)
        self.route_list = AsRouteListApi(session=session)

    async def validate_pattern(self, dial_patterns: Union[str, List[str]], org_id: str = None) -> DialPatternValidationResult:
        """
        Validate a Dial Pattern.

        Dial plans route calls to on-premises destinations by use of trunks or route groups. They are configured
        globally for an enterprise and apply to all users, regardless of location. A dial plan also specifies the
        routing choice (trunk or route group) for calls that match any of its dial patterns. Specific dial patterns
        can be defined as part of your dial plan.

        Validating a dial pattern requires a full administrator auth token with a scope
        of spark-admin:telephony_config_write.

        :param dial_patterns: Array of dial patterns.
        :type dial_patterns: list[str] or str
        :param org_id: Organization to which dial plan belongs.
        :return: validation result
        :rtype: :class:`DialPatternValidationResult`
        """
        if isinstance(dial_patterns, str):
            dial_patterns = [dial_patterns]

        url = self.ep('actions/validateDialPatterns/invoke')
        params = org_id and {'orgId': org_id} or None
        body = {'dialPatterns': dial_patterns}
        data = await self.post(url=url, params=params, json=body)
        return DialPatternValidationResult.model_validate(data)


class AsPrivateNetworkConnectApi(AsApiChild, base='telephony/config/locations'):
    """
    API for location private network connect API settings
    """

    async def read(self, location_id: str, org_id: str = None) -> NetworkConnectionType:
        """
        Get Private Network Connect

        Retrieve the location's network connection type.

        Network Connection Type determines if the location's network connection is public or private.

        Retrieving location's network connection type requires a full, user or read-only administrator auth token with
        a scope of spark-admin:telephony_config_read.

        :param location_id: Retrieve network connection type for this location.
        :type location_id: str
        :param org_id: Retrieve network connection type for this organization.
        :type org_id: str
        :return: location PNC settings
        :rtype: NetworkConnectionType
        """
        params = org_id and {'orgId': org_id} or None
        url = self.session.ep(f'telephony/config/locations/{location_id}/privateNetworkConnect')
        data = await self.get(url, params=params)
        return TypeAdapter(NetworkConnectionType).validate_python(data['networkConnectionType'])

    async def update(self, location_id: str, connection_type: NetworkConnectionType, org_id: str = None):
        """
        Get Private Network Connect

        Retrieve the location's network connection type.

        Network Connection Type determines if the location's network connection is public or private.

        Retrieving location's network connection type requires a full, user or read-only administrator auth token with
        a scope of spark-admin:telephony_config_read.

        :param location_id: Update network connection type for this location.
        :type location_id: str
        :param connection_type: Network Connection Type for the location.
        :type connection_type: NetworkConnectionType
        :param org_id: Update network connection type for this organization.
        :type org_id: str
        """
        params = org_id and {'orgId': org_id} or None
        url = self.session.ep(f'telephony/config/locations/{location_id}/privateNetworkConnect')
        body = {'networkConnectionType': connection_type.value}
        await self.put(url, json=body, params=params)


class AsSupervisorApi(AsApiChild, base='telephony/config/supervisors'):
    """
    Supervisors

    Supervisors are users who manage agents and who perform functions including monitoring, coaching, and more.

    Viewing these read-only device settings requires a full, device or
    read-only administrator auth token with a scope of `spark-admin:telephony_config_read`.

    Modifying these device settings requires a full or device
    administrator auth token with a scope of `spark-admin:telephony_config_write`.
    """

    def list_gen(self, name: str = None, phone_number: str = None, order: str = None,
             has_cx_essentials: bool = None, org_id: str = None,
             **params) -> AsyncGenerator[AgentOrSupervisor, None, None]:
        """
        Get List of Supervisors

        Get list of supervisors for an organization.

        Agents in a call queue can be associated with a supervisor who can silently monitor, coach, barge in or to take
        over calls that their assigned agents are currently handling.

        Requires a full, location, user or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param name: Only return the supervisors that match the given name.
        :type name: str
        :param phone_number: Only return the supervisors that match the given phone number, extension, or ESN.
        :type phone_number: str
        :param order: Sort results alphabetically by supervisor name, in ascending or descending order.
        :type order: str
        :param has_cx_essentials: Returns only the list of supervisors with Customer Experience Essentials license,
            when `true`. Otherwise returns the list of supervisors with Customer Experience Basic license.
        :type has_cx_essentials: bool
        :param org_id: List the supervisors in this organization.
        :type org_id: str
        :return: Generator yielding :class:`AgentOrSupervisor` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if name is not None:
            params['name'] = name
        if phone_number is not None:
            params['phoneNumber'] = phone_number
        if order is not None:
            params['order'] = order
        if has_cx_essentials is not None:
            params['hasCxEssentials'] = str(has_cx_essentials).lower()
        url = self.ep()
        return self.session.follow_pagination(url=url, model=AgentOrSupervisor, item_key='supervisors',
                                              params=params)

    async def list(self, name: str = None, phone_number: str = None, order: str = None,
             has_cx_essentials: bool = None, org_id: str = None,
             **params) -> List[AgentOrSupervisor]:
        """
        Get List of Supervisors

        Get list of supervisors for an organization.

        Agents in a call queue can be associated with a supervisor who can silently monitor, coach, barge in or to take
        over calls that their assigned agents are currently handling.

        Requires a full, location, user or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param name: Only return the supervisors that match the given name.
        :type name: str
        :param phone_number: Only return the supervisors that match the given phone number, extension, or ESN.
        :type phone_number: str
        :param order: Sort results alphabetically by supervisor name, in ascending or descending order.
        :type order: str
        :param has_cx_essentials: Returns only the list of supervisors with Customer Experience Essentials license,
            when `true`. Otherwise returns the list of supervisors with Customer Experience Basic license.
        :type has_cx_essentials: bool
        :param org_id: List the supervisors in this organization.
        :type org_id: str
        :return: Generator yielding :class:`AgentOrSupervisor` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if name is not None:
            params['name'] = name
        if phone_number is not None:
            params['phoneNumber'] = phone_number
        if order is not None:
            params['order'] = order
        if has_cx_essentials is not None:
            params['hasCxEssentials'] = str(has_cx_essentials).lower()
        url = self.ep()
        return [o async for o in self.session.follow_pagination(url=url, model=AgentOrSupervisor, item_key='supervisors',
                                              params=params)]

    async def create(self, id: str, agents: List[str],
               has_cx_essentials: bool = None,
               org_id: str = None):
        """
        Create a Supervisor

        Create a new supervisor. The supervisor must be created with at least one agent.

        Agents in a call queue can be associated with a supervisor who can silently monitor, coach, barge in or to take
        over calls that their assigned agents are currently handling.

        This operation requires a full or location administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param id: A unique identifier for the supervisor.
        :type id: str
        :param agents: People, workspaces and virtual lines that are eligible to receive calls.
        :type agents: list[str]
        :param has_cx_essentials: Creates a Customer Experience Essentials queue supervisor, when `true`. Customer
            Experience Essentials queue supervisors must have a Customer Experience Essentials license.
        :type has_cx_essentials: bool
        :param org_id: The organization ID where the supervisor needs to be created.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        if has_cx_essentials is not None:
            params['hasCxEssentials'] = str(has_cx_essentials).lower()
        body = dict()
        body['id'] = id
        body['agents'] = [{'id': agent_id} for agent_id in agents]
        url = self.ep()
        await super().post(url, params=params, json=body)

    async def delete(self, supervisor_id: str, org_id: str = None):
        """
        Delete A Supervisor

        Deletes the supervisor from an organization.

        Supervisors are users who manage agents and who perform functions including monitoring, coaching, and more.

        Requires a full administrator auth token with a scope of `spark-admin:telephony_config_write`.

        :param supervisor_id: Delete the specified supervisor.
        :type supervisor_id: str
        :param org_id: Delete the supervisor in the specified organization.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(supervisor_id)
        await super().delete(url, params=params)

    async def delete_bulk(self, supervisors_ids: List[str], delete_all: bool = None, org_id: str = None):
        """
        Delete Bulk supervisors

        Deletes supervisors in bulk from an organization.

        Supervisors are users who manage agents and who perform functions including monitoring, coaching, and more.

        Requires a full administrator auth token with a scope of `spark-admin:telephony_config_write`.

        :param supervisors_ids: Array of supervisors IDs to be deleted.
        :type supervisors_ids: list[str]
        :param delete_all: If present the `supervisorIds` array is ignored, and all supervisors in the context are
            deleted. **WARNING**: This will remove all supervisors from the organization.
        :type delete_all: bool
        :param org_id: Delete supervisors in bulk for this organization.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        body['supervisorsIds'] = supervisors_ids
        if delete_all is not None:
            body['deleteAll'] = delete_all
        url = self.ep()
        await super().delete(url, params=params, json=body)

    def available_supervisors_gen(self, name: str = None, phone_number: str = None, order: str = None,
                              has_cx_essentials: bool = None, org_id: str = None,
                              **params) -> AsyncGenerator[AgentOrSupervisor, None, None]:
        """
        List Available Supervisors

        Get list of available supervisors for an organization.

        Agents in a call queue can be associated with a supervisor who can silently monitor, coach, barge in or to take
        over calls that their assigned agents are currently handling.

        This operation requires a full, user or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param name: Only return the supervisors that match the given name.
        :type name: str
        :param phone_number: Only return the supervisors that match the given phone number, extension, or ESN.
        :type phone_number: str
        :param order: Sort results alphabetically by supervisor name, in ascending or descending order.
        :type order: str
        :param has_cx_essentials: Returns only the list of available supervisors with Customer Experience Essentials
            license, when `true`. When ommited or set to 'false', will return the list of available supervisors with
            Customer Experience Basic license.
        :type has_cx_essentials: bool
        :param org_id: List the available supervisors in this organization.
        :type org_id: str
        :return: Generator yielding :class:`AgentOrSupervisor` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if name is not None:
            params['name'] = name
        if phone_number is not None:
            params['phoneNumber'] = phone_number
        if order is not None:
            params['order'] = order
        if has_cx_essentials is not None:
            params['hasCxEssentials'] = str(has_cx_essentials).lower()
        url = self.ep('availableSupervisors')
        return self.session.follow_pagination(url=url, model=AgentOrSupervisor, item_key='supervisors',
                                              params=params)

    async def available_supervisors(self, name: str = None, phone_number: str = None, order: str = None,
                              has_cx_essentials: bool = None, org_id: str = None,
                              **params) -> List[AgentOrSupervisor]:
        """
        List Available Supervisors

        Get list of available supervisors for an organization.

        Agents in a call queue can be associated with a supervisor who can silently monitor, coach, barge in or to take
        over calls that their assigned agents are currently handling.

        This operation requires a full, user or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param name: Only return the supervisors that match the given name.
        :type name: str
        :param phone_number: Only return the supervisors that match the given phone number, extension, or ESN.
        :type phone_number: str
        :param order: Sort results alphabetically by supervisor name, in ascending or descending order.
        :type order: str
        :param has_cx_essentials: Returns only the list of available supervisors with Customer Experience Essentials
            license, when `true`. When ommited or set to 'false', will return the list of available supervisors with
            Customer Experience Basic license.
        :type has_cx_essentials: bool
        :param org_id: List the available supervisors in this organization.
        :type org_id: str
        :return: Generator yielding :class:`AgentOrSupervisor` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if name is not None:
            params['name'] = name
        if phone_number is not None:
            params['phoneNumber'] = phone_number
        if order is not None:
            params['order'] = order
        if has_cx_essentials is not None:
            params['hasCxEssentials'] = str(has_cx_essentials).lower()
        url = self.ep('availableSupervisors')
        return [o async for o in self.session.follow_pagination(url=url, model=AgentOrSupervisor, item_key='supervisors',
                                              params=params)]

    def details_gen(self, supervisor_id: str, name: str = None,
                phone_number: str = None, order: str = None, has_cx_essentials: bool = None,
                org_id: str = None, **additional_params) -> AsyncGenerator[AgentOrSupervisor, None, None]:
        """
        GET Supervisor Details

        Get details of a specific supervisor, which includes the agents associated agents with the supervisor, in an
        organization.

        Agents in a call queue can be associated with a supervisor who can silently monitor, coach, barge in or to take
        over calls that their assigned agents are currently handling.

        This operation requires a full, user or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param supervisor_id: List the agents assigned to this supervisor.
        :type supervisor_id: str
        :param name: Only return the agents that match the given name.
        :type name: str
        :param phone_number: Only return agents that match the given phone number, extension, or ESN.
        :type phone_number: str
        :param order: Sort results alphabetically by supervisor name, in ascending or descending order.
        :type order: str
        :param has_cx_essentials: Must be set to `true`, to view the details of a supervisor with Customer Experience
            Essentials license. This can otherwise be ommited or set to `false`.
        :type has_cx_essentials: bool
        :param org_id: List the agents assigned to a supervisor in this organization.
        :type org_id: str
        :return: Generator yieldig :class:`AgentOtSupervisor` instances
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        if name is not None:
            params['name'] = name
        if phone_number is not None:
            params['phoneNumber'] = phone_number
        if order is not None:
            params['order'] = order
        if has_cx_essentials is not None:
            params['hasCxEssentials'] = str(has_cx_essentials).lower()
        params.update(additional_params)
        url = self.ep(supervisor_id)
        return self.session.follow_pagination(url=url, model=AgentOrSupervisor, params=params, item_key='agents')

    async def details(self, supervisor_id: str, name: str = None,
                phone_number: str = None, order: str = None, has_cx_essentials: bool = None,
                org_id: str = None, **additional_params) -> List[AgentOrSupervisor]:
        """
        GET Supervisor Details

        Get details of a specific supervisor, which includes the agents associated agents with the supervisor, in an
        organization.

        Agents in a call queue can be associated with a supervisor who can silently monitor, coach, barge in or to take
        over calls that their assigned agents are currently handling.

        This operation requires a full, user or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param supervisor_id: List the agents assigned to this supervisor.
        :type supervisor_id: str
        :param name: Only return the agents that match the given name.
        :type name: str
        :param phone_number: Only return agents that match the given phone number, extension, or ESN.
        :type phone_number: str
        :param order: Sort results alphabetically by supervisor name, in ascending or descending order.
        :type order: str
        :param has_cx_essentials: Must be set to `true`, to view the details of a supervisor with Customer Experience
            Essentials license. This can otherwise be ommited or set to `false`.
        :type has_cx_essentials: bool
        :param org_id: List the agents assigned to a supervisor in this organization.
        :type org_id: str
        :return: Generator yieldig :class:`AgentOtSupervisor` instances
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        if name is not None:
            params['name'] = name
        if phone_number is not None:
            params['phoneNumber'] = phone_number
        if order is not None:
            params['order'] = order
        if has_cx_essentials is not None:
            params['hasCxEssentials'] = str(has_cx_essentials).lower()
        params.update(additional_params)
        url = self.ep(supervisor_id)
        return [o async for o in self.session.follow_pagination(url=url, model=AgentOrSupervisor, params=params, item_key='agents')]

    async def assign_unassign_agents(self, supervisor_id: str, agents: List[IdAndAction],
                               has_cx_essentials: bool = None,
                               org_id: str = None) -> Optional[List[SupervisorAgentStatus]]:
        """
        Assign or Unassign Agents to Supervisor

        Assign or unassign agents to the supervisor for an organization.

        Agents in a call queue can be associated with a supervisor who can silently monitor, coach, barge in or to take
        over calls that their assigned agents are currently handling.

        Requires a full administrator auth token with a scope of `spark-admin:telephony_config_write`.

        :param supervisor_id: Identifier of the supervisor to be updated.
        :type supervisor_id: str
        :param agents: People, workspaces and virtual lines that are eligible to receive calls.
        :type agents: list[PutPersonPlaceVirtualLineAgentObject]
        :param has_cx_essentials: Must be set to `true` to modify a supervisor with Customer Experience Essentials
            license. This can otherwise be omitted or set to `false`.
        :type has_cx_essentials: bool
        :param org_id: Assign or unassign agents to a supervisor in this organization.
        :type org_id: str
        :rtype: list[SupervisorAgentStatus]
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        if has_cx_essentials is not None:
            params['hasCxEssentials'] = str(has_cx_essentials).lower()
        body = dict()
        body['agents'] = TypeAdapter(list[IdAndAction]).dump_python(agents, mode='json', by_alias=True)
        url = self.ep(supervisor_id)
        data = await super().put(url, params=params, json=body)
        if not data:
            return None
        r = TypeAdapter(List[SupervisorAgentStatus]).validate_python(data['supervisorAgentStatus'])
        return r

    def available_agents_gen(self, name: str = None, phone_number: str = None, order: str = None,
                         has_cx_essentials: bool = None, org_id: str = None,
                         **params) -> AsyncGenerator[AgentOrSupervisor, None, None]:
        """
        List Available Agents

        Get list of available agents for an organization.

        Agents in a call queue can be associated with a supervisor who can silently monitor, coach, barge in or to take
        over calls that their assigned agents are currently handling.

        This operation requires a full, user or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param name: Returns only the agents that match the given name.
        :type name: str
        :param phone_number: Returns only the agents that match the phone number, extension, or ESN.
        :type phone_number: str
        :param order: Sort results alphabetically by supervisor name, in ascending or descending order.
        :type order: str
        :param has_cx_essentials: Returns only the list of available agents with Customer Experience Essentials
            license, when `true`. When ommited or set to `false`, will return the list of available agents with
            Customer Experience Basic license.
        :type has_cx_essentials: bool
        :param org_id: List of available agents in a supervisor's list for this organization.
        :type org_id: str
        :return: Generator yielding :class:`AgentOrSupervisor` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if name is not None:
            params['name'] = name
        if phone_number is not None:
            params['phoneNumber'] = phone_number
        if order is not None:
            params['order'] = order
        if has_cx_essentials is not None:
            params['hasCxEssentials'] = str(has_cx_essentials).lower()
        url = self.ep('availableAgents')
        return self.session.follow_pagination(url=url, model=AgentOrSupervisor, item_key='agents',
                                              params=params)

    async def available_agents(self, name: str = None, phone_number: str = None, order: str = None,
                         has_cx_essentials: bool = None, org_id: str = None,
                         **params) -> List[AgentOrSupervisor]:
        """
        List Available Agents

        Get list of available agents for an organization.

        Agents in a call queue can be associated with a supervisor who can silently monitor, coach, barge in or to take
        over calls that their assigned agents are currently handling.

        This operation requires a full, user or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param name: Returns only the agents that match the given name.
        :type name: str
        :param phone_number: Returns only the agents that match the phone number, extension, or ESN.
        :type phone_number: str
        :param order: Sort results alphabetically by supervisor name, in ascending or descending order.
        :type order: str
        :param has_cx_essentials: Returns only the list of available agents with Customer Experience Essentials
            license, when `true`. When ommited or set to `false`, will return the list of available agents with
            Customer Experience Basic license.
        :type has_cx_essentials: bool
        :param org_id: List of available agents in a supervisor's list for this organization.
        :type org_id: str
        :return: Generator yielding :class:`AgentOrSupervisor` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if name is not None:
            params['name'] = name
        if phone_number is not None:
            params['phoneNumber'] = phone_number
        if order is not None:
            params['order'] = order
        if has_cx_essentials is not None:
            params['hasCxEssentials'] = str(has_cx_essentials).lower()
        url = self.ep('availableAgents')
        return [o async for o in self.session.follow_pagination(url=url, model=AgentOrSupervisor, item_key='agents',
                                              params=params)]


class AsDevicesDynamicSettingsApi(AsApiChild, base='telephony/config'):
    """
    Telephony devices API
    """

    async def get_settings_groups(self, family_or_model_display_name: str = None,
                            include_settings_type: SettingsType = None,
                            org_id: str = None) -> DynamicSettingsGroups:
        """
        Get Settings Groups

        This API returns the `settingsGroups` that define the structure and association of tags for dynamic device
        settings.

        The `settingsGroups` are used to organize the tags into logical groups, making it easier to manage and
        configure dynamic device settings.

        :param family_or_model_display_name: Device family or model display name to filter the `settingsGroups`.
        :type family_or_model_display_name: str
        :param include_settings_type: To show groups or tabs or both. Query param is case insensitive. Default is
            `ALL`.
        :type include_settings_type: SettingsType
        :param org_id: Settings groups for devices in this organization.
        :type org_id: str
        :rtype: :class:`DynamicSettingsGroups`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        if family_or_model_display_name is not None:
            params['familyOrModelDisplayName'] = family_or_model_display_name
        if include_settings_type is not None:
            params['includeSettingsType'] = enum_str(include_settings_type)
        url = self.ep('devices/dynamicSettings/settingsGroups')
        data = await super().get(url, params=params)
        r = DynamicSettingsGroups.model_validate(data)
        return r

    async def get_validation_schema(self, family_or_model_display_name: str = None, org_id: str = None) -> list[DeviceTag]:
        """
        Get Validation Schema

        This API returns the validation schema for `tags` of all or specific `familyOrModelDisplayName`.

        The schema is used to validate the `tag` for devices in the `Webex Calling` platform. The schema includes
        information about the required fields, data types, and validation rules for each setting.

        :param family_or_model_display_name: Device family or model display name to filter the schema.
        :type family_or_model_display_name: str
        :param org_id: Validation schema for devices in this organization.
        :type org_id: str
        :rtype: list[DeviceTag]
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        if family_or_model_display_name is not None:
            params['familyOrModelDisplayName'] = family_or_model_display_name
        url = self.ep('devices/dynamicSettings/validationSchema')
        data = await super().get(url, params=params)
        r = TypeAdapter(list[DeviceTag]).validate_python(data['tags'])
        return r

    async def update_specified_settings_for_the_device(self, device_id: str, tags: list[DevicePutItem] = None,
                                                 org_id: str = None):
        """
        Update specified settings for the device.

        Modify dynamic settings for a specified device.

        This API updates device settings based on the specified `tags`. If the `tags` field is empty, the request has
        no effect.

        This requires a full, device, or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param device_id: Device for which to update settings.
        :type device_id: str
        :param tags: Optional array of `tag` identifiers representing specific settings to update. If omitted or
            provided as an empty array, the request will have no effect.
        :type tags: list[DevicePutItem]
        :param org_id: Organization to which the device belongs.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        if tags is not None:
            body['tags'] = TypeAdapter(list[DevicePutItem]).dump_python(tags, mode='json', by_alias=True,
                                                                        exclude_none=True)
        url = self.ep(f'devices/{device_id}/dynamicSettings')
        await super().put(url, params=params, json=body)

    async def get_customer_device_settings(self, family_or_model_display_name: str, tags: list[str] = None,
                                     org_id: str = None) -> DeviceDynamicSettings:
        """
        Get Customer Device Dynamic Settings

        Retrieve dynamic settings for specific device tags at customer level, allowing filters by
        `familyOrModelDisplayName` and `tag` identifier.

        This API lets you request the values of multiple `Device Settings` at once by specifying a list of
        `familyOrModelDisplayName` and tag combinations.

        This requires a full, device, or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param family_or_model_display_name: The family or model name for the device. If no tag is specified, all tags
            related to `familyOrModelDisplayName` are returned.
        :type family_or_model_display_name: str
        :param tags: Optional array of device tag identifiers to request settings for. Each identifier must have a
            length between 1 and 64 characters.
        :type tags: list[str]
        :param org_id: List of device dynamic settings in this organization.
        :type org_id: str
        :rtype: :class:`CustomerDeviceDynamicSettings`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        params['familyOrModelDisplayName'] = family_or_model_display_name
        body = dict()
        if tags is not None:
            body['tags'] = tags
        url = self.ep('lists/devices/dynamicSettings/actions/getSettings/invoke')
        data = await super().post(url, params=params, json=body)
        r = DeviceDynamicSettings.model_validate(data)
        return r

    async def get_device_settings(self, device_id: str, tags: list[str] = None,
                            org_id: str = None) -> DeviceDynamicSettings:
        """
        Get Device Dynamic Settings

        Retrieve settings for a specified device.

        This API retrieves device settings based on the specified `tags`; if the `tags` field is empty or missing, all
        settings for the device are returned.

        This requires a full, device, or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param device_id: Device for which to retrieve settings.
        :type device_id: str
        :param tags: Optional array of tag identifiers representing specific settings to fetch. If omitted or provided
            as an empty array, all settings for the device will be returned.
        :type tags: list[str]
        :param org_id: Organization to which the `device` belongs.
        :type org_id: str
        :rtype: :class:`DeviceDynamicSettings`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        if tags is not None:
            body['tags'] = tags
        url = self.ep(f'lists/devices/{device_id}/dynamicSettings/actions/getSettings/invoke')
        data = await super().post(url, params=params, json=body)
        r = DeviceDynamicSettings.model_validate(data)
        return r

    async def get_location_device_settings(self, location_id: str, family_or_model_display_name: str,
                                     tags: list[str] = None,
                                     org_id: str = None) -> DeviceDynamicSettings:
        """
        Get Location Device Dynamic Settings

        Retrieve dynamic settings for specific device tags at the specified location level, allowing filters by
        `familyOrModelDisplayName` and `tag` identifier.

        This API lets you request the values of multiple `Device Settings` at once by specifying a list of
        `familyOrModelDisplayName` and tag combinations for a specific location.

        This requires a full, device, or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param location_id: Unique identifier for the `location`.
        :type location_id: str
        :param family_or_model_display_name: The family or model name for the device. If no tag is specified, all tags
            related to `familyOrModelDisplayName` are returned.
        :type family_or_model_display_name: str
        :param tags: Optional array of device tag identifiers to request settings for. Each identifier must have a
            length between 1 and 64 characters.
        :type tags: list[str]
        :param org_id: Unique identifier for the `organization` to which this location belongs.
        :type org_id: str
        :rtype: :class:`DeviceDynamicSettings`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        params['familyOrModelDisplayName'] = family_or_model_display_name
        body = dict()
        if tags is not None:
            body['tags'] = tags
        url = self.ep(f'lists/locations/{location_id}/devices/dynamicSettings/actions/getSettings/invoke')
        data = await super().post(url, params=params, json=body)
        r = DeviceDynamicSettings.model_validate(data)
        return r


class AsTelephonyDevicesApi(AsApiChild, base='telephony/config'):
    """
    Telephony devices API
    """
    dynamic_settings: AsDevicesDynamicSettingsApi

    def __init__(self, session: AsRestSession):
        super().__init__(session=session)
        self.dynamic_settings = AsDevicesDynamicSettingsApi(session=session)

    async def supported_devices(self, allow_configure_layout_enabled: bool = None, type_: str = None,
                          org_id: str = None) -> SupportedDevices:
        """
        Read the List of Supported Devices

        Gets the list of supported devices for an organization.

        Retrieving this list requires a full or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param allow_configure_layout_enabled: List supported devices that allow the user to configure the layout.
        :type allow_configure_layout_enabled: bool
        :param type_: List supported devices of a specific type. To excluded device types from a request or query, add
            `type=not:DEVICE_TYPE`. For example, `type=not:MPP`.
        :type type_: str
        :param org_id: List supported devices for an organization.
        :type org_id: str
        :rtype: SupportedDevices
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        if allow_configure_layout_enabled is not None:
            params['allowConfigureLayoutEnabled'] = str(allow_configure_layout_enabled).lower()
        if type is not None:
            params['type'] = type_
        url = self.ep('supportedDevices')
        data = await self.get(url=url, params=params)
        return SupportedDevices.model_validate(data)

    async def details(self, device_id: str, org_id: str = None) -> TelephonyDeviceDetails:
        """
        Get Webex Calling Device Details

        Not supported for Webex for Government (FedRAMP)

        Retrieves Webex Calling device details that include information needed for third-party device management.

        Webex calling devices are associated with a specific user Workspace or Virtual Line. Webex Calling devices
        share the location with the entity that owns them.

        Person or workspace to which the device is assigned. Its fields point to a primary line/port of the device.

        Requires a full, location, user, or read-only admin auth token with the scope of
        `spark-admin:telephony_config_read`.

        :param device_id: Unique identifier for the device.
        :type device_id: str
        :param org_id: ID of the organization in which the device resides.
        :type org_id: str
        :rtype: :class:`TelephonyDeviceDetails`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'devices/{device_id}')
        data = await super().get(url, params=params)
        r = TelephonyDeviceDetails.model_validate(data)
        return r

    async def update_third_party_device(self, device_id: str, sip_password: str, org_id: str = None):
        """
        Update Third Party Device

        Not supported for Webex for Government (FedRAMP)

        Modify a device's `sipPassword`.

        Updating `sipPassword` on the device requires a full or user administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param device_id: Unique identifier for the device.
        :type device_id: str
        :param sip_password: Password to be updated.
        :type sip_password: str
        :param org_id: ID of the organization in which the device resides.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        body['sipPassword'] = sip_password
        url = self.ep(f'devices/{device_id}')
        await super().put(url, params=params, json=body)

    async def members(self, device_id: str, org_id: str = None) -> DeviceMembersResponse:
        """
        Get Device Members

        Get the list of all the members of the device including primary and secondary users.

        A device member can be either a person or a workspace. An admin can access the list of member details, modify
        member details and search for available members on a device.

        Retrieving this list requires a full or read-only administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param device_id: Unique identifier for the device.
        :type device_id: str
        :param org_id: Retrieves the list of all members of the device in this Organization.
        :type org_id: str
        :return: Device model, line count, and members
        :rtype: DeviceMembersResponse
        """
        params = org_id and {'orgId': org_id} or None
        url = self.ep(f'devices/{device_id}/members')
        data = await self.get(url=url, params=params)
        return DeviceMembersResponse.model_validate(data)

    async def update_members(self, device_id: str, members: list[Union[DeviceMember, AvailableMember]] = None,
                       org_id: str = None):
        """
        Modify member details on the device.

        A device member can be either a person or a workspace. An admin can access the list of member details,
        modify member details and search for available members on a device.

        Modifying members on the device requires a full administrator auth token with a scope
        of spark-admin:telephony_config_write.

        :param device_id: Unique identifier for the device.
        :type device_id: str
        :param members: New member details for the device. If the member's list is missing then all the users are
            removed except the primary user.
        :type members: list[Union[DeviceMember, AvailableMember]
        :param org_id: Modify members on the device in this organization.
        :type org_id: str
        """
        members_for_update = []
        for member in members or []:
            if isinstance(member, AvailableMember):
                member = DeviceMember.from_available(member)
            else:
                member = member.model_copy(deep=True)
            members_for_update.append(member)

        if members_for_update:
            # now assign port indices
            port = 1
            for member in members_for_update:
                member.port = port
                port += member.line_weight

        # create body
        if members_for_update:
            members = ','.join(m.model_dump_json(include={'member_id', 'port', 't38_fax_compression_enabled',
                                                          'primary_owner', 'line_type', 'line_weight', 'line_label',
                                                          'hotline_enabled', 'hotline_destination',
                                                          'allow_call_decline_enabled'})
                               for m in members_for_update)
            body = f'{{"members": [{members}]}}'
        else:
            body = None

        url = self.ep(f'devices/{device_id}/members')
        params = org_id and {'orgId': org_id} or None
        await self.put(url=url, data=body, params=params)

    def available_members_gen(self, device_id: str, location_id: str = None, member_name: str = None,
                          phone_number: str = None, extension: str = None, org_id: str = None,
                          usage_type: UsageType = None,
                          order: str = None,
                          **params) -> AsyncGenerator[AvailableMember, None, None]:
        """
        Search members that can be assigned to the device.

        A device member can be either a person or a workspace. A admin can access the list of member details,
        modify member details and search for available members on a device.

        This requires a full or read-only administrator auth token with a scope of spark-admin:telephony_config_read.

        :param device_id: Unique identifier for the device.
        :type device_id: str
        :param location_id: Search (Contains) based on number.
        :type location_id: str
        :param member_name: Search (Contains) numbers based on member name.
        :type member_name: str
        :param phone_number: Search (Contains) based on number.
        :type phone_number: str
        :param extension: Search (Contains) based on extension.
        :type extension: str
        :param usage_type: Search for members eligible to become the owner of the device, or share line on the device.
        :type usage_type: UsageType
        :param order: Sort the list of available members on the device in ascending order by name, use either last name
            `lname` or first name `fname`. Default: last name in ascending order.
        :type order: str
        :param org_id: Retrieves the list of available members on the device in this Organization.
        :type org_id: str
        :return: list of available members
        """
        params.update((to_camel(p), v) for p, v in locals().items()
                      if p not in {'self', 'params', 'device_id'} and v is not None)
        url = self.ep(f'devices/{device_id}/availableMembers')
        # noinspection PyTypeChecker
        return self.session.follow_pagination(url=url, model=AvailableMember, params=params, item_key='members')

    async def available_members(self, device_id: str, location_id: str = None, member_name: str = None,
                          phone_number: str = None, extension: str = None, org_id: str = None,
                          usage_type: UsageType = None,
                          order: str = None,
                          **params) -> List[AvailableMember]:
        """
        Search members that can be assigned to the device.

        A device member can be either a person or a workspace. A admin can access the list of member details,
        modify member details and search for available members on a device.

        This requires a full or read-only administrator auth token with a scope of spark-admin:telephony_config_read.

        :param device_id: Unique identifier for the device.
        :type device_id: str
        :param location_id: Search (Contains) based on number.
        :type location_id: str
        :param member_name: Search (Contains) numbers based on member name.
        :type member_name: str
        :param phone_number: Search (Contains) based on number.
        :type phone_number: str
        :param extension: Search (Contains) based on extension.
        :type extension: str
        :param usage_type: Search for members eligible to become the owner of the device, or share line on the device.
        :type usage_type: UsageType
        :param order: Sort the list of available members on the device in ascending order by name, use either last name
            `lname` or first name `fname`. Default: last name in ascending order.
        :type order: str
        :param org_id: Retrieves the list of available members on the device in this Organization.
        :type org_id: str
        :return: list of available members
        """
        params.update((to_camel(p), v) for p, v in locals().items()
                      if p not in {'self', 'params', 'device_id'} and v is not None)
        url = self.ep(f'devices/{device_id}/availableMembers')
        # noinspection PyTypeChecker
        return [o async for o in self.session.follow_pagination(url=url, model=AvailableMember, params=params, item_key='members')]

    async def apply_changes(self, device_id: str, org_id: str = None):
        """
        Apply Changes for a specific device

        Issues request to the device to download and apply changes to the configuration.

        Applying changes for a specific device requires a full administrator auth token with a scope
        of spark-admin:telephony_config_write.
        :param device_id: Unique identifier for the device.
        :type device_id: str
        :param org_id: Apply changes for a device in this Organization.
        :type org_id: str
        """
        params = org_id and {'orgId': org_id} or None
        url = self.ep(f'devices/{device_id}/actions/applyChanges/invoke')
        await self.post(url=url, params=params)

    async def device_settings(self, device_id: str, device_model: str = None, org_id: str = None) -> DeviceCustomization:
        """
        Get override settings for a device.

        Device settings lists all the applicable settings for an MPP and an ATA devices at the device level. An admin
        can also modify the settings. DECT devices do not support settings at the device level.

        This requires a full or read-only administrator auth token with a scope of spark-admin:telephony_config_read.

        :param device_id: Unique identifier for the device.
        :type device_id: str
        :param device_model: The model type of the device. The corresponding device model display name sometimes called
            the product name, can also be used to specify the model.
        :type device_model: str
        :param org_id: Settings on the device in this organization.
        :type org_id: str
        :return: Device settings
        :rtype: DeviceCustomization
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        if device_model is not None:
            params['deviceModel'] = device_model
        url = self.ep(f'devices/{device_id}/settings')
        data = await self.get(url=url, params=params)
        return DeviceCustomization.model_validate(data)

    async def update_device_settings(self, device_id: str, device_model: str, customization: DeviceCustomization,
                               org_id: str = None):
        """
        Modify override settings for a device.

        Device settings list all the applicable settings for an MPP and an ATA devices at the device level. Admins
        can also modify the settings. NOTE: DECT devices do not support settings at the device level.

        Updating settings on the device requires a full administrator auth token with a scope
        of spark-admin:telephony_config_write.

        :param device_id: Unique identifier for the device.
        :type device_id: str
        :param device_model: Device model name.
        :type device_model: str
        :param customization: Indicates the customization object of the device settings.
        :type customization: DeviceCustomization
        :param org_id: Organization in which the device resides..
        :type org_id: str

        Example :

            .. code-block:: python

                # target_device is a TelephonyDevice object
                target_device: TelephonyDevice

                # get device level settings
                settings = api.telephony.devices.device_settings(device_id=target_device.device_id,
                                                                 device_model=target_device.model)

                # update settings (display name format) and enable device level customization
                settings.customizations.mpp.display_name_format = DisplayNameSelection.person_last_then_first_name
                settings.custom_enabled = True

                # update the device level settings
                api.telephony.devices.update_device_settings(device_id=target_device.device_id,
                                                             device_model=target_device.model,
                                                             customization=settings)

                # apply changes to device
                api.telephony.devices.apply_changes(device_id=target_device.device_id)

        """
        params = {'model': device_model}
        if org_id:
            params['orgId'] = org_id
        url = self.ep(f'devices/{device_id}/settings')
        body = customization.model_dump_json(include={'customizations', 'custom_enabled'})
        await self.put(url=url, params=params, data=body)

    async def validate_macs(self, macs: list[str], org_id: str = None) -> MACValidationResponse:
        """
        Validate a list of MAC addresses.

        Validating this list requires a full or read-only administrator auth token with a scope
        of spark-admin:telephony_config_write.

        :param macs: MAC addresses to be validated.
        :type macs: list[str]
        :param org_id: Validate the mac address(es) for this organization.
        :type org_id: str
        :return: validation response
        :rtype: :class:`MACValidationResponse`
        """
        params = org_id and {'orgId': org_id} or None
        url = self.ep('devices/actions/validateMacs/invoke')
        data = await self.post(url=url, params=params, json={'macs': macs})
        return MACValidationResponse.model_validate(data)

    async def create_line_key_template(self, template: LineKeyTemplate,
                                 org_id: str = None) -> str:
        """
        Create a Line Key Template

        Create a Line Key Template in this organization.

        Line Keys, also known as Programmable Line Keys (PLK) are the keys found on either sides of a typical desk phone
        display.
        A Line Key Template is a definition of actions that will be performed by each of the Line Keys for a particular
        device model.
        This API allows customers to create a Line Key Template for a device model.

        Creating a Line Key Template requires a full administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param template: Line key template to create
        :type template: LineKeyTemplate
        :param org_id: id of organization to create the line key template in
        :type org_id: str
        :return: id of new line key template
        :rtype: str
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = template.create_or_update()
        url = self.ep('devices/lineKeyTemplates')
        data = await super().post(url, params=params, json=body)
        r = data['id']
        return r

    async def list_line_key_templates(self, org_id: str = None) -> list[LineKeyTemplate]:
        """
        Read the list of Line Key Templates

        List all Line Key Templates available for this organization.

        Line Keys, also known as Programmable Line Keys (PLK) are the keys found on either sides of a typical desk phone
        display.
        A Line Key Template is a definition of actions that will be performed by each of the Line Keys for a particular
        device model.
        This API allows users to retrieve the list of Line Key Templates that are available for the organization.

        Retrieving this list requires a full, user or read-only administrator or location administrator auth token with
        a scope of `spark-admin:telephony_config_read`.

        :param org_id: List line key templates for this organization.
        :type org_id: str
        :rtype: list[LineKeyTemplate]
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep('devices/lineKeyTemplates')
        data = await super().get(url, params=params)
        r = TypeAdapter(list[LineKeyTemplate]).validate_python(data['lineKeyTemplates'])
        return r

    async def line_key_template_details(self, template_id: str, org_id: str = None) -> LineKeyTemplate:
        """
        Get details of a Line Key Template

        Get detailed information about a Line Key Template by template ID in an organization.

        Line Keys, also known as Programmable Line Keys (PLK) are the keys found on either sides of a typical desk phone
        display.
        A Line Key Template is a definition of actions that will be performed by each of the Line Keys for a particular
        device model.
        This API allows users to retrieve a line key template by its ID in an organization.

        Retrieving a line key template requires a full, user or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param template_id: Get line key template for this template ID.
        :type template_id: str
        :param org_id: Retrieve a line key template for this organization.
        :type org_id: str
        :rtype: :class:`GetLineKeyTemplateResponse`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'devices/lineKeyTemplates/{template_id}')
        data = await super().get(url, params=params)
        r = LineKeyTemplate.model_validate(data)
        return r

    async def modify_line_key_template(self, template: LineKeyTemplate, org_id: str = None):
        """
        Modify a Line Key Template

        Modify a line key template by its template ID in an organization.

        Line Keys, also known as Programmable Line Keys (PLK) are the keys found on either sides of a typical desk phone
        display.
        A Line Key Template is a definition of actions that will be performed by each of the Line Keys for a particular
        device model.
        This API allows users to modify an existing Line Key Template by its ID in an organization.

        Modifying an existing line key template requires a full administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param template: new line key template settings
        :type template: LineKeyTemplate
        :param org_id: Modify a line key template for this organization.
        :type org_id: str
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'devices/lineKeyTemplates/{template.id}')
        await super().put(url, params=params, json=template.create_or_update())

    async def delete_line_key_template(self, template_id: str, org_id: str = None):
        """
        Delete a Line Key Template

        Delete a Line Key Template by its template ID in an organization.

        Line Keys, also known as Programmable Line Keys (PLK) are the keys found on either sides of a typical desk phone
        display.
        A Line Key Template is a definition of actions that will be performed by each of the Line Keys for a particular
        device model.
        This API allows users to delete an existing Line Key Templates by its ID in an organization.

        Deleting an existing line key template requires a full administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param template_id: Delete line key template with this template ID.
        :type template_id: str
        :param org_id: Delete a line key template for this organization.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'devices/lineKeyTemplates/{template_id}')
        await super().delete(url, params=params)

    async def preview_apply_line_key_template(self, action: ApplyLineKeyTemplateAction, template_id: str = None,
                                        location_ids: list[str] = None,
                                        exclude_devices_with_custom_layout: bool = None,
                                        include_device_tags: list[str] = None, exclude_device_tags: list[str] = None,
                                        advisory_types: LineKeyTemplateAdvisoryTypes = None,
                                        org_id: str = None) -> int:
        """
        Preview Apply Line Key Template

        Preview the number of devices that will be affected by the application of a Line Key Template or when resetting
        devices to their factory Line Key settings.

        Line Keys, also known as Programmable Line Keys (PLK) are the keys found on either sides of a typical desk phone
        display.
        A Line Key Template is a definition of actions that will be performed by each of the Line Keys for a particular
        device model.
        This API allows users to preview the number of devices that will be affected if a customer were to apply a Line
        Key Template or apply factory default Line Key settings to devices.

        Retrieving the number of devices affected requires a full administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param action: Line key Template action to perform.
        :type action: PostApplyLineKeyTemplateRequestAction
        :param template_id: `templateId` is required for `APPLY_TEMPLATE` action.
        :type template_id: str
        :param location_ids: Used to search for devices only in the given locations.
        :type location_ids: list[str]
        :param exclude_devices_with_custom_layout: Indicates whether to exclude devices with custom layout.
        :type exclude_devices_with_custom_layout: bool
        :param include_device_tags: Include devices only with these tags.
        :type include_device_tags: list[str]
        :param exclude_device_tags: Exclude devices with these tags.
        :type exclude_device_tags: list[str]
        :param advisory_types: Refine search with advisories.
        :type advisory_types: LineKeyTemplateAdvisoryTypes
        :param org_id: Preview Line Key Template for this organization.
        :type org_id: str
        :rtype: int
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        body['action'] = enum_str(action)
        if template_id is not None:
            body['templateId'] = template_id
        if location_ids is not None:
            body['locationIds'] = location_ids
        if exclude_devices_with_custom_layout is not None:
            body['excludeDevicesWithCustomLayout'] = exclude_devices_with_custom_layout
        if include_device_tags is not None:
            body['includeDeviceTags'] = include_device_tags
        if exclude_device_tags is not None:
            body['excludeDeviceTags'] = exclude_device_tags
        if advisory_types is not None:
            body['advisoryTypes'] = advisory_types.model_dump(mode='json', by_alias=True, exclude_none=True)
        url = self.ep('devices/actions/previewApplyLineKeyTemplate/invoke')
        data = await super().post(url, params=params, json=body)
        r = data['deviceCount']
        return r

    async def get_device_layout(self, device_id: str, org_id: str = None) -> DeviceLayout:
        """
        Get Device Layout by Device ID

        Get layout information of a device by device ID in an organization.

        Device layout customizes a user’s programmable line keys (PLK) on the phone and any attached Key Expansion
        Modules (KEM) with the existing configured line members and the user’s monitoring list.

        This API requires a full or location administrator auth token with a scope
        of `spark-admin:telephony_config_read`.

        :param device_id: Get device layout for this device ID.
        :type device_id: str
        :param org_id: Retrieve a device layout for the device in this organization.
        :type org_id: str
        :rtype: :class:`DeviceLayout`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'devices/{device_id}/layout')
        data = await super().get(url, params=params)
        r = DeviceLayout.model_validate(data)
        return r

    async def modify_device_layout(self, device_id: str, layout: DeviceLayout,
                             org_id: str = None):
        """
        Modify Device Layout by Device ID

        Modify the layout of a device by device ID in an organization.

        Device layout customizes a user’s programmable line keys (PLK) on the phone and any attached Key Expansion
        Modules (KEM) with the existing configured line members and the user’s monitoring list.

        This API requires a full or location administrator auth token with a scope
        of `spark-admin:telephony_config_write`.

        :param device_id: Modify device layout for this device ID.
        :type device_id: str
        :param layout: New layout
        :param org_id: Modify a device layout for the device in this organization.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = layout.update()
        url = self.ep(f'devices/{device_id}/layout')
        await super().put(url, params=params, json=body)

    async def get_person_device_settings(self, person_id: str, org_id: str = None) -> DeviceSettings:
        """
        Get Device Settings for a Person

        Device settings list the compression settings for a person.

        Device settings customize a device's behavior and performance. The compression field optimizes call quality for
        inbound and outbound calls.

        This API requires a full, location, user, or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param person_id: ID of the person to retrieve device settings.
        :type person_id: str
        :param org_id: Retrieves the device settings for a person in this organization.
        :type org_id: str
        :rtype: Compression
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'people/{person_id}/devices/settings')
        data = await super().get(url, params=params)
        r = DeviceSettings.model_validate(data)
        return r

    async def update_person_device_settings(self, person_id: str, settings: DeviceSettings, org_id: str = None):
        """
        Update Device Settings for a Person

        Update device settings modifies the compression settings for a person.

        Device settings customize a device's behavior and performance. The compression field optimizes call quality for
        inbound and outbound calls.

        This API requires a full, location, or user administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param person_id: ID of the person to update device settings.
        :type person_id: str
        :param settings: New device settings
        :type settings: DeviceSettings
        :param org_id: Modify device settings for a person in this organization.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = settings.model_dump(mode='json', exclude_none=True, by_alias=True)
        url = self.ep(f'people/{person_id}/devices/settings')
        await super().put(url, params=params, json=body)

    async def get_workspace_device_settings(self, workspace_id: str, org_id: str = None) -> DeviceSettings:
        """
        Get Device Settings for a Workspace

        Device settings list the compression settings for a workspace.

        Device settings customize a device's behavior and performance. The compression field optimizes call quality for
        inbound and outbound calls.

        This API requires a full, location, user, or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param workspace_id: ID of the workspace for which to retrieve device settings.
        :type workspace_id: str
        :param org_id: Retrieves the device settings for a workspace in this organization.
        :type org_id: str
        :rtype: Compression
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'workspaces/{workspace_id}/devices/settings')
        data = await super().get(url, params=params)
        r = DeviceSettings.model_validate(data)
        return r

    async def update_workspace_device_settings(self, workspace_id: str, settings: DeviceSettings, org_id: str = None):
        """
        Update Device Settings for a Workspace

        Update device settings modifies the compression settings for a workspace.

        Device settings customize a device's behavior and performance. The compression field optimizes call quality for
        inbound and outbound calls.

        This API requires a full, location, or user administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param workspace_id: ID of the workspace for which to update device settings.
        :type workspace_id: str
        :param settings: New device settings
        :type settings: DeviceSettings
        :param org_id: Modify the device settings for a workspace in this organization.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = settings.model_dump(mode='json', exclude_none=True, by_alias=True)
        url = self.ep(f'workspaces/{workspace_id}/devices/settings')
        await super().put(url, params=params, json=body)

    async def list_background_images(self, org_id: str = None) -> BackgroundImages:
        """
        Read the List of Background Images

        Gets the list of device background images for an organization.

        Webex Calling supports the upload of up to 100 background image files for each org. These image files can then
        be referenced by MPP phones in that org for use as their background image.

        Retrieving this list requires a full, device, or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param org_id: Retrieves the list of images in this organization.
        :type org_id: str
        :rtype: :class:`BackgroundImages`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep('devices/backgroundImages')
        data = await super().get(url, params=params)
        r = BackgroundImages.model_validate(data)
        return r

    async def upload_background_image(self, device_id: str, file: Union[BufferedReader, str], file_name: str = None,
                                org_id: str = None) -> BackgroundImage:
        """
        Upload a Device Background Image

        Configure a device's background image by uploading an image with file format, `.jpeg` or `.png`, encoded image
        file. Maximum image file size allowed to upload is 625 KB.

        The request must be a multipart/form-data request rather than JSON, using the image/jpeg or image/png
        content-type.

        Webex Calling supports the upload of up to 100 background image files for each org. These image files can then
        be referenced by MPP phones in that org for use as their background image.

        Uploading a device background image requires a full or device administrator auth token with a scope
        of `spark-admin:telephony_config_write`.

        :param device_id: Unique identifier for the device.
        :type device_id: str
        :param file: the file to be uploaded, can be a path to a file or a buffered reader (opened file); if a
            reader referring to an open file is passed then make sure to open the file as binary b/c otherwise the
            content length might be calculated wrong
        :type file: Union[BufferedReader, str]
        :param file_name: filename for the content. Only required if content is a reader
        :type file_name: str
        :param org_id: Uploads the image in this organization.
        :type org_id: str
        :rtype: :class:`BackgroundImage`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'devices/{device_id}/actions/backgroundImageUpload/invoke')
        if isinstance(file, str):
            file_name = file_name or os.path.basename(file)
            file = open(file, mode='rb')
            must_close = True
        else:
            must_close = False
            # an existing reader
            if not file_name:
                raise ValueError('file_name is required')
        encoder = MultipartEncoder({'fileName': file_name,
                                    'file': (file_name, file, f'image/{file_name.split(".")[-1].lower()}')})
        try:
            data = await super().post(url, data=encoder, headers={'Content-Type': encoder.content_type},
                                params=params)
        finally:
            if must_close:
                file.close()
        r = BackgroundImage.model_validate(data)
        return r

    async def delete_background_images(self, background_images: list[DeleteImageRequestObject],
                                 org_id: str = None) -> DeleteDeviceBackgroundImagesResponse:
        """
        Delete Device Background Images

        Delete the list of designated device background images for an organization. Maximum is 10 images per request.

        Deleting a device background image requires a full or device administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param background_images: Array of images to be deleted.
        :type background_images: list[DeleteImageRequestObject]
        :param org_id: Deletes the list of images in this organization.
        :type org_id: str
        :rtype: :class:`DeleteDeviceBackgroundImagesResponse`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        body['backgroundImages'] = TypeAdapter(list[DeleteImageRequestObject]).dump_python(background_images,
                                                                                           mode='json', by_alias=True,
                                                                                           exclude_none=True)
        url = self.ep('devices/backgroundImages')
        data = await super().delete(url, params=params, json=body)
        r = DeleteDeviceBackgroundImagesResponse.model_validate(data)
        return r

    async def user_devices_count(self, person_id: str, org_id: str = None) -> UserDeviceCount:
        """
        Get User Devices Count

        Get the total device and application count for a person.

        The device count can be used to determine if more devices can be added for users with a device count limit. For
        example, users with standard calling licenses can only have one physical device.

        This requires a full or read-only administrator or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param person_id: Person for whom to retrieve the device count.
        :type person_id: str
        :param org_id: Organization to which the person belongs.
        :type org_id: str
        :rtype: :class:`UserDeviceCount`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'people/{person_id}/devices/count')
        data = await super().get(url, params=params)
        r = UserDeviceCount.model_validate(data)
        return r


class AsInternalDialingApi(AsApiChild, base='telephony/config/locations'):
    """
    Internal dialing settings for location
    """

    def url(self, location_id: str) -> str:
        return super().ep(f'{location_id}/internalDialing')

    async def read(self, location_id: str, org_id: str = None) -> InternalDialing:
        """
        Read the Internal Dialing configuration for a location

        Get current configuration for routing unknown extensions to the Premises as internal calls

        If some users in a location are registered to a PBX, retrieve the setting to route unknown extensions (digits
        that match the extension length) to the PBX.

        Retrieving the internal dialing configuration requires a full or read-only administrator or location
        administrator auth token with a scope of `spark-admin:telephony_config_read`.

        :param location_id: location for which internal calling configuration is being requested
        :type location_id: str
        :param org_id:
        :type org_id: str
        :return: settings
        :rtype: :class:`InternalDialing`
        """
        url = self.url(location_id=location_id)
        params = org_id and {'orgId': org_id} or None
        data = await self.get(url=url, params=params)
        return InternalDialing.model_validate(data)

    async def update(self, location_id: str, update: InternalDialing, org_id: str = None):
        """
        Modify the Internal Dialing configuration for a location

        Modify current configuration for routing unknown extensions to the premise as internal calls

        If some users in a location are registered to a PBX, enable the setting to route unknown extensions (digits
        that match the extension length) to the PBX.

        Editing the internal dialing configuration requires a full administrator or location administrator auth token
        with a scope of `spark-admin:telephony_config_write`.

        :param location_id: location for which internal calling configuration is being requested
        :type location_id: str
        :param update: new settings
        :type update: :class:`InternalDialing`
        :param org_id:
        :type org_id: str
        """
        url = self.url(location_id=location_id)
        params = org_id and {'orgId': org_id} or None
        data = update.model_dump_json(exclude_none=False)
        await self.put(url=url, params=params, data=data)


class AsLocationEmergencyServicesApi(AsApiChild, base='telephony/config/locations'):
    """
    Emergency Call Notifications can be enabled at the organization level, allowing specified email addresses to
    receive email notifications when an emergency call is made. Once activated at the organization level, individual
    locations can configure this setting to direct notifications to specific email addresses. To comply with U.S.
    Public Law 115-127, also known as Kari’s Law, any call that's made from within your organization to emergency
    services must generate an email notification.

    Viewing these organization settings requires a full or read-only administrator auth token with a scope
    of `spark-admin:telephony_config_read`. Modifying these organization settings requires a full administrator auth
    token with a scope of `spark-admin:telephony_config_write`.
    """

    async def read_emergency_call_notification(self, location_id: str,
                                         org_id: str = None) -> LocationEmergencyCallNotification:
        """
        Get a Location Emergency Call Notification

        Get location emergency call notification.

        Emergency Call Notifications can be enabled at the organization level, allowing specified email addresses to
        receive email notifications when an emergency call is made. Once activated at the organization level,
        individual locations can configure this setting to direct notifications to specific email addresses. To comply
        with U.S. Public Law 115-127, also known as Kari’s Law, any call that's made from within your organization to
        emergency services must generate an email notification.

        To retrieve location call notifications requires a full, user or read-only administrator or location
        administrator auth token with a scope of `spark-admin:telephony_config_read`.

        :param location_id: Retrieve Emergency Call Notification attributes for this location.
        :type location_id: str
        :param org_id: Retrieve Emergency Call Notification attributes for the location in this organization.
        :type org_id: str
        :rtype: :class:`LocationEmergencyCallNotification`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'{location_id}/emergencyCallNotification')
        data = await super().get(url, params=params)
        r = LocationEmergencyCallNotification.model_validate(data)
        return r

    async def update_emergency_call_notification(self, location_id: str, setting: LocationEmergencyCallNotification,
                                           org_id: str = None):
        """
        Update a location emergency call notification.

        Once settings enabled at the organization level, the configured email address will receive emergency call
        notifications for all locations; for specific location customization, users can navigate to Management >
        Locations, select the Calling tab, and update the Emergency Call Notification settings.

        Emergency Call Notifications can be enabled at the organization level, allowing specified email addresses to
        receive email notifications when an emergency call is made. Once activated at the organization level,
        individual locations can configure this setting to direct notifications to specific email addresses. To comply
        with U.S. Public Law 115-127, also known as Kari’s Law, any call that's made from within your organization to
        emergency services must generate an email notification.

        To update location call notification requires a full, user or location administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param location_id: Update Emergency Call Notification attributes for this location.
        :type location_id: str
        :param setting: new settings
        :type setting: LocationEmergencyCallNotification
        :param org_id: Update Emergency Call Notification attributes for a location in this organization.
        :type org_id: str
        :rtype: None
        """
        params = org_id and {'orgId': org_id} or None
        body = setting.update()
        url = self.ep(f'{location_id}/emergencyCallNotification')
        await super().put(url, params=params, json=body)


class AsLocationMoHApi(AsApiChild, base='telephony/config/locations'):
    """
    Location Music on Hold API
    """

    def _endpoint(self, *, location_id: str, path: str = None) -> str:
        """
        location specific feature endpoint like
        /v1/telephony/config/locations/{locationId}/musicOnHold

        :meta private:
        :param location_id: Unique identifier for the location.
        :type location_id: str
        :param path: additional path
        :type: path: str
        :return: full endpoint
        :rtype: str
        """
        path = path and f'/{path}' or ''
        ep = self.session.ep(f'telephony/config/locations/{location_id}/musicOnHold{path}')
        return ep

    async def read(self, location_id: str, org_id: str = None) -> LocationMoHSetting:
        """
        Get Music On Hold

        Retrieve the location's music on hold settings.

        Location's music on hold settings allows you to play music when a call is placed on hold or parked.

        Retrieving location's music on hold settings requires a full, user or read-only administrator auth token with
        a scope of spark-admin:telephony_config_read.

        :param location_id: Retrieve access codes details for this location.
        :type location_id: str
        :param org_id: Retrieve access codes details for a customer location in this organization
        :type org_id: str
        :return: MoH settings
        :rtype: :class:`LocationMoHSetting`
        """
        params = org_id and {'orgId': org_id} or None
        url = self._endpoint(location_id=location_id)
        data = await self.get(url, params=params)
        return LocationMoHSetting.model_validate(data)

    async def update(self, location_id: str, settings: LocationMoHSetting, org_id: str = None):
        """
        Update Music On Hold

        Update the location's music on hold settings.

        Location music on hold settings allows you to play music when a call is placed on hold or parked.

        Updating a location's music on hold settings requires a full administrator or location administrator auth token
        with a scope of `spark-admin:telephony_config_write`.

        :param location_id: Update music on hold settings for this location.
        :type location_id: str
        :param settings: new settings
        :type settings: :class:`LocationMoHSetting`
        :param org_id: Update music on hold settings for this organization.
        :type org_id: str
        """
        params = org_id and {'orgId': org_id} or None
        url = self._endpoint(location_id=location_id)
        await self.put(url, params=params, json=settings.update())


class AsLocationNumbersApi(AsApiChild, base='telephony/config/locations'):
    """
    Numbers supports reading and writing of Webex Calling phone numbers for a
    specific organization.

    Viewing these read-only organization settings requires a full or read-only administrator auth token with a scope of
    `spark-admin:telephony_config_read`.

    Modifying these organization settings requires a full administrator auth token with a scope of
    `spark-admin:telephony_config_write`.

    A partner administrator can retrieve or change settings in a customer's organization using the optional `orgId`
    query parameter.
    """

    def _url(self, location_id: str, path: str = None):
        """

        :meta private:
        """
        path = path and f'/{path}' or ''
        return self.ep(f'{location_id}/numbers{path}')

    async def remove(self, location_id: str, phone_numbers: list[str], org_id: str = None):
        """
        Remove phone numbers from a location

        Remove the specified set of phone numbers from a location for an organization.

        Phone numbers must follow the E.164 format.

        Removing a mobile number may require more time depending on mobile carrier capabilities.

        Removing a phone number from a location requires a full administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        A location's main number cannot be removed.

        This API is only supported for non-integrated PSTN connection types of Local
        Gateway (LGW) and Non-integrated CPP. It should never be used for locations with integrated PSTN connection
        types like Cisco Calling Plans or Integrated CCP because backend data issues may occur.

        :param location_id: LocationId from which numbers should be removed.
        :type location_id: str
        :param phone_numbers: List of phone numbers to be removed.
        :type phone_numbers: list[str]
        :param org_id: Organization to manage
        :type org_id: str
        """
        url = self._url(location_id)
        params = org_id and {'orgId': org_id} or None
        body = {'phoneNumbers': phone_numbers}
        await self.delete(url=url, params=params, json=body)

    async def add(self, location_id: str, phone_numbers: list[str], number_type: TelephoneNumberType = None,
            number_usage_type: NumberUsageType = None,
            state: NumberState = NumberState.inactive, subscription_id: str = None,
            carrier_id: str = None,
            org_id: str = None) -> NumberAddResponse:
        """
        Add Phone Numbers to a location

        Adds a specified set of phone numbers to a location for an organization. Phone numbers must follow the E.164
        format.

        Each location has a set of phone numbers that can be assigned to people, workspaces, or features. Active phone
        numbers are in service.

        Adding a phone number to a location requires a full administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        Phone numbers included in the request that already exist in the location will simply be ignored.

        This API is only supported for adding DID and Toll-free numbers to non-integrated
        PSTN connection types such as Local Gateway (LGW) and Non-integrated CPP. It should never be used for
        locations with integrated PSTN connection types like Cisco Calling Plans or Integrated CCP because backend
        data issues may occur.

        Mobile numbers can be added to any location that has PSTN connection setup. Only
        20 mobile numbers can be added per request.

        :param location_id: LocationId to which numbers should be added.
        :type location_id: str
        :param phone_numbers: List of phone numbers that need to be added.
        :type phone_numbers: list[str]
        :param number_type: Type of the number. Required for `MOBILE` number type.
        :type number_type: TelephoneNumberType
        :param number_usage_type: Type of usage expected for the number.
        :type number_usage_type: NumberUsageType
        :param state: Reflects the state of the number. By default, the state of a number is set to `ACTIVE` for DID
            and toll-free numbers only. Mobile numbers will be activated upon assignment to a user.
        :type state: NumberState
        :param subscription_id: The `subscriptionId` to be used for the mobile number order.
        :type subscription_id: str
        :param carrier_id: The `carrierId` to be used for the mobile number order.
        :type carrier_id: str
        :param org_id: Organization to manage.
        :type org_id: str
        """
        url = self._url(location_id)
        params = org_id and {'orgId': org_id} or None
        body = dict()
        body['phoneNumbers'] = phone_numbers
        if number_type is not None:
            body['numberType'] = enum_str(number_type)
        if number_usage_type is not None:
            body['numberUsageType'] = enum_str(number_usage_type)
        if state is not None:
            body['state'] = enum_str(state)
        if subscription_id is not None:
            body['subscriptionId'] = subscription_id
        if carrier_id is not None:
            body['carrierId'] = carrier_id
        r = await self.post(url=url, params=params, json=body, ignore_status=400)
        if isinstance(r, list):
            return NumberAddResponse.model_validate(r[0])
        return NumberAddResponse.model_validate({})

    def activate(self, location_id: str, phone_numbers: list[str], org_id: str = None):
        """
        Activate Phone Numbers in a location

        Activate the specified set of phone numbers in a location for an organization.

        Each location has a set of phone numbers that can be assigned to people, workspaces, or features. Phone numbers
        must follow the E.164 format. Active phone numbers are in service.

        A mobile number is activated when assigned to a user. This API will not activate mobile numbers.

        Activating a phone number in a location requires a full administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        This API is only supported for non-integrated PSTN connection types of Local
        Gateway (LGW) and Non-integrated CPP. It should never be used for locations with integrated PSTN connection
        types like Cisco Calling Plans or Integrated CCP because backend data issues may occur.

        :param location_id: LocationId in which numbers should be activated.
        :type location_id: str
        :param phone_numbers: List of phone numbers to be activated.
        :type phone_numbers: list[str]
        :param org_id: Organization to manage
        :type org_id: str
        """
        return self.manage_number_state(location_id, phone_numbers,
                                        action=NumbersRequestAction.activate, org_id=org_id)

    async def manage_number_state(self, location_id: str, phone_numbers: list[str],
                            action: NumbersRequestAction = None, org_id: str = None):
        """
        Manage Number State in a location

        Activate or deactivate the specified set of phone numbers in a location for an organization.

        Each location has a set of phone numbers that can be assigned to people, workspaces, or features. Phone numbers
        must follow the E.164 format.

        Active phone numbers are in service.A mobile number is activated when assigned to a user. This API will not
        activate or deactivate mobile numbers.Managing phone number state in a location requires a full administrator
        auth token with a scope of `spark-admin:telephony_config_write`.

        This API is only supported for non-integrated PSTN connection types of Local
        Gateway (LGW) and Non-integrated CCP.

        :param location_id: Unique identifier of the location where phone number activation states will be managed.
        :type location_id: str
        :param phone_numbers: List of phone numbers whose activation state will be modified according to the specified
            action.
        :type phone_numbers: list[str]
        :param action: Specifies the action to execute on the provided phone numbers. If no action is specified, the
            default is set to ACTIVATE.

            For DEACTIVATE action here are few limitations: 1) a maximum of 500 phone numbers can be processed, 2) the
            numbers must be unassigned, 3) the numbers cannot serve as ECBN (Emergency Callback Number), 4) the numbers
            must not be mobile numbers, and 5) this action is only applicable to non-integrated PSTN connection types,
            specifically Local Gateway (LGW) and Non-integrated CCP
        :type action: NumbersRequestAction
        :param org_id: Organization of the Route Group.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        body['phoneNumbers'] = phone_numbers
        if action is not None:
            body['action'] = enum_str(action)
        url = self._url(location_id)
        await super().put(url, params=params, json=body)


class AsLocationVoicemailSettingsApi(AsApiChild, base='telephony/config/locations'):
    """
    location voicemail settings API, for now only enable/disable Vm transcription
    """

    def _endpoint(self, *, location_id: str, path: str = None) -> str:
        """
        location specific
        telephony/config/locations/{locationId}/voicemail

        :meta private:
        :param location_id: Unique identifier for the location.
        :type location_id: str
        :param path: additional path
        :type: path: str
        :return: full endpoint
        :rtype: str
        """
        path = path and f'/{path}' or ''
        ep = self.session.ep(f'telephony/config/locations/{location_id}/voicemail{path}')
        return ep

    async def read(self, location_id: str, org_id: str = None) -> LocationVoiceMailSettings:
        """
        Get Location Voicemail

        Retrieve voicemail settings for a specific location.

        Location's voicemail settings allows you to enable voicemail transcription for a specific location.

        Retrieving location's voicemail settings requires a full, user or read-only administrator auth token with
        a scope of spark-admin:telephony_config_read.


        :param location_id: Retrieve access codes details for this location.
        :type location_id: str
        :param org_id: Retrieve access codes details for a customer location in this organization
        :type org_id: str
        :return: location voicemail settings
        :rtype: :class:`LocationVoiceMailSettings`
        """
        params = org_id and {'orgId': org_id} or None
        url = self._endpoint(location_id=location_id)
        data = await self.get(url, params=params)
        return LocationVoiceMailSettings.model_validate(data)

    async def update(self, location_id: str, settings: LocationVoiceMailSettings, org_id: str = None):
        """
        Update Location Voicemail

        Update the voicemail settings for a specific location.

        Location voicemail settings allows you to enable voicemail transcription for a specific location.

        Updating a location's voicemail settings requires a full administrator or location administrator auth token
        with a scope of `spark-admin:telephony_config_write`.

        :param location_id: Update voicemail settings for this location.
        :type location_id: str
        :param settings: new settings
        :type settings: :class:`LocationVoiceMailSettings`
        :param org_id: Update voicemail settings for this organization.
        :type org_id: str
        """
        params = org_id and {'orgId': org_id} or None
        url = self._endpoint(location_id=location_id)
        body = settings.update()
        await self.put(url, params=params, json=body)


class AsReceptionistContactsDirectoryApi(AsApiChild, base='telephony/config/locations'):
    """
    Webex Calling Location Receptionist Contacts supports creation of directories and assigning custom groups of
    users to directories for a location within an organization.

    Receptionist Contact Directories are named custom groups of users.

    Viewing these read-only directories requires a full or read-only administrator auth token with a scope of
    spark-admin:telephony_config_read, as the current set of APIs is designed to provide supplemental information for
    administrators utilizing People Webex Calling APIs.

    Modifying these directories requires a full administrator auth token with a scope
    of spark-admin:telephony_config_write.

    A partner administrator can retrieve or change settings in a customer's organization using the optional OrgId
    query parameter.

    """

    # TODO: create test cases
    # TODO: add details(), delete(), and update()

    def _url(self, location_id: str):
        return self.ep(f'{location_id}/receptionistContacts/directories')

    async def create(self, location_id: str, name: str, contacts: list[str], org_id: str = None) -> str:
        """
        Creates a new Receptionist Contact Directory for a location.

        Receptionist Contact Directories can be used to create named directories of users.

        Adding a directory requires a full or write-only administrator auth token with a scope
        of spark-admin:telephony_config_write.

        :param location_id: Add a Receptionist Contact Directory to this location.
        :type location_id: str
        :param name: Receptionist Contact Directory name.
        :type name: str
        :param contacts: Array of user or workspace ids assigned to this Receptionist Contact Directory
        :type contacts: list[str]
        :param org_id: Add a Receptionist Contact Directory to this organization.
        :type org_id: str
        :return: Receptionist Contact Directory ID.
        """
        url = self._url(location_id)
        params = org_id and {'orgId': org_id} or None
        body = {'name': name,
                'contacts': [{'personId': contact} for contact in contacts]}
        data = await self.post(url=url, params=params, json=body)
        return data['id']

    def list_gen(self, location_id: str, org_id: str = None) -> AsyncGenerator[IdAndName, None, None]:
        """
        List all Receptionist Contact Directories for a location.

        Receptionist Contact Directories can be used to create named directories of users.

        Retrieving this list requires a full or read-only administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param location_id: List Receptionist Contact Directories for this location.
        :type location_id: str
        :param org_id: List Receptionist Contact Directories for this organization.
        :type org_id: str
        :return: Yields IdAndName instances
        """
        url = self._url(location_id)
        params = org_id and {'orgId': org_id} or None
        return self.session.follow_pagination(url=url, model=IdAndName, params=params, item_key='directories')

    async def list(self, location_id: str, org_id: str = None) -> List[IdAndName]:
        """
        List all Receptionist Contact Directories for a location.

        Receptionist Contact Directories can be used to create named directories of users.

        Retrieving this list requires a full or read-only administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param location_id: List Receptionist Contact Directories for this location.
        :type location_id: str
        :param org_id: List Receptionist Contact Directories for this organization.
        :type org_id: str
        :return: Yields IdAndName instances
        """
        url = self._url(location_id)
        params = org_id and {'orgId': org_id} or None
        return [o async for o in self.session.follow_pagination(url=url, model=IdAndName, params=params, item_key='directories')]

    async def delete(self, location_id: str, directory_id: str, org_id: str = None):
        """
        Delete a Receptionist Contact Directory from a location.

        Receptionist Contact Directories can be used to create named directories of users.

        Deleting a directory requires a full or write-only administrator auth token with a scope
        of spark-admin:telephony_config_write.

        :param location_id: Delete a Receptionist Contact Directory from this location.
        :param directory_id: ID of directory to delete.
        :param org_id: Delete a Receptionist Contact Directory from this organization.
        """
        url = f'{self._url(location_id)}/{directory_id}'
        params = org_id and {'orgId': org_id} or None
        await super().delete(url=url, params=params)


class AsTelephonyLocationApi(AsApiChild, base='telephony/config/locations'):
    #: emergency services
    emergency_services: AsLocationEmergencyServicesApi
    #: call intercept settings
    intercept: AsLocationInterceptApi
    #: internal dialing settings
    internal_dialing: AsInternalDialingApi
    #: moh settings
    moh: AsLocationMoHApi
    #: number settings
    number: AsLocationNumbersApi
    #: outgoing permissions settings
    permissions_out: AsOutgoingPermissionsApi
    #: Location VM settings (only enable/disable transcription for now)
    voicemail: AsLocationVoicemailSettingsApi
    #: Receptionist contacts directories
    receptionist_contacts_directory: AsReceptionistContactsDirectoryApi

    def __init__(self, session: AsRestSession):
        super().__init__(session=session)
        self.emergency_services = AsLocationEmergencyServicesApi(session=session)
        self.intercept = AsLocationInterceptApi(session=session)
        self.internal_dialing = AsInternalDialingApi(session=session)
        self.moh = AsLocationMoHApi(session=session)
        self.number = AsLocationNumbersApi(session=session)
        self.permissions_out = AsOutgoingPermissionsApi(session=session, selector=ApiSelector.location)
        self.voicemail = AsLocationVoicemailSettingsApi(session=session)
        self.receptionist_contacts_directory = AsReceptionistContactsDirectoryApi(session=session)

    async def generate_password(self, location_id: str, generate: list[str] = None, org_id: str = None):
        """
        Generate example password for Location

        Generates an example password using the effective password settings for the location. If you don't specify
        anything in the generate field or don't provide a request body, then you will receive a SIP password by default.

        It's used while creating a trunk and shouldn't be used anywhere else.

        Generating an example password requires a full or write-only administrator auth token with a scope
        of spark-admin:telephony_config_write.

        :param location_id: Location for which example password has to be generated.
        :type location_id: str
        :param generate: password settings array.
        :type generate: list[str]
        :param org_id: Organization to which location belongs.
        :type org_id: str
        :return: new password
        :rtype: str
        """
        params = org_id and {'orgId': org_id} or None
        body = generate and {'generate': generate} or {}
        url = self.ep(f'{location_id}/actions/generatePassword/invoke')
        data = await self.post(url=url, params=params, json=body)
        return data['exampleSipPassword']

    async def validate_extensions(self, location_id: str, extensions: list[str],
                            org_id: str = None) -> ValidateExtensionsResponse:
        """
        Validate Extensions

        Validate extensions for a specific location.

        Validating extensions requires a full administrator auth token with a scope
        of spark-admin:telephony_config_write.

        :param location_id: Validate extensions for this location.
        :type location_id: str
        :param extensions: Array of extensions that will be validated.
        :type extensions: list[str]
        :param org_id: Validate extensions for this organization.
        :type org_id: str
        :return: Validation result
        :rtype: :class:`wxc_sdk.common.ValidateExtensionsResponse`
        """
        url = self.ep(f'{location_id}/actions/validateExtensions/invoke')
        body = {'extensions': extensions}
        params = org_id and {'orgId': org_id} or None
        data = await self.post(url=url, params=params, json=body)
        return ValidateExtensionsResponse.model_validate(data)

    async def details(self, location_id: str, org_id: str = None) -> TelephonyLocation:
        """
        Get Location Webex Calling Details

        Shows Webex Calling details for a location, by ID.

        Specify the location ID in the locationId parameter in the URI.

        Searching and viewing location in your organization requires an administrator auth token with
        the spark-admin:telephony_config_read scope.

        :param location_id: Retrieve Webex Calling location attributes for this location.
        :type location_id: str
        :param org_id: Retrieve Webex Calling location attributes for this organization.
        :type org_id: str
        :return: Webex Calling details for location
        :rtype: :class:`TelephonyLocation`
        """
        params = org_id and {'orgId': org_id}
        url = self.ep(location_id)
        data = await self.get(url=url, params=params)
        return TelephonyLocation.model_validate(data)

    async def enable_for_calling(self, location: Location, org_id: str = None) -> str:
        """
        Enable a Location for Webex Calling

        Enable a location by adding it to Webex Calling. This add Webex Calling support to a
        location created using the POST /v1/locations API.

        Locations are used to support calling features which can be defined at the location level.

        This API requires a full administrator auth token with a scope of `spark-admin:telephony_config_write`.

        :return: A unique identifier for the location.
        :rtype: str
        """
        params = org_id and {'orgId': org_id}
        url = self.ep()
        body = location.model_dump_json()
        data = await self.post(url=url, data=body, params=params)
        return data['id']

    def list_gen(self, name: str = None, order: str = None, org_id: str = None) -> AsyncGenerator[TelephonyLocation, None, None]:
        """
        List Locations Webex Calling Details

        Lists Webex Calling locations for an organization with Webex Calling details.

        Searching and viewing locations with Webex Calling details in your
        organization require an administrator auth token with the
        `spark-admin:telephony_config_read` scope.

        :param name: List locations whose name contains this string.
        :type name: str
        :param order: Sort the list of locations based on name, either asc or desc.
        :type order: str
        :param org_id: List locations for this organization.
        :type org_id: str
        :return: generator of :class:`TelephonyLocation` instances
        """
        params = {to_camel(k): v
                  for k, v in locals().items()
                  if k != 'self' and v is not None}
        url = self.ep()
        return self.session.follow_pagination(url=url, model=TelephonyLocation, params=params, item_key='locations')

    async def list(self, name: str = None, order: str = None, org_id: str = None) -> List[TelephonyLocation]:
        """
        List Locations Webex Calling Details

        Lists Webex Calling locations for an organization with Webex Calling details.

        Searching and viewing locations with Webex Calling details in your
        organization require an administrator auth token with the
        `spark-admin:telephony_config_read` scope.

        :param name: List locations whose name contains this string.
        :type name: str
        :param order: Sort the list of locations based on name, either asc or desc.
        :type order: str
        :param org_id: List locations for this organization.
        :type org_id: str
        :return: generator of :class:`TelephonyLocation` instances
        """
        params = {to_camel(k): v
                  for k, v in locals().items()
                  if k != 'self' and v is not None}
        url = self.ep()
        return [o async for o in self.session.follow_pagination(url=url, model=TelephonyLocation, params=params, item_key='locations')]

    async def update(self, location_id: str, settings: TelephonyLocation, org_id: str = None) -> Optional[str]:
        """
        Update Location Webex Calling Details

        Update Webex Calling details for a location, by ID.

        Specifies the location ID in the `locationId` parameter in the URI.

        Modifying the `connection` via API is only supported for the local PSTN types of `TRUNK` and `ROUTE_GROUP`.

        Updating a location in your organization requires an administrator auth token with the
        `spark-admin:telephony_config_write` scope.

        Example :

            .. code-block:: python

                api.telephony.location.update(location_id=location_id,
                                              settings=TelephonyLocation(
                                                  calling_line_id=CallingLineId(
                                                      phone_number=tn),
                                                  routing_prefix=routing_prefix,
                                                  outside_dial_digit='9'))

        :param location_id: Updating Webex Calling location attributes for this location.
        :type location_id: str
        :param settings: settings to update
        :type settings: :class:`TelephonyLocation`
        :param org_id: Updating Webex Calling location attributes for this organization.
        :type org_id: str
        :return: batch job id of update job if one is created
        :rtype: str
        """
        data = settings.update()
        params = org_id and {'orgId': org_id} or None
        url = self.ep(location_id)
        data = await self.put(url=url, json=data, params=params)
        if data:
            return data.get('batchJobId')
        return

    async def change_announcement_language(self, location_id: str, language_code: str, agent_enabled: bool = None,
                                     service_enabled: bool = None, org_id: str = None):
        """
        Change Announcement Language

        Change announcement language for the given location.

        Change announcement language for current people/workspaces and/or existing feature configurations. This does
        not change the default announcement language which is applied to new users/workspaces and new feature
        configurations.

        Changing announcement language for the given location requires a full administrator auth token with a scope
        of spark-admin:telephony_config_write.

        :param location_id: Change announcement language for this location.
        :type location_id: str
        :param language_code: Language code.
        :type language_code: str
        :param agent_enabled: Set to true to change announcement language for existing people and workspaces.
        :type agent_enabled: bool
        :param service_enabled: Set to true to change announcement language for existing feature configurations.
        :type service_enabled: bool
        :param org_id: Change announcement language for this organization.
        :type org_id: str
        """
        params = org_id and {'orgId': org_id} or None
        body = {'announcementLanguageCode': language_code}
        if agent_enabled is not None:
            body['agentEnabled'] = agent_enabled
        if service_enabled is not None:
            body['serviceEnabled'] = service_enabled
        url = self.session.ep(f'{location_id}/actions/modifyAnnouncementLanguage/invoke')
        await self.put(url, json=body, params=params)

    async def read_ecbn(self, location_id: str,
                  org_id: str = None) -> LocationECBN:
        """
        Get a Location Emergency callback number

        Get location emergency callback number.

        * To retrieve location callback number requires a full, user or read-only administrator or location
        administrator auth token with a scope of `spark-admin:telephony_config_read`.

        :param location_id: Update location attributes for this location.
        :type location_id: str
        :param org_id: Update location attributes for this organization.
        :type org_id: str
        :rtype: :class:`LocationECBN`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'{location_id}/features/emergencyCallbackNumber')
        data = await super().get(url, params=params)
        r = LocationECBN.model_validate(data)
        return r

    async def update_ecbn(self, location_id: str, selected: CallBackSelected,
                    location_member_id: str = None, org_id: str = None):
        """
        Update a Location Emergency callback number

        Update details for a location emergency callback number.

        * Updating a location callback number requires a full administrator or location administrator auth token with a
        scope of `spark-admin:telephony_config_write`.

        :param location_id: Update location attributes for this location.
        :type location_id: str
        :param selected: Selected number type to configure emergency call back.
        :type selected: CallBackSelected
        :param location_member_id: Member ID of user/place within the location. Required if `LOCATION_MEMBER_NUMBER` is
            selected.
        :type location_member_id: str
        :param org_id: Update location attributes for this organization.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        body['selected'] = enum_str(selected)
        if location_member_id is not None:
            body['locationMemberId'] = location_member_id
        url = self.ep(f'{location_id}/features/emergencyCallbackNumber')
        await super().put(url, params=params, json=body)

    async def device_settings(self, location_id: str, org_id: str = None) -> DeviceCustomization:
        """
        Get device override settings for a location.

        This requires a full or read-only administrator auth token with a scope of spark-admin:telephony_config_read.

        :param location_id: Unique identifier for the location
        :type location_id: str
        :param org_id: Settings on the device in this organization
        :type org_id: str
        :return: device customization response
        :rtype: DeviceCustomization
        """
        params = org_id and {'orgId': org_id} or None
        url = self.ep(f'{location_id}/devices/settings')
        data = await self.get(url=url, params=params)
        return DeviceCustomization.model_validate(data)

    def phone_numbers_available_for_external_caller_id_gen(self, location_id: str,
                                                       phone_number: List[str] = None,
                                                       owner_name: str = None, person_id: str = None,
                                                       org_id: str = None,
                                                       **params) -> AsyncGenerator[AvailableNumber, None, None]:
        """
        Get the List of Phone Numbers Available for External Caller ID

        Get the list of phone numbers available for external caller ID usage by a Webex Calling entity (such as a
        person, virtual line, or workspace) within the specified location.
        Numbers from the specified location are returned and cross location numbers are returned as well where the
        number's location has the same country, PSTN provider, and zone (only applicable for India locations) as the
        specified location.
        When `personId` is specified, and the person belongs to a cisco PSTN location, has a mobile number assigned as
        primary DN, and does not have a billing plan, only the assigned mobile number is returned as the available
        number for caller ID.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full or read-only administrator or location administrator auth token with a
        scope of `spark-admin:telephony_config_read`.

        :param location_id: Retrieve available external caller ID numbers for this location.
        :type location_id: str
        :param phone_number: Filter phone numbers based on the provided list in the `phoneNumber` array.
        :type phone_number: list[str]
        :param owner_name: Return the list of phone numbers that are owned by the given `ownerName`. Maximum length is
            255.
        :type owner_name: str
        :param person_id: Retrieve available external caller ID numbers for this person. If `personId` is not provided
            it may result in the unsuccessful assignment of the returned number. This parameter has no effect when
            workspace or virtual line ID is used.
        :type person_id: str
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableNumber` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        if owner_name is not None:
            params['ownerName'] = owner_name
        if person_id is not None:
            params['personId'] = person_id
        url = self.ep(f'{location_id}/externalCallerId/availableNumbers')
        return self.session.follow_pagination(url=url, model=AvailableNumber, item_key='phoneNumbers', params=params)

    async def phone_numbers_available_for_external_caller_id(self, location_id: str,
                                                       phone_number: List[str] = None,
                                                       owner_name: str = None, person_id: str = None,
                                                       org_id: str = None,
                                                       **params) -> List[AvailableNumber]:
        """
        Get the List of Phone Numbers Available for External Caller ID

        Get the list of phone numbers available for external caller ID usage by a Webex Calling entity (such as a
        person, virtual line, or workspace) within the specified location.
        Numbers from the specified location are returned and cross location numbers are returned as well where the
        number's location has the same country, PSTN provider, and zone (only applicable for India locations) as the
        specified location.
        When `personId` is specified, and the person belongs to a cisco PSTN location, has a mobile number assigned as
        primary DN, and does not have a billing plan, only the assigned mobile number is returned as the available
        number for caller ID.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full or read-only administrator or location administrator auth token with a
        scope of `spark-admin:telephony_config_read`.

        :param location_id: Retrieve available external caller ID numbers for this location.
        :type location_id: str
        :param phone_number: Filter phone numbers based on the provided list in the `phoneNumber` array.
        :type phone_number: list[str]
        :param owner_name: Return the list of phone numbers that are owned by the given `ownerName`. Maximum length is
            255.
        :type owner_name: str
        :param person_id: Retrieve available external caller ID numbers for this person. If `personId` is not provided
            it may result in the unsuccessful assignment of the returned number. This parameter has no effect when
            workspace or virtual line ID is used.
        :type person_id: str
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableNumber` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        if owner_name is not None:
            params['ownerName'] = owner_name
        if person_id is not None:
            params['personId'] = person_id
        url = self.ep(f'{location_id}/externalCallerId/availableNumbers')
        return [o async for o in self.session.follow_pagination(url=url, model=AvailableNumber, item_key='phoneNumbers', params=params)]

    def phone_numbers_gen(self, location_id: str,
                      phone_number: List[str] = None,
                      owner_name: str = None, org_id: str = None,
                      **params) -> AsyncGenerator[AvailableNumber, None, None]:
        """
        Get Available Phone Numbers for a Location with Given Criteria

        List the service and standard PSTN numbers that are available to be assigned as the location's main number.
        These numbers are associated with the location specified in the request URL and can be active/inactive and
        assigned to an owning entity or unassigned.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full or read-only administrator or location administrator auth token with a
        scope of `spark-admin:telephony_config_read`.

        :param location_id: Return the list of phone numbers for this location within the given organization. The
            maximum length is 36.
        :type location_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param owner_name: Return the list of phone numbers that are owned by the given `ownerName`. Maximum length is
            255.
        :type owner_name: str
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableNumber` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        if owner_name is not None:
            params['ownerName'] = owner_name
        url = self.ep(f'{location_id}/availableNumbers')
        return self.session.follow_pagination(url=url, model=AvailableNumber, item_key='phoneNumbers', params=params)

    async def phone_numbers(self, location_id: str,
                      phone_number: List[str] = None,
                      owner_name: str = None, org_id: str = None,
                      **params) -> List[AvailableNumber]:
        """
        Get Available Phone Numbers for a Location with Given Criteria

        List the service and standard PSTN numbers that are available to be assigned as the location's main number.
        These numbers are associated with the location specified in the request URL and can be active/inactive and
        assigned to an owning entity or unassigned.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full or read-only administrator or location administrator auth token with a
        scope of `spark-admin:telephony_config_read`.

        :param location_id: Return the list of phone numbers for this location within the given organization. The
            maximum length is 36.
        :type location_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param owner_name: Return the list of phone numbers that are owned by the given `ownerName`. Maximum length is
            255.
        :type owner_name: str
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableNumber` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        if owner_name is not None:
            params['ownerName'] = owner_name
        url = self.ep(f'{location_id}/availableNumbers')
        return [o async for o in self.session.follow_pagination(url=url, model=AvailableNumber, item_key='phoneNumbers', params=params)]

    def webex_go_available_phone_numbers_gen(self, location_id: str, phone_number: List[str] = None,
                                         org_id: str = None,
                                         **params) -> AsyncGenerator[AvailableNumber, None, None]:
        """
        Get Webex Go Available Phone Numbers

        List standard numbers that are available to be assigned as the webex go phone number.
        These numbers are associated with the location specified in the request URL, can be active or inactive, and are
        unassigned.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full, read-only or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param location_id: Return the list of phone numbers for this location within the given organization. The
            maximum length is 36.
        :type location_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableNumber` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        url = self.ep(f'{location_id}/webexGo/availableNumbers')
        return self.session.follow_pagination(url=url, model=AvailableNumber, item_key='phoneNumbers', params=params)

    async def webex_go_available_phone_numbers(self, location_id: str, phone_number: List[str] = None,
                                         org_id: str = None,
                                         **params) -> List[AvailableNumber]:
        """
        Get Webex Go Available Phone Numbers

        List standard numbers that are available to be assigned as the webex go phone number.
        These numbers are associated with the location specified in the request URL, can be active or inactive, and are
        unassigned.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full, read-only or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param location_id: Return the list of phone numbers for this location within the given organization. The
            maximum length is 36.
        :type location_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableNumber` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        url = self.ep(f'{location_id}/webexGo/availableNumbers')
        return [o async for o in self.session.follow_pagination(url=url, model=AvailableNumber, item_key='phoneNumbers', params=params)]

    def ecbn_available_phone_numbers_gen(self, location_id: str, phone_number: List[str] = None,
                                     owner_name: str = None, org_id: str = None,
                                     **params) -> AsyncGenerator[AvailableNumber, None, None]:
        """
        Get Location ECBN Available Phone Numbers

        List standard numbers that are available to be assigned as the location's emergency callback number.
        These numbers are associated with the location specified in the request URL, can be active or inactive, and are
        assigned to an owning entity.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full, read-only or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param location_id: Return the list of phone numbers for this location within the given organization. The
            maximum length is 36.
        :type location_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param owner_name: Return the list of phone numbers that are owned by the given `ownerName`. Maximum length is
            255.
        :type owner_name: str
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableNumber` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        if owner_name is not None:
            params['ownerName'] = owner_name
        url = self.ep(f'{location_id}/emergencyCallbackNumber/availableNumbers')
        return self.session.follow_pagination(url=url, model=AvailableNumber, item_key='phoneNumbers', params=params)

    async def ecbn_available_phone_numbers(self, location_id: str, phone_number: List[str] = None,
                                     owner_name: str = None, org_id: str = None,
                                     **params) -> List[AvailableNumber]:
        """
        Get Location ECBN Available Phone Numbers

        List standard numbers that are available to be assigned as the location's emergency callback number.
        These numbers are associated with the location specified in the request URL, can be active or inactive, and are
        assigned to an owning entity.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full, read-only or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param location_id: Return the list of phone numbers for this location within the given organization. The
            maximum length is 36.
        :type location_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param owner_name: Return the list of phone numbers that are owned by the given `ownerName`. Maximum length is
            255.
        :type owner_name: str
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableNumber` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        if owner_name is not None:
            params['ownerName'] = owner_name
        url = self.ep(f'{location_id}/emergencyCallbackNumber/availableNumbers')
        return [o async for o in self.session.follow_pagination(url=url, model=AvailableNumber, item_key='phoneNumbers', params=params)]

    def charge_number_available_phone_numbers_gen(self, location_id: str, phone_number: List[str] = None,
                                              owner_name: str = None, org_id: str = None,
                                              **params) -> AsyncGenerator[AvailableNumber, None, None]:
        """
        Get Available Charge Numbers for a Location with Given Criteria

        List the numbers that are available to be assigned as the location's charge number.

        These numbers are non-toll-free and non-mobile numbers assigned to the location specified in the request URL.

        Retrieving this list requires a full or read-only administrator or location administrator auth token with a
        scope of `spark-admin:telephony_config_read`.

        :param location_id: Return the list of available charge numbers for this location within the given
            organization. The maximum length is 36.
        :type location_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param owner_name: Return the list of phone numbers that are owned by the given `ownerName`. Maximum length is
            255.
        :type owner_name: str
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableNumber` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        if owner_name is not None:
            params['ownerName'] = owner_name
        url = self.ep(f'{location_id}/chargeNumber/availableNumbers')
        return self.session.follow_pagination(url=url, model=AvailableNumber,
                                              item_key='phoneNumbers', params=params)

    async def charge_number_available_phone_numbers(self, location_id: str, phone_number: List[str] = None,
                                              owner_name: str = None, org_id: str = None,
                                              **params) -> List[AvailableNumber]:
        """
        Get Available Charge Numbers for a Location with Given Criteria

        List the numbers that are available to be assigned as the location's charge number.

        These numbers are non-toll-free and non-mobile numbers assigned to the location specified in the request URL.

        Retrieving this list requires a full or read-only administrator or location administrator auth token with a
        scope of `spark-admin:telephony_config_read`.

        :param location_id: Return the list of available charge numbers for this location within the given
            organization. The maximum length is 36.
        :type location_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param owner_name: Return the list of phone numbers that are owned by the given `ownerName`. Maximum length is
            255.
        :type owner_name: str
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableNumber` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        if owner_name is not None:
            params['ownerName'] = owner_name
        url = self.ep(f'{location_id}/chargeNumber/availableNumbers')
        return [o async for o in self.session.follow_pagination(url=url, model=AvailableNumber,
                                              item_key='phoneNumbers', params=params)]

    def call_intercept_available_phone_numbers_gen(self, location_id: str, phone_number: List[str] = None,
                                               owner_name: str = None, org_id: str = None,
                                               **params) -> AsyncGenerator[AvailableNumber, None, None]:
        """
        Get Location Call Intercept Available Phone Numbers

        List service and standard numbers that are available to be assigned as the location's call intercept number.
        These numbers are associated with the location specified in the request URL and can be active/inactive and
        assigned to an owning entity or unassigned.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full or read-only administrator or location administrator auth token with a
        scope of `spark-admin:telephony_config_read`.

        :param location_id: Return the list of phone numbers for this location within the given organization. The
            maximum length is 36.
        :type location_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param owner_name: Return the list of phone numbers that are owned by the given `ownerName`. Maximum length is
            255.
        :type owner_name: str
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`LocationAvailableNumberObject` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        if owner_name is not None:
            params['ownerName'] = owner_name
        url = self.ep(f'{location_id}/callIntercept/availableNumbers')
        return self.session.follow_pagination(url=url, model=AvailableNumber, item_key='phoneNumbers', params=params)

    async def call_intercept_available_phone_numbers(self, location_id: str, phone_number: List[str] = None,
                                               owner_name: str = None, org_id: str = None,
                                               **params) -> List[AvailableNumber]:
        """
        Get Location Call Intercept Available Phone Numbers

        List service and standard numbers that are available to be assigned as the location's call intercept number.
        These numbers are associated with the location specified in the request URL and can be active/inactive and
        assigned to an owning entity or unassigned.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full or read-only administrator or location administrator auth token with a
        scope of `spark-admin:telephony_config_read`.

        :param location_id: Return the list of phone numbers for this location within the given organization. The
            maximum length is 36.
        :type location_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param owner_name: Return the list of phone numbers that are owned by the given `ownerName`. Maximum length is
            255.
        :type owner_name: str
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`LocationAvailableNumberObject` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        if owner_name is not None:
            params['ownerName'] = owner_name
        url = self.ep(f'{location_id}/callIntercept/availableNumbers')
        return [o async for o in self.session.follow_pagination(url=url, model=AvailableNumber, item_key='phoneNumbers', params=params)]

    async def create_receptionist_contact_directory(self, location_id: str, name: str, contacts: List[str],
                                              org_id: str = None) -> str:
        """
        Create a Receptionist Contact Directory

        Create a new Receptionist Contact Directory for a location.

        Receptionist Contact Directories can be used to create named directories of users and/or location features
        (Auto Attendant, Call Queue, Hunt Group, Single Number Reach, and Paging Group).

        Adding a directory requires a full or write-only administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param location_id: Add a Receptionist Contact Directory to this location.
        :type location_id: str
        :param name: Receptionist Contact Directory name. The directory name should be greater than 0 and less than 41
            characters in length.
        :type name: str
        :param contacts: Non-empty array of users or location features assigned to this Receptionist Contact Directory.
        :type contacts: list[str]
        :param org_id: Add a Receptionist Contact Directory to this organization.
        :type org_id: str
        :rtype: str
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        body['name'] = name
        body['contacts'] = contacts
        url = self.ep(f'{location_id}/receptionistContacts/directories')
        data = await super().post(url, params=params, json=body)
        r = data['id']
        return r

    async def list_receptionist_contact_directories(self, location_id: str,
                                              org_id: str = None) -> List[IdAndName]:
        """
        Read list of Receptionist Contact Directories

        List all Receptionist Contact Directories for a location.

        Receptionist Contact Directories can be used to create named directories of users.

        Retrieving this list requires a full or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param location_id: List Receptionist Contact Directories for this location.
        :type location_id: str
        :param org_id: List Receptionist Contact Directories for this organization.
        :type org_id: str
        :rtype: list[IdAndName]
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'{location_id}/receptionistContacts/directories')
        data = await super().get(url, params=params)
        r = TypeAdapter(list[IdAndName]).validate_python(data['directories'])
        return r

    async def receptionist_contact_directory_details(self, location_id: str, directory_id: str,
                                               search_criteria_mode_or: bool = None, first_name: str = None,
                                               last_name: str = None, phone_number: str = None,
                                               extension: str = None, person_id: str = None,
                                               org_id: str = None) -> List[ContactDetails]:
        """
        Get details for a Receptionist Contact Directory

        Get details for a specific Receptionist Contact Directory from a location.

        Receptionist Contact Directories are uniquely named per location and contain directories of Persons, Auto
        Attendants, Call Queues, Hunt Groups, Single Number Reaches, and Paging Groups.

        This API is currently supported for Webex calling organizations with fewer than 2000 users or location-based
        calling features. For organizations with more than 2000 users or location features, the API will throw an
        error 25395.

        Retrieving details requires a full or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param location_id: Get a Receptionist Contact Directory from this location.
        :type location_id: str
        :param directory_id: Get details for the Receptionist Contact Directory with this identifier.
        :type directory_id: str
        :param search_criteria_mode_or: When `true`, results matching any one of the search criteria are included. The
            value can only be `true` or not included in the request. Specifying `searchCriteriaModeOr` without any
            search criteria, or setting it to `false` results in an `ErrorResponse`. If no search criteria is
            specified, all results are returned.
        :type search_criteria_mode_or: bool
        :param first_name: Search for directories that contain people with the indicated first name.
        :type first_name: str
        :param last_name: Search for directories that contain people with the indicated last name.
        :type last_name: str
        :param phone_number: Search for directories that contain people with the indicated phone number.
        :type phone_number: str
        :param extension: Search for directories that contain people with the indicated extension.
        :type extension: str
        :param person_id: Search for directories that contain people with the indicated person ID.
        :type person_id: str
        :param org_id: Get a Receptionist Contact Directory from this organization.
        :type org_id: str
        :rtype: list[ContactDetails]
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        if search_criteria_mode_or is not None:
            params['searchCriteriaModeOr'] = str(search_criteria_mode_or).lower()
        if first_name is not None:
            params['firstName'] = first_name
        if last_name is not None:
            params['lastName'] = last_name
        if phone_number is not None:
            params['phoneNumber'] = phone_number
        if extension is not None:
            params['extension'] = extension
        if person_id is not None:
            params['personId'] = person_id
        url = self.ep(f'{location_id}/receptionistContacts/directories/{directory_id}')
        data = await super().get(url, params=params)
        r = TypeAdapter(list[ContactDetails]).validate_python(data['contacts'])
        return r

    async def delete_receptionist_contact_directory(self, location_id: str, directory_id: str, org_id: str = None):
        """
        Delete a Receptionist Contact Directory

        Delete a Receptionist Contact Directory from a location.

        Receptionist Contact Directories can be used to create named directories of users.

        Deleting a directory requires a full or write-only administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param location_id: Delete a Receptionist Contact Directory from this location.
        :type location_id: str
        :param directory_id: Add a Receptionist Contact Directory ID.
        :type directory_id: str
        :param org_id: Delete a Receptionist Contact Directory from this organization.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'{location_id}/receptionistContacts/directories/{directory_id}')
        await super().delete(url, params=params)

    async def modify_receptionist_contact_directory(self, location_id: str, directory_id: str, name: str,
                                              contacts: List[str], org_id: str = None) -> str:
        """
        Modify a Receptionist Contact Directory

        Modify Receptionist Contact Directories attached to a location. This modification will replace the existing
        list of contacts with the new incoming contacts list from the request body. The API does not support
        incremental updates.

        Receptionist Contact Directories can be used to create named groups of Persons, Auto Attendants, Call Queues,
        Hunt Groups, Single Number Reaches, and Paging Groups.

        Modifying a directory requires a full or write-only administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param location_id: Modify list of Receptionist Contact Directories for this location.
        :type location_id: str
        :param directory_id: Get details for the Receptionist Contact Directory with this identifier.
        :type directory_id: str
        :param name: Receptionist Contact Directory name. The directory name should be greater than 0 and less than 41
            characters in length.
        :type name: str
        :param contacts: Non-empty array of users or location features assigned to this Receptionist Contact Directory.
        :type contacts: list[str]
        :param org_id: Modify list of Receptionist Contact Directories for this organization.
        :type org_id: str
        :rtype: str
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        body['name'] = name
        body['contacts'] = contacts
        url = self.ep(f'{location_id}/receptionistContacts/directories/{directory_id}')
        data = await super().put(url, params=params, json=body)
        r = data['id']
        return r

    async def safe_delete_check_before_disabling_calling_location(self, location_id: str,
                                                            org_id: str = None) -> SafeDeleteCheckResponse:
        """
        Safe Delete Check Before Disabling a Location for Webex Calling

        Performs a safe delete check operation to identify any issues that would prevent the calling location from
        being disabled. This API helps identify resources that need to be addressed before a calling location can be
        successfully disabled.

        This API requires a full administrator auth token with a scope of `spark-admin:telephony_config_read`.

        :param location_id: Unique identifier for the location to be checked.
        :type location_id: str
        :param org_id: Organization ID for which the safe delete check operation is being performed.
        :type org_id: str
        :rtype: :class:`SafeDeleteCheckResponse`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'{location_id}/actions/precheckForDeletion/invoke')
        data = await super().post(url, params=params)
        r = SafeDeleteCheckResponse.model_validate(data)
        return r

    async def get_call_captions_settings(self, location_id: str,
                                   org_id: str = None) -> LocationCallCaptions:
        """
        Get the location call captions settings

        Retrieve the location's call captions settings.

        **NOTE**: The call captions feature is not supported for locations in India.

        The call caption feature allows the customer to enable and manage closed captions and transcript functionality
        (rolling caption panel) in Webex Calling, without requiring the user to escalate the call to a meeting.

        This API requires a full, read-only, or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param location_id: Unique identifier for the location.
        :type location_id: str
        :param org_id: Unique identifier for the organization.
        :type org_id: str
        :rtype: :class:`LocationCallCaptions`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'{location_id}/callCaptions')
        data = await super().get(url, params=params)
        r = LocationCallCaptions.model_validate(data)
        return r

    async def update_call_captions_settings(self, location_id: str, settings: LocationCallCaptions,
                                      org_id: str = None):
        """
        Update the location call captions settings

        Update the location's call captions settings.

        **NOTE**: The call captions feature is not supported for locations in India.

        The call caption feature allows the customer to enable and manage closed captions and transcript functionality
        (rolling caption panel) in Webex Calling, without requiring the user to escalate the call to a meeting.

        This API requires a full or location administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param location_id: Unique identifier for the location.
        :type location_id: str
        :param settings: settings to update
        :type settings: :class:`LocationCallCaptions`
        :param org_id: Unique identifier for the organization.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = settings.update()
        url = self.ep(f'{location_id}/callCaptions')
        await super().put(url, params=params, json=body)


class AsVirtualExtensionsApi(AsApiChild, base='telephony/config'):
    """
    Features: Virtual Extensions

    Features: Virtual Extensions allow assigning extensions to frequently called external numbers for simplified
    dialing within Webex Calling.

    Viewing these read-only organization settings requires a full or read-only administrator auth token with a scope of
    `spark-admin:telephony_config_read`.

    Modifying these organization settings requires a full administrator auth token with a scope of
    `spark-admin:telephony_config_write`.

    A partner administrator can retrieve or change settings in a customer's organization using the optional `orgId`
    query parameter.
    """

    def list_range_gen(self, order: str = None, name: str = None, prefix: str = None,
                   location_id: str = None, org_level_only: bool = None,
                   org_id: str = None,
                   **params) -> AsyncGenerator[VirtualExtensionRange, None, None]:
        """
        Get a list of a Virtual Extension Range

        Retrieves the list of Virtual Extension Ranges.

        Virtual extension ranges integrate remote workers on a separate telephony system into Webex Calling and enable
        extension dialing. Using these ranges, you can define patterns that can be used to route calls at a location
        level or an organization level. You are allowed to define virtual extensions ranges in addition to individual
        virtual extensions.
        This works in both Standard and Enhanced modes

        Retrieving a virtual extension range requires a full or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param order: Sort the list of virtual extension ranges by name or prefix, either ASC or DSC. Default sort
            order is ASC.
        :type order: str
        :param name: Filter the list of virtual extension ranges by name.
        :type name: str
        :param prefix: Filter the list of virtual extension ranges by prefix.
        :type prefix: str
        :param location_id: Filter the list of virtual extension ranges by location ID. Only one of the `locationId`
            and `OrgLevelOnly` query parameters is allowed at the same time.
        :type location_id: str
        :param org_level_only: Filter the list of virtual extension ranges by organization level. If `orgLevelOnly` is
            true, return only the organization level virtual extension ranges.
        :type org_level_only: bool
        :param org_id: Unique identifier for the organization.
        :type org_id: str
        :return: Generator yielding :class:`GetVirtualExtensionRangeListObject` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if order is not None:
            params['order'] = order
        if name is not None:
            params['name'] = name
        if prefix is not None:
            params['prefix'] = prefix
        if location_id is not None:
            params['locationId'] = location_id
        if org_level_only is not None:
            params['orgLevelOnly'] = str(org_level_only).lower()
        url = self.ep('virtualExtensionRanges')
        return self.session.follow_pagination(url=url, model=VirtualExtensionRange,
                                              item_key='virtualExtensionRanges', params=params)

    async def list_range(self, order: str = None, name: str = None, prefix: str = None,
                   location_id: str = None, org_level_only: bool = None,
                   org_id: str = None,
                   **params) -> List[VirtualExtensionRange]:
        """
        Get a list of a Virtual Extension Range

        Retrieves the list of Virtual Extension Ranges.

        Virtual extension ranges integrate remote workers on a separate telephony system into Webex Calling and enable
        extension dialing. Using these ranges, you can define patterns that can be used to route calls at a location
        level or an organization level. You are allowed to define virtual extensions ranges in addition to individual
        virtual extensions.
        This works in both Standard and Enhanced modes

        Retrieving a virtual extension range requires a full or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param order: Sort the list of virtual extension ranges by name or prefix, either ASC or DSC. Default sort
            order is ASC.
        :type order: str
        :param name: Filter the list of virtual extension ranges by name.
        :type name: str
        :param prefix: Filter the list of virtual extension ranges by prefix.
        :type prefix: str
        :param location_id: Filter the list of virtual extension ranges by location ID. Only one of the `locationId`
            and `OrgLevelOnly` query parameters is allowed at the same time.
        :type location_id: str
        :param org_level_only: Filter the list of virtual extension ranges by organization level. If `orgLevelOnly` is
            true, return only the organization level virtual extension ranges.
        :type org_level_only: bool
        :param org_id: Unique identifier for the organization.
        :type org_id: str
        :return: Generator yielding :class:`GetVirtualExtensionRangeListObject` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if order is not None:
            params['order'] = order
        if name is not None:
            params['name'] = name
        if prefix is not None:
            params['prefix'] = prefix
        if location_id is not None:
            params['locationId'] = location_id
        if org_level_only is not None:
            params['orgLevelOnly'] = str(org_level_only).lower()
        url = self.ep('virtualExtensionRanges')
        return [o async for o in self.session.follow_pagination(url=url, model=VirtualExtensionRange,
                                              item_key='virtualExtensionRanges', params=params)]

    async def create_range(self, name: str, prefix: str, patterns: list[str] = None,
                     location_id: str = None, org_id: str = None) -> str:
        """
        Create a Virtual Extension Range

        Create a new Virtual Extension Range for the given organization or location.

        Virtual extension ranges integrate remote workers on a separate telephony system into Webex Calling and enable
        extension dialing. Using these ranges, you can define patterns that can be used to route calls at a location
        level or an organization level. You are allowed to define virtual extensions ranges in addition to individual
        virtual extensions.
        This works in both Standard and Enhanced modes

        Virtual extension range can be set up at the organization or location level.

        Creating a virtual extension range requires a full administrator or location administrator auth token with a
        scope of `spark-admin:telephony_config_write`.

        :param name: Name of the virtual extension range. This is a unique name for the virtual extension range.
        :type name: str
        :param prefix: Prefix used for a virtual extension range. Prefix works in Standard and Enhanced modes. In
            Standard mode, it must be E.164 and unique. In Enhanced mode, it can be E.164 or non-E.164.
        :type prefix: str
        :param patterns: List of virtual extension patterns. You can add up to 100 patterns at a time. Extension
            patterns can include one or more right-justified wildcards “X” matching any digit.
        :type patterns: list[str]
        :param location_id: ID of the location to which the virtual extension range is assigned. The location ID is a
            unique identifier for the location in Webex Calling. This is set only when location level virtual
            extension range is added.
        :type location_id: str
        :param org_id: Unique identifier for the organization.
        :type org_id: str
        :rtype: str
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        body['name'] = name
        body['prefix'] = prefix
        if patterns is not None:
            body['patterns'] = patterns
        if location_id is not None:
            body['locationId'] = location_id
        url = self.ep('virtualExtensionRanges')
        data = await super().post(url, params=params, json=body)
        r = data['id']
        return r

    async def validate_range(self, location_id: str = None,
                       name: str = None, prefix: str = None,
                       patterns: list[str] = None,
                       range_id: str = None,
                       org_id: str = None) -> ValidateVirtualExtensionRange:
        """
        Validate the prefix and extension pattern for a Virtual Extension Range.

        Virtual extension ranges integrate remote workers on a separate telephony system into Webex Calling and enable
        extension dialing. Using these ranges, you can define patterns that can be used to route calls at a location
        level or an organization level. You are allowed to define virtual extensions ranges in addition to individual
        virtual extensions.
        This works in both Standard and Enhanced modes

        Validating a prefix and extension pattern for a Virtual Extension Range requires a full administrator or
        location administrator auth token with a scope of `spark-admin:telephony_config_write`.

        :param location_id: ID of the location to which the virtual extension range is assigned. The location ID is a
            unique identifier for the location in Webex Calling.
        :type location_id: str
        :param name: Name of the virtual extension range. This is a unique name for the virtual extension range.
        :type name: str
        :param prefix: Prefix used for a virtual extension range.
        :type prefix: str
        :param patterns: List of virtual extension patterns. The maximum number of patterns supported at a time is 100.
        :type patterns: list[str]
        :param range_id: ID of the virtual extension range. This is mandatory when validating for an existing virtual
            extension range, not present when validating a new virtual extension range before adding it.
        :type range_id: str
        :param org_id: Unique identifier for the organization.
        :type org_id: str
        :rtype: :class:`ValidateVirtualExtensionRange`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        if location_id is not None:
            body['locationId'] = location_id
        if name is not None:
            body['name'] = name
        if prefix is not None:
            body['prefix'] = prefix
        if patterns is not None:
            body['patterns'] = patterns
        if range_id is not None:
            body['rangeId'] = range_id
        url = self.ep('virtualExtensionRanges/actions/validate/invoke')
        data = await super().post(url, params=params, json=body)
        r = ValidateVirtualExtensionRange.model_validate(data)
        return r

    async def delete_range(self, extension_range_id: str, org_id: str = None):
        """
        Delete a Virtual Extension Range

        Delete a virtual extension range for the given extension range ID.

        Virtual extension ranges integrate remote workers on a separate telephony system into Webex Calling and enable
        extension dialing. Using these ranges, you can define patterns that can be used to route calls at a location
        level or an organization level. You are allowed to define virtual extensions ranges in addition to individual
        virtual extensions.
        This works in both Standard and Enhanced modes

        Deleting a virtual extension range requires a full administrator or location administrator auth token with a
        scope of `spark-admin:telephony_config_write`.

        :param extension_range_id: ID of the virtual extension range.
        :type extension_range_id: str
        :param org_id: Unique identifier for the organization.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'virtualExtensionRanges/{extension_range_id}')
        await super().delete(url, params=params)

    async def details_range(self, extension_range_id: str,
                      org_id: str = None) -> VirtualExtensionRange:
        """
        Get details of a Virtual Extension Range

        Retrieve virtual extension range details for the given extension range ID.

        Virtual extension ranges integrate remote workers on a separate telephony system into Webex Calling and enable
        extension dialing. Using these ranges, you can define patterns that can be used to route calls at a location
        level or an organization level. You are allowed to define virtual extensions ranges in addition to individual
        virtual extensions.
        This works in both Standard and Enhanced modes

        Retrieving a virtual extension range requires a full or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param extension_range_id: ID of the virtual extension range.
        :type extension_range_id: str
        :param org_id: Unique identifier for the organization.
        :type org_id: str
        :rtype: :class:`VirtualExtensionRange`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'virtualExtensionRanges/{extension_range_id}')
        data = await super().get(url, params=params)
        r = VirtualExtensionRange.model_validate(data)
        return r

    async def modify_range(self, extension_range_id: str, name: str = None, prefix: str = None,
                     patterns: list[str] = None,
                     action: VirtualExtensionRangeAction = None, org_id: str = None):
        """
        Modify Virtual Extension Range

        Modify virtual extension range for the given extension range ID.

        Virtual extension ranges integrate remote workers on a separate telephony system into Webex Calling and enable
        extension dialing. Using these ranges, you can define patterns that can be used to route calls at a location
        level or an organization level. You are allowed to define virtual extensions ranges in addition to individual
        virtual extensions.
        This works in both Standard and Enhanced modes

        Modifying a virtual extension range requires a full administrator or location administrator auth token with a
        scope of `spark-admin:telephony_config_write`.

        :param extension_range_id: ID of the virtual extension range.
        :type extension_range_id: str
        :param name: Name of the virtual extension range. This is a unique name for the virtual extension range.
        :type name: str
        :param prefix: Prefix used for a virtual extension range.
        :type prefix: str
        :param patterns: The pattern to be added, replaced, or removed from a virtual extension range. The maximum
            number of patterns supported at a time is 100.
        :type patterns: list[str]
        :param action: Action to be performed on the virtual extension range. It can be either `ADD`, `REMOVE` or
            `REPLACE`. This is mandatory when `patterns` are provided.
        :type action: VirtualExtensionRangeAction
        :param org_id: Unique identifier for the organization.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        if name is not None:
            body['name'] = name
        if prefix is not None:
            body['prefix'] = prefix
        if patterns is not None:
            body['patterns'] = patterns
        if action is not None:
            body['action'] = enum_str(action)
        url = self.ep(f'virtualExtensionRanges/{extension_range_id}')
        await super().put(url, params=params, json=body)

    def list_extensions_gen(self, order: str = None, extension: str = None, phone_number: str = None,
                        name: str = None, location_name: str = None, location_id: str = None,
                        org_level_only: bool = None, org_id: str = None,
                        **params) -> AsyncGenerator[VirtualExtension, None, None]:
        """
        Read the List of Virtual Extensions

        Retrieve virtual extensions associated with a specific customer.

        The GET Virtual Extensions API allows administrators to retrieve a list of virtual extensions configured within
        their organization. Virtual extensions enable users to dial extension numbers that route to external phone
        numbers, such as those of remote workers or frequently contacted clients.
        This API returns key information including the  extension, associated  phone number (in E.164 format), display
        name, and the location to which the virtual extension belongs
        The API supports filtering by various parameters, such as extension number, phone number, and location name.
        The results can be paginated using the `max` and `start` parameters, and the order of the results can be
        specified using the `order` parameter.

        Retrieving a Virtual Extension requires a full or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param order: Order the list of virtual extensions in ascending or descending order. Default is ascending.
        :type order: str
        :param extension: Filter the list of virtual extensions by extension number.
        :type extension: str
        :param phone_number: Filter the list of virtual extensions by phone number.
        :type phone_number: str
        :param name: Filter the list of virtual extensions by name. This can be either first name or last name.
        :type name: str
        :param location_name: Filter the list of virtual extensions by location name.(Only one of the locationName,
            locationId, and OrgLevelOnly query parameters is allowed at the same time.)
        :type location_name: str
        :param location_id: Filter the list of virtual extensions by location ID.
        :type location_id: str
        :param org_level_only: Filter the list of virtual extensions by organization level. If orgLevelOnly is true,
            return only the organization level virtual extensions.
        :type org_level_only: bool
        :param org_id: Unique identifier for the organization.
        :type org_id: str
        :return: Generator yielding :class:`GetVirtualExtensionObject` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if order is not None:
            params['order'] = order
        if extension is not None:
            params['extension'] = extension
        if phone_number is not None:
            params['phoneNumber'] = phone_number
        if name is not None:
            params['name'] = name
        if location_name is not None:
            params['locationName'] = location_name
        if location_id is not None:
            params['locationId'] = location_id
        if org_level_only is not None:
            params['orgLevelOnly'] = str(org_level_only).lower()
        url = self.ep('virtualExtensions')
        return self.session.follow_pagination(url=url, model=VirtualExtension, item_key='virtualExtensions',
                                              params=params)

    async def list_extensions(self, order: str = None, extension: str = None, phone_number: str = None,
                        name: str = None, location_name: str = None, location_id: str = None,
                        org_level_only: bool = None, org_id: str = None,
                        **params) -> List[VirtualExtension]:
        """
        Read the List of Virtual Extensions

        Retrieve virtual extensions associated with a specific customer.

        The GET Virtual Extensions API allows administrators to retrieve a list of virtual extensions configured within
        their organization. Virtual extensions enable users to dial extension numbers that route to external phone
        numbers, such as those of remote workers or frequently contacted clients.
        This API returns key information including the  extension, associated  phone number (in E.164 format), display
        name, and the location to which the virtual extension belongs
        The API supports filtering by various parameters, such as extension number, phone number, and location name.
        The results can be paginated using the `max` and `start` parameters, and the order of the results can be
        specified using the `order` parameter.

        Retrieving a Virtual Extension requires a full or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param order: Order the list of virtual extensions in ascending or descending order. Default is ascending.
        :type order: str
        :param extension: Filter the list of virtual extensions by extension number.
        :type extension: str
        :param phone_number: Filter the list of virtual extensions by phone number.
        :type phone_number: str
        :param name: Filter the list of virtual extensions by name. This can be either first name or last name.
        :type name: str
        :param location_name: Filter the list of virtual extensions by location name.(Only one of the locationName,
            locationId, and OrgLevelOnly query parameters is allowed at the same time.)
        :type location_name: str
        :param location_id: Filter the list of virtual extensions by location ID.
        :type location_id: str
        :param org_level_only: Filter the list of virtual extensions by organization level. If orgLevelOnly is true,
            return only the organization level virtual extensions.
        :type org_level_only: bool
        :param org_id: Unique identifier for the organization.
        :type org_id: str
        :return: Generator yielding :class:`GetVirtualExtensionObject` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if order is not None:
            params['order'] = order
        if extension is not None:
            params['extension'] = extension
        if phone_number is not None:
            params['phoneNumber'] = phone_number
        if name is not None:
            params['name'] = name
        if location_name is not None:
            params['locationName'] = location_name
        if location_id is not None:
            params['locationId'] = location_id
        if org_level_only is not None:
            params['orgLevelOnly'] = str(org_level_only).lower()
        url = self.ep('virtualExtensions')
        return [o async for o in self.session.follow_pagination(url=url, model=VirtualExtension, item_key='virtualExtensions',
                                              params=params)]

    async def create_extension(self, display_name: str, phone_number: str, extension: str, first_name: str = None,
                         last_name: str = None, location_id: str = None, org_id: str = None) -> str:
        """
        Create a Virtual Extension

        Create new Virtual Extension for the given organization or location.

        You can set up virtual extensions at the organization or location level. The organization level enables
        everyone across your organization to dial the same extension number to reach someone.
        You can use the location level virtual extension like any other extension assigned to the specific location.
        Users at the specific location can dial the extension. However, users at other locations can reach the virtual
        extension by dialing the ESN.

        Creating a virtual extension requires a full administrator or location administrator auth token with a scope of
        `spark-admin:telephony_config_write` and `identity:contacts_rw`.

        :param display_name: Display name of the person at the virtual extension.
        :type display_name: str
        :param phone_number: Directory number of the virtual extension.
        :type phone_number: str
        :param extension: Extension of the virtual extension.
        :type extension: str
        :param first_name: First name of the person at the virtual extension.
        :type first_name: str
        :param last_name: Last name of the person at the virtual extension.
        :type last_name: str
        :param location_id: ID of the location to which the virtual extension is assigned. The location ID is a unique
            identifier for the location in Webex Calling.
        :type location_id: str
        :param org_id: Unique identifier for the organization.
        :type org_id: str
        :rtype: str
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        if first_name is not None:
            body['firstName'] = first_name
        if last_name is not None:
            body['lastName'] = last_name
        body['displayName'] = display_name
        body['phoneNumber'] = phone_number
        body['extension'] = extension
        if location_id is not None:
            body['locationId'] = location_id
        url = self.ep('virtualExtensions')
        data = await super().post(url, params=params, json=body)
        r = data['id']
        return r

    async def validate_external_phone_number(self, phone_numbers: list[str], org_id: str = None) -> ValidatePhoneNumber:
        """
        Validate an external phone number

        Validate external phone number for the given organization.

        This API is designed to validate external phone numbers before they are assigned as virtual extensions for a
        customer.
        It ensures that the provided numbers are properly formatted, eligible for use, and not already in use within
        the system.
        This validation is typically part of a pre-check process during provisioning or number assignment workflows,
        helping administrators or systems prevent conflicts or errors related to number reuse or format issues.

        Creating a virtual extension requires a full administrator or location administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param phone_numbers: List of external phone numbers to be validated.
        :type phone_numbers: list[str]
        :param org_id: Unique identifier for the organization.
        :type org_id: str
        :rtype: ValidatePhoneNumber
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        body['phoneNumbers'] = phone_numbers
        url = self.ep('virtualExtensions/actions/validateNumbers/invoke')
        data = await super().post(url, params=params, json=body)
        r = ValidatePhoneNumber.model_validate(data)
        return r

    async def get_extension_settings(self, org_id: str = None) -> VirtualExtensionMode:
        """
        Get Virtual extension settings

        Retrieve Virtual Extension settings for the given Org.

        This API retrieves the virtual extension mode settings configured for a given organization. Virtual extensions
        can operate in two modes: STANDARD and ENHANCED. The selected mode determines how the system handles routing
        and signaling for virtual extensions.
        By default, the virtual extensions that you create use the Standard mode. Another mode, enhanced signaling
        mode, is available to all customers, however, virtual extensions won't function properly in this mode unless
        your PSTN provider supports special network signaling extensions and there aren't many PSTN providers that do.

        Retrieving a Virtual Extension settings requires a full or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param org_id: Unique identifier for the organization.
        :type org_id: str
        :rtype: VirtualExtensionMode
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep('virtualExtensions/settings')
        data = await super().get(url, params=params)
        r = VirtualExtensionMode.model_validate(data['mode'])
        return r

    async def modify_extension_settings(self, mode: VirtualExtensionMode, org_id: str = None):
        """
        Modify Virtual Extension Settings

        Update Virtual Extension details for the given extension ID.

        This endpoint updates the virtual extension settings for an organization. It is primarily used to configure the
        operating mode for virtual extensions.
        Modes determine how virtual extensions are assigned or managed within the system.

        Updating a Virtual Extension requires a full administrator or location administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param mode: Mode of the virtual extension. It can be either `STANDARD` or `ENHANCED`.

        + `STANDARD` -  Standard Virtual extension mode in which virtual extensions must be associated with a valid
        E.164 number, but this requires no enhanced signaling support from the PSTN provider.
        + `ENHANCED` - Enhanced signaling mode: only a few PSTN providers support this special network signaling
        extension.
        :type mode: VirtualExtensionMode
        :param org_id: Unique identifier for the organization.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        body['mode'] = enum_str(mode)
        url = self.ep('virtualExtensions/settings')
        await super().put(url, params=params, json=body)

    async def delete_extension(self, extension_id: str, org_id: str = None):
        """
        Delete a Virtual Extension

        Delete Virtual Extension using the extension ID.

        This API permanently deletes a virtual extension from the organization. Once deleted, the extension will no
        longer route calls to the external phone number, and users won’t be able to reach it via the assigned
        extension.

        Deleting a Virtual Extension requires a full administrator or location administrator auth token with a scope of
        `spark-admin:telephony_config_write` and `identity:contacts_rw`.

        :param extension_id: ID of the virtual extension.
        :type extension_id: str
        :param org_id: Unique identifier for the organization.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'virtualExtensions/{extension_id}')
        await super().delete(url, params=params)

    async def details_extension(self, extension_id: str, org_id: str = None) -> VirtualExtension:
        """
        Get a Virtual Extension

        Retrieve Virtual Extension details for the given extension ID.

        Virtual extensions integrate remote workers on separate telephony systems into Webex Calling, enabling users to
        reach them via extension dialing.
        This endpoint allows administrators to retrieve configuration details for a specific virtual extension,
        ensuring visibility into the mapping between extensions and external phone numbers.

        Retrieving a Virtual Extension requires a full or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param extension_id: ID of the virtual extension.
        :type extension_id: str
        :param org_id: Unique identifier for the organization.
        :type org_id: str
        :rtype: :class:`VirtualExtension`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'virtualExtensions/{extension_id}')
        data = await super().get(url, params=params)
        r = VirtualExtension.model_validate(data)
        return r

    async def update_extension(self, extension_id: str, first_name: str = None, last_name: str = None,
                         display_name: str = None, phone_number: str = None, extension: str = None,
                         org_id: str = None):
        """
        Update a Virtual Extension

        Update Virtual Extension details for the given extension ID.

        This API updates the configuration of an existing virtual extension identified by its unique extension ID.
        Administrators can modify fields such as the extension, associated phone number (in E.164 format), display
        name, and location etc.

        Updating a Virtual Extension requires a full administrator or location administrator auth token with a scope of
        `spark-admin:telephony_config_write` and `identity:contacts_rw`.

        :param extension_id: ID of the virtual extension.
        :type extension_id: str
        :param first_name: First name of the person at the virtual extension.
        :type first_name: str
        :param last_name: Last name of the person at the virtual extension.
        :type last_name: str
        :param display_name: Display name of the person at the virtual extension.
        :type display_name: str
        :param phone_number: Directory number of the virtual extension.
        :type phone_number: str
        :param extension: Extension of the virtual extension.
        :type extension: str
        :param org_id: Unique identifier for the organization.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        if first_name is not None:
            body['firstName'] = first_name
        if last_name is not None:
            body['lastName'] = last_name
        if display_name is not None:
            body['displayName'] = display_name
        if phone_number is not None:
            body['phoneNumber'] = phone_number
        if extension is not None:
            body['extension'] = extension
        url = self.ep(f'virtualExtensions/{extension_id}')
        await super().put(url, params=params, json=body)


class AsVirtualLinesApi(AsApiChild, base='telephony/config/virtualLines'):
    #: agent caller id Api
    agent_caller_id: AsAgentCallerIdApi
    #: Available numbers for a virtual line
    available_numbers: AsAvailableNumbersApi
    #: barge settings
    barge: AsBargeApi
    #: Call bridge settings
    call_bridge: AsCallBridgeApi
    #: call intercept settings
    call_intercept: AsCallInterceptApi
    #: call recording settings
    call_recording: AsCallRecordingApi
    #: call waiting settings
    call_waiting: AsCallWaitingApi
    #: caller id settings
    caller_id: AsCallerIdApi
    #: DND settings
    dnd: AsDndApi
    #: ECBN settings
    ecbn: AsECBNApi
    #: forwarding settings
    forwarding: AsPersonForwardingApi
    #: music on hold settings
    music_on_hold: AsMusicOnHoldApi
    #: incoming permissions
    permissions_in: AsIncomingPermissionsApi
    #: outgoing permissions
    permissions_out: AsOutgoingPermissionsApi
    #: Privacy Settings
    privacy: AsPrivacyApi
    #: Push to Talk Settings
    push_to_talk: AsPushToTalkApi
    #: Voicemail Settings
    voicemail: AsVoicemailApi

    def __init__(self, session):
        super().__init__(session=session)
        self.agent_caller_id = AsAgentCallerIdApi(session=session, selector=ApiSelector.virtual_line)
        self.available_numbers = AsAvailableNumbersApi(session=session, selector=ApiSelector.virtual_line)
        self.barge = AsBargeApi(session=session, selector=ApiSelector.virtual_line)
        self.call_bridge = AsCallBridgeApi(session=session, selector=ApiSelector.virtual_line)
        self.call_intercept = AsCallInterceptApi(session=session, selector=ApiSelector.virtual_line)
        self.call_recording = AsCallRecordingApi(session=session, selector=ApiSelector.virtual_line)
        self.call_waiting = AsCallWaitingApi(session=session, selector=ApiSelector.virtual_line)
        self.caller_id = AsCallerIdApi(session=session, selector=ApiSelector.virtual_line)
        self.dnd = AsDndApi(session=session, selector=ApiSelector.virtual_line)
        self.ecbn = AsECBNApi(session=session, selector=ApiSelector.virtual_line)
        self.forwarding = AsPersonForwardingApi(session=session, selector=ApiSelector.virtual_line)
        self.music_on_hold = AsMusicOnHoldApi(session=session, selector=ApiSelector.virtual_line)
        self.permissions_in = AsIncomingPermissionsApi(session=session, selector=ApiSelector.virtual_line)
        self.permissions_out = AsOutgoingPermissionsApi(session=session, selector=ApiSelector.virtual_line)
        self.privacy = AsPrivacyApi(session=session, selector=ApiSelector.virtual_line)
        self.push_to_talk = AsPushToTalkApi(session=session, selector=ApiSelector.virtual_line)
        self.voicemail = AsVoicemailApi(session=session, selector=ApiSelector.virtual_line)

    async def create(self, first_name: str, last_name: str, location_id: str, display_name: str = None,
               phone_number: str = None, extension: str = None, caller_id_last_name: str = None,
               caller_id_first_name: str = None, caller_id_number: str = None,
               org_id: str = None) -> str:
        """
        Create a Virtual Line

        Create new Virtual Line for the given location.

        Virtual line is a capability in Webex Calling that allows administrators to configure multiple lines to Webex
        Calling users.

        Creating a virtual line requires a full or user administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param first_name: First name defined for a virtual line. Minimum length is 1. Maximum length is 30.
        :type first_name: str
        :param last_name: Last name defined for a virtual line. Minimum length is 1. Maximum length is 30.
        :type last_name: str
        :param location_id: ID of location for virtual line.
        :type location_id: str
        :param display_name: Display name defined for a virtual line.
        :type display_name: str
        :param phone_number: Phone number of a virtual line. Minimum length is 1. Maximum length is 23. Either
            `phoneNumber` or `extension` is mandatory.
        :type phone_number: str
        :param extension: Extension of a virtual line. Minimum length is 2. Maximum length is 10. Either `phoneNumber`
            or `extension` is mandatory.
        :type extension: str
        :param caller_id_last_name: Last name used in the Calling Line ID and for dial-by-name functions. Minimum
            length is 1. Maximum length is 30.
        :type caller_id_last_name: str
        :param caller_id_first_name: First name used in the Calling Line ID and for dial-by-name functions. Minimum
            length is 1. Maximum length is 30.
        :type caller_id_first_name: str
        :param caller_id_number: Phone number to appear as the CLID for all calls. Minimum length is 1. Maximum length
            is 23.
        :type caller_id_number: str
        :param org_id: Create the virtual line for this organization.
        :type org_id: str
        :rtype: str
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        body['firstName'] = first_name
        body['lastName'] = last_name
        if display_name is not None:
            body['displayName'] = display_name
        if phone_number is not None:
            body['phoneNumber'] = phone_number
        if extension is not None:
            body['extension'] = extension
        body['locationId'] = location_id
        if caller_id_last_name is not None:
            body['callerIdLastName'] = caller_id_last_name
        if caller_id_first_name is not None:
            body['callerIdFirstName'] = caller_id_first_name
        if caller_id_number is not None:
            body['callerIdNumber'] = caller_id_number
        url = self.ep()
        data = await super().post(url, params=params, json=body)
        r = data['id']
        return r

    async def delete(self, virtual_line_id: str, org_id: str = None):
        """
        Delete a Virtual Line

        Delete the designated Virtual Line.

        Virtual line is a capability in Webex Calling that allows administrators to configure multiple lines to Webex
        Calling users.

        Deleting a virtual line requires a full or user administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param virtual_line_id: Delete the virtual line with the matching ID.
        :type virtual_line_id: str
        :param org_id: Delete the virtual line from this organization.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'{virtual_line_id}')
        await super().delete(url, params=params)

    async def details(self, virtual_line_id: str, org_id: str = None) -> VirtualLine:
        """
        Get Details for a Virtual Line

        Retrieve Virtual Line details.

        Virtual line is a capability in Webex Calling that allows administrators to configure multiple lines to Webex
        Calling users.

        Retrieving virtual line details requires a full or user or read-only administrator or location administrator
        auth token with a scope of `spark-admin:telephony_config_read`.

        :param virtual_line_id: Retrieve settings for a virtual line with the matching ID.
        :type virtual_line_id: str
        :param org_id: Retrieve virtual line settings from this organization.
        :type org_id: str
        :rtype: :class:`GetVirtualLineObject`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'{virtual_line_id}')
        data = await super().get(url, params=params)
        r = VirtualLine.model_validate(data)
        return r

    async def update(self, virtual_line_id: str, first_name: str = None, last_name: str = None,
               display_name: str = None, phone_number: str = None, extension: str = None,
               announcement_language: str = None, caller_id_last_name: str = None,
               caller_id_first_name: str = None, caller_id_number: str = None, time_zone: str = None,
               org_id: str = None):
        """
        Update a Virtual Line

        Update the designated Virtual Line.

        Virtual line is a capability in Webex Calling that allows administrators to configure multiple lines to Webex
        Calling users.

        Updating a virtual line requires a full or user or location administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param virtual_line_id: Update settings for a virtual line with the matching ID.
        :type virtual_line_id: str
        :param first_name: First name defined for a virtual line. Minimum length is 1. Maximum length is 30.
        :type first_name: str
        :param last_name: Last name defined for a virtual line. Minimum length is 1. Maximum length is 30.
        :type last_name: str
        :param display_name: Display name defined for a virtual line.
        :type display_name: str
        :param phone_number: Phone number of a virtual line. Minimum length is 1. Maximum length is 23. Either
            `phoneNumber` or `extension` is mandatory.
        :type phone_number: str
        :param extension: Extension of a virtual line. Minimum length is 2. Maximum length is 10. Either `phoneNumber`
            or `extension` is mandatory.
        :type extension: str
        :param announcement_language: Virtual Line's announcement language.
        :type announcement_language: str
        :param caller_id_last_name: Last name used in the Calling Line ID and for dial-by-name functions. Minimum
            length is 1. Maximum length is 30.
        :type caller_id_last_name: str
        :param caller_id_first_name: First name used in the Calling Line ID and for dial-by-name functions. Minimum
            length is 1. Maximum length is 30.
        :type caller_id_first_name: str
        :param caller_id_number: Phone number to appear as the CLID for all calls. Minimum length is 1. Maximum length
            is 23.
        :type caller_id_number: str
        :param time_zone: Time zone defined for the virtual line.
        :type time_zone: str
        :param org_id: Update virtual line settings from this organization.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        if first_name is not None:
            body['firstName'] = first_name
        if last_name is not None:
            body['lastName'] = last_name
        if display_name is not None:
            body['displayName'] = display_name
        if phone_number is not None:
            body['phoneNumber'] = phone_number
        if extension is not None:
            body['extension'] = extension
        if announcement_language is not None:
            body['announcementLanguage'] = announcement_language
        if caller_id_last_name is not None:
            body['callerIdLastName'] = caller_id_last_name
        if caller_id_first_name is not None:
            body['callerIdFirstName'] = caller_id_first_name
        if caller_id_number is not None:
            body['callerIdNumber'] = caller_id_number
        if time_zone is not None:
            body['timeZone'] = time_zone
        url = self.ep(f'{virtual_line_id}')
        await super().put(url, params=params, json=body)

    async def get_phone_number(self, virtual_line_id: str,
                         org_id: str = None) -> VirtualLineNumberPhoneNumber:
        """
        Get Phone Number assigned for a Virtual Line

        Get details on the assigned phone number and extension for the virtual line.

        Retrieving virtual line phone number details requires a full or user or read-only administrator auth token
        with
        a scope of `spark-admin:telephony_config_read`.

        :param virtual_line_id: Retrieve settings for a virtual line with the matching ID.
        :type virtual_line_id: str
        :param org_id: Retrieve virtual line settings from this organization.
        :type org_id: str
        :rtype: GetVirtualLineNumberObjectPhoneNumber
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'{virtual_line_id}/number')
        data = await super().get(url, params=params)
        r = VirtualLineNumberPhoneNumber.model_validate(data['phoneNumber'])
        return r

    async def update_directory_search(self, virtual_line_id: str, enabled: bool, org_id: str = None):
        """
        Update Directory search for a Virtual Line

        Update the directory search for a designated Virtual Line.

        Virtual line is a capability in Webex Calling that allows administrators to configure multiple lines to Webex
        Calling users.

        Updating Directory search for a virtual line requires a full or user administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param virtual_line_id: Update settings for a virtual line with the matching ID.
        :type virtual_line_id: str
        :param enabled: Whether or not the directory search for a virtual line is enabled.
        :type enabled: bool
        :param org_id: Update virtual line settings from this organization.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        body['enabled'] = enabled
        url = self.ep(f'{virtual_line_id}/directorySearch')
        await super().put(url, params=params, json=body)

    async def assigned_devices(self, virtual_line_id: str, org_id: str = None) -> VirtualLineDevices:
        """
        Get List of Devices assigned for a Virtual Line

        Retrieve Device details assigned for a virtual line.

        Virtual line is a capability in Webex Calling that allows administrators to configure multiple lines to Webex
        Calling users.

        Retrieving the assigned device detials for a virtual line requires a full or user or read-only administrator
        auth token with a scope of `spark-admin:telephony_config_read`.

        :param virtual_line_id: Retrieve settings for a virtual line with the matching ID.
        :type virtual_line_id: str
        :param org_id: Retrieve virtual line settings from this organization.
        :type org_id: str
        :rtype: :class:`VirtualLineDevices`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'{virtual_line_id}/devices')
        data = await super().get(url, params=params)
        r = VirtualLineDevices.model_validate(data)
        return r

    async def dect_networks(self, virtual_line_id: str, org_id: str = None) -> list[AssignedDectNetwork]:
        """
        Get List of Dect Networks Handsets for a Virtual Line

        Retrieve DECT Network details assigned for a virtual line.

        Virtual line is a capability in Webex Calling that allows administrators to configure multiple lines to Webex
        Calling users.

        Retrieving the assigned device detials for a virtual line requires a full or user or read-only administrator
        auth token with a scope of `spark-admin:telephony_config_read`.

        :param virtual_line_id: Retrieve settings for a virtual line with the matching ID.
        :type virtual_line_id: str
        :param org_id: Retrieve virtual line settings from this organization.
        :type org_id: str
        :rtype: list[AssignedDectNetwork]
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'{virtual_line_id}/dectNetworks')
        data = await super().get(url, params=params)
        r = TypeAdapter(list[AssignedDectNetwork]).validate_python(data['dectNetworks'])
        return r

    def list_gen(self, org_id: str = None, location_id: list[str] = None,
             id: list[str] = None, owner_name: list[str] = None, phone_number: list[str] = None,
             location_name: list[str] = None, order: list[str] = None, has_device_assigned: bool = None,
             has_extension_assigned: bool = None, has_dn_assigned: bool = None,
             **params) -> AsyncGenerator[VirtualLine, None, None]:
        """
        List all Virtual Lines for the organization.
        Virtual line is a capability in Webex Calling that allows administrators to configure multiple lines to Webex
        Calling users.
        Retrieving this list requires a full or read-only administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param org_id: List virtual lines for this organization.
        :type org_id: str
        :param location_id: Return the list of virtual lines matching these location ids. Example for multiple values -
            ?locationId=locId1&locationId=locId2.
        :type location_id: List[str]
        :param id: Return the list of virtual lines matching these virtualLineIds.
        :type id: List[str]
        :param owner_name: Return the list of virtual lines matching these owner names.
        :type owner_name: List[str]
        :param phone_number: Return the list of virtual lines matching these phone numbers.
        :type phone_number: List[str]
        :param location_name: Return the list of virtual lines matching the location names.
        :type location_name: List[str]
        :param order: Return the list of virtual lines based on the order. Default sort will be in an Ascending order.
            Maximum 3 orders allowed at a time.
        :type order: List[str]
        :param has_device_assigned: If true, includes only virtual lines with devices assigned. When not explicitly
            specified, the default includes both virtual lines with devices assigned and not assigned.
        :type has_device_assigned: bool
        :param has_extension_assigned: If true, includes only virtual lines with an extension assigned. When not
            explicitly specified, the default includes both virtual lines with extension assigned and not assigned.
        :type has_extension_assigned: bool
        :param has_dn_assigned: If true, includes only virtual lines with an assigned directory number, also known as a
            Dn. When not explicitly specified, the default includes both virtual lines with a Dn assigned and not
            assigned.
        :type has_dn_assigned: bool
        """
        if org_id is not None:
            params['orgId'] = org_id
        if location_id is not None:
            params['locationId'] = location_id
        if id is not None:
            params['id'] = id
        if owner_name is not None:
            params['ownerName'] = owner_name
        if phone_number is not None:
            params['phoneNumber'] = phone_number
        if location_name is not None:
            params['locationName'] = location_name
        if order is not None:
            params['order'] = order
        if has_device_assigned is not None:
            params['hasDeviceAssigned'] = has_device_assigned
        if has_extension_assigned is not None:
            params['hasExtensionAssigned'] = has_extension_assigned
        if has_dn_assigned is not None:
            params['hasDnAssigned'] = has_dn_assigned
        url = self.ep()
        return self.session.follow_pagination(url=url, model=VirtualLine, params=params, item_key='virtualLines')

    async def list(self, org_id: str = None, location_id: list[str] = None,
             id: list[str] = None, owner_name: list[str] = None, phone_number: list[str] = None,
             location_name: list[str] = None, order: list[str] = None, has_device_assigned: bool = None,
             has_extension_assigned: bool = None, has_dn_assigned: bool = None,
             **params) -> List[VirtualLine]:
        """
        List all Virtual Lines for the organization.
        Virtual line is a capability in Webex Calling that allows administrators to configure multiple lines to Webex
        Calling users.
        Retrieving this list requires a full or read-only administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param org_id: List virtual lines for this organization.
        :type org_id: str
        :param location_id: Return the list of virtual lines matching these location ids. Example for multiple values -
            ?locationId=locId1&locationId=locId2.
        :type location_id: List[str]
        :param id: Return the list of virtual lines matching these virtualLineIds.
        :type id: List[str]
        :param owner_name: Return the list of virtual lines matching these owner names.
        :type owner_name: List[str]
        :param phone_number: Return the list of virtual lines matching these phone numbers.
        :type phone_number: List[str]
        :param location_name: Return the list of virtual lines matching the location names.
        :type location_name: List[str]
        :param order: Return the list of virtual lines based on the order. Default sort will be in an Ascending order.
            Maximum 3 orders allowed at a time.
        :type order: List[str]
        :param has_device_assigned: If true, includes only virtual lines with devices assigned. When not explicitly
            specified, the default includes both virtual lines with devices assigned and not assigned.
        :type has_device_assigned: bool
        :param has_extension_assigned: If true, includes only virtual lines with an extension assigned. When not
            explicitly specified, the default includes both virtual lines with extension assigned and not assigned.
        :type has_extension_assigned: bool
        :param has_dn_assigned: If true, includes only virtual lines with an assigned directory number, also known as a
            Dn. When not explicitly specified, the default includes both virtual lines with a Dn assigned and not
            assigned.
        :type has_dn_assigned: bool
        """
        if org_id is not None:
            params['orgId'] = org_id
        if location_id is not None:
            params['locationId'] = location_id
        if id is not None:
            params['id'] = id
        if owner_name is not None:
            params['ownerName'] = owner_name
        if phone_number is not None:
            params['phoneNumber'] = phone_number
        if location_name is not None:
            params['locationName'] = location_name
        if order is not None:
            params['order'] = order
        if has_device_assigned is not None:
            params['hasDeviceAssigned'] = has_device_assigned
        if has_extension_assigned is not None:
            params['hasExtensionAssigned'] = has_extension_assigned
        if has_dn_assigned is not None:
            params['hasDnAssigned'] = has_dn_assigned
        url = self.ep()
        return [o async for o in self.session.follow_pagination(url=url, model=VirtualLine, params=params, item_key='virtualLines')]


class AsVoiceMessagingApi(AsApiChild, base='telephony/voiceMessages'):
    """
    Voice Messaging APIs provide support for handling voicemail and message waiting indicators in Webex Calling.  The
    APIs are limited to user access (no admin access), and all GET commands require the spark:calls_read scope, while
    the other commands require the spark:calls_write scope.
    """

    async def summary(self) -> MessageSummary:
        """
        Get a summary of the voicemail messages for the user.
        """
        url = self.ep('summary')
        data = await super().get(url=url)
        return MessageSummary.model_validate(data)

    def list_gen(self, **params) -> AsyncGenerator[VoiceMessageDetails, None, None]:
        """
        Get the list of all voicemail messages for the user.
        """
        url = self.ep()
        return self.session.follow_pagination(url=url, model=VoiceMessageDetails, params=params)

    async def list(self, **params) -> List[VoiceMessageDetails]:
        """
        Get the list of all voicemail messages for the user.
        """
        url = self.ep()
        return [o async for o in self.session.follow_pagination(url=url, model=VoiceMessageDetails, params=params)]

    async def delete(self, message_id: str):
        """
        Delete a specific voicemail message for the user.

        :param message_id: The message identifier of the voicemail message to delete
        :type message_id: str
        """
        url = self.ep(f'{message_id}')
        await super().delete(url=url)
        return

    async def mark_as_read(self, message_id: str):
        """
        Update the voicemail message(s) as read for the user.
        If the messageId is provided, then only mark that message as read.  Otherwise, all messages for the user are
        marked as read.

        :param message_id: The voicemail message identifier of the message to mark as read.  If the messageId is not
            provided, then all voicemail messages for the user are marked as read.
        :type message_id: str
        """
        body = {'messageId': message_id}
        url = self.ep('markAsRead')
        await super().post(url=url, json=body)
        return

    async def mark_as_unread(self, message_id: str):
        """
        Update the voicemail message(s) as unread for the user.
        If the messageId is provided, then only mark that message as unread.  Otherwise, all messages for the user are
        marked as unread.

        :param message_id: The voicemail message identifier of the message to mark as unread.  If the messageId is not
            provided, then all voicemail messages for the user are marked as unread.
        :type message_id: str
        """
        body = {'messageId': message_id}
        url = self.ep('markAsUnread')
        await super().post(url=url, json=body)
        return


class AsVoicePortalApi(AsApiChild, base='telephony/config/locations'):
    """
    location voice portal API
    """

    def _endpoint(self, *, location_id: str, path: str = None) -> str:
        """
        location specific
        telephony/config/locations/{locationId}/voicePortal

        :meta private:
        :param location_id: Unique identifier for the location.
        :type location_id: str
        :param path: additional path
        :type: path: str
        :return: full endpoint
        :rtype: str
        """
        path = path and f'/{path}' or ''
        ep = self.session.ep(f'telephony/config/locations/{location_id}/voicePortal{path}')
        return ep

    async def read(self, location_id: str, org_id: str = None) -> VoicePortalSettings:
        """
        Get VoicePortal

        Retrieve Voice portal information for the location.

        Voice portals provide an interactive voice response (IVR)
        system so administrators can manage auto attendant announcements.

        Retrieving voice portal information for an organization requires a full read-only administrator or location
        administrator auth token with a scope of `spark-admin:telephony_config_read`.

        :param location_id: Location to which the voice portal belongs.
        :type location_id: str
        :param org_id: Organization to which the voice portal belongs.
        :type org_id: str
        :return: location voice portal settings
        :rtype: VoicePortalSettings
        """
        params = org_id and {'orgId': org_id} or None
        url = self._endpoint(location_id=location_id)
        return VoicePortalSettings.model_validate(await self.get(url, params=params))

    async def update(self, location_id: str, settings: VoicePortalSettings, passcode: str = None, org_id: str = None):
        """
        Update VoicePortal

        Update Voice portal information for the location.

        Voice portals provide an interactive voice response (IVR) system so administrators can manage auto attendant
        announcements.

        Updating voice portal information for organization and/or rules requires a full administrator auth token with
        a scope of spark-admin:telephony_config_write.

        :param location_id: Location to which the voice portal belongs.
        :type location_id: str
        :param settings: new settings
        :type settings: VoicePortalSettings
        :param passcode: new passcode
        :type passcode: str
        :param org_id: Organization to which the voice portal belongs.
        :type org_id: str
        """
        data = json.loads(settings.model_dump_json(exclude={'portal_id': True,
                                                            'language': True}))
        if passcode is not None:
            data['passcode'] = {'newPasscode': passcode,
                                'confirmPasscode': passcode}
        params = org_id and {'orgId': org_id} or None
        url = self._endpoint(location_id=location_id)
        await self.put(url, params=params, json=data)

    def available_phone_numbers_gen(self, location_id: str, phone_number: list[str] = None,
                                org_id: str = None,
                                **params) -> AsyncGenerator[AvailableNumber, None, None]:
        """
        Get VoicePortal Available Phone Numbers

        List service and standard numbers that are available to be assigned as the location voice portal's phone
        number.
        These numbers are associated with the location specified in the request URL, can be active or inactive, and are
        unassigned.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full, read-only or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param location_id: Return the list of phone numbers for this location within the given organization. The
            maximum length is 36.
        :type location_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableNumber` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        url = self._endpoint(location_id=location_id, path='availableNumbers')
        return self.session.follow_pagination(url=url, model=AvailableNumber, item_key='phoneNumbers', params=params)

    async def available_phone_numbers(self, location_id: str, phone_number: list[str] = None,
                                org_id: str = None,
                                **params) -> List[AvailableNumber]:
        """
        Get VoicePortal Available Phone Numbers

        List service and standard numbers that are available to be assigned as the location voice portal's phone
        number.
        These numbers are associated with the location specified in the request URL, can be active or inactive, and are
        unassigned.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full, read-only or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param location_id: Return the list of phone numbers for this location within the given organization. The
            maximum length is 36.
        :type location_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableNumber` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        url = self._endpoint(location_id=location_id, path='availableNumbers')
        return [o async for o in self.session.follow_pagination(url=url, model=AvailableNumber, item_key='phoneNumbers', params=params)]

    async def passcode_rules(self, location_id: str, org_id: str = None) -> PasscodeRules:
        """
        Get VoicePortal Passcode Rule

        Retrieve the voice portal passcode rule for a location.

        Voice portals provide an interactive voice response (IVR) system so administrators can manage auto attendant
        announcements

        Retrieving the voice portal passcode rule requires a full read-only administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param location_id: Retrieve voice portal passcode rules for this location.
        :type location_id: str
        :param org_id: Retrieve voice portal passcode rules for this organization.
        :type org_id: str
        :return: passcode rules
        :rtype: PasscodeRules
        """
        params = org_id and {'orgId': org_id} or None
        url = self._endpoint(location_id=location_id, path='passcodeRules')
        return PasscodeRules.model_validate(await self.get(url, params=params))


class AsVoicemailGroupsApi(AsApiChild, base='telephony/config/voicemailGroups'):
    """
    API for voicemail groups
    """

    def ep(self, location_id: str = None, path: str = None):
        """
        :param location_id:
        :param path:
        :return:
        """
        path = path and f'/{path}' or ''
        if location_id is None:
            return super().ep(path)
        return self.session.ep(f'telephony/config/locations/{location_id}/voicemailGroups{path}')

    def list_gen(self, location_id: str = None, name: str = None, phone_number: str = None,
             org_id: str = None, **params) -> AsyncGenerator[VoicemailGroup, None, None]:
        """
        List the voicemail group information for the organization.

        You can create a shared voicemail box and inbound fax box to assign to users or call routing features like an
        auto attendant, call queue, or hunt group.

        Retrieving voicemail Group for the organization requires a full read-only administrator auth token with a
        scope of spark-admin:telephony_config_read.

        :param location_id: Location to which the voicemail group belongs.
        :type location_id: str
        :param name: Search (Contains) based on voicemail group name
        :type name: str
        :param phone_number: Search (Contains) based on number or extension
        :type phone_number: str
        :param org_id: Organization to which the voicemail group belongs.
        :type org_id: str
        :return: yields ::class::`VoicemailGroup` instances
        """
        params.update((to_camel(p), v) for p, v in locals().items() if p not in {'self', 'params'} and v is not None)
        url = self.ep()
        return self.session.follow_pagination(url=url, model=VoicemailGroup, params=params, item_key='voicemailGroups')

    async def list(self, location_id: str = None, name: str = None, phone_number: str = None,
             org_id: str = None, **params) -> List[VoicemailGroup]:
        """
        List the voicemail group information for the organization.

        You can create a shared voicemail box and inbound fax box to assign to users or call routing features like an
        auto attendant, call queue, or hunt group.

        Retrieving voicemail Group for the organization requires a full read-only administrator auth token with a
        scope of spark-admin:telephony_config_read.

        :param location_id: Location to which the voicemail group belongs.
        :type location_id: str
        :param name: Search (Contains) based on voicemail group name
        :type name: str
        :param phone_number: Search (Contains) based on number or extension
        :type phone_number: str
        :param org_id: Organization to which the voicemail group belongs.
        :type org_id: str
        :return: yields ::class::`VoicemailGroup` instances
        """
        params.update((to_camel(p), v) for p, v in locals().items() if p not in {'self', 'params'} and v is not None)
        url = self.ep()
        return [o async for o in self.session.follow_pagination(url=url, model=VoicemailGroup, params=params, item_key='voicemailGroups')]

    async def details(self, location_id: str, voicemail_group_id: str, org_id: str = None) -> VoicemailGroupDetail:
        """
        Retrieve voicemail group details for a location.

        Manage your voicemail group settings for a specific location, like when you want your voicemail to be active,
        message storage settings, and how you would like to be notified of new voicemail messages.

        Retrieving voicemail group details requires a full, user or read-only administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param location_id: Retrieve voicemail group details for this location.
        :type location_id: str
        :param voicemail_group_id: Retrieve voicemail group details for this voicemail group ID.
        :type voicemail_group_id: str
        :param org_id: Retrieve voicemail group details for a customer location.
        :type org_id: str
        :return: Voicemail group settings
        :type: :class:`VoicemailGroupDetail`
        """
        params = org_id and {'orgId': org_id} or None
        url = self.ep(location_id, voicemail_group_id)
        data = await self.get(url=url, params=params)
        return VoicemailGroupDetail.model_validate(data)

    async def update(self, location_id: str, voicemail_group_id: str, settings: VoicemailGroupDetail, org_id: str = None):
        """
        Modifies the voicemail group location details for a particular location for a customer.

        Manage your voicemail settings, like when you want your voicemail to be active, message storage settings, and
        how you would like to be notified of new voicemail messages.

        Modifying the voicemail group location details requires a full, user administrator auth token with a scope
        of spark-admin:telephony_config_write.

        :param location_id: Modifies the voicemail group details for this location.
        :type location_id: str
        :param voicemail_group_id: Modifies the voicemail group details for this voicemail group ID.
        :type voicemail_group_id: str
        :param settings: New settings
        :type settings: :class:`VoicemailGroupDetail`
        :param org_id: Modifies the voicemail group details for a customer location.
        :type org_id: str
        """
        params = org_id and {'orgId': org_id} or None
        url = self.ep(location_id, voicemail_group_id)
        body = settings.for_update()
        await self.put(url=url, json=body, params=params)

    async def create(self, location_id: str, settings: VoicemailGroupDetail, org_id: str = None) -> str:
        """
        Create new voicemail group for the given location for a customer.

        Voicemail group can be created for given location for a customer.

        Creating voicemail group for the given location requires a full or user administrator auth token with a scope
        of spark-admin:telephony_config_write.

        :param location_id: Create new voice mail group for this location.
        :type location_id: str
        :param settings: settings for new voicemail group
            Example:

            .. code-block:: python

                settings = VoicemailGroupDetail.create(
                                        name=vmg_name, extension=extension,
                                        first_name='first', last_name='last',
                                        passcode=740384)
                vmg_id = api.telephony.voicemail_groups.create(location_id=location_id,
                                                               settings=settings)

        :type settings: :class:`VoicemailGroupDetail`
        :param org_id: Create new voice mail group for this organization.
        :type org_id: str
        :return: UUID of the newly created voice mail group.
        :rtype: str
        """
        body = settings.for_create()
        params = org_id and {'orgId': org_id} or None
        url = self.ep(location_id)
        data = await self.post(url=url, json=body, params=params)
        return data['id']

    async def delete(self, location_id: str, voicemail_group_id: str, org_id: str = None):
        """
        Delete the designated voicemail group.

        Deleting a voicemail group requires a full administrator auth token with a scope
        of spark-admin:telephony_config_write.

        :param location_id: Location from which to delete a voicemail group.
        :type location_id: str
        :param voicemail_group_id: Delete the voicemail group with the matching ID.
        :type voicemail_group_id: str
        :param org_id: Delete the voicemail group from this organization.
        :type org_id: str
        """
        url = self.ep(location_id, voicemail_group_id)
        await super().delete(url=url)

    def fax_message_available_phone_numbers_gen(self, location_id: str, phone_number: List[str] = None,
                                            org_id: str = None,
                                            **params) -> AsyncGenerator[AvailableNumber, None, None]:
        """
        Get Voicemail Group Fax Message Available Phone Numbers

        List service and standard numbers that are available to be assigned as a voicemail group's FAX message phone
        number.
        These numbers are associated with the location specified in the request URL, can be active or inactive, and are
        unassigned.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full, read-only or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param location_id: Return the list of phone numbers for this location within the given organization. The
            maximum length is 36.
        :type location_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableNumber` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        url = self.ep(location_id=location_id, path='faxMessage/availableNumbers')
        return self.session.follow_pagination(url=url, model=AvailableNumber, item_key='phoneNumbers', params=params)

    async def fax_message_available_phone_numbers(self, location_id: str, phone_number: List[str] = None,
                                            org_id: str = None,
                                            **params) -> List[AvailableNumber]:
        """
        Get Voicemail Group Fax Message Available Phone Numbers

        List service and standard numbers that are available to be assigned as a voicemail group's FAX message phone
        number.
        These numbers are associated with the location specified in the request URL, can be active or inactive, and are
        unassigned.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full, read-only or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param location_id: Return the list of phone numbers for this location within the given organization. The
            maximum length is 36.
        :type location_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableNumber` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        url = self.ep(location_id=location_id, path='faxMessage/availableNumbers')
        return [o async for o in self.session.follow_pagination(url=url, model=AvailableNumber, item_key='phoneNumbers', params=params)]

    def available_phone_numbers_gen(self, location_id: str, phone_number: List[str] = None,
                                org_id: str = None,
                                **params) -> AsyncGenerator[AvailableNumber, None, None]:
        """
        Get Voicemail Group Available Phone Numbers

        List service and standard numbers that are available to be assigned as a voicemail group's phone number.
        These numbers are associated with the location specified in the request URL, can be active or inactive, and are
        unassigned.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full, read-only or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param location_id: Return the list of phone numbers for this location within the given organization. The
            maximum length is 36.
        :type location_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableNumber` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        url = self.ep(location_id=location_id, path='availableNumbers')
        return self.session.follow_pagination(url=url, model=AvailableNumber, item_key='phoneNumbers', params=params)

    async def available_phone_numbers(self, location_id: str, phone_number: List[str] = None,
                                org_id: str = None,
                                **params) -> List[AvailableNumber]:
        """
        Get Voicemail Group Available Phone Numbers

        List service and standard numbers that are available to be assigned as a voicemail group's phone number.
        These numbers are associated with the location specified in the request URL, can be active or inactive, and are
        unassigned.

        The available numbers APIs help identify candidate numbers and their owning entities to simplify the assignment
        or association of these numbers to members or features.

        Retrieving this list requires a full, read-only or location administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param location_id: Return the list of phone numbers for this location within the given organization. The
            maximum length is 36.
        :type location_id: str
        :param phone_number: Filter phone numbers based on the comma-separated list provided in the `phoneNumber`
            array.
        :type phone_number: list[str]
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: Generator yielding :class:`AvailableNumber` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if phone_number is not None:
            params['phoneNumber'] = ','.join(phone_number)
        url = self.ep(location_id=location_id, path='availableNumbers')
        return [o async for o in self.session.follow_pagination(url=url, model=AvailableNumber, item_key='phoneNumbers', params=params)]


class AsVoicemailRulesApi(AsApiChild, base='telephony/config/voicemail/rules'):
    """
    API for voicemail rules settings
    """

    async def read(self, org_id: str = None) -> VoiceMailRules:
        """
        Get Voicemail Rules

        Retrieve the organization's voicemail rules.

        Organizational voicemail rules specify the default passcode requirements.

        Retrieving the organization's voicemail rules requires a full, user or read-only administrator auth token with
        a scope of spark-admin:telephony_config_read.

        :param org_id: Retrieve voicemail settings for this organization.
        :type org_id: str
        :return: VM settings
        :rtype: OrganisationVoicemailSettings
        """
        params = org_id and {'orgId': org_id} or None
        url = self.ep()
        return VoiceMailRules.model_validate(await self.get(url, params=params))

    async def update(self, settings: VoiceMailRules, org_id: str = None):
        """
        Update Voicemail Rules

        Update the organization's default voicemail passcode and/or rules.

        Organizational voicemail rules specify the default passcode requirements.

        If you choose to set default passcode for new people added to your organization, communicate to your people
        what that passcode is, and that it must be reset before they can access their voicemail. If this feature is
        not turned on, each new person must initially set their own passcode.

        Updating organization's voicemail passcode and/or rules requires a full administrator auth token with a scope
        of spark-admin:telephony_config_write.

        :param settings: new settings
        :type settings: VoiceMailRules
        :param org_id: Update voicemail rules for this organization.
        :type org_id: str
        """
        params = org_id and {'orgId': org_id} or None
        url = self.ep()
        data = settings.model_dump_json(exclude={'default_voicemail_pin_rules': True})
        await self.put(url, params=params, data=data)


class AsTelephonyApi(AsApiChild, base='telephony/config'):
    """
    The telephony settings (features) API.
    """
    #: access or authentication codes at location level
    access_codes: AsLocationAccessCodesApi
    announcements_repo: AsAnnouncementsRepositoryApi
    auto_attendant: AsAutoAttendantApi
    #: location call intercept settings
    call_intercept: AsLocationInterceptApi
    call_routing: AsCallRoutingApi
    calls: AsCallsApi
    callpark: AsCallParkApi
    callpark_extension: AsCallparkExtensionApi
    callqueue: AsCallQueueApi
    call_recording: AsCallRecordingSettingsApi
    conference: AsConferenceControlsApi
    cx_essentials: AsCustomerExperienceEssentialsApi
    dect_devices: AsDECTDevicesApi
    #: WxC device operations
    devices: AsTelephonyDevicesApi
    emergency_address: AsEmergencyAddressApi
    #: emergency services
    emergency_services: AsOrgEmergencyServicesApi
    guest_calling: AsGuestCallingApi
    hotdesk: AsHotDeskApi
    hotdesking_voiceportal: AsHotDeskingSigninViaVoicePortalApi
    huntgroup: AsHuntGroupApi
    jobs: AsJobsApi
    #: location specific settings
    location: AsTelephonyLocationApi
    locations: AsTelephonyLocationApi
    ms_teams: AsOrgMSTeamsSettingApi
    operating_modes: AsOperatingModesApi
    #: organisation access codes
    organisation_access_codes: AsOrganisationAccessCodesApi
    #: organisation voicemail settings
    organisation_voicemail: AsOrganisationVoicemailSettingsAPI
    paging: AsPagingApi
    #: location level outgoing permissions
    permissions_out: AsOutgoingPermissionsApi
    pickup: AsCallPickupApi
    playlist: AsPlayListApi
    pnc: AsPrivateNetworkConnectApi
    prem_pstn: AsPremisePstnApi
    pstn: AsPSTNApi
    schedules: AsScheduleApi
    supervisors: AsSupervisorApi
    virtual_extensions: AsVirtualExtensionsApi
    virtual_lines: AsVirtualLinesApi
    # location voicemail groups
    voicemail_groups: AsVoicemailGroupsApi
    voicemail_rules: AsVoicemailRulesApi
    voice_messaging: AsVoiceMessagingApi
    voiceportal: AsVoicePortalApi

    def __init__(self, session: AsRestSession):
        super().__init__(session=session)
        self.access_codes = AsLocationAccessCodesApi(session=session)
        self.announcements_repo = AsAnnouncementsRepositoryApi(session=session)
        self.auto_attendant = AsAutoAttendantApi(session=session)
        self.call_intercept = AsLocationInterceptApi(session=session)
        self.call_routing = AsCallRoutingApi(session=session)
        self.call_recording = AsCallRecordingSettingsApi(session=session)
        self.calls = AsCallsApi(session=session)
        self.callpark = AsCallParkApi(session=session)
        self.callpark_extension = AsCallparkExtensionApi(session=session)
        self.callqueue = AsCallQueueApi(session=session)
        self.conference = AsConferenceControlsApi(session=session)
        self.cx_essentials = AsCustomerExperienceEssentialsApi(session=session)
        self.dect_devices = AsDECTDevicesApi(session=session)
        self.devices = AsTelephonyDevicesApi(session=session)
        self.emergency_address = AsEmergencyAddressApi(session=session)
        self.emergency_services = AsOrgEmergencyServicesApi(session=session)
        self.guest_calling = AsGuestCallingApi(session=session)
        self.hotdesk = AsHotDeskApi(session=session)
        self.hotdesking_voiceportal = AsHotDeskingSigninViaVoicePortalApi(session=session)
        self.huntgroup = AsHuntGroupApi(session=session)
        self.jobs = AsJobsApi(session=session)
        self.location = AsTelephonyLocationApi(session=session)
        self.locations = self.location
        self.ms_teams = AsOrgMSTeamsSettingApi(session=session)
        self.operating_modes = AsOperatingModesApi(session=session)
        self.organisation_access_codes = AsOrganisationAccessCodesApi(session=session)
        self.organisation_voicemail = AsOrganisationVoicemailSettingsAPI(session=session)
        self.paging = AsPagingApi(session=session)
        self.permissions_out = AsOutgoingPermissionsApi(session=session, selector=ApiSelector.location)
        self.pickup = AsCallPickupApi(session=session)
        self.playlist = AsPlayListApi(session=session)
        self.pnc = AsPrivateNetworkConnectApi(session=session)
        self.prem_pstn = AsPremisePstnApi(session=session)
        self.pstn = AsPSTNApi(session=session)
        self.schedules = AsScheduleApi(session=session, base=ScheduleApiBase.locations)
        self.supervisors = AsSupervisorApi(session=session)
        self.virtual_extensions = AsVirtualExtensionsApi(session=session)
        self.virtual_lines = AsVirtualLinesApi(session=session)
        self.voicemail_groups = AsVoicemailGroupsApi(session=session)
        self.voicemail_rules = AsVoicemailRulesApi(session=session)
        self.voice_messaging = AsVoiceMessagingApi(session=session)
        self.voiceportal = AsVoicePortalApi(session=session)

    def phone_numbers_gen(self, location_id: str = None, phone_number: str = None, available: bool = None,
                      order: str = None,
                      owner_name: str = None, owner_id: str = None, owner_type: OwnerType = None,
                      extension: str = None, number_type: NumberType = None,
                      phone_number_type: NumberListPhoneNumberType = None,
                      state: NumberState = None, details: bool = None, toll_free_numbers: bool = None,
                      restricted_non_geo_numbers: bool = None,
                      included_telephony_type: TelephonyType = None,
                      service_number: bool = None,
                      org_id: str = None, **params) -> AsyncGenerator[NumberListPhoneNumber, None, None]:
        """
        Get Phone Numbers for an Organization with Given Criterias

        List all the phone numbers for the given organization along with the status and owner (if any).

        Numbers can be standard, service, or mobile. Both standard and service numbers are PSTN numbers.
        Service numbers are considered as high-utilization or high-concurrency phone numbers and can be assigned to
        features like auto-attendants, call queues, and hunt groups.
        Phone numbers can be linked to a specific location, be active or inactive, and be assigned or unassigned.
        The owner of a number is the person, workspace, or feature to which the number is assigned.
        Only a person can own a mobile number.

        Retrieving this list requires a full or read-only administrator or location administrator auth token with a
        scope of `spark-admin:telephony_config_read`.

        :param location_id: Return the list of phone numbers for this location within the given organization.
        :type location_id: str
        :param phone_number: Search for this phone number.
        :type phone_number: str
        :param available: Search among the available phone numbers. This parameter cannot be used along with owner_type
            parameter when set to true.
        :type available: bool
        :param order: Sort the list of phone numbers based on the following:lastName,dn,extension. Default sort will
            be based on number and extension in an Ascending order
        :type order: str
        :param owner_name: Return the list of phone numbers that is owned by given owner name. Maximum length is 255.
        :type owner_name: str
        :param owner_id: Returns only the matched number/extension entries assigned to the feature with specified
            uuid/broadsoftId.
        :type owner_id: str
        :param owner_type: Returns the list of phone numbers that are of given owner_type.
        :type owner_type: OwnerType
        :param extension: Returns the list of PSTN phone numbers with given extension.
        :type extension: str
        :param number_type: Returns the filtered list of phone numbers that contain a given type of number. `available`
            or `state` query parameters cannot be used when `numberType=EXTENSION`.
        :type number_type: NumberType
        :param phone_number_type: Returns the filtered list of PSTN phone numbers that are of given phoneNumberType.
        :type phone_number_type: NumberListPhoneNumberType
        :param state: Returns the list of PSTN phone numbers with matching state.
        :type state: NumberState
        :param details: Returns the overall count of the PSTN phone numbers along with other details for given
            organization.
        :type details: bool
        :param toll_free_numbers: Returns the list of toll free phone numbers.
        :type toll_free_numbers: bool
        :param restricted_non_geo_numbers: Returns the list of restricted non geographical numbers.
        :type restricted_non_geo_numbers: bool
        :param included_telephony_type: Returns the list of phone numbers that are of given `includedTelephonyType`.
            By default if this query parameter is not provided, it will list both PSTN and Mobile Numbers. Possible
            input values are PSTN_NUMBER, MOBILE_NUMBER.
        :type included_telephony_type: TelephonyType
        :param service_number: Returns the list of service phone numbers.
        :type service_number: bool
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: yields :class:`NumberListPhoneNumber` instances
        """
        params.update((to_camel(p), v) for i, (p, v) in enumerate(locals().items())
                      if i and v is not None and p != 'params')
        # parameter is actually called included_telephony_types
        if itp := params.pop('includedTelephonyType', None):
            params['includedTelephonyTypes'] = itp
        for param, value in params.items():
            if isinstance(value, bool):
                value = 'true' if value else 'false'
                params[param] = value
            elif isinstance(value, Enum):
                value = value.value
                params[param] = value
        url = self.ep(path='numbers')
        # noinspection PyTypeChecker
        return self.session.follow_pagination(url=url, model=NumberListPhoneNumber, params=params,
                                              item_key='phoneNumbers')

    async def phone_numbers(self, location_id: str = None, phone_number: str = None, available: bool = None,
                      order: str = None,
                      owner_name: str = None, owner_id: str = None, owner_type: OwnerType = None,
                      extension: str = None, number_type: NumberType = None,
                      phone_number_type: NumberListPhoneNumberType = None,
                      state: NumberState = None, details: bool = None, toll_free_numbers: bool = None,
                      restricted_non_geo_numbers: bool = None,
                      included_telephony_type: TelephonyType = None,
                      service_number: bool = None,
                      org_id: str = None, **params) -> List[NumberListPhoneNumber]:
        """
        Get Phone Numbers for an Organization with Given Criterias

        List all the phone numbers for the given organization along with the status and owner (if any).

        Numbers can be standard, service, or mobile. Both standard and service numbers are PSTN numbers.
        Service numbers are considered as high-utilization or high-concurrency phone numbers and can be assigned to
        features like auto-attendants, call queues, and hunt groups.
        Phone numbers can be linked to a specific location, be active or inactive, and be assigned or unassigned.
        The owner of a number is the person, workspace, or feature to which the number is assigned.
        Only a person can own a mobile number.

        Retrieving this list requires a full or read-only administrator or location administrator auth token with a
        scope of `spark-admin:telephony_config_read`.

        :param location_id: Return the list of phone numbers for this location within the given organization.
        :type location_id: str
        :param phone_number: Search for this phone number.
        :type phone_number: str
        :param available: Search among the available phone numbers. This parameter cannot be used along with owner_type
            parameter when set to true.
        :type available: bool
        :param order: Sort the list of phone numbers based on the following:lastName,dn,extension. Default sort will
            be based on number and extension in an Ascending order
        :type order: str
        :param owner_name: Return the list of phone numbers that is owned by given owner name. Maximum length is 255.
        :type owner_name: str
        :param owner_id: Returns only the matched number/extension entries assigned to the feature with specified
            uuid/broadsoftId.
        :type owner_id: str
        :param owner_type: Returns the list of phone numbers that are of given owner_type.
        :type owner_type: OwnerType
        :param extension: Returns the list of PSTN phone numbers with given extension.
        :type extension: str
        :param number_type: Returns the filtered list of phone numbers that contain a given type of number. `available`
            or `state` query parameters cannot be used when `numberType=EXTENSION`.
        :type number_type: NumberType
        :param phone_number_type: Returns the filtered list of PSTN phone numbers that are of given phoneNumberType.
        :type phone_number_type: NumberListPhoneNumberType
        :param state: Returns the list of PSTN phone numbers with matching state.
        :type state: NumberState
        :param details: Returns the overall count of the PSTN phone numbers along with other details for given
            organization.
        :type details: bool
        :param toll_free_numbers: Returns the list of toll free phone numbers.
        :type toll_free_numbers: bool
        :param restricted_non_geo_numbers: Returns the list of restricted non geographical numbers.
        :type restricted_non_geo_numbers: bool
        :param included_telephony_type: Returns the list of phone numbers that are of given `includedTelephonyType`.
            By default if this query parameter is not provided, it will list both PSTN and Mobile Numbers. Possible
            input values are PSTN_NUMBER, MOBILE_NUMBER.
        :type included_telephony_type: TelephonyType
        :param service_number: Returns the list of service phone numbers.
        :type service_number: bool
        :param org_id: List numbers for this organization.
        :type org_id: str
        :return: yields :class:`NumberListPhoneNumber` instances
        """
        params.update((to_camel(p), v) for i, (p, v) in enumerate(locals().items())
                      if i and v is not None and p != 'params')
        # parameter is actually called included_telephony_types
        if itp := params.pop('includedTelephonyType', None):
            params['includedTelephonyTypes'] = itp
        for param, value in params.items():
            if isinstance(value, bool):
                value = 'true' if value else 'false'
                params[param] = value
            elif isinstance(value, Enum):
                value = value.value
                params[param] = value
        url = self.ep(path='numbers')
        # noinspection PyTypeChecker
        return [o async for o in self.session.follow_pagination(url=url, model=NumberListPhoneNumber, params=params,
                                              item_key='phoneNumbers')]

    async def phone_number_details(self, org_id: str = None) -> NumberDetails:
        """
        get summary (counts) of phone numbers

        :param org_id: details for numbers in this organization.
        :type org_id: str
        :return: phone number details
        :rtype: :class:`NumberDetails`
        """
        params = {to_camel(p): v for i, (p, v) in enumerate(locals().items())
                  if i and v is not None}
        params['details'] = 'true'
        params['max'] = 1
        url = self.ep(path='numbers')
        data = await self.get(url, params=params)
        return NumberDetails.model_validate(data['count'])

    async def validate_extensions(self, extensions: list[str]) -> ValidateExtensionsResponse:
        """
        Validate the List of Extensions

        Validates the list of Extensions provided by the customer at the organization level. It checks the extension
        meets the current extension length limits and does not conflict with the extensions of organization-level
        entities and settings. To check for extension use across all locations, use the `Get Phone Numbers
        <https://developer.webex.com/v1/telephony/config/numbers>`_ API. To
        validate an extension and check for conflicts for a specific location, use the `Validate Extensions
        <https://developer.webex.com/docs/api/v1/location-call-settings/validate-extensions>`_ API.

        Retrieving this list requires a full or read-only administrator or location administrator auth token with a
        scope of `spark-admin:telephony_config_read`.

        :param extensions: Array of Strings of ID of Extensions.
        :type extensions: list[str]
        :return: validation response
        :rtype: :class:`wxc_sdk.common.ValidateExtensionsResponse`
        """
        url = self.ep(path='actions/validateExtensions/invoke')
        data = await self.post(url, json={'extensions': extensions})
        return ValidateExtensionsResponse.model_validate(data)

    async def validate_phone_numbers(self, phone_numbers: list[str], org_id: str = None) -> ValidatePhoneNumbersResponse:
        """
        Validate phone numbers

        Validate the list of phone numbers in an organization. Each phone number's availability is indicated in the
        response.

        Each location has a set of phone numbers that can be assigned to people, workspaces, or features. Phone numbers
        must follow E.164 format for all countries, except for the United States, which can also follow the National
        format. Active phone numbers are in service.

        Validating a phone number in an organization requires a full administrator auth token with a scope
        of spark-admin:telephony_config_write.

        :param phone_numbers: List of phone numbers to be validated.
        :type phone_numbers: list[str]
        :param org_id: Organization of the Route Group.
        :type org_id: str
        :return: validation result
        :rtype: :class:`wxc_sdk.common.ValidatePhoneNumbersResponse`
        """
        url = self.ep('actions/validateNumbers/invoke')
        body = {'phoneNumbers': phone_numbers}
        params = org_id and {'orgId': org_id} or None
        data = await self.post(url=url, params=params, json=body)
        return ValidatePhoneNumbersResponse.model_validate(data)

    async def ucm_profiles(self, org_id: str = None) -> list[UCMProfile]:
        """
        Read the List of UC Manager Profiles

        List all calling UC Manager Profiles for the organization.

        UC Manager Profiles are applicable if your organization uses Jabber in Team Messaging mode or Calling in Webex
        (Unified CM).

        The UC Manager Profile has an organization-wide default and may be overridden for individual persons, although
        currently only setting at a user level is supported by Webex APIs.

        Retrieving this list requires a full or read-only administrator auth token with a scope
        of `spark-admin:people_read` as this API is designed to be used in conjunction with calling behavior at the user
        level.

        :param org_id: List manager profiles in this organization.
        :type org_id: str
        :return: list of :class:`UCMProfile`
        """
        params = org_id and {'orgId': org_id} or None
        url = self.ep(path='callingProfiles')
        data = await self.get(url, params=params)
        return TypeAdapter(list[UCMProfile]).validate_python(data['callingProfiles'])

    def route_choices_gen(self, route_group_name: str = None, trunk_name: str = None, order: str = None,
                      org_id: str = None) -> AsyncGenerator[RouteIdentity, None, None]:
        """
        Read the List of Routing Choices

        List all Routes for the organization.

        Trunk and Route Group qualify as Route. Trunks and Route Groups provide you the ability to configure Webex
        Calling to manage calls between Webex Calling hosted users and premises PBX(s) users. This solution lets you
        configure users to use Cloud PSTN (CCP or Cisco PSTN) or Premises-based PSTN.

        Retrieving this list requires a full or read-only administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param route_group_name: Return the list of route identities matching the route group name.
        :param trunk_name: Return the list of route identities matching the trunk name.
        :param order: Order the route identities according to the designated fields.
            Available sort fields: routeName, routeType.
        :param org_id: List route identities for this organization.
        :return:
        """
        params = {to_camel(p): v for i, (p, v) in enumerate(locals().items())
                  if i and v is not None}
        url = self.ep('routeChoices')
        # noinspection PyTypeChecker
        return self.session.follow_pagination(url=url, model=RouteIdentity, params=params, item_key='routeIdentities')

    async def route_choices(self, route_group_name: str = None, trunk_name: str = None, order: str = None,
                      org_id: str = None) -> List[RouteIdentity]:
        """
        Read the List of Routing Choices

        List all Routes for the organization.

        Trunk and Route Group qualify as Route. Trunks and Route Groups provide you the ability to configure Webex
        Calling to manage calls between Webex Calling hosted users and premises PBX(s) users. This solution lets you
        configure users to use Cloud PSTN (CCP or Cisco PSTN) or Premises-based PSTN.

        Retrieving this list requires a full or read-only administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param route_group_name: Return the list of route identities matching the route group name.
        :param trunk_name: Return the list of route identities matching the trunk name.
        :param order: Order the route identities according to the designated fields.
            Available sort fields: routeName, routeType.
        :param org_id: List route identities for this organization.
        :return:
        """
        params = {to_camel(p): v for i, (p, v) in enumerate(locals().items())
                  if i and v is not None}
        url = self.ep('routeChoices')
        # noinspection PyTypeChecker
        return [o async for o in self.session.follow_pagination(url=url, model=RouteIdentity, params=params, item_key='routeIdentities')]

    async def test_call_routing(self, originator_id: str, originator_type: OriginatorType, destination: str,
                          originator_number: str = None, include_applied_services: bool = None,
                          org_id: str = None) -> TestCallRoutingResult:
        """
        Test Call Routing

        Validates that an incoming call can be routed.

        Dial plans route calls to on-premises destinations by use of trunks or route groups.
        They are configured globally for an enterprise and apply to all users, regardless of location.
        A dial plan also specifies the routing choice (trunk or route group) for calls that match any of its dial
        patterns.
        Specific dial patterns can be defined as part of your dial plan.

        Test call routing requires a full administrator auth token with a scope of
        `spark-admin:telephony_config_write`.

        :param originator_id: This element is used to identify the originating party. It can be a person ID or a trunk
            ID.
        :type originator_id: str
        :param originator_type: This element is used to identify if the `originatorId` is of type `PEOPLE` or `TRUNK`.
        :type originator_type: OriginatorType
        :param destination: This element specifies the called party. It can be any dialable string, for example, an ESN
            number, E.164 number, hosted user DN, extension, extension with location code, URL, or FAC code.
        :type destination: str
        :param originator_number: Only used when `originatorType` is `TRUNK`. The `originatorNumber` can be a phone
            number or URI.
        :type originator_number: str
        :param include_applied_services: This element is used to retrieve if any translation pattern, call intercept,
            permission by type or permission by digit pattern is present for the called party.
        :type include_applied_services: bool
        :param org_id: Organization in which we are validating a call routing.
        :type org_id: str
        :rtype: :class:`TestCallRoutingResult`
        """
        params = org_id and {'orgId': org_id} or None
        body = dict()
        body['originatorId'] = originator_id
        body['originatorType'] = enum_str(originator_type)
        if originator_number is not None:
            body['originatorNumber'] = originator_number
        body['destination'] = destination
        if include_applied_services is not None:
            body['includeAppliedServices'] = include_applied_services
        url = self.ep('actions/testCallRouting/invoke')
        data = await self.post(url=url, params=params, json=body)
        return TestCallRoutingResult.model_validate(data)

    async def supported_devices(self, allow_configure_layout_enabled: bool = None, type_: str = None,
                          org_id: str = None) -> SupportedDevices:
        """
        Read the List of Supported Devices

        Gets the list of supported devices for an organization.

        Retrieving this list requires a full or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param allow_configure_layout_enabled: List supported devices that allow the user to configure the layout.
        :type allow_configure_layout_enabled: bool
        :param type_: List supported devices of a specific type. To excluded device types from a request or query, add
            `type=not:DEVICE_TYPE`. For example, `type=not:MPP`.
        :type type_: str
        :param org_id: List supported devices for an organization.
        :type org_id: str
        :rtype: SupportedDevices
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        if allow_configure_layout_enabled is not None:
            params['allowConfigureLayoutEnabled'] = str(allow_configure_layout_enabled).lower()
        if type is not None:
            params['type'] = type_
        url = self.ep('supportedDevices')
        data = await self.get(url=url, params=params)
        return SupportedDevices.model_validate(data)

    async def device_settings(self, org_id: str = None) -> DeviceCustomization:
        """
        Get device override settings for an organization.

        Retrieving this list requires a full or read-only administrator auth token with a scope
        of spark-admin:telephony_config_read.

        :param org_id: List supported devices for an organization location.
        :type org_id: str
        :return: device customization response
        :rtype: DeviceCustomization
        """
        params = org_id and {'orgId': org_id} or None
        url = self.ep('devices/settings')
        data = await self.get(url=url, params=params)
        return DeviceCustomization.model_validate(data)

    async def read_list_of_announcement_languages(self) -> list[AnnouncementLanguage]:
        """
        Read the List of Announcement Languages

        List all languages supported by Webex Calling for announcements and voice prompts.

        Retrieving announcement languages requires a full or read-only administrator or location administrator auth
        token with a scope of `spark-admin:telephony_config_read`.
        """
        url = self.ep('announcementLanguages')
        data = await super().get(url=url)
        return TypeAdapter(list[AnnouncementLanguage]).validate_python(data["languages"])

    async def read_moh(self, org_id: str = None) -> MoHConfig:
        """
        Get the organization Music on Hold configuration

        Retrieve the organization's Music on Hold settings.

        :param org_id: Retrieve Music on Hold settings for this organization.
        :type org_id: str
        :rtype: MoHConfig
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep('moh/settings')
        data = await super().get(url, params=params)
        return MoHConfig.model_validate(data)

    async def update_moh(self, settings: MoHConfig, org_id: str = None):
        """
        Update the organization Music on Hold configuration

        Update the organization's Music on Hold settings.

        :param settings: Music on Hold settings
        :type settings: MoHConfig
        :param org_id: Patch Music on Hold for this organization.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = settings.model_dump(mode='json', by_alias=True, exclude_none=True)
        url = self.ep('moh/settings')
        await super().put(url, params=params, json=body)

    async def create_a_call_token(self, called_number: str, guest_name: str = None) -> str:
        """
        Create a call token

        Create a call token before initiating the click-to-call interaction.
        The call token embeds information related to click-to-call interaction into an encrypted JWE token.
        This token is used along with the `guest token
        <https://developer.webex.com/docs/api/v1/guests-management/create-a-guest>`_ to initialise the web calling SDK

        Creating a call token requires a service app access token with a scope of `spark:webrtc_calling`.

        :param called_number: Number to be called. This number should be enabled as guest calling destination at
            `Webex Control Hub
            <https://admin.webex.com>`_ by the administrator.
        :type called_number: str
        :param guest_name: Name of the guest caller.
        :type guest_name: str
        :rtype: str
        """
        body = dict()
        body['calledNumber'] = called_number
        if guest_name is not None:
            body['guestName'] = guest_name
        url = self.session.ep('telephony/click2call/callToken')
        data = await super().post(url, json=body)
        r = data['callToken']
        return r

    async def get_call_captions_settings(self, org_id: str = None) -> OrgCallCaptions:
        """
        Get the organization call captions settings

        Retrieve the organization's call captions settings.

        The call caption feature allows the customer to enable and manage closed captions and transcript functionality
        (rolling caption panel) in Webex Calling, without requiring the user to escalate the call to a meeting.

        This API requires a full or read-only administrator auth token with a scope of
        `spark-admin:telephony_config_read`.

        :param org_id: Unique identifier for the organization.
        :type org_id: str
        :rtype: :class:`OrgCallCaptions`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep('callCaptions')
        data = await super().get(url, params=params)
        r = OrgCallCaptions.model_validate(data)
        return r

    async def update_call_captions_settings(self, settings: OrgCallCaptions, org_id: str = None):
        """
        Update the organization call captions settings

        Update the organization's call captions settings.

        The call caption feature allows the customer to enable and manage closed captions and transcript functionality
        (rolling caption panel) in Webex Calling, without requiring the user to escalate the call to a meeting.

        This API requires a full administrator auth token with a scope of `spark-admin:telephony_config_write`.

        :param settings: Organization call captions settings
        :type settings: OrgCallCaptions
        :param org_id: Unique identifier for the organization.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = settings.update()
        url = self.ep('callCaptions')
        await super().put(url, params=params, json=body)


class AsWebhookApi(AsApiChild, base='webhooks'):
    """
    API for webhook management
    """

    def list_gen(self, owned_by: str = None, **params) -> AsyncGenerator[Webhook, None, None]:
        """
        List Webhooks

        List all of your webhooks.

        :param owned_by: Limit the result list to org wide webhooks. Only allowed value is `org`.
        :type owned_by: str
        :return: Generator yielding :class:`Webhook` instances
        """
        if owned_by is not None:
            params['ownedBy'] = owned_by
        url = self.ep()
        # noinspection PyTypeChecker
        return self.session.follow_pagination(url=url, model=Webhook, item_key='items', params=params)

    async def list(self, owned_by: str = None, **params) -> List[Webhook]:
        """
        List Webhooks

        List all of your webhooks.

        :param owned_by: Limit the result list to org wide webhooks. Only allowed value is `org`.
        :type owned_by: str
        :return: Generator yielding :class:`Webhook` instances
        """
        if owned_by is not None:
            params['ownedBy'] = owned_by
        url = self.ep()
        # noinspection PyTypeChecker
        return [o async for o in self.session.follow_pagination(url=url, model=Webhook, item_key='items', params=params)]

    async def create(self, name: str, target_url: str, resource: WebhookResource, event: WebhookEventType, filter: str = None,
               secret: str = None,
               owned_by: str = None) -> Webhook:
        """
        Creates a webhook.

        To learn more about how to create and use webhooks, see The `Webhooks Guide
        <https://developer.webex.com/docs/api/guides/webhooks>`_.

        :param name: A user-friendly name for the webhook.
        :type name: str
        :param target_url: The URL that receives POST requests for each event.
        :type target_url: str
        :param resource: The resource type for the webhook. Creating a webhook requires 'read' scope on the resource
            the webhook is for.
        :type resource: WebhookResource
        :param event: The event type for the webhook.
        :type event: WebhookEvent
        :param filter: Filter that defines the webhook scope. See `Filtering Webhooks
            <https://developer.webex.com/docs/api/guides/webhooks#filtering-webhooks>`_ for more information. Please note
            that if a filter of `hostEmail`, `hostUserId`, `ownerEmail` or `ownerId` is specified, `ownedBy` must be
            set to `org`.
        :type filter: str
        :param secret: The secret used to generate payload signature.
        :param secret: str
        :param owned_by: Specify `org` when creating an org/admin level webhook. Supported for `meetings`,
            `recordings`, `convergedRecordings`,`meetingParticipants`, `meetingTranscripts`, `videoMeshAlerts`,
            `controlHubAlerts`, `rooms`, `messaging` and `adminBatchJobs` (for Compliance Officers and messages with
            file attachments only - see `inline file DLP
            <https://developer.webex.com/docs/api/guides/webex-real-time-file-dlp-basics>`_) resources.
        :param owned_by: str
        :return: the new webhook
        """
        body = dict()
        body['name'] = name
        body['targetUrl'] = target_url
        body['resource'] = enum_str(resource)
        body['event'] = enum_str(event)
        if filter is not None:
            body['filter'] = filter
        if secret is not None:
            body['secret'] = secret
        if owned_by is not None:
            body['ownedBy'] = owned_by
        ep = self.ep()
        data = await self.post(ep, json=body)
        result = Webhook.model_validate(data)
        return result

    async def details(self, webhook_id: str) -> Webhook:
        """
        Get Webhook Details

        Shows details for a webhook, by ID.

        :param webhook_id: The unique identifier for the webhook.
        :type webhook_id: str
        :return: Webhook details
        """
        url = self.ep(webhook_id)
        return Webhook.model_validate(await self.get(url))

    async def update(self, webhook_id: str, update: Webhook) -> Webhook:
        """
        Updates a webhook, by ID. You cannot use this call to deactivate a webhook, only to activate a webhook that
        was auto deactivated. The fields that can be updated are name, targetURL, secret and status. All other fields,
        if supplied, are ignored.

        :param webhook_id: The unique identifier for the webhook.
        :type webhook_id: str
        :param update: The webhook update
        :type update: Webhook
        :return: updated :class:`Webhook` object
        """
        url = self.ep(webhook_id)
        webhook_data = update.model_dump(mode='json', include={'name', 'target_url', 'secret', 'owned_by', 'status'},
                                         exclude_unset=True)
        return Webhook.model_validate(await self.put(url, data=webhook_data))

    async def webhook_delete(self, webhook_id: str):
        """
        Deletes a webhook, by ID.

        :param webhook_id: The unique identifier for the webhook.
        :type webhook_id: str
        :return: None
        """
        ep = self.ep(f'{webhook_id}')
        await self.delete(ep)


class AsWorkspaceLocationFloorApi(AsApiChild, base='workspaceLocations'):
    # noinspection PyMethodOverriding
    def ep(self, location_id: str, floor_id: str = None):
        path = f'{location_id}/floors'
        if floor_id:
            path = f'{path}/{floor_id}'
        return super().ep(path=path)

    def list_gen(self, location_id: str, org_id: str = None) -> AsyncGenerator[WorkspaceLocationFloor, None, None]:
        """

        :param location_id:
        :param org_id:
        :return:
        """
        url = self.ep(location_id=location_id)
        params = org_id and {'orgId': org_id} or None
        return self.session.follow_pagination(url=url, model=WorkspaceLocationFloor, params=params, item_key='items')

    async def list(self, location_id: str, org_id: str = None) -> List[WorkspaceLocationFloor]:
        """

        :param location_id:
        :param org_id:
        :return:
        """
        url = self.ep(location_id=location_id)
        params = org_id and {'orgId': org_id} or None
        return [o async for o in self.session.follow_pagination(url=url, model=WorkspaceLocationFloor, params=params, item_key='items')]

    async def create(self, location_id: str, floor_number: int, display_name: str = None,
               org_id: str = None) -> WorkspaceLocationFloor:
        """
        Create a Workspace Location Floor

        Create a new floor in the given location. The displayName parameter is optional, and omitting it will result
        in the creation of a floor without that value set.

        :param location_id: A unique identifier for the location.
        :param floor_number:
        :param display_name:
        :type location_id: str
        :param org_id:
        :type org_id: str
        :return: new workspace location floor
        :rtype: WorkspaceLocationFloor
        """
        logging.warning('use of the workspace locations API is not recommended. use locations API instead')
        body = {to_camel(p): v for p, v in locals().items()
                if p not in {'self', 'location_id', 'org_id'} and v is not None}
        url = self.ep(location_id=location_id)
        params = org_id and {'orgId': org_id} or None
        data = await self.post(url=url, params=params, json=body)
        return WorkspaceLocationFloor.model_validate(data)

    async def details(self, location_id: str, floor_id: str, org_id: str = None) -> WorkspaceLocationFloor:
        """
        Get a Workspace Location Floor Details

        Shows details for a floor, by ID. Specify the floor ID in the floorId parameter in the URI.

        :param location_id: A unique identifier for the location.
        :type location_id: str
        :param floor_id: A unique identifier for the floor.
        :type floor_id: str
        :param org_id:
        :type org_id: str
        :return: workspace location floor details
        :rtype: WorkspaceLocationFloor
        """
        url = self.ep(location_id=location_id, floor_id=floor_id)
        params = org_id and {'orgId': org_id} or None
        data = await self.get(url=url, params=params)
        return WorkspaceLocationFloor.model_validate(data)

    async def update(self, location_id: str, floor_id: str, settings: WorkspaceLocationFloor,
               org_id: str = None) -> WorkspaceLocationFloor:
        """
        Updates details for a floor, by ID. Specify the floor ID in the floorId parameter in the URI. Include all
        details for the floor that are present in a Get Workspace Location Floor Details. Not including the optional
        displayName field will result in the field no longer being defined for the floor.

        :param location_id: A unique identifier for the location.
        :type location_id: str
        :param floor_id: A unique identifier for the floor.
        :type floor_id: str
        :param settings: new settings
        :type settings: WorkspaceLocationFloor
        :param org_id:
        :type org_id: str
        :return: updated workspace location floor
        """
        logging.warning('use of the workspace locations API is not recommended. use locations API instead')
        data = settings.model_dump_json(exclude_none=True, exclude_unset=True, exclude={'id', 'location_id'})
        url = self.ep(location_id=location_id, floor_id=floor_id)
        params = org_id and {'orgId': org_id} or None
        data = await self.put(url=url, data=data, params=params)
        return WorkspaceLocationFloor.model_validate(data)

    async def delete(self, location_id: str, floor_id: str, org_id: str = None):
        """
        Delete a Workspace Location Floor
        Deletes a floor, by ID.

        :param location_id: A unique identifier for the location.
        :type location_id: str
        :param floor_id: A unique identifier for the floor.
        :type floor_id: str
        :param org_id:
        :type org_id: str
        """
        logging.warning('use of the workspace locations API is not recommended. use locations API instead')
        url = self.ep(location_id=location_id, floor_id=floor_id)
        params = org_id and {'orgId': org_id} or None
        await super().delete(url=url, params=params)


class AsWorkspaceLocationApi(AsApiChild, base='workspaceLocations'):
    #: Workspace location floor API :class:`AsWorkspaceLocationFloorApi`
    floors: AsWorkspaceLocationFloorApi

    def __init__(self, *, session: AsRestSession, base: str = None):
        super().__init__(session=session, base=base)
        self.floors = AsWorkspaceLocationFloorApi(session=session)

    def ep(self, location_id: str = None):
        logging.warning('use of the workspace locations API is not recommended. use locations API instead')
        return super().ep(path=location_id)

    def list_gen(self, display_name: str = None, address: str = None, country_code: str = None, city_name: str = None,
             org_id: str = None, **params) -> AsyncGenerator[WorkspaceLocation, None, None]:
        """
        List workspace locations

        :param display_name: Location display name.
        :type display_name: str
        :param address: Location address
        :type address: str
        :param country_code: Location country code (ISO 3166-1).
        :type country_code: str
        :param city_name: Location city name.
        :type city_name: str
        :param org_id: Organization id
        :type org_id: str
        :param params: addtl. parameters
        :return: generator of :class:`WorkspaceLocation` instances
        """
        params.update((to_camel(p), v) for p, v in locals().items()
                      if p not in {'self', 'params'} and v is not None)
        url = self.ep()
        return self.session.follow_pagination(url=url, model=WorkspaceLocation, params=params, item_key='items')

    async def list(self, display_name: str = None, address: str = None, country_code: str = None, city_name: str = None,
             org_id: str = None, **params) -> List[WorkspaceLocation]:
        """
        List workspace locations

        :param display_name: Location display name.
        :type display_name: str
        :param address: Location address
        :type address: str
        :param country_code: Location country code (ISO 3166-1).
        :type country_code: str
        :param city_name: Location city name.
        :type city_name: str
        :param org_id: Organization id
        :type org_id: str
        :param params: addtl. parameters
        :return: generator of :class:`WorkspaceLocation` instances
        """
        params.update((to_camel(p), v) for p, v in locals().items()
                      if p not in {'self', 'params'} and v is not None)
        url = self.ep()
        return [o async for o in self.session.follow_pagination(url=url, model=WorkspaceLocation, params=params, item_key='items')]

    async def create(self, display_name: str, address: str, country_code: str, latitude: float, longitude: float,
               city_name: str = None, notes: str = None, org_id: str = None) -> WorkspaceLocation:
        """
        Create a location. The cityName and notes parameters are optional, and omitting them will result in the
        creation of a location without these values set.

        :param display_name: A friendly name for the location.
        :param address: The location address.
        :param country_code: The location country code (ISO 3166-1).
        :param latitude: The location latitude.
        :param longitude: The location longitude.
        :param city_name: The location city name.
        :param notes: Notes associated to the location.
        :param org_id:
        :return: created workspace location
        :rtype: WorkspaceLocation
        """
        logging.warning('use of the workspace locations API is not recommended. use locations API instead')
        body = {to_camel(p): v for p, v in locals().items()
                if p not in {'self', 'org_id'} and v is not None}
        params = org_id and {'orgId': org_id} or None
        url = self.ep()
        data = await self.post(url=url, json=body, params=params)
        return WorkspaceLocation.model_validate(data)

    async def details(self, location_id: str, org_id: str = None) -> WorkspaceLocation:
        """
        Get a Workspace Location Details
        Shows details for a location, by ID. Specify the location ID in the locationId parameter in the URI.

        :param location_id: A unique identifier for the location.
        :type location_id: str
        :param org_id:
        :type org_id: str
        :return: Workspace location details
        :rtype: WorkspaceLocation
        """
        params = org_id and {'orgId': org_id} or None
        url = self.ep(location_id=location_id)
        data = await self.get(url=url, params=params)
        return WorkspaceLocation.model_validate(data)

    async def update(self, location_id: str, settings: WorkspaceLocation, org_id: str = None) -> WorkspaceLocation:
        """
        Update a Workspace Location
        Updates details for a location, by ID. Specify the location ID in the locationId parameter in the URI.
        Include all details for the location that are present in a Get Workspace Location Details. Not including the
        optional cityName or notes fields (setting them to None) will result in the fields no longer being defined for
        the location.

        :param location_id: A unique identifier for the location.
        :type location_id: str
        :param settings: new settings
        :type settings: WorkspaceLocation
        :param org_id:
        :type org_id: str
        :return: updated workspace location
        :rtype: WorkspaceLocation
        """
        logging.warning('use of the workspace locations API is not recommended. use locations API instead')
        params = org_id and {'orgId': org_id} or None
        url = self.ep(location_id=location_id)
        body = settings.model_dump_json(exclude_none=True, exclude_unset=True, exclude={'id'})
        data = await self.put(url=url, data=body, params=params)
        return WorkspaceLocation.model_validate(data)

    async def delete(self, location_id: str, org_id: str = None):
        """
        Delete a Workspace Location
        Deletes a location, by ID. The workspaces associated to that location will no longer have a location, but a new
        location can be reassigned to them.
        """
        logging.warning('use of the workspace locations API is not recommended. use locations API instead')
        params = org_id and {'orgId': org_id} or None
        url = self.ep(location_id=location_id)
        await super().delete(url=url, params=params)


class AsWorkspacePersonalizationApi(AsApiChild, base='workspaces'):
    """
    Workspace Personalization

    **This API collection applies only to Webex Edge registered devices.**

    The Workspace Personalization API is designed to help administrators enable Personal Mode for `Webex Edge
    <https://help.webex.com/en-us/article/cy2l2z/Webex-Edge-for-Devices>`_ registered
    devices. This one-time operation allows for end users to receive calls and meeting notifications directly on their
    device, without needing to pair. This API aids with the process of the migration from on-premise to
    cloud-registered personal mode systems.

    For the personalization of a device to be successful, the following requirements must be satisfied:

    - The workspace must contain a single, Webex Edge registered, shared mode device.

    - The workspace must not have any calendars configured.

    - The device belonging to the workspace must be online.

    Invoking this API requires the `spark-admin:devices_write`, `spark:xapi_commands`, `spark:xapi_statuses` and
    `Identity:one_time_password` scopes.
    """

    async def personalize_a_workspace(self, workspace_id: str, email: str):
        """
        Personalize a Workspace

        Initializes the personalization for a given workspace for the user email provided.

        The personalization process is asynchronous and thus a background task is created when this endpoint is
        invoked.
        After successful invocation of this endpoint a personalization task status URL will be returned in the
        `Location` header, which will point to the `Get Personalization Task
        <https://developer.webex.com/docs/api/v1/workspace-personalization/get-personalization-task>`_ endpoint for this workspace.
        The task should be completed in approximately 30 seconds.

        :param workspace_id: A unique identifier for the workspace.
        :type workspace_id: str
        :param email: The user that the device will become personalised for.
        :type email: str
        :rtype: None
        """
        body = dict()
        body['email'] = email
        url = self.ep(f'{workspace_id}/personalize')
        await super().post(url, json=body)

    async def get_personalization_task(self, workspace_id: str) -> WorkspacePersonalizationTaskResponse:
        """
        Get Personalization Task

        Returns the status of a personalization task for a given workspace.

        Whilst in progress the endpoint will return `Accepted` and provide a `Retry-After` header indicating the number
        of seconds a client should wait before retrying.

        Upon completion of the task, the endpoint will return `OK` with a body detailing if the personalization was
        successful and an error description if appropriate.

        :param workspace_id: A unique identifier for the workspace.
        :type workspace_id: str
        :rtype: :class:`WorkspacePersonalizationTaskResponse`
        """
        url = self.ep(f'{workspace_id}/personalizationTask')
        data = await super().get(url)
        r = WorkspacePersonalizationTaskResponse.model_validate(data)
        return r


class AsAnonCallsApi(AsPersonSettingsApiChild):
    """
    API for anonymous call reject settings; so far only used for workspaces
    """

    feature = 'anonymousCallReject'

    async def read(self, entity_id: str, org_id: str = None) -> bool:
        """
        Retrieve Anonymous Call Settings for an entity.

        Anonymous Call Rejection, when enabled, blocks all incoming calls from unidentified or blocked caller IDs.

        **NOTE**: This API is only available for professional licensed workspaces.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param org_id: ID of the organization within which the entity resides. Only admin users of another
            organization (such as partners) may use this parameter as the default is the same organization as the
            token used to access API.
        :type org_id: str
        :rtype: bool
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.f_ep(entity_id)
        data = await super().get(url, params=params)
        r = data['enabled']
        return r

    async def configure(self, entity_id: str, enabled: bool, org_id: str = None):
        """
        Modify Anonymous Call Settings for an entity.

        Anonymous Call Rejection, when enabled, blocks all incoming calls from unidentified or blocked caller IDs.

        **NOTE**: This API is only available for professional licensed workspaces.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param enabled: `true` if the Anonymous Call Rejection feature is enabled.
        :type enabled: bool
        :param org_id: ID of the organization within which the entity resides. Only admin users of another
            organization (such as partners) may use this parameter as the default is the same organization as the
            token used to access API.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        body['enabled'] = enabled
        url = self.f_ep(entity_id)
        await super().put(url, params=params, json=body)


class AsCallPolicyApi(AsPersonSettingsApiChild):
    """
    API for call policy settings.

    For now only used for workspaces
    """

    feature = 'callPolicies'

    async def read(self, entity_id: str,
             org_id: str = None) -> PrivacyOnRedirectedCalls:
        """
        Read Call Policy Settings for an entity

        Retrieve Call Policies settings.

        The call policy feature enables administrator to configure call policy settings such as Connected Line
        Identification Privacy on Redirected Calls for a professional workspace.

        This API requires a full or read-only administrator or location administrator auth token with a scope of
        `spark-admin:workspaces_read` scope can be used to read workspace settings.

        **NOTE**: This API is only available for professional licensed workspaces.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param org_id: ID of the organization in which the entity resides. Only admin users of another organization
            (such as partners) may use this parameter as the default is the same organization as the token used to
            access API.
        :type org_id: str
        :rtype: PrivacyOnRedirectedCalls
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.f_ep(entity_id)
        data = await super().get(url, params=params)
        r = TypeAdapter(PrivacyOnRedirectedCalls).validate_python(data['connectedLineIdPrivacyOnRedirectedCalls'])
        return r

    async def configure(self, entity_id: str,
                  connected_line_id_privacy_on_redirected_calls: PrivacyOnRedirectedCalls,
                  org_id: str = None):
        """
        Configure Call Policy Settings for an entity

        Configure Call Policies settings.

        The call policy feature enables administrator to configure call policy settings such as Connected Line
        Identification Privacy on Redirected Calls for a professional workspace.

        This API requires a full or user administrator or location administrator auth token with the
        `spark-admin:workspaces_write` scope can be used to update workspace settings.

        **NOTE**: This API is only available for professional licensed workspaces.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param connected_line_id_privacy_on_redirected_calls: Specifies the connection type to be used.
        :type connected_line_id_privacy_on_redirected_calls: PrivacyOnRedirectedCalls
        :param org_id: ID of the organization in which the entity resides. Only admin users of another organization
            (such as partners) may use this parameter as the default is the same organization as the token used to
            access API.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        body['connectedLineIdPrivacyOnRedirectedCalls'] = enum_str(connected_line_id_privacy_on_redirected_calls)
        url = self.f_ep(entity_id)
        await super().put(url, params=params, json=body)


class AsPriorityAlertApi(AsPersonSettingsApiChild):
    """
    API for priority alert settings

    For now only used for workspaces
    """

    feature = 'priorityAlert'

    async def read_criteria(self, entity_id: str, id: str,
                      org_id: str = None) -> PriorityAlertCriteria:
        """
        Retrieve Priority Alert Criteria for a Workspace

        Retrieve Priority Alert Criteria Settings for a Workspace.

        The priority alert feature enables administrators to configure priority alert settings for a professional
        workspace.

        Priority Alert Criteria (Schedules) can also be set up to alert these phones during certain times of the day or
        days of the week.

        **NOTE**: This API is only available for professional licensed workspaces.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param id: Unique identifier for the criteria.
        :type id: str
        :param org_id: ID of the organization within which the entity resides. Only admin users of another
            organization (such as partners) may use this parameter as the default is the same organization as the
            token used to access API.
        :type org_id: str
        :rtype: :class:`PriorityAlertCriteria`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.f_ep(entity_id, f'criteria/{id}')
        data = await super().get(url, params=params)
        r = PriorityAlertCriteria.model_validate(data)
        return r

    async def configure_criteria(self, entity_id: str, id: str, settings: PriorityAlertCriteria,
                           org_id: str = None):
        """
        Modify Priority Alert Criteria for a Workspace

        Modify Priority Alert Criteria Settings for a Workspace.

        The priority alert feature enables administrators to configure priority alert settings for a professional
        workspace.

        Priority Alert Criteria (Schedules) can also be set up to alert these phones during certain times of the day or
        days of the week.

        **NOTE**: This API is only available for professional licensed workspaces.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param id: Unique identifier for the criteria.
        :type id: str
        :param settings: new settings to be applied.
        :type settings: PriorityAlertCriteria
        :param org_id: ID of the organization within which the entity resides. Only admin users of another
            organization (such as partners) may use this parameter as the default is the same organization as the
            token used to access API.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = settings.update()

        url = self.f_ep(entity_id, f'criteria/{id}')
        await super().put(url, params=params, json=body)

    async def delete_criteria(self, entity_id: str, id: str, org_id: str = None):
        """
        Delete Priority Alert Criteria for a Workspace

        Delete Priority Alert criteria Settings for a workspace.

        The priority alert feature enables administrators to configure priority alert settings for a professional
        workspace.

        Priority Alert Criteria (Schedules) can also be set up to alert these phones during certain times of the day or
        days of the week.

        **NOTE**: This API is only available for professional licensed workspaces.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param id: Unique identifier for the criteria.
        :type id: str
        :param org_id: ID of the organization within which the entity resides. Only admin users of another
            organization (such as partners) may use this parameter as the default is the same organization as the
            token used to access API.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.f_ep(entity_id, f'criteria/{id}')
        await super().delete(url, params=params)

    async def create_criteria(self, entity_id: str, settings: PriorityAlertCriteria, org_id: str = None) -> str:
        """
        Create Priority Alert Criteria for a Workspace

        Create Priority Alert Criteria Settings for a Workspace.

        The priority alert feature enables administrators to configure priority alert settings for a professional
        workspace.

        Priority Alert Criteria (Schedules) can also be set up to alert these phones during certain times of the day or
        days of the week.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param settings: new settings to be applied.
        :type settings: PriorityAlertCriteria
        :param org_id: ID of the organization within which the entity resides. Only admin users of another
            organization (such as partners) may use this parameter as the default is the same organization as the
            token used to access API.
        :type org_id: str
        :rtype: str
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = settings.update()
        url = self.f_ep(entity_id, 'criteria')
        data = await super().post(url, params=params, json=body)
        r = data['id']
        return r

    async def read(self, entity_id: str,
             org_id: str = None) -> PriorityAlert:
        """
        Retrieve Priority Alert Settings for a Workspace.

        The priority alert feature enables administrators to configure priority alert settings for a professional
        workspace.

        **NOTE**: This API is only available for professional licensed workspaces.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param org_id: ID of the organization within which the entity resides. Only admin users of another
            organization (such as partners) may use this parameter as the default is the same organization as the
            token used to access API.
        :type org_id: str
        :rtype: :class:`PriorityAlert`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.f_ep(entity_id)
        data = await super().get(url, params=params)
        r = PriorityAlert.model_validate(data)
        return r

    async def configure(self, entity_id: str, settings: PriorityAlert,
                  org_id: str = None):
        """
        Configure Priority Alert Settings for a Workspace

        Configure a workspace Priority Alert Settings.

        The priority alert feature enables administrator to configure priority alert settings for a professional
        workspace.

        **NOTE**: This API is only available for professional licensed workspaces.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param settings: Settings for new criteria
        :type settings: PriorityAlert
        :param org_id: ID of the organization within which the entity resides. Only admin users of another
            organization (such as partners) may use this parameter as the default is the same organization as the
            token used to access API.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = settings.update()
        url = self.f_ep(entity_id)
        await super().put(url, params=params, json=body)


class AsSequentialRingApi(AsPersonSettingsApiChild):
    """
    API for sequential ring settings

    For now only used for workspaces
    """

    feature = 'sequentialRing'

    async def read_criteria(self, entity_id: str, id: str,
                      org_id: str = None) -> SequentialRingCriteria:
        """
        Retrieve sequential ring criteria for an entity.

        The sequential ring feature enables you to create a list of up to five phone numbers. When the workspace
        receives incoming calls, these numbers will ring one after another.
        The sequential ring criteria specify settings such as schedule and incoming numbers for which to sequentially
        ring or not.

        **NOTE**: This API is only available for professional licensed workspaces.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param id: Unique identifier for the criteria.
        :type id: str
        :param org_id: ID of the organization within which the entity resides. Only admin users of another
            organization (such as partners) may use this parameter as the default is the same organization as the
            token used to access API.
        :type org_id: str
        :rtype: :class:`SelectiveCriteria`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.f_ep(entity_id, f'criteria/{id}')
        data = await super().get(url, params=params)
        r = SequentialRingCriteria.model_validate(data)
        return r

    async def configure_criteria(self, entity_id: str, id: str, settings: SequentialRingCriteria,
                           org_id: str = None):
        """
        Modify sequential ring criteria for an entity.

        The sequential ring feature enables you to create a list of up to five phone numbers. When the workspace
        receives incoming calls, these numbers will ring one after another.
        The sequential ring criteria specify settings such as schedule and incoming numbers for which to sequentially
        ring or not.

        **NOTE**: This API is only available for professional licensed workspaces.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param id: Unique identifier for the criteria.
        :type id: str
        :param settings: new settings to be applied.
        :type settings: SequentialRingCriteria
        :param org_id: ID of the organization within which the entity resides. Only admin users of another
            organization (such as partners) may use this parameter as the default is the same organization as the
            token used to access API.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = settings.update()
        url = self.f_ep(entity_id, f'criteria/{id}')
        await super().put(url, params=params, json=body)

    async def delete_criteria(self, entity_id: str, id: str, org_id: str = None):
        """
        Delete sequential ring criteria for an entity.

        The sequential ring feature enables you to create a list of up to five phone numbers. When the workspace
        receives incoming calls, these numbers will ring one after another.
        The sequential ring criteria specify settings such as schedule and incoming numbers for which to sequentially
        ring or not.

        **NOTE**: This API is only available for professional licensed workspaces.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param id: Unique identifier for the criteria.
        :type id: str
        :param org_id: ID of the organization within which the entity resides. Only admin users of another
            organization (such as partners) may use this parameter as the default is the same organization as the
            token used to access API.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.f_ep(entity_id, f'criteria/{id}')
        await super().delete(url, params=params)

    async def create_criteria(self, entity_id: str, settings: SequentialRingCriteria,
                        org_id: str = None) -> str:
        """
        Create sequential ring criteria for an entity.

        The sequential ring feature enables you to create a list of up to five phone numbers. When the workspace
        receives incoming calls, these numbers will ring one after another.
        The sequential ring criteria specify settings such as schedule and incoming numbers for which to sequentially
        ring or not.

        **NOTE**: This API is only available for professional licensed workspaces.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param settings: new settings to be applied.
        :type settings: SelectiveCriteria
        :param org_id: ID of the organization within which the entity resides. Only admin users of another
            organization (such as partners) may use this parameter as the default is the same organization as the
            token used to access API.
        :type org_id: str
        :rtype: str
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = settings.update()

        url = self.f_ep(entity_id, 'criteria')
        data = await super().post(url, params=params, json=body)
        r = data['id']
        return r

    async def read(self, entity_id: str,
             org_id: str = None) -> SequentialRing:
        """
        Retrieve sequential ring settings for an entity.

        The sequential ring feature enables you to create a list of up to five phone numbers. When the workspace
        receives incoming calls, these numbers will ring one after another.

        **NOTE**: This API is only available for professional licensed workspaces.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param org_id: ID of the organization within which the entity resides. Only admin users of another
            organization (such as partners) may use this parameter as the default is the same organization as the
            token used to access API.
        :type org_id: str
        :rtype: :class:`SequentialRing`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.f_ep(entity_id)
        data = await super().get(url, params=params)
        r = SequentialRing.model_validate(data)
        return r

    async def configure(self, entity_id: str, settings: SequentialRing,
                  org_id: str = None):
        """
        Modify sequential ring settings for an entity.

        The sequential ring feature enables you to create a list of up to five phone numbers. When the entity
        receives incoming calls, these numbers will ring one after another.

        **NOTE**: This API is only available for professional licensed workspaces.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param settings: Settings for new criteria
        :type settings: SequentialRing
        :param org_id: ID of the organization within which the entity resides. Only admin users of another
            organization (such as partners) may use this parameter as the default is the same organization as the
            token used to access API.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = settings.update()
        url = self.f_ep(entity_id)
        await super().put(url, params=params, json=body)


class AsSimRingApi(AsPersonSettingsApiChild):
    """
    API for simultaneous ring settings

    For now only used for workspaces
    """

    feature = 'simultaneousRing'

    async def read_criteria(self, entity_id: str, id: str,
                      org_id: str = None) -> SimRingCriteria:
        """
        Retrieve Simultaneous Ring Criteria for an entity

        Retrieve Simultaneous Ring Criteria Settings for an entity.

        The Simultaneous Ring feature allows you to configure your office phone and other phones of your choice to ring
        simultaneously.
        Simultaneous Ring Criteria (Schedules) can also be set up to ring these phones during certain times of the day
        or days of the week.

        This API requires a full, read-only or location administrator auth token with a scope of
        `spark-admin:workspaces_read` or a user auth token with a scope of `spark:workspaces_read` to read workspace
        settings.

        **NOTE**: This API is only available for professional licensed workspaces.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param id: Unique identifier for the criteria.
        :type id: str
        :param org_id: ID of the organization within which the entity resides. Only admin users of another
            organization (such as partners) may use this parameter as the default is the same organization as the
            token used to access API.
        :type org_id: str
        :rtype: :class:`SimRingCriteria`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.f_ep(entity_id, f'criteria/{id}')
        data = await super().get(url, params=params)
        r = SimRingCriteria.model_validate(data)
        return r

    async def configure_criteria(self, entity_id: str, id: str, settings: SimRingCriteria,
                           org_id: str = None):
        """
        Modify Simultaneous Ring Criteria for an entity

        Modify Simultaneous Ring Criteria Settings for an entity.

        The Simultaneous Ring feature allows you to configure your office phone and other phones of your choice to ring
        simultaneously.
        Simultaneous Ring Criteria (Schedules) can also be set up to ring these phones during certain times of the day
        or days of the week.

        This API requires a full, user or location administrator auth token with the `spark-admin:workspaces_write`
        scope or a user auth token with a scope of `spark:workspaces_write` to update workspace settings.

        **NOTE**: This API is only available for professional licensed workspaces.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param id: Unique identifier for the criteria.
        :type id: str
        :param settings: new settings to be applied.
        :type settings: SimRingCriteria
        :param org_id: ID of the organization within which the entity resides. Only admin users of another
            organization (such as partners) may use this parameter as the default is the same organization as the
            token used to access API.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = settings.update()

        url = self.f_ep(entity_id, f'criteria/{id}')
        await super().put(url, params=params, json=body)

    async def delete_criteria(self, entity_id: str, id: str, org_id: str = None):
        """
        Delete Simultaneous Ring Criteria for an entity

        Delete simultaneous ring criteria Settings for an entity.

        The Simultaneous Ring feature allows you to configure your office phone and other phones of your choice to ring
        simultaneously.
        Simultaneous Ring Criteria (Schedules) can also be set up to ring these phones during certain times of the day
        or days of the week.

        This API requires a full, user or location administrator auth token with the `spark-admin:workspaces_write`
        scope or a user auth token with a scope of `spark:workspaces_write` to update workspace settings.

        **NOTE**: This API is only available for professional licensed workspaces.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param id: Unique identifier for the criteria.
        :type id: str
        :param org_id: ID of the organization within which the entity resides. Only admin users of another
            organization (such as partners) may use this parameter as the default is the same organization as the
            token used to access API.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.f_ep(entity_id, f'criteria/{id}')
        await super().delete(url, params=params)

    async def create_criteria(self, entity_id: str, settings: SimRingCriteria, org_id: str = None) -> str:
        """
        Create Simultaneous Ring Criteria for an entity

        Create Simultaneous Ring Criteria Settings for an entity.

        The Simultaneous Ring feature allows you to configure your office phone and other phones of your choice to ring
        simultaneously.
        Simultaneous Ring Criteria (Schedules) can also be set up to ring these phones during certain times of the day
        or days of the week.

        This API requires a full, user or location administrator auth token with the `spark-admin:workspaces_write`
        scope or a user auth token with a scope of `spark:workspaces_write` to update workspace settings.

        **NOTE**: This API is only available for professional licensed workspaces.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param settings: new settings to be applied.
        :type settings: SimRingCriteria
        :param org_id: ID of the organization within which the entity resides. Only admin users of another
            organization (such as partners) may use this parameter as the default is the same organization as the
            token used to access API.
        :type org_id: str
        :rtype: str
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = settings.update()
        url = self.f_ep(entity_id, 'criteria')
        data = await super().post(url, params=params, json=body)
        r = data['id']
        return r

    async def read(self, entity_id: str,
             org_id: str = None) -> SimRing:
        """
        Retrieve Simultaneous Ring Settings for an entity.

        The Simultaneous Ring feature allows you to configure your office phone and other phones of your choice to ring
        simultaneously.
        Schedules can also be set up to ring these phones during certain times of the day or days of the week.

        This API requires a full, read-only or location administrator auth token with a scope
        of `spark-admin:workspaces_read` or a user auth token with a scope of `spark:workspaces_read` to read workspace
        settings.

        **NOTE**: This API is only available for professional licensed workspaces.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param org_id: ID of the organization within which the entity resides. Only admin users of another
            organization (such as partners) may use this parameter as the default is the same organization as the
            token used to access API.
        :type org_id: str
        :rtype: :class:`SimRing`
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.f_ep(entity_id)
        data = await super().get(url, params=params)
        r = SimRing.model_validate(data)
        return r

    async def configure(self, entity_id: str, settings: SimRing,
                  org_id: str = None):
        """
        Modify Simultaneous Ring Settings for an entity.

        The Simultaneous Ring feature allows you to configure the workspace phones of your choice to ring
        simultaneously.
        Schedules can also be set up to ring these phones during certain times of the day or days of the week.

        This API requires a full, user or location administrator auth token with the `spark-admin:workspaces_write`
        scope or a user auth token with a scope of `spark:workspaces_write` to update workspace settings.

        **NOTE**: This API is only available for professional licensed workspaces.

        :param entity_id: Unique identifier for the entity.
        :type entity_id: str
        :param settings: Settings for new criteria
        :type settings: SimRing
        :param org_id: ID of the organization within which the entity resides. Only admin users of another
            organization (such as partners) may use this parameter as the default is the same organization as the
            token used to access API.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = settings.update()
        url = self.f_ep(entity_id)
        await super().put(url, params=params, json=body)


class AsWorkspaceDevicesApi(AsApiChild, base='telephony/config/workspaces'):
    def list_gen(self, workspace_id: str, org_id: str = None) -> AsyncGenerator[TelephonyDevice, None, None]:
        """
        Get all devices for a workspace.
        This requires a full or read-only administrator auth token with a scope of spark-admin:telephony_config_read.

        :param workspace_id: ID of the workspace for which to retrieve devices.
        :type workspace_id: str
        :param org_id: Organization to which the workspace belongs.
        :type org_id: str

        documentation: https://developer.webex.com/docs/api/v1/webex-calling-organization-settings/get-workspace-devices
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'{workspace_id}/devices')
        return self.session.follow_pagination(url=url, model=TelephonyDevice, params=params, item_key='devices')

    async def list(self, workspace_id: str, org_id: str = None) -> List[TelephonyDevice]:
        """
        Get all devices for a workspace.
        This requires a full or read-only administrator auth token with a scope of spark-admin:telephony_config_read.

        :param workspace_id: ID of the workspace for which to retrieve devices.
        :type workspace_id: str
        :param org_id: Organization to which the workspace belongs.
        :type org_id: str

        documentation: https://developer.webex.com/docs/api/v1/webex-calling-organization-settings/get-workspace-devices
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'{workspace_id}/devices')
        return [o async for o in self.session.follow_pagination(url=url, model=TelephonyDevice, params=params, item_key='devices')]

    async def list_and_counts(self, workspace_id: str, org_id: str = None) -> DeviceList:
        """
        Get all devices for a workspace.
        This requires a full or read-only administrator auth token with a scope of spark-admin:telephony_config_read.

        :param workspace_id: ID of the workspace for which to retrieve devices.
        :type workspace_id: str
        :param org_id: Organization to which the workspace belongs.
        :type org_id: str

        documentation: https://developer.webex.com/docs/api/v1/webex-calling-organization-settings/get-workspace-devices
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'{workspace_id}/devices')
        data = await self.get(url=url, params=params)
        return DeviceList.model_validate(data)

    async def modify_hoteling(self, workspace_id: str, hoteling: Hoteling, org_id: str = None):
        """
        Modify devices for a workspace.
        Modifying devices for a workspace requires a full administrator auth token with a scope of
        spark-admin:telephony_config_write.

        :param workspace_id: ID of the workspace for which to modify devices.
        :type workspace_id: str
        :param hoteling: hoteling settings
        :type hoteling: Hoteling
        :param org_id: Organization to which the workspace belongs.
        :type org_id: str

        documentation: https://developer.webex.com/docs/api/v1/webex-calling-organization-settings/modify-workspace-devices
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        url = self.ep(f'{workspace_id}/devices')
        await super().put(url=url, params=params, data=hoteling.model_dump_json())


class AsWorkspaceNumbersApi(AsApiChild, base='workspaces'):

    # noinspection PyMethodOverriding
    def ep(self, workspace_id: str, path: str = None):
        """

        :meta private:
        """
        path = path and '/path' or ''
        return super().ep(path=f'{workspace_id}/features/numbers/{path}')

    async def read(self, workspace_id: str, org_id: str = None) -> WorkspaceNumbers:
        """
        List the PSTN phone numbers associated with a specific workspace, by ID, within the organization. Also shows
        the location and Organization associated with the workspace.

        Retrieving this list requires a full or read-only administrator auth token with a scope
        of spark-admin:workspaces_read.

        :param workspace_id: List numbers for this workspace.
        :type workspace_id: str
        :param org_id: List numbers for a workspace within this organization.
        :type org_id: str
        :return: Workspace numbers
        :rtype: WorkspaceNumbers
        """
        params = org_id and {'org_id': org_id} or None
        url = self.ep(workspace_id=workspace_id)
        data = await self.get(url=url, params=params)
        return TypeAdapter(WorkspaceNumbers).validate_python(data)

    async def update(self, workspace_id: str,
               phone_numbers: list[UpdateWorkspacePhoneNumber],
               distinctive_ring_enabled: bool = None,
               org_id: str = None):
        """
        Assign or Unassign numbers associated with a specific workspace

        Assign or unassign alternate phone numbers associated with a specific workspace.

        Each location has a set of phone numbers that can be assigned to people, workspaces, or features. Phone numbers
        must follow the E.164 format for all countries, except for the United States, which can also follow the
        National format. Active phone numbers are in service.

        This API requires a full, user or location administrator auth token with the `spark-admin:workspaces_write` to
        update workspace settings.

        **NOTE**: This API is only available for professional licensed workspaces.

        :param workspace_id: Unique identifier for the workspace.
        :type workspace_id: str
        :param phone_numbers: List of phone numbers that are assigned to a person.
        :type phone_numbers: list[UpdateWorkspacePhoneNumber]
        :param distinctive_ring_enabled: Enables a distinctive ring pattern for the person.
        :type distinctive_ring_enabled: bool
        :param org_id: ID of the organization within which the workspace resides. Only admin users of another
            organization (such as partners) may use this parameter as the default is the same organization as the
            token used to access API.
        :type org_id: str
        :rtype: None
        """
        params = {}
        if org_id is not None:
            params['orgId'] = org_id
        body = dict()
        if distinctive_ring_enabled is not None:
            body['distinctiveRingEnabled'] = distinctive_ring_enabled
        body['phoneNumbers'] = TypeAdapter(list[UpdateWorkspacePhoneNumber]).dump_python(phone_numbers, mode='json',
                                                                                         by_alias=True,
                                                                                         exclude_none=True)
        url = self.session.ep(f'telephony/config/workspaces/{workspace_id}/numbers')
        await super().put(url, params=params, json=body)


@dataclass(init=False, repr=False)
class AsWorkspaceSettingsApi(AsApiChild, base='workspaces'):
    """
    API for all workspace settings.

    Most of the workspace settings are equivalent to corresponding user settings. For these settings the attributes of
    this class are instances of the respective user settings APIs. When calling endpoints of these APIs workspace IDs
    need to be passed to the ``person_id`` parameter of the called function.
    """
    anon_calls: AsAnonCallsApi
    available_numbers: AsAvailableNumbersApi
    barge: AsBargeApi
    call_bridge: AsCallBridgeApi
    call_intercept: AsCallInterceptApi
    call_policy: AsCallPolicyApi
    call_waiting: AsCallWaitingApi
    caller_id: AsCallerIdApi
    dnd: AsDndApi
    devices: AsWorkspaceDevicesApi
    ecbn: AsECBNApi
    forwarding: AsPersonForwardingApi
    monitoring: AsMonitoringApi
    music_on_hold: AsMusicOnHoldApi
    numbers: AsWorkspaceNumbersApi
    permissions_in: AsIncomingPermissionsApi
    permissions_out: AsOutgoingPermissionsApi
    priority_alert: AsPriorityAlertApi
    privacy: AsPrivacyApi
    push_to_talk: AsPushToTalkApi
    selective_accept: AsSelectiveAcceptApi
    selective_forward: AsSelectiveForwardApi
    selective_reject: AsSelectiveRejectApi
    sequential_ring: AsSequentialRingApi
    sim_ring: AsSimRingApi
    voicemail: AsVoicemailApi

    def __init__(self, session: AsRestSession):
        super().__init__(session=session)
        self.anon_calls = AsAnonCallsApi(session=session, selector=ApiSelector.workspace)
        self.available_numbers = AsAvailableNumbersApi(session=session, selector=ApiSelector.workspace)
        self.barge = AsBargeApi(session=session, selector=ApiSelector.workspace)
        self.call_bridge = AsCallBridgeApi(session=session, selector=ApiSelector.workspace)
        self.call_intercept = AsCallInterceptApi(session=session, selector=ApiSelector.workspace)
        self.call_policy = AsCallPolicyApi(session=session, selector=ApiSelector.workspace)
        self.call_waiting = AsCallWaitingApi(session=session, selector=ApiSelector.workspace)
        self.caller_id = AsCallerIdApi(session=session, selector=ApiSelector.workspace)
        self.devices = AsWorkspaceDevicesApi(session=session)
        self.dnd = AsDndApi(session=session, selector=ApiSelector.workspace)
        self.ecbn = AsECBNApi(session=session, selector=ApiSelector.workspace)
        self.forwarding = AsPersonForwardingApi(session=session, selector=ApiSelector.workspace)
        self.monitoring = AsMonitoringApi(session=session, selector=ApiSelector.workspace)
        self.music_on_hold = AsMusicOnHoldApi(session=session, selector=ApiSelector.workspace)
        self.numbers = AsWorkspaceNumbersApi(session=session)
        self.permissions_in = AsIncomingPermissionsApi(session=session, selector=ApiSelector.workspace)
        self.permissions_out = AsOutgoingPermissionsApi(session=session, selector=ApiSelector.workspace)
        self.priority_alert = AsPriorityAlertApi(session=session, selector=ApiSelector.workspace)
        self.privacy = AsPrivacyApi(session=session, selector=ApiSelector.workspace)
        self.push_to_talk = AsPushToTalkApi(session=session, selector=ApiSelector.workspace)
        self.selective_accept = AsSelectiveAcceptApi(session=session, selector=ApiSelector.workspace)
        self.selective_forward = AsSelectiveForwardApi(session=session, selector=ApiSelector.workspace)
        self.selective_reject = AsSelectiveRejectApi(session=session, selector=ApiSelector.workspace)
        self.sequential_ring = AsSequentialRingApi(session=session, selector=ApiSelector.workspace)
        self.sim_ring = AsSimRingApi(session=session, selector=ApiSelector.workspace)
        self.voicemail = AsVoicemailApi(session=session, selector=ApiSelector.workspace)


class AsWorkspacesApi(AsApiChild, base='workspaces'):
    """
    Workspaces API

    Workspaces represent where people work, such as conference rooms, meeting spaces, lobbies, and lunch rooms. Devices
    may be associated with workspaces.
    Viewing the list of workspaces in an organization requires an administrator auth token with the
    spark-admin:workspaces_read scope. Adding, updating, or deleting workspaces in an organization requires an
    administrator auth token with the spark-admin:workspaces_write scope.
    The Workspaces API can also be used by partner administrators acting as administrators of a different organization
    than their own. In those cases an orgId value must be supplied, as indicated in the reference documentation for the
    relevant endpoints.
    """

    def list_gen(self, location_id: str = None, workspace_location_id: str = None, floor_id: str = None,
             display_name: str = None, capacity: int = None, workspace_type: WorkSpaceType = None,
             calling: CallingType = None, supported_devices: WorkspaceSupportedDevices = None,
             calendar: CalendarType = None, device_hosted_meetings_enabled: bool = None,
             device_platform: DevicePlatform = None, health_level: WorkspaceHealthLevel = None,
             include_devices: bool = None, include_capabilities: bool = None,
             planned_maintenance: MaintenanceMode = None, custom_attribute: str = None, org_id: str = None,
             **params) -> AsyncGenerator[Workspace, None, None]:
        """
        List Workspaces

        Use query parameters to filter the response. The `orgId` parameter can only be used by admin users of another
        organization (such as partners). The `locationId`, `workspaceLocationId`, `indoorNavigation`, `floorId`,
        `capacity` and `type` fields will only be present for workspaces that have a value set for them. The special
        values `notSet` (for filtering on category) and `-1` (for filtering on capacity) can be used to filter for
        workspaces without a type and/or capacity.

        :param location_id: Location associated with the workspace. Values must originate from the /locations API and
            not the legacy /workspaceLocations API.
        :type location_id: str
        :param workspace_location_id: Location associated with the workspace. Both values from the /locations API and
            the legacy /workspaceLocations API are supported. This field is deprecated and integrations should prefer
            `locationId` going forward.
        :type workspace_location_id: str
        :param floor_id: Floor associated with the workspace.
        :type floor_id: str
        :param display_name: List workspaces by display name.
        :type display_name: str
        :param capacity: List workspaces with the given capacity. Must be -1 or higher. A value of -1 lists workspaces
            with no capacity set.
        :type capacity: int
        :param workspace_type: List workspaces by type. Possible values: notSet, focus, huddle, meetingRoom, open,
            desk, other
        :type workspace_type: :class:`WorkSpaceType`
        :param calling: List workspaces by calling type. Possible values: freeCalling, hybridCalling, webexCalling,
            webexEdgeForDevices, thirdPartySipCalling, none
        :type calling: :class:`CallingType`
        :param supported_devices: List workspaces by supported devices. Possible values: collaborationDevices, phones
        :type supported_devices: str
        :param calendar: List workspaces by calendar type. Possible values: none, google, microsoft
        :type calendar: :class:`CalendarType`
        :param device_hosted_meetings_enabled: List workspaces enabled for device hosted meetings.
        :type device_hosted_meetings_enabled: bool
        :param device_platform: List workspaces by device platform.
        :type device_platform: DevicePlatform
        :param health_level: List workspaces by health level.
        :type health_level: WorkspaceHealthLevel
        :param include_devices: Flag identifying whether to include the devices associated with the workspace in the
            response.
        :type include_devices: bool
        :param include_capabilities: Flag identifying whether to include the workspace capabilities in the response.
        :type include_capabilities: bool
        :param planned_maintenance: List workspaces with given maintenance mode.
        :type planned_maintenance: WorkspacePlannedMaintenanceMode
        :param custom_attribute: List workspaces with given custom attribute key.
        :type custom_attribute: str
        :param org_id: List workspaces in this organization. Only admin users of another organization
            (such as partners) may use this parameter.
        :type org_id: str
        :return: generator of :class:`Workspace` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if location_id is not None:
            params['locationId'] = location_id
        if workspace_location_id is not None:
            params['workspaceLocationId'] = workspace_location_id
        if floor_id is not None:
            params['floorId'] = floor_id
        if display_name is not None:
            params['displayName'] = display_name
        if capacity is not None:
            params['capacity'] = capacity
        if workspace_type is not None:
            params['type'] = enum_str(workspace_type)
        if calling is not None:
            params['calling'] = calling
        if supported_devices is not None:
            params['supportedDevices'] = enum_str(supported_devices)
        if calendar is not None:
            params['calendar'] = calendar
        if device_hosted_meetings_enabled is not None:
            params['deviceHostedMeetingsEnabled'] = str(device_hosted_meetings_enabled).lower()
        if device_platform is not None:
            params['devicePlatform'] = enum_str(device_platform)
        if health_level is not None:
            params['healthLevel'] = enum_str(health_level)
        if include_devices is not None:
            params['includeDevices'] = str(include_devices).lower()
        if include_capabilities is not None:
            params['includeCapabilities'] = str(include_capabilities).lower()
        if planned_maintenance is not None:
            params['plannedMaintenance'] = enum_str(planned_maintenance)
        if custom_attribute is not None:
            params['customAttribute'] = custom_attribute
        ep = self.ep()
        # noinspection PyTypeChecker
        return self.session.follow_pagination(url=ep, model=Workspace, params=params)

    async def list(self, location_id: str = None, workspace_location_id: str = None, floor_id: str = None,
             display_name: str = None, capacity: int = None, workspace_type: WorkSpaceType = None,
             calling: CallingType = None, supported_devices: WorkspaceSupportedDevices = None,
             calendar: CalendarType = None, device_hosted_meetings_enabled: bool = None,
             device_platform: DevicePlatform = None, health_level: WorkspaceHealthLevel = None,
             include_devices: bool = None, include_capabilities: bool = None,
             planned_maintenance: MaintenanceMode = None, custom_attribute: str = None, org_id: str = None,
             **params) -> List[Workspace]:
        """
        List Workspaces

        Use query parameters to filter the response. The `orgId` parameter can only be used by admin users of another
        organization (such as partners). The `locationId`, `workspaceLocationId`, `indoorNavigation`, `floorId`,
        `capacity` and `type` fields will only be present for workspaces that have a value set for them. The special
        values `notSet` (for filtering on category) and `-1` (for filtering on capacity) can be used to filter for
        workspaces without a type and/or capacity.

        :param location_id: Location associated with the workspace. Values must originate from the /locations API and
            not the legacy /workspaceLocations API.
        :type location_id: str
        :param workspace_location_id: Location associated with the workspace. Both values from the /locations API and
            the legacy /workspaceLocations API are supported. This field is deprecated and integrations should prefer
            `locationId` going forward.
        :type workspace_location_id: str
        :param floor_id: Floor associated with the workspace.
        :type floor_id: str
        :param display_name: List workspaces by display name.
        :type display_name: str
        :param capacity: List workspaces with the given capacity. Must be -1 or higher. A value of -1 lists workspaces
            with no capacity set.
        :type capacity: int
        :param workspace_type: List workspaces by type. Possible values: notSet, focus, huddle, meetingRoom, open,
            desk, other
        :type workspace_type: :class:`WorkSpaceType`
        :param calling: List workspaces by calling type. Possible values: freeCalling, hybridCalling, webexCalling,
            webexEdgeForDevices, thirdPartySipCalling, none
        :type calling: :class:`CallingType`
        :param supported_devices: List workspaces by supported devices. Possible values: collaborationDevices, phones
        :type supported_devices: str
        :param calendar: List workspaces by calendar type. Possible values: none, google, microsoft
        :type calendar: :class:`CalendarType`
        :param device_hosted_meetings_enabled: List workspaces enabled for device hosted meetings.
        :type device_hosted_meetings_enabled: bool
        :param device_platform: List workspaces by device platform.
        :type device_platform: DevicePlatform
        :param health_level: List workspaces by health level.
        :type health_level: WorkspaceHealthLevel
        :param include_devices: Flag identifying whether to include the devices associated with the workspace in the
            response.
        :type include_devices: bool
        :param include_capabilities: Flag identifying whether to include the workspace capabilities in the response.
        :type include_capabilities: bool
        :param planned_maintenance: List workspaces with given maintenance mode.
        :type planned_maintenance: WorkspacePlannedMaintenanceMode
        :param custom_attribute: List workspaces with given custom attribute key.
        :type custom_attribute: str
        :param org_id: List workspaces in this organization. Only admin users of another organization
            (such as partners) may use this parameter.
        :type org_id: str
        :return: generator of :class:`Workspace` instances
        """
        if org_id is not None:
            params['orgId'] = org_id
        if location_id is not None:
            params['locationId'] = location_id
        if workspace_location_id is not None:
            params['workspaceLocationId'] = workspace_location_id
        if floor_id is not None:
            params['floorId'] = floor_id
        if display_name is not None:
            params['displayName'] = display_name
        if capacity is not None:
            params['capacity'] = capacity
        if workspace_type is not None:
            params['type'] = enum_str(workspace_type)
        if calling is not None:
            params['calling'] = calling
        if supported_devices is not None:
            params['supportedDevices'] = enum_str(supported_devices)
        if calendar is not None:
            params['calendar'] = calendar
        if device_hosted_meetings_enabled is not None:
            params['deviceHostedMeetingsEnabled'] = str(device_hosted_meetings_enabled).lower()
        if device_platform is not None:
            params['devicePlatform'] = enum_str(device_platform)
        if health_level is not None:
            params['healthLevel'] = enum_str(health_level)
        if include_devices is not None:
            params['includeDevices'] = str(include_devices).lower()
        if include_capabilities is not None:
            params['includeCapabilities'] = str(include_capabilities).lower()
        if planned_maintenance is not None:
            params['plannedMaintenance'] = enum_str(planned_maintenance)
        if custom_attribute is not None:
            params['customAttribute'] = custom_attribute
        ep = self.ep()
        # noinspection PyTypeChecker
        return [o async for o in self.session.follow_pagination(url=ep, model=Workspace, params=params)]

    async def create(self, settings: Workspace, org_id: str = None):
        """
        Create a Workspace

        The `locationId`, `workspaceLocationId`, `floorId`, `indoorNavigation`, `capacity`, `type`, `notes` and
        `hotdeskingStatus` parameters are optional, and omitting them will result in the creation of a workspace
        without these values set, or set to their default. A `locationId` must be provided when the `floorId` is set.
        Calendar and calling can also be set for a new workspace. Omitting them will default to free calling and no
        calendaring. The `orgId` parameter can only be used by admin users of another organization (such as partners).

        * Information for Webex Calling fields may be found here: `locations
        <https://developer.webex.com/docs/api/v1/locations/list-locations>`_, `available numbers

        * The `locationId` and `supportedDevices` fields cannot be changed once configured.

        * When creating a `webexCalling` workspace that is not hot desk only, a `locationId` and either a `phoneNumber`
          or `extension` or both is required. Furthermore, it is possible to set the `licenses` field with a list of
          Webex Calling license IDs, if desired. If multiple license IDs are provided, the oldest suitable one will be
          applied. If no licenses are supplied, the oldest suitable one from the active subscriptions will be
          automaticaly applied.

        * When creating a hot desk only workspace, `phoneNumber` and `extension` fields are not applicable.
          Furthermore, `deviceHostedMeetingsEnabled`, and `calendar` services are not applicable. If any of these fields
           are provided the API will return an error. The `calling` type is `webexCalling`.

        :param settings: settings for new Workspace
        :type settings: :class:`Workspace`
        :param org_id: OrgId associated with the workspace. Only admin users of another organization
            (such as partners) may use this parameter.
        :type org_id: str
        :return: new workspace
        :rtype: :class:`Workspace`
        """
        if org_id:
            settings.org_id = org_id
        data = settings.update_or_create()
        url = self.ep()
        data = await self.post(url, json=data)
        return Workspace.model_validate(data)

    async def details(self, workspace_id, include_devices: bool = None) -> Workspace:
        """
        Get Workspace Details

        Shows details for a workspace, by ID. The `locationId`, `workspaceLocationId`, `floorId`, `indoorNavigation`,
        `capacity`, `type` and `notes` fields will only be present if they have been set for the workspace.

        :param workspace_id: A unique identifier for the workspace.
        :type workspace_id: str
        :param include_devices: Flag identifying whether to include the devices associated with the workspace in the
            response.
        :type include_devices: bool
        :return: workspace details
        :rtype: :class:`Workspace`
        """
        params = {}
        if include_devices is not None:
            params['includeDevices'] = str(include_devices).lower()
        url = self.ep(workspace_id)
        return Workspace.model_validate(await self.get(url, params=params))

    async def update(self, workspace_id, settings: Workspace) -> Workspace:
        """
        Update a Workspace

        Updates details for a workspace by ID.

        Specify the workspace ID in the `workspaceId` parameter in the URI. Include all details for the workspace that
        are present in a `GET request for the workspace details
        <https://developer.webex.com/docs/api/v1/workspaces/get-workspace-details>`_. Not including the optional
        `capacity`, `type` or
        `notes` fields will result in the fields no longer being defined for the workspace. A `locationId` must be
        provided when the `floorId` is set. The `locationId`, `workspaceLocationId`, `floorId`, `supportedDevices`,
        `calendar` and `calling` fields do not change when omitted from the update request.

        * Information for Webex Calling fields may be found here: `locations
        <https://developer.webex.com/docs/api/v1/locations/list-locations>`_ and `available numbers

        * Updating the `calling` parameter is only supported if the existing `calling` type is `freeCalling`, `none`,
          `thirdPartySipCalling` or `webexCalling`.

        * Updating the `calling` parameter to `none`, `thirdPartySipCalling` or `webexCalling` is not supported if the
          workspace contains any devices.

        * The `locationId` and `supportedDevices` fields cannot be changed once configured.

        * When updating `webexCalling` information on a workspace that is not hot desk only, a `locationId` and either
          a `phoneNumber` or `extension` or both is required. Furthermore, the `licenses` field can be set with a list
          of Webex Calling license IDs, if desired. If multiple license IDs are provided, the oldest suitable one will
          be applied. If a previously applied license ID is omitted, it will be replaced with one from the list
          provided. If the `licenses` field is omitted, the current calling license will be retained.

        * When specifying a hot desk only license on a hot desk only workspace, `deviceHostedMeetingsEnabled`, and
          `calendar` services are not supported and will be automatically disabled. In addition to this, the
          `phoneNumber` and `extension` will be removed from the workspace. Attempting to enable any of these services,
          or provide a `phoneNumber` or `extension` will result in an error. The `calling` type for these requests is
          `webexCalling`.

        :param workspace_id: A unique identifier for the workspace.
        :type workspace_id: str
        :param settings: new workspace settings
        :type settings: :class:`Workspace`
        :return: updated workspace
        :rtype: :class:`Workspace`
        """
        url = self.ep(workspace_id)
        j_data = settings.update_or_create(for_update=True)
        data = await self.put(url, json=j_data)
        return Workspace.model_validate(data)

    async def delete_workspace(self, workspace_id):
        """
        Delete a Workspace

        Deletes a workspace, by ID. Will also delete all devices associated with the workspace. Any deleted devices
        will need to be reactivated.

        :param workspace_id: A unique identifier for the workspace.
        :type workspace_id: str
        """
        url = self.ep(workspace_id)
        await self.delete(url)

    async def capabilities(self, workspace_id: str) -> CapabilityMap:
        """
        Shows the capabilities for a workspace by ID.
        Returns a set of capabilities, including whether or not the capability is supported by any device in the
        workspace, and if the capability is configured (enabled). For example for a specific capability like
        occupancyDetection, the API will return if the capability is supported and/or configured such that occupancy
        detection data will flow from the workspace (device) to the cloud. Specify the workspace ID in the workspaceId
        parameter in the URI.

        :param workspace_id: A unique identifier for the workspace.
        :type workspace_id: str

        """
        url = self.ep(f'{workspace_id}/capabilities')
        data = await super().get(url=url)
        return CapabilityMap.model_validate(data["capabilities"])


class AsXApi(AsApiChild, base='xapi'):
    """
    xAPI

    The xAPI allows developers to programmatically invoke commands and query the status of devices that run Webex
    RoomOS software.

    Executing commands requires an auth token with the `spark:xapi_commands` scope. Querying devices requires an auth
    token with the `spark:xapi_statuses` scope.

    All xAPI requests require a `deviceId` which can be obtained using the `Devices API
    <https://developer.webex.com/docs/api/v1/devices>`_. xAPI commands and statuses are
    described in the `Cisco Collaboration Endpoint Software API Reference Guide
    <https://www.cisco.com/c/en/us/support/collaboration-endpoints/spark-room-kit-series/products-command-reference
    -list.html>`_. For more information about developing
    applications for cloud connected devices, see the `Device Developers Guide
    <https://developer.webex.com/docs/api/guides/device-developers-guide>`_.
    """

    async def query_status(self, device_id: str, name: list[str]) -> QueryStatusResponse:
        """
        Query Status

        Query the current status of the Webex RoomOS Device. You specify the target device in the `deviceId` parameter
        in the URI. The target device is queried for statuses according to the expression in the `name` parameter.

        See the `xAPI section of the Device Developers Guide
        <https://developer.webex.com/docs/api/guides/device-developers-guide#xapi>`_ for a description of status
        expressions.

        :param device_id: The unique identifier for the Webex RoomOS Device.
        :type device_id: str
        :param name: A list of status expressions used to query the Webex RoomOS Device. See the
            `xAPI section of the Device Developers Guide
            <https://developer.webex.com/docs/api/guides/device-developers-guide#xapi>`_ for a description of status
            expressions. A request can contain
            at most 10 different status expressions.
        :type name: list[str]
        """
        params = {}
        params['deviceId'] = device_id
        params['name'] = ','.join(name)
        url = self.ep('status')
        data = await super().get(url, params=params)
        r = QueryStatusResponse.model_validate(data)
        return r

    async def execute_command(self, command_name: str, device_id: str, arguments: dict = None,
                        body: Union[dict, str] = None) -> ExecuteCommandResponse:
        """
        Execute Command

        Executes a command on the Webex RoomOS Device. Specify the command to execute in the `commandName` URI
        parameter.

        See the `xAPI section of the Device Developers Guide <https://developer.webex.com/docs/devices#xapi>`_ for a
        description of command expressions.

        :param command_name: Command to execute on the Webex RoomOS Device.
        :type command_name: str
        :param device_id: The unique identifier for the Webex RoomOS Device.
        :type device_id: str
        :param arguments: xAPI command arguments
        :type arguments: dict
        :param body: xAPI command body, as a complex JSON object or as a string
        :type body: ExecuteCommandBody
        :rtype: :class:`ExecuteCommandResponse`
        """
        json_body = dict()
        json_body['deviceId'] = device_id
        if arguments is not None:
            json_body['arguments'] = arguments
        if body is not None:
            json_body['body'] = body
        url = self.ep(f'command/{command_name}')
        data = await super().post(url, json=json_body)
        r = ExecuteCommandResponse.model_validate(data)
        return r

    def system_unit_boot(self, device_id: str, force: bool = False) -> ExecuteCommandResponse:
        """
        Reboot the device

        :param device_id: The unique identifier for the Webex RoomOS Device.
        :type device_id: str
        :param force: If True, the device will be rebooted immediately. If False, the device will wait for a period of time before rebooting.
        :type force: bool
        """
        return self.execute_command('SystemUnit.Boot', device_id, arguments={'Force': str(force)})


@dataclass(init=False, repr=False)
class AsWebexSimpleApi:
    """
    The main API object
    """

    #: Admin Audit Events API :class:`AsAdminAuditEventsApi`
    admin_audit: AsAdminAuditEventsApi
    #: Attachment actions API :class:`AsAttachmentActionsApi`
    attachment_actions: AsAttachmentActionsApi
    #: Authorizations API :class:`AsAuthorizationsApi`
    authorizations: AsAuthorizationsApi
    #: CDR API :class:`AsDetailedCDRApi`
    cdr: AsDetailedCDRApi
    #: converged recordings API :class:`AsConvergedRecordingsApi`
    converged_recordings: AsConvergedRecordingsApi
    #: device configurations API :class:`AsDeviceConfigurationsApi`
    device_configurations: AsDeviceConfigurationsApi
    #: devices API :class:`AsDevicesApi`
    devices: AsDevicesApi
    #: events API; :class:`AsEventsApi`
    events: AsEventsApi
    #: groups API :class:`AsGroupsApi`
    groups: AsGroupsApi
    #: guests API :class:`AsGuestManagementApi`
    guests: AsGuestManagementApi
    #: jobs API: :class:`AsJobsApi`
    jobs: AsJobsApi
    #: Licenses API :class:`AsLicensesApi`
    licenses: AsLicensesApi
    #: Location API :class:`AsLocationsApi`
    locations: AsLocationsApi
    #: call settings for me  API :class:`AsMeSettingsApi`
    me: AsMeSettingsApi
    #: meetings API :class:`AsMeetingsApi`
    meetings: AsMeetingsApi
    #: membership API :class:`AsMembershipApi`
    membership: AsMembershipApi
    #: Messages API :class:`AsMessagesApi`
    messages: AsMessagesApi
    #: org contacts API :class:`AsOrganizationContactsApi`
    org_contacts: AsOrganizationContactsApi
    #: organization settings API
    organizations: AsOrganizationApi
    #: Person settings API :class:`AsPersonSettingsApi`
    person_settings: AsPersonSettingsApi
    #: People API :class:`AsPeopleApi`
    people: AsPeopleApi
    #: Reports API :class:`AsReportsApi`
    reports: AsReportsApi
    #: Roles API :class:`AsRolesApi`
    roles: AsRolesApi
    #: Rooms API :class:`AsRoomsApi`
    rooms: AsRoomsApi
    #: Room tabs API :class:`AsRoomTabsApi`
    room_tabs: AsRoomTabsApi
    #: Webex Status API :class:`AsStatusAPI`
    status: AsStatusAPI
    #: ScimV2 API: :class:`AsScimV2Api`
    scim: AsScimV2Api
    #: Teams API :class:`AsTeamsApi`
    teams: AsTeamsApi
    #: Team memberships API :class:`AsTeamMembershipsApi`
    team_memberships: AsTeamMembershipsApi
    #: Telephony (features) API :class:`AsTelephonyApi`
    telephony: AsTelephonyApi
    #: Webhooks API :class:`AsWebhookApi`
    webhook: AsWebhookApi
    #: Workspaces API :class:`AsWorkspacesApi`
    workspaces: AsWorkspacesApi
    #: Workspace locations API; :class:`AsWorkspaceLocationApi`
    workspace_locations: AsWorkspaceLocationApi
    #: Workspace personalization API :class:workspace_personalization.AsWorkspacePersonalizationApi`
    workspace_personalization: AsWorkspacePersonalizationApi
    #: Workspace setting API :class:`AsWorkspaceSettingsApi`
    workspace_settings: AsWorkspaceSettingsApi
    #: XAPI API :class:`AsXApi`
    xapi: AsXApi
    #: :class:`AsRestSession` used for all API requests
    session: AsRestSession
    #: whether the session used for all requests must be closed when :meth:`close` is called
    _must_close_session: bool

    def __init__(self, *, tokens: Union[str, Tokens] = None, concurrent_requests: int = 10, retry_429: bool = True,
                 session: AsRestSession = None, **kwargs):
        """

        :param tokens: token to be used by the API. Can be a :class:`tokens.Tokens` instance, a string or None. If
            None then an access token is expected in the WEBEX_ACCESS_TOKEN environment variable.
        :param concurrent_requests: number of concurrent requests when using multi-threading
        :type concurrent_requests: int
        :param retry_429: automatically retry for 429 throttling response
        :type retry_429: bool
        :param session: if given, this :class:`rest.AsRestSession` instance will be used for all requests instead of
            creating a new one
        :type session: AsRestSession
        :param kwargs: additional arguments to be passed to the constructor of the :attr:`session` object to be used
            for all requests
        """
        if isinstance(tokens, str):
            tokens = Tokens(access_token=tokens)
        elif tokens is None:
            tokens = os.getenv('WEBEX_ACCESS_TOKEN')
            if tokens is None:
                raise ValueError('if no access token is passed, then a valid access token has to be present in '
                                 'WEBEX_ACCESS_TOKEN environment variable')
            tokens = Tokens(access_token=tokens)

        if session is None:
            session = AsRestSession(tokens=tokens, concurrent_requests=concurrent_requests, retry_429=retry_429,
                                  **kwargs)
            self._must_close_session = True
        else:
            self._must_close_session = False
        self.session = session

        self.admin_audit = AsAdminAuditEventsApi(session=session)
        self.attachment_actions = AsAttachmentActionsApi(session=session)
        self.authorizations = AsAuthorizationsApi(session=session)
        self.cdr = AsDetailedCDRApi(session=session)
        self.converged_recordings = AsConvergedRecordingsApi(session=session)
        self.device_configurations = AsDeviceConfigurationsApi(session=session)
        self.devices = AsDevicesApi(session=session)
        self.events = AsEventsApi(session=session)
        self.groups = AsGroupsApi(session=session)
        self.guests = AsGuestManagementApi(session=session)
        self.jobs = AsJobsApi(session=session)
        self.licenses = AsLicensesApi(session=session)
        self.locations = AsLocationsApi(session=session)
        self.me = AsMeSettingsApi(session=session)
        self.meetings = AsMeetingsApi(session=session)
        self.membership = AsMembershipApi(session=session)
        self.messages = AsMessagesApi(session=session)
        self.org_contacts = AsOrganizationContactsApi(session=session)
        self.organizations = AsOrganizationApi(session=session)
        self.person_settings = AsPersonSettingsApi(session=session)
        self.people = AsPeopleApi(session=session)
        self.reports = AsReportsApi(session=session)
        self.roles = AsRolesApi(session=session)
        self.rooms = AsRoomsApi(session=session)
        self.room_tabs = AsRoomTabsApi(session=session)
        self.scim = AsScimV2Api(session=session)
        self.status = AsStatusAPI(session=session)
        self.teams = AsTeamsApi(session=session)
        self.team_memberships = AsTeamMembershipsApi(session=session)
        self.telephony = AsTelephonyApi(session=session)
        self.webhook = AsWebhookApi(session=session)
        self.workspaces = AsWorkspacesApi(session=session)
        self.workspace_locations = AsWorkspaceLocationApi(session=session)
        self.workspace_personalization = AsWorkspacePersonalizationApi(session=session)
        self.workspace_settings = AsWorkspaceSettingsApi(session=session)
        self.xapi = AsXApi(session=session)

    @property
    def access_token(self) -> str:
        """
        access token used for all requests

        :return: access token
        :rtype: str
        """
        return self.session.access_token

    async def close(self):
        if self._must_close_session:
            await self.session.close()

    async def __aenter__(self):
        return self

    async def __aexit__(self, exc_type, exc_val, exc_tb):
        await self.close()