# pylint: disable=line-too-long,useless-suppression
# 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.
# --------------------------------------------------------------------------
from collections.abc import MutableMapping
from io import IOBase
from typing import Any, AsyncIterator, Callable, IO, Optional, TypeVar, Union, cast, overload

from azure.core import AsyncPipelineClient
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.polling.async_base_polling import AsyncLROBasePolling
from azure.core.rest import AsyncHttpResponse, HttpRequest
from azure.core.tracing.decorator_async import distributed_trace_async
from azure.core.utils import case_insensitive_dict

from ..._operations._operations import build_email_send_request
from ..._utils.utils import ClientMixinABC
from .._configuration import EmailClientConfiguration

JSON = MutableMapping[str, Any]
T = TypeVar("T")
ClsType = Optional[Callable[[PipelineResponse[HttpRequest, AsyncHttpResponse], T, dict[str, Any]], Any]]


class _EmailClientOperationsMixin(
    ClientMixinABC[AsyncPipelineClient[HttpRequest, AsyncHttpResponse], EmailClientConfiguration]
):

    async def _send_initial(
        self, message: Union[JSON, IO[bytes]], *, operation_id: Optional[str] = None, **kwargs: Any
    ) -> AsyncIterator[bytes]:
        error_map: MutableMapping = {
            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 = kwargs.pop("params", {}) or {}

        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(message, (IOBase, bytes)):
            _content = message
        else:
            _json = message

        _request = build_email_send_request(
            operation_id=operation_id,
            content_type=content_type,
            api_version=self._config.api_version,
            json=_json,
            content=_content,
            headers=_headers,
            params=_params,
        )
        path_format_arguments = {
            "endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True),
        }
        _request.url = self._client.format_url(_request.url, **path_format_arguments)

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

        response = pipeline_response.http_response

        if response.status_code not in [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)
            raise HttpResponseError(response=response)

        response_headers = {}
        response_headers["Operation-Location"] = self._deserialize("str", response.headers.get("Operation-Location"))
        response_headers["retry-after"] = self._deserialize("int", response.headers.get("retry-after"))

        deserialized = response.iter_bytes()

        if cls:
            return cls(pipeline_response, cast(AsyncIterator[bytes], deserialized), response_headers)  # type: ignore

        return cast(AsyncIterator[bytes], deserialized)  # type: ignore

    @overload
    async def begin_send(
        self,
        message: JSON,
        *,
        operation_id: Optional[str] = None,
        content_type: str = "application/json",
        **kwargs: Any
    ) -> AsyncLROPoller[JSON]:
        """Queues an email message to be sent to one or more recipients.

        Queues an email message to be sent to one or more recipients.

        :param message: Message payload for sending an email. Required.
        :type message: JSON
        :keyword operation_id: This is the ID provided by the customer to identify the long running
         operation. If an ID is not provided by the customer, the service will generate one. Default
         value is None.
        :paramtype operation_id: str
        :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 JSON object
        :rtype: ~azure.core.polling.AsyncLROPoller[JSON]
        :raises ~azure.core.exceptions.HttpResponseError:

        Example:
            .. code-block:: python

                # JSON input template you can fill out and use as your body input.
                message = {
                    "content": {
                        "subject": "str",
                        "html": "str",
                        "plainText": "str"
                    },
                    "recipients": {
                        "to": [
                            {
                                "address": "str",
                                "displayName": "str"
                            }
                        ],
                        "bcc": [
                            {
                                "address": "str",
                                "displayName": "str"
                            }
                        ],
                        "cc": [
                            {
                                "address": "str",
                                "displayName": "str"
                            }
                        ]
                    },
                    "senderAddress": "str",
                    "attachments": [
                        {
                            "contentInBase64": "str",
                            "contentType": "str",
                            "name": "str",
                            "contentId": "str"
                        }
                    ],
                    "headers": {
                        "str": "str"
                    },
                    "replyTo": [
                        {
                            "address": "str",
                            "displayName": "str"
                        }
                    ],
                    "userEngagementTrackingDisabled": bool
                }

                # response body for status code(s): 202
                response == {
                    "id": "str",
                    "status": "str",
                    "error": {
                        "additionalInfo": [
                            {
                                "info": {},
                                "type": "str"
                            }
                        ],
                        "code": "str",
                        "details": [
                            ...
                        ],
                        "message": "str",
                        "target": "str"
                    }
                }
        """

    @overload
    async def begin_send(
        self,
        message: IO[bytes],
        *,
        operation_id: Optional[str] = None,
        content_type: str = "application/json",
        **kwargs: Any
    ) -> AsyncLROPoller[JSON]:
        """Queues an email message to be sent to one or more recipients.

        Queues an email message to be sent to one or more recipients.

        :param message: Message payload for sending an email. Required.
        :type message: IO[bytes]
        :keyword operation_id: This is the ID provided by the customer to identify the long running
         operation. If an ID is not provided by the customer, the service will generate one. Default
         value is None.
        :paramtype operation_id: str
        :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 JSON object
        :rtype: ~azure.core.polling.AsyncLROPoller[JSON]
        :raises ~azure.core.exceptions.HttpResponseError:

        Example:
            .. code-block:: python

                # response body for status code(s): 202
                response == {
                    "id": "str",
                    "status": "str",
                    "error": {
                        "additionalInfo": [
                            {
                                "info": {},
                                "type": "str"
                            }
                        ],
                        "code": "str",
                        "details": [
                            ...
                        ],
                        "message": "str",
                        "target": "str"
                    }
                }
        """

    @distributed_trace_async
    async def begin_send(
        self, message: Union[JSON, IO[bytes]], *, operation_id: Optional[str] = None, **kwargs: Any
    ) -> AsyncLROPoller[JSON]:
        """Queues an email message to be sent to one or more recipients.

        Queues an email message to be sent to one or more recipients.

        :param message: Message payload for sending an email. Is either a JSON type or a IO[bytes]
         type. Required.
        :type message: JSON or IO[bytes]
        :keyword operation_id: This is the ID provided by the customer to identify the long running
         operation. If an ID is not provided by the customer, the service will generate one. Default
         value is None.
        :paramtype operation_id: str
        :return: An instance of AsyncLROPoller that returns JSON object
        :rtype: ~azure.core.polling.AsyncLROPoller[JSON]
        :raises ~azure.core.exceptions.HttpResponseError:

        Example:
            .. code-block:: python

                # JSON input template you can fill out and use as your body input.
                message = {
                    "content": {
                        "subject": "str",
                        "html": "str",
                        "plainText": "str"
                    },
                    "recipients": {
                        "to": [
                            {
                                "address": "str",
                                "displayName": "str"
                            }
                        ],
                        "bcc": [
                            {
                                "address": "str",
                                "displayName": "str"
                            }
                        ],
                        "cc": [
                            {
                                "address": "str",
                                "displayName": "str"
                            }
                        ]
                    },
                    "senderAddress": "str",
                    "attachments": [
                        {
                            "contentInBase64": "str",
                            "contentType": "str",
                            "name": "str",
                            "contentId": "str"
                        }
                    ],
                    "headers": {
                        "str": "str"
                    },
                    "replyTo": [
                        {
                            "address": "str",
                            "displayName": "str"
                        }
                    ],
                    "userEngagementTrackingDisabled": bool
                }

                # response body for status code(s): 202
                response == {
                    "id": "str",
                    "status": "str",
                    "error": {
                        "additionalInfo": [
                            {
                                "info": {},
                                "type": "str"
                            }
                        ],
                        "code": "str",
                        "details": [
                            ...
                        ],
                        "message": "str",
                        "target": "str"
                    }
                }
        """
        _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {})
        _params = kwargs.pop("params", {}) or {}

        content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None))
        cls: ClsType[JSON] = 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._send_initial(
                message=message,
                operation_id=operation_id,
                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):
            response_headers = {}
            response = pipeline_response.http_response
            response_headers["Operation-Location"] = self._deserialize(
                "str", response.headers.get("Operation-Location")
            )
            response_headers["retry-after"] = self._deserialize("int", response.headers.get("retry-after"))

            if response.content:
                deserialized = response.json()
            else:
                deserialized = None
            if cls:
                return cls(pipeline_response, deserialized, response_headers)  # type: ignore
            return deserialized

        path_format_arguments = {
            "endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True),
        }

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