# pylint: disable=too-many-lines,too-many-statements
# coding=utf-8
# --------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# Code generated by Microsoft (R) AutoRest Code Generator.
# Changes may cause incorrect behavior and will be lost if the code is regenerated.
# --------------------------------------------------------------------------
import datetime
from io import IOBase
import sys
from typing import (
    Any,
    AsyncIterable,
    AsyncIterator,
    Callable,
    Dict,
    IO,
    List,
    Optional,
    Type,
    TypeVar,
    Union,
    cast,
    overload,
)
import urllib.parse

from azure.core.async_paging import AsyncItemPaged, AsyncList
from azure.core.exceptions import (
    ClientAuthenticationError,
    HttpResponseError,
    ResourceExistsError,
    ResourceNotFoundError,
    ResourceNotModifiedError,
    StreamClosedError,
    StreamConsumedError,
    map_error,
)
from azure.core.pipeline import PipelineResponse
from azure.core.polling import AsyncLROPoller, AsyncNoPolling, AsyncPollingMethod
from azure.core.rest import AsyncHttpResponse, HttpRequest
from azure.core.tracing.decorator import distributed_trace
from azure.core.tracing.decorator_async import distributed_trace_async
from azure.core.utils import case_insensitive_dict
from azure.mgmt.core.exceptions import ARMErrorFormat
from azure.mgmt.core.polling.async_arm_polling import AsyncARMPolling

from ... import models as _models
from ...operations._billing_accounts_operations import (
    build_add_payment_terms_request,
    build_cancel_payment_terms_request,
    build_confirm_transition_request,
    build_get_request,
    build_list_invoice_sections_by_create_subscription_permission_request,
    build_list_request,
    build_update_request,
    build_validate_payment_terms_request,
)

if sys.version_info >= (3, 9):
    from collections.abc import MutableMapping
else:
    from typing import MutableMapping  # type: ignore  # pylint: disable=ungrouped-imports
T = TypeVar("T")
ClsType = Optional[Callable[[PipelineResponse[HttpRequest, AsyncHttpResponse], T, Dict[str, Any]], Any]]


class BillingAccountsOperations:
    """
    .. warning::
        **DO NOT** instantiate this class directly.

        Instead, you should access the following operations through
        :class:`~azure.mgmt.billing.aio.BillingManagementClient`'s
        :attr:`billing_accounts` attribute.
    """

    models = _models

    def __init__(self, *args, **kwargs) -> None:
        input_args = list(args)
        self._client = input_args.pop(0) if input_args else kwargs.pop("client")
        self._config = input_args.pop(0) if input_args else kwargs.pop("config")
        self._serialize = input_args.pop(0) if input_args else kwargs.pop("serializer")
        self._deserialize = input_args.pop(0) if input_args else kwargs.pop("deserializer")

    async def _add_payment_terms_initial(
        self, billing_account_name: str, parameters: Union[List[_models.PaymentTerm], IO[bytes]], **kwargs: Any
    ) -> AsyncIterator[bytes]:
        error_map: MutableMapping[int, Type[HttpResponseError]] = {
            401: ClientAuthenticationError,
            404: ResourceNotFoundError,
            409: ResourceExistsError,
            304: ResourceNotModifiedError,
        }
        error_map.update(kwargs.pop("error_map", {}) or {})

        _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {})
        _params = case_insensitive_dict(kwargs.pop("params", {}) or {})

        api_version: str = kwargs.pop("api_version", _params.pop("api-version", self._config.api_version))
        content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None))
        cls: ClsType[AsyncIterator[bytes]] = kwargs.pop("cls", None)

        content_type = content_type or "application/json"
        _json = None
        _content = None
        if isinstance(parameters, (IOBase, bytes)):
            _content = parameters
        else:
            _json = self._serialize.body(parameters, "[PaymentTerm]")

        _request = build_add_payment_terms_request(
            billing_account_name=billing_account_name,
            api_version=api_version,
            content_type=content_type,
            json=_json,
            content=_content,
            headers=_headers,
            params=_params,
        )
        _request.url = self._client.format_url(_request.url)

        _decompress = kwargs.pop("decompress", True)
        _stream = True
        pipeline_response: PipelineResponse = await self._client._pipeline.run(  # pylint: disable=protected-access
            _request, stream=_stream, **kwargs
        )

        response = pipeline_response.http_response

        if response.status_code not in [200, 202]:
            try:
                await response.read()  # Load the body in memory and close the socket
            except (StreamConsumedError, StreamClosedError):
                pass
            map_error(status_code=response.status_code, response=response, error_map=error_map)
            error = self._deserialize.failsafe_deserialize(_models.ErrorResponse, pipeline_response)
            raise HttpResponseError(response=response, model=error, error_format=ARMErrorFormat)

        response_headers = {}
        if response.status_code == 202:
            response_headers["Location"] = self._deserialize("str", response.headers.get("Location"))
            response_headers["Retry-After"] = self._deserialize("int", response.headers.get("Retry-After"))

        deserialized = response.stream_download(self._client._pipeline, decompress=_decompress)

        if cls:
            return cls(pipeline_response, deserialized, response_headers)  # type: ignore

        return deserialized  # type: ignore

    @overload
    async def begin_add_payment_terms(
        self,
        billing_account_name: str,
        parameters: List[_models.PaymentTerm],
        *,
        content_type: str = "application/json",
        **kwargs: Any
    ) -> AsyncLROPoller[_models.BillingAccount]:
        """Adds payment terms to all the billing profiles under the billing account. Currently, payment
        terms can be added only on billing accounts that have Agreement Type as 'Microsoft Customer
        Agreement' and AccountType as 'Enterprise'. This action needs pre-authorization and only Field
        Sellers are authorized to add the payment terms and is not a self-serve action.

        .. seealso::
           - https://docs.microsoft.com/en-us/rest/api/billing/

        :param billing_account_name: The ID that uniquely identifies a billing account. Required.
        :type billing_account_name: str
        :param parameters: The properties of payment term. Required.
        :type parameters: list[~azure.mgmt.billing.models.PaymentTerm]
        :keyword content_type: Body Parameter content-type. Content type parameter for JSON body.
         Default value is "application/json".
        :paramtype content_type: str
        :return: An instance of AsyncLROPoller that returns either BillingAccount or the result of
         cls(response)
        :rtype: ~azure.core.polling.AsyncLROPoller[~azure.mgmt.billing.models.BillingAccount]
        :raises ~azure.core.exceptions.HttpResponseError:
        """

    @overload
    async def begin_add_payment_terms(
        self, billing_account_name: str, parameters: IO[bytes], *, content_type: str = "application/json", **kwargs: Any
    ) -> AsyncLROPoller[_models.BillingAccount]:
        """Adds payment terms to all the billing profiles under the billing account. Currently, payment
        terms can be added only on billing accounts that have Agreement Type as 'Microsoft Customer
        Agreement' and AccountType as 'Enterprise'. This action needs pre-authorization and only Field
        Sellers are authorized to add the payment terms and is not a self-serve action.

        .. seealso::
           - https://docs.microsoft.com/en-us/rest/api/billing/

        :param billing_account_name: The ID that uniquely identifies a billing account. Required.
        :type billing_account_name: str
        :param parameters: The properties of payment term. Required.
        :type parameters: IO[bytes]
        :keyword content_type: Body Parameter content-type. Content type parameter for binary body.
         Default value is "application/json".
        :paramtype content_type: str
        :return: An instance of AsyncLROPoller that returns either BillingAccount or the result of
         cls(response)
        :rtype: ~azure.core.polling.AsyncLROPoller[~azure.mgmt.billing.models.BillingAccount]
        :raises ~azure.core.exceptions.HttpResponseError:
        """

    @distributed_trace_async
    async def begin_add_payment_terms(
        self, billing_account_name: str, parameters: Union[List[_models.PaymentTerm], IO[bytes]], **kwargs: Any
    ) -> AsyncLROPoller[_models.BillingAccount]:
        """Adds payment terms to all the billing profiles under the billing account. Currently, payment
        terms can be added only on billing accounts that have Agreement Type as 'Microsoft Customer
        Agreement' and AccountType as 'Enterprise'. This action needs pre-authorization and only Field
        Sellers are authorized to add the payment terms and is not a self-serve action.

        .. seealso::
           - https://docs.microsoft.com/en-us/rest/api/billing/

        :param billing_account_name: The ID that uniquely identifies a billing account. Required.
        :type billing_account_name: str
        :param parameters: The properties of payment term. Is either a [PaymentTerm] type or a
         IO[bytes] type. Required.
        :type parameters: list[~azure.mgmt.billing.models.PaymentTerm] or IO[bytes]
        :return: An instance of AsyncLROPoller that returns either BillingAccount or the result of
         cls(response)
        :rtype: ~azure.core.polling.AsyncLROPoller[~azure.mgmt.billing.models.BillingAccount]
        :raises ~azure.core.exceptions.HttpResponseError:
        """
        _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {})
        _params = case_insensitive_dict(kwargs.pop("params", {}) or {})

        api_version: str = kwargs.pop("api_version", _params.pop("api-version", self._config.api_version))
        content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None))
        cls: ClsType[_models.BillingAccount] = kwargs.pop("cls", None)
        polling: Union[bool, AsyncPollingMethod] = kwargs.pop("polling", True)
        lro_delay = kwargs.pop("polling_interval", self._config.polling_interval)
        cont_token: Optional[str] = kwargs.pop("continuation_token", None)
        if cont_token is None:
            raw_result = await self._add_payment_terms_initial(
                billing_account_name=billing_account_name,
                parameters=parameters,
                api_version=api_version,
                content_type=content_type,
                cls=lambda x, y, z: x,
                headers=_headers,
                params=_params,
                **kwargs
            )
            await raw_result.http_response.read()  # type: ignore
        kwargs.pop("error_map", None)

        def get_long_running_output(pipeline_response):
            deserialized = self._deserialize("BillingAccount", pipeline_response.http_response)
            if cls:
                return cls(pipeline_response, deserialized, {})  # type: ignore
            return deserialized

        if polling is True:
            polling_method: AsyncPollingMethod = cast(
                AsyncPollingMethod, AsyncARMPolling(lro_delay, lro_options={"final-state-via": "location"}, **kwargs)
            )
        elif polling is False:
            polling_method = cast(AsyncPollingMethod, AsyncNoPolling())
        else:
            polling_method = polling
        if cont_token:
            return AsyncLROPoller[_models.BillingAccount].from_continuation_token(
                polling_method=polling_method,
                continuation_token=cont_token,
                client=self._client,
                deserialization_callback=get_long_running_output,
            )
        return AsyncLROPoller[_models.BillingAccount](
            self._client, raw_result, get_long_running_output, polling_method  # type: ignore
        )

    async def _cancel_payment_terms_initial(
        self, billing_account_name: str, parameters: datetime.datetime, **kwargs: Any
    ) -> AsyncIterator[bytes]:
        error_map: MutableMapping[int, Type[HttpResponseError]] = {
            401: ClientAuthenticationError,
            404: ResourceNotFoundError,
            409: ResourceExistsError,
            304: ResourceNotModifiedError,
        }
        error_map.update(kwargs.pop("error_map", {}) or {})

        _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {})
        _params = case_insensitive_dict(kwargs.pop("params", {}) or {})

        api_version: str = kwargs.pop("api_version", _params.pop("api-version", self._config.api_version))
        content_type: str = kwargs.pop("content_type", _headers.pop("Content-Type", "application/json"))
        cls: ClsType[AsyncIterator[bytes]] = kwargs.pop("cls", None)

        _json = self._serialize.body(parameters, "iso-8601")

        _request = build_cancel_payment_terms_request(
            billing_account_name=billing_account_name,
            api_version=api_version,
            content_type=content_type,
            json=_json,
            headers=_headers,
            params=_params,
        )
        _request.url = self._client.format_url(_request.url)

        _decompress = kwargs.pop("decompress", True)
        _stream = True
        pipeline_response: PipelineResponse = await self._client._pipeline.run(  # pylint: disable=protected-access
            _request, stream=_stream, **kwargs
        )

        response = pipeline_response.http_response

        if response.status_code not in [200, 202]:
            try:
                await response.read()  # Load the body in memory and close the socket
            except (StreamConsumedError, StreamClosedError):
                pass
            map_error(status_code=response.status_code, response=response, error_map=error_map)
            error = self._deserialize.failsafe_deserialize(_models.ErrorResponse, pipeline_response)
            raise HttpResponseError(response=response, model=error, error_format=ARMErrorFormat)

        response_headers = {}
        if response.status_code == 202:
            response_headers["Location"] = self._deserialize("str", response.headers.get("Location"))
            response_headers["Retry-After"] = self._deserialize("int", response.headers.get("Retry-After"))

        deserialized = response.stream_download(self._client._pipeline, decompress=_decompress)

        if cls:
            return cls(pipeline_response, deserialized, response_headers)  # type: ignore

        return deserialized  # type: ignore

    @distributed_trace_async
    async def begin_cancel_payment_terms(
        self, billing_account_name: str, parameters: datetime.datetime, **kwargs: Any
    ) -> AsyncLROPoller[_models.BillingAccount]:
        """Cancels all the payment terms on billing account that falls after the cancellation date in the
        request. Currently, cancel payment terms is only served by admin actions and is not a
        self-serve action.

        .. seealso::
           - https://docs.microsoft.com/en-us/rest/api/billing/

        :param billing_account_name: The ID that uniquely identifies a billing account. Required.
        :type billing_account_name: str
        :param parameters: Date after which any payment terms that needs to be cancelled. Required.
        :type parameters: ~datetime.datetime
        :return: An instance of AsyncLROPoller that returns either BillingAccount or the result of
         cls(response)
        :rtype: ~azure.core.polling.AsyncLROPoller[~azure.mgmt.billing.models.BillingAccount]
        :raises ~azure.core.exceptions.HttpResponseError:
        """
        _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {})
        _params = case_insensitive_dict(kwargs.pop("params", {}) or {})

        api_version: str = kwargs.pop("api_version", _params.pop("api-version", self._config.api_version))
        content_type: str = kwargs.pop("content_type", _headers.pop("Content-Type", "application/json"))
        cls: ClsType[_models.BillingAccount] = kwargs.pop("cls", None)
        polling: Union[bool, AsyncPollingMethod] = kwargs.pop("polling", True)
        lro_delay = kwargs.pop("polling_interval", self._config.polling_interval)
        cont_token: Optional[str] = kwargs.pop("continuation_token", None)
        if cont_token is None:
            raw_result = await self._cancel_payment_terms_initial(
                billing_account_name=billing_account_name,
                parameters=parameters,
                api_version=api_version,
                content_type=content_type,
                cls=lambda x, y, z: x,
                headers=_headers,
                params=_params,
                **kwargs
            )
            await raw_result.http_response.read()  # type: ignore
        kwargs.pop("error_map", None)

        def get_long_running_output(pipeline_response):
            deserialized = self._deserialize("BillingAccount", pipeline_response.http_response)
            if cls:
                return cls(pipeline_response, deserialized, {})  # type: ignore
            return deserialized

        if polling is True:
            polling_method: AsyncPollingMethod = cast(
                AsyncPollingMethod, AsyncARMPolling(lro_delay, lro_options={"final-state-via": "location"}, **kwargs)
            )
        elif polling is False:
            polling_method = cast(AsyncPollingMethod, AsyncNoPolling())
        else:
            polling_method = polling
        if cont_token:
            return AsyncLROPoller[_models.BillingAccount].from_continuation_token(
                polling_method=polling_method,
                continuation_token=cont_token,
                client=self._client,
                deserialization_callback=get_long_running_output,
            )
        return AsyncLROPoller[_models.BillingAccount](
            self._client, raw_result, get_long_running_output, polling_method  # type: ignore
        )

    @distributed_trace_async
    async def confirm_transition(self, billing_account_name: str, **kwargs: Any) -> _models.TransitionDetails:
        """Gets the transition details for a billing account that has transitioned from agreement type
        Microsoft Online Services Program to agreement type Microsoft Customer Agreement.

        .. seealso::
           - https://docs.microsoft.com/en-us/rest/api/billing/

        :param billing_account_name: The ID that uniquely identifies a billing account. Required.
        :type billing_account_name: str
        :return: TransitionDetails or the result of cls(response)
        :rtype: ~azure.mgmt.billing.models.TransitionDetails
        :raises ~azure.core.exceptions.HttpResponseError:
        """
        error_map: MutableMapping[int, Type[HttpResponseError]] = {
            401: ClientAuthenticationError,
            404: ResourceNotFoundError,
            409: ResourceExistsError,
            304: ResourceNotModifiedError,
        }
        error_map.update(kwargs.pop("error_map", {}) or {})

        _headers = kwargs.pop("headers", {}) or {}
        _params = case_insensitive_dict(kwargs.pop("params", {}) or {})

        api_version: str = kwargs.pop("api_version", _params.pop("api-version", self._config.api_version))
        cls: ClsType[_models.TransitionDetails] = kwargs.pop("cls", None)

        _request = build_confirm_transition_request(
            billing_account_name=billing_account_name,
            api_version=api_version,
            headers=_headers,
            params=_params,
        )
        _request.url = self._client.format_url(_request.url)

        _stream = False
        pipeline_response: PipelineResponse = await self._client._pipeline.run(  # pylint: disable=protected-access
            _request, stream=_stream, **kwargs
        )

        response = pipeline_response.http_response

        if response.status_code not in [200]:
            map_error(status_code=response.status_code, response=response, error_map=error_map)
            error = self._deserialize.failsafe_deserialize(_models.ErrorResponse, pipeline_response)
            raise HttpResponseError(response=response, model=error, error_format=ARMErrorFormat)

        deserialized = self._deserialize("TransitionDetails", pipeline_response.http_response)

        if cls:
            return cls(pipeline_response, deserialized, {})  # type: ignore

        return deserialized  # type: ignore

    @distributed_trace
    def list_invoice_sections_by_create_subscription_permission(  # pylint: disable=name-too-long
        self, billing_account_name: str, filter: Optional[str] = None, **kwargs: Any
    ) -> AsyncIterable["_models.InvoiceSectionWithCreateSubPermission"]:
        """Lists the invoice sections for which the user has permission to create Azure subscriptions. The
        operation is supported only for billing accounts with agreement type Microsoft Customer
        Agreement.

        .. seealso::
           - https://docs.microsoft.com/en-us/rest/api/billing/

        :param billing_account_name: The ID that uniquely identifies a billing account. Required.
        :type billing_account_name: str
        :param filter: The filter query option allows clients to filter a collection of resources that
         are addressed by a request URL. Default value is None.
        :type filter: str
        :return: An iterator like instance of either InvoiceSectionWithCreateSubPermission or the
         result of cls(response)
        :rtype:
         ~azure.core.async_paging.AsyncItemPaged[~azure.mgmt.billing.models.InvoiceSectionWithCreateSubPermission]
        :raises ~azure.core.exceptions.HttpResponseError:
        """
        _headers = kwargs.pop("headers", {}) or {}
        _params = case_insensitive_dict(kwargs.pop("params", {}) or {})

        api_version: str = kwargs.pop("api_version", _params.pop("api-version", self._config.api_version))
        cls: ClsType[_models.InvoiceSectionWithCreateSubPermissionListResult] = kwargs.pop("cls", None)

        error_map: MutableMapping[int, Type[HttpResponseError]] = {
            401: ClientAuthenticationError,
            404: ResourceNotFoundError,
            409: ResourceExistsError,
            304: ResourceNotModifiedError,
        }
        error_map.update(kwargs.pop("error_map", {}) or {})

        def prepare_request(next_link=None):
            if not next_link:

                _request = build_list_invoice_sections_by_create_subscription_permission_request(
                    billing_account_name=billing_account_name,
                    filter=filter,
                    api_version=api_version,
                    headers=_headers,
                    params=_params,
                )
                _request.url = self._client.format_url(_request.url)

            else:
                # make call to next link with the client's api-version
                _parsed_next_link = urllib.parse.urlparse(next_link)
                _next_request_params = case_insensitive_dict(
                    {
                        key: [urllib.parse.quote(v) for v in value]
                        for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items()
                    }
                )
                _next_request_params["api-version"] = self._config.api_version
                _request = HttpRequest(
                    "GET", urllib.parse.urljoin(next_link, _parsed_next_link.path), params=_next_request_params
                )
                _request.url = self._client.format_url(_request.url)
                _request.method = "GET"
            return _request

        async def extract_data(pipeline_response):
            deserialized = self._deserialize("InvoiceSectionWithCreateSubPermissionListResult", pipeline_response)
            list_of_elem = deserialized.value
            if cls:
                list_of_elem = cls(list_of_elem)  # type: ignore
            return deserialized.next_link or None, AsyncList(list_of_elem)

        async def get_next(next_link=None):
            _request = prepare_request(next_link)

            _stream = False
            pipeline_response: PipelineResponse = await self._client._pipeline.run(  # pylint: disable=protected-access
                _request, stream=_stream, **kwargs
            )
            response = pipeline_response.http_response

            if response.status_code not in [200]:
                map_error(status_code=response.status_code, response=response, error_map=error_map)
                error = self._deserialize.failsafe_deserialize(_models.ErrorResponse, pipeline_response)
                raise HttpResponseError(response=response, model=error, error_format=ARMErrorFormat)

            return pipeline_response

        return AsyncItemPaged(get_next, extract_data)

    @overload
    async def validate_payment_terms(
        self,
        billing_account_name: str,
        parameters: List[_models.PaymentTerm],
        *,
        content_type: str = "application/json",
        **kwargs: Any
    ) -> _models.PaymentTermsEligibilityResult:
        """Validates payment terms on a billing account with agreement type 'Microsoft Customer Agreement'
        and account type 'Enterprise'.

        .. seealso::
           - https://docs.microsoft.com/en-us/rest/api/billing/

        :param billing_account_name: The ID that uniquely identifies a billing account. Required.
        :type billing_account_name: str
        :param parameters: The properties of payment term. Required.
        :type parameters: list[~azure.mgmt.billing.models.PaymentTerm]
        :keyword content_type: Body Parameter content-type. Content type parameter for JSON body.
         Default value is "application/json".
        :paramtype content_type: str
        :return: PaymentTermsEligibilityResult or the result of cls(response)
        :rtype: ~azure.mgmt.billing.models.PaymentTermsEligibilityResult
        :raises ~azure.core.exceptions.HttpResponseError:
        """

    @overload
    async def validate_payment_terms(
        self, billing_account_name: str, parameters: IO[bytes], *, content_type: str = "application/json", **kwargs: Any
    ) -> _models.PaymentTermsEligibilityResult:
        """Validates payment terms on a billing account with agreement type 'Microsoft Customer Agreement'
        and account type 'Enterprise'.

        .. seealso::
           - https://docs.microsoft.com/en-us/rest/api/billing/

        :param billing_account_name: The ID that uniquely identifies a billing account. Required.
        :type billing_account_name: str
        :param parameters: The properties of payment term. Required.
        :type parameters: IO[bytes]
        :keyword content_type: Body Parameter content-type. Content type parameter for binary body.
         Default value is "application/json".
        :paramtype content_type: str
        :return: PaymentTermsEligibilityResult or the result of cls(response)
        :rtype: ~azure.mgmt.billing.models.PaymentTermsEligibilityResult
        :raises ~azure.core.exceptions.HttpResponseError:
        """

    @distributed_trace_async
    async def validate_payment_terms(
        self, billing_account_name: str, parameters: Union[List[_models.PaymentTerm], IO[bytes]], **kwargs: Any
    ) -> _models.PaymentTermsEligibilityResult:
        """Validates payment terms on a billing account with agreement type 'Microsoft Customer Agreement'
        and account type 'Enterprise'.

        .. seealso::
           - https://docs.microsoft.com/en-us/rest/api/billing/

        :param billing_account_name: The ID that uniquely identifies a billing account. Required.
        :type billing_account_name: str
        :param parameters: The properties of payment term. Is either a [PaymentTerm] type or a
         IO[bytes] type. Required.
        :type parameters: list[~azure.mgmt.billing.models.PaymentTerm] or IO[bytes]
        :return: PaymentTermsEligibilityResult or the result of cls(response)
        :rtype: ~azure.mgmt.billing.models.PaymentTermsEligibilityResult
        :raises ~azure.core.exceptions.HttpResponseError:
        """
        error_map: MutableMapping[int, Type[HttpResponseError]] = {
            401: ClientAuthenticationError,
            404: ResourceNotFoundError,
            409: ResourceExistsError,
            304: ResourceNotModifiedError,
        }
        error_map.update(kwargs.pop("error_map", {}) or {})

        _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {})
        _params = case_insensitive_dict(kwargs.pop("params", {}) or {})

        api_version: str = kwargs.pop("api_version", _params.pop("api-version", self._config.api_version))
        content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None))
        cls: ClsType[_models.PaymentTermsEligibilityResult] = kwargs.pop("cls", None)

        content_type = content_type or "application/json"
        _json = None
        _content = None
        if isinstance(parameters, (IOBase, bytes)):
            _content = parameters
        else:
            _json = self._serialize.body(parameters, "[PaymentTerm]")

        _request = build_validate_payment_terms_request(
            billing_account_name=billing_account_name,
            api_version=api_version,
            content_type=content_type,
            json=_json,
            content=_content,
            headers=_headers,
            params=_params,
        )
        _request.url = self._client.format_url(_request.url)

        _stream = False
        pipeline_response: PipelineResponse = await self._client._pipeline.run(  # pylint: disable=protected-access
            _request, stream=_stream, **kwargs
        )

        response = pipeline_response.http_response

        if response.status_code not in [200]:
            map_error(status_code=response.status_code, response=response, error_map=error_map)
            error = self._deserialize.failsafe_deserialize(_models.ErrorResponse, pipeline_response)
            raise HttpResponseError(response=response, model=error, error_format=ARMErrorFormat)

        deserialized = self._deserialize("PaymentTermsEligibilityResult", pipeline_response.http_response)

        if cls:
            return cls(pipeline_response, deserialized, {})  # type: ignore

        return deserialized  # type: ignore

    @distributed_trace_async
    async def get(self, billing_account_name: str, **kwargs: Any) -> _models.BillingAccount:
        """Gets a billing account by its ID.

        .. seealso::
           - https://docs.microsoft.com/en-us/rest/api/billing/

        :param billing_account_name: The ID that uniquely identifies a billing account. Required.
        :type billing_account_name: str
        :return: BillingAccount or the result of cls(response)
        :rtype: ~azure.mgmt.billing.models.BillingAccount
        :raises ~azure.core.exceptions.HttpResponseError:
        """
        error_map: MutableMapping[int, Type[HttpResponseError]] = {
            401: ClientAuthenticationError,
            404: ResourceNotFoundError,
            409: ResourceExistsError,
            304: ResourceNotModifiedError,
        }
        error_map.update(kwargs.pop("error_map", {}) or {})

        _headers = kwargs.pop("headers", {}) or {}
        _params = case_insensitive_dict(kwargs.pop("params", {}) or {})

        api_version: str = kwargs.pop("api_version", _params.pop("api-version", self._config.api_version))
        cls: ClsType[_models.BillingAccount] = kwargs.pop("cls", None)

        _request = build_get_request(
            billing_account_name=billing_account_name,
            api_version=api_version,
            headers=_headers,
            params=_params,
        )
        _request.url = self._client.format_url(_request.url)

        _stream = False
        pipeline_response: PipelineResponse = await self._client._pipeline.run(  # pylint: disable=protected-access
            _request, stream=_stream, **kwargs
        )

        response = pipeline_response.http_response

        if response.status_code not in [200]:
            map_error(status_code=response.status_code, response=response, error_map=error_map)
            error = self._deserialize.failsafe_deserialize(_models.ErrorResponse, pipeline_response)
            raise HttpResponseError(response=response, model=error, error_format=ARMErrorFormat)

        deserialized = self._deserialize("BillingAccount", pipeline_response.http_response)

        if cls:
            return cls(pipeline_response, deserialized, {})  # type: ignore

        return deserialized  # type: ignore

    async def _update_initial(
        self, billing_account_name: str, parameters: Union[_models.BillingAccountPatch, IO[bytes]], **kwargs: Any
    ) -> AsyncIterator[bytes]:
        error_map: MutableMapping[int, Type[HttpResponseError]] = {
            401: ClientAuthenticationError,
            404: ResourceNotFoundError,
            409: ResourceExistsError,
            304: ResourceNotModifiedError,
        }
        error_map.update(kwargs.pop("error_map", {}) or {})

        _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {})
        _params = case_insensitive_dict(kwargs.pop("params", {}) or {})

        api_version: str = kwargs.pop("api_version", _params.pop("api-version", self._config.api_version))
        content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None))
        cls: ClsType[AsyncIterator[bytes]] = kwargs.pop("cls", None)

        content_type = content_type or "application/json"
        _json = None
        _content = None
        if isinstance(parameters, (IOBase, bytes)):
            _content = parameters
        else:
            _json = self._serialize.body(parameters, "BillingAccountPatch")

        _request = build_update_request(
            billing_account_name=billing_account_name,
            api_version=api_version,
            content_type=content_type,
            json=_json,
            content=_content,
            headers=_headers,
            params=_params,
        )
        _request.url = self._client.format_url(_request.url)

        _decompress = kwargs.pop("decompress", True)
        _stream = True
        pipeline_response: PipelineResponse = await self._client._pipeline.run(  # pylint: disable=protected-access
            _request, stream=_stream, **kwargs
        )

        response = pipeline_response.http_response

        if response.status_code not in [200, 202]:
            try:
                await response.read()  # Load the body in memory and close the socket
            except (StreamConsumedError, StreamClosedError):
                pass
            map_error(status_code=response.status_code, response=response, error_map=error_map)
            error = self._deserialize.failsafe_deserialize(_models.ErrorResponse, pipeline_response)
            raise HttpResponseError(response=response, model=error, error_format=ARMErrorFormat)

        response_headers = {}
        if response.status_code == 202:
            response_headers["Location"] = self._deserialize("str", response.headers.get("Location"))
            response_headers["Retry-After"] = self._deserialize("int", response.headers.get("Retry-After"))

        deserialized = response.stream_download(self._client._pipeline, decompress=_decompress)

        if cls:
            return cls(pipeline_response, deserialized, response_headers)  # type: ignore

        return deserialized  # type: ignore

    @overload
    async def begin_update(
        self,
        billing_account_name: str,
        parameters: _models.BillingAccountPatch,
        *,
        content_type: str = "application/json",
        **kwargs: Any
    ) -> AsyncLROPoller[_models.BillingAccount]:
        """Updates the properties of a billing account. Currently, displayName and address can be updated
        for billing accounts with agreement type Microsoft Customer Agreement. Currently address and
        notification email address can be updated for billing accounts with agreement type Microsoft
        Online Services Agreement. Currently, purchase order number can be edited for billing accounts
        with agreement type Enterprise Agreement.

        .. seealso::
           - https://docs.microsoft.com/en-us/rest/api/billing/

        :param billing_account_name: The ID that uniquely identifies a billing account. Required.
        :type billing_account_name: str
        :param parameters: A billing account. Required.
        :type parameters: ~azure.mgmt.billing.models.BillingAccountPatch
        :keyword content_type: Body Parameter content-type. Content type parameter for JSON body.
         Default value is "application/json".
        :paramtype content_type: str
        :return: An instance of AsyncLROPoller that returns either BillingAccount or the result of
         cls(response)
        :rtype: ~azure.core.polling.AsyncLROPoller[~azure.mgmt.billing.models.BillingAccount]
        :raises ~azure.core.exceptions.HttpResponseError:
        """

    @overload
    async def begin_update(
        self, billing_account_name: str, parameters: IO[bytes], *, content_type: str = "application/json", **kwargs: Any
    ) -> AsyncLROPoller[_models.BillingAccount]:
        """Updates the properties of a billing account. Currently, displayName and address can be updated
        for billing accounts with agreement type Microsoft Customer Agreement. Currently address and
        notification email address can be updated for billing accounts with agreement type Microsoft
        Online Services Agreement. Currently, purchase order number can be edited for billing accounts
        with agreement type Enterprise Agreement.

        .. seealso::
           - https://docs.microsoft.com/en-us/rest/api/billing/

        :param billing_account_name: The ID that uniquely identifies a billing account. Required.
        :type billing_account_name: str
        :param parameters: A billing account. Required.
        :type parameters: IO[bytes]
        :keyword content_type: Body Parameter content-type. Content type parameter for binary body.
         Default value is "application/json".
        :paramtype content_type: str
        :return: An instance of AsyncLROPoller that returns either BillingAccount or the result of
         cls(response)
        :rtype: ~azure.core.polling.AsyncLROPoller[~azure.mgmt.billing.models.BillingAccount]
        :raises ~azure.core.exceptions.HttpResponseError:
        """

    @distributed_trace_async
    async def begin_update(
        self, billing_account_name: str, parameters: Union[_models.BillingAccountPatch, IO[bytes]], **kwargs: Any
    ) -> AsyncLROPoller[_models.BillingAccount]:
        """Updates the properties of a billing account. Currently, displayName and address can be updated
        for billing accounts with agreement type Microsoft Customer Agreement. Currently address and
        notification email address can be updated for billing accounts with agreement type Microsoft
        Online Services Agreement. Currently, purchase order number can be edited for billing accounts
        with agreement type Enterprise Agreement.

        .. seealso::
           - https://docs.microsoft.com/en-us/rest/api/billing/

        :param billing_account_name: The ID that uniquely identifies a billing account. Required.
        :type billing_account_name: str
        :param parameters: A billing account. Is either a BillingAccountPatch type or a IO[bytes] type.
         Required.
        :type parameters: ~azure.mgmt.billing.models.BillingAccountPatch or IO[bytes]
        :return: An instance of AsyncLROPoller that returns either BillingAccount or the result of
         cls(response)
        :rtype: ~azure.core.polling.AsyncLROPoller[~azure.mgmt.billing.models.BillingAccount]
        :raises ~azure.core.exceptions.HttpResponseError:
        """
        _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {})
        _params = case_insensitive_dict(kwargs.pop("params", {}) or {})

        api_version: str = kwargs.pop("api_version", _params.pop("api-version", self._config.api_version))
        content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None))
        cls: ClsType[_models.BillingAccount] = kwargs.pop("cls", None)
        polling: Union[bool, AsyncPollingMethod] = kwargs.pop("polling", True)
        lro_delay = kwargs.pop("polling_interval", self._config.polling_interval)
        cont_token: Optional[str] = kwargs.pop("continuation_token", None)
        if cont_token is None:
            raw_result = await self._update_initial(
                billing_account_name=billing_account_name,
                parameters=parameters,
                api_version=api_version,
                content_type=content_type,
                cls=lambda x, y, z: x,
                headers=_headers,
                params=_params,
                **kwargs
            )
            await raw_result.http_response.read()  # type: ignore
        kwargs.pop("error_map", None)

        def get_long_running_output(pipeline_response):
            deserialized = self._deserialize("BillingAccount", pipeline_response.http_response)
            if cls:
                return cls(pipeline_response, deserialized, {})  # type: ignore
            return deserialized

        if polling is True:
            polling_method: AsyncPollingMethod = cast(
                AsyncPollingMethod, AsyncARMPolling(lro_delay, lro_options={"final-state-via": "location"}, **kwargs)
            )
        elif polling is False:
            polling_method = cast(AsyncPollingMethod, AsyncNoPolling())
        else:
            polling_method = polling
        if cont_token:
            return AsyncLROPoller[_models.BillingAccount].from_continuation_token(
                polling_method=polling_method,
                continuation_token=cont_token,
                client=self._client,
                deserialization_callback=get_long_running_output,
            )
        return AsyncLROPoller[_models.BillingAccount](
            self._client, raw_result, get_long_running_output, polling_method  # type: ignore
        )

    @distributed_trace
    def list(
        self,
        include_all: bool = False,
        include_all_without_billing_profiles: bool = False,
        include_deleted: bool = False,
        include_pending_agreement: bool = False,
        include_resellee: bool = False,
        legal_owner_tid: Optional[str] = None,
        legal_owner_oid: Optional[str] = None,
        filter: Optional[str] = None,
        expand: Optional[str] = None,
        top: Optional[int] = None,
        skip: Optional[int] = None,
        search: Optional[str] = None,
        **kwargs: Any
    ) -> AsyncIterable["_models.BillingAccount"]:
        """Lists the billing accounts that a user has access to.

        .. seealso::
           - https://docs.microsoft.com/en-us/rest/api/billing/

        :param include_all: When true, results will include Billing Accounts that the user does not
         have a direct role assignment on if the user has one of the following AAD roles: Global
         Administrator, Global Reader, Billing Administrator. Default value is False.
        :type include_all: bool
        :param include_all_without_billing_profiles: When true, results will include Billing Accounts
         that are not fully created if the user has one of the following AAD roles: Global
         Administrator, Global Reader, Billing Administrator. Default value is False.
        :type include_all_without_billing_profiles: bool
        :param include_deleted: When true, results will include any billing accounts in a deleted
         state. Default value is False.
        :type include_deleted: bool
        :param include_pending_agreement: Includes billing accounts with agreement pending signature
         that the user has access to. Default value is False.
        :type include_pending_agreement: bool
        :param include_resellee: Includes the customer's billing account of Microsoft Partner Agreement
         that the user has access to. Default value is False.
        :type include_resellee: bool
        :param legal_owner_tid: Must be combined with legalOwnerOID, results will only include Billing
         Accounts for whom is legally responsible for the Billing Accounts. Optional. Default value is
         None.
        :type legal_owner_tid: str
        :param legal_owner_oid: Must be combined with legalOwnerTID, results will only include Billing
         Accounts for whom is legally responsible for the Billing Accounts. Optional. Default value is
         None.
        :type legal_owner_oid: str
        :param filter: The filter query option allows clients to filter a collection of resources that
         are addressed by a request URL. Default value is None.
        :type filter: str
        :param expand: Expand is allowed for SoldTo and EnrollmentDetails/PONumber. Default value is
         None.
        :type expand: str
        :param top: The top query option requests the number of items in the queried collection to be
         included in the result. The maximum supported value for top is 50. Default value is None.
        :type top: int
        :param skip: The skip query option requests the number of items in the queried collection that
         are to be skipped and not included in the result. Default value is None.
        :type skip: int
        :param search: The search query option allows clients to request items within a collection
         matching a free-text search expression. search is only supported for string fields. Default
         value is None.
        :type search: str
        :return: An iterator like instance of either BillingAccount or the result of cls(response)
        :rtype: ~azure.core.async_paging.AsyncItemPaged[~azure.mgmt.billing.models.BillingAccount]
        :raises ~azure.core.exceptions.HttpResponseError:
        """
        _headers = kwargs.pop("headers", {}) or {}
        _params = case_insensitive_dict(kwargs.pop("params", {}) or {})

        api_version: str = kwargs.pop("api_version", _params.pop("api-version", self._config.api_version))
        cls: ClsType[_models.BillingAccountListResult] = kwargs.pop("cls", None)

        error_map: MutableMapping[int, Type[HttpResponseError]] = {
            401: ClientAuthenticationError,
            404: ResourceNotFoundError,
            409: ResourceExistsError,
            304: ResourceNotModifiedError,
        }
        error_map.update(kwargs.pop("error_map", {}) or {})

        def prepare_request(next_link=None):
            if not next_link:

                _request = build_list_request(
                    include_all=include_all,
                    include_all_without_billing_profiles=include_all_without_billing_profiles,
                    include_deleted=include_deleted,
                    include_pending_agreement=include_pending_agreement,
                    include_resellee=include_resellee,
                    legal_owner_tid=legal_owner_tid,
                    legal_owner_oid=legal_owner_oid,
                    filter=filter,
                    expand=expand,
                    top=top,
                    skip=skip,
                    search=search,
                    api_version=api_version,
                    headers=_headers,
                    params=_params,
                )
                _request.url = self._client.format_url(_request.url)

            else:
                # make call to next link with the client's api-version
                _parsed_next_link = urllib.parse.urlparse(next_link)
                _next_request_params = case_insensitive_dict(
                    {
                        key: [urllib.parse.quote(v) for v in value]
                        for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items()
                    }
                )
                _next_request_params["api-version"] = self._config.api_version
                _request = HttpRequest(
                    "GET", urllib.parse.urljoin(next_link, _parsed_next_link.path), params=_next_request_params
                )
                _request.url = self._client.format_url(_request.url)
                _request.method = "GET"
            return _request

        async def extract_data(pipeline_response):
            deserialized = self._deserialize("BillingAccountListResult", pipeline_response)
            list_of_elem = deserialized.value
            if cls:
                list_of_elem = cls(list_of_elem)  # type: ignore
            return deserialized.next_link or None, AsyncList(list_of_elem)

        async def get_next(next_link=None):
            _request = prepare_request(next_link)

            _stream = False
            pipeline_response: PipelineResponse = await self._client._pipeline.run(  # pylint: disable=protected-access
                _request, stream=_stream, **kwargs
            )
            response = pipeline_response.http_response

            if response.status_code not in [200]:
                map_error(status_code=response.status_code, response=response, error_map=error_map)
                error = self._deserialize.failsafe_deserialize(_models.ErrorResponse, pipeline_response)
                raise HttpResponseError(response=response, model=error, error_format=ARMErrorFormat)

            return pipeline_response

        return AsyncItemPaged(get_next, extract_data)
