# 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
import sys
from typing import Any, AsyncIterable, Callable, Dict, Optional, TypeVar, cast
import urllib.parse

from azure.core.async_paging import AsyncItemPaged, AsyncList
from azure.core.exceptions import (
    ClientAuthenticationError,
    HttpResponseError,
    ResourceExistsError,
    ResourceNotFoundError,
    ResourceNotModifiedError,
    map_error,
)
from azure.core.pipeline import PipelineResponse
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 ...operations._operations import (
    build_metric_definitions_list_request,
    build_metric_namespaces_list_request,
    build_metrics_list_request,
)

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


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

        Instead, you should access the following operations through
        :class:`~monitor_metrics_client.aio.MonitorMetricsClient`'s
        :attr:`metric_definitions` attribute.
    """

    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")

    @distributed_trace
    def list(self, resource_uri: str, *, metricnamespace: Optional[str] = None, **kwargs: Any) -> AsyncIterable[JSON]:
        """Lists the metric definitions for the resource.

        :param resource_uri: The identifier of the resource. Required.
        :type resource_uri: str
        :keyword metricnamespace: Metric namespace where the metrics you want reside. Default value is
         None.
        :paramtype metricnamespace: str
        :return: An iterator like instance of JSON object
        :rtype: ~azure.core.async_paging.AsyncItemPaged[JSON]
        :raises ~azure.core.exceptions.HttpResponseError:

        Example:
            .. code-block:: python

                # response body for status code(s): 200
                response == {
                    "category": "str",
                    "dimensions": [
                        {
                            "value": "str",
                            "localizedValue": "str"
                        }
                    ],
                    "displayDescription": "str",
                    "id": "str",
                    "isDimensionRequired": bool,
                    "metricAvailabilities": [
                        {
                            "retention": "1 day, 0:00:00",
                            "timeGrain": "1 day, 0:00:00"
                        }
                    ],
                    "metricClass": "str",
                    "name": {
                        "value": "str",
                        "localizedValue": "str"
                    },
                    "namespace": "str",
                    "primaryAggregationType": "str",
                    "resourceId": "str",
                    "supportedAggregationTypes": [
                        "str"
                    ],
                    "unit": "str"
                }
        """
        _headers = kwargs.pop("headers", {}) or {}
        _params = kwargs.pop("params", {}) or {}

        cls: ClsType[JSON] = kwargs.pop("cls", None)

        error_map: MutableMapping = {
            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_metric_definitions_list_request(
                    resource_uri=resource_uri,
                    metricnamespace=metricnamespace,
                    api_version=self._config.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)

            return _request

        async def extract_data(pipeline_response):
            deserialized = pipeline_response.http_response.json()
            list_of_elem = deserialized["value"]
            if cls:
                list_of_elem = cls(list_of_elem)  # type: ignore
            return 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)
                raise HttpResponseError(response=response)

            return pipeline_response

        return AsyncItemPaged(get_next, extract_data)


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

        Instead, you should access the following operations through
        :class:`~monitor_metrics_client.aio.MonitorMetricsClient`'s
        :attr:`metrics` attribute.
    """

    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")

    @distributed_trace_async
    async def list(
        self,
        resource_uri: str,
        *,
        timespan: Optional[str] = None,
        interval: Optional[datetime.timedelta] = None,
        metricnames: Optional[str] = None,
        aggregation: Optional[str] = None,
        top: Optional[int] = None,
        orderby: Optional[str] = None,
        filter: Optional[str] = None,
        result_type: Optional[str] = None,
        metricnamespace: Optional[str] = None,
        auto_adjust_timegrain: Optional[bool] = None,
        validate_dimensions: Optional[bool] = None,
        rollupby: Optional[str] = None,
        **kwargs: Any
    ) -> JSON:
        """**Lists the metric values for a resource**.

        :param resource_uri: The identifier of the resource. Required.
        :type resource_uri: str
        :keyword timespan: The timespan of the query. It is a string with the following format
         'startDateTime_ISO/endDateTime_ISO'. Default value is None.
        :paramtype timespan: str
        :keyword interval: The interval (i.e. timegrain) of the query in ISO 8601 duration format.
         Defaults to PT1M. Special case for 'FULL' value that returns single datapoint for entire time
         span requested.
         *Examples: PT15M, PT1H, P1D, FULL*. Default value is None.
        :paramtype interval: ~datetime.timedelta
        :keyword metricnames: The names of the metrics (comma separated) to retrieve. Default value is
         None.
        :paramtype metricnames: str
        :keyword aggregation: The list of aggregation types (comma separated) to retrieve.
         *Examples: average, minimum, maximum*. Default value is None.
        :paramtype aggregation: str
        :keyword top: The maximum number of records to retrieve per resource ID in the request.
         Valid only if filter is specified.
         Defaults to 10. Default value is None.
        :paramtype top: int
        :keyword orderby: The aggregation to use for sorting results and the direction of the sort.
         Only one order can be specified.
         *Examples: sum asc*. Default value is None.
        :paramtype orderby: str
        :keyword filter: The **$filter** is used to reduce the set of metric data
         returned.:code:`<br>`Example::code:`<br>`Metric contains metadata A, B and C.:code:`<br>`-
         Return all time series of C where A = a1 and B = b1 or b2:code:`<br>`\\ **$filter=A eq ‘a1’ and
         B eq ‘b1’ or B eq ‘b2’ and C eq ‘*’**\\ :code:`<br>`- Invalid variant::code:`<br>`\\
         **$filter=A eq ‘a1’ and B eq ‘b1’ and C eq ‘*’ or B = ‘b2’**\\ :code:`<br>`This is invalid
         because the logical or operator cannot separate two different metadata names.:code:`<br>`-
         Return all time series where A = a1, B = b1 and C = c1::code:`<br>`\\ **$filter=A eq ‘a1’ and B
         eq ‘b1’ and C eq ‘c1’**\\ :code:`<br>`- Return all time series where A = a1:code:`<br>`\\
         **$filter=A eq ‘a1’ and B eq ‘\\ *’ and C eq ‘*\\ ’**. Default value is None.
        :paramtype filter: str
        :keyword result_type: Reduces the set of data collected. The syntax allowed depends on the
         operation. See the operation's description for details. Known values are: "Data" and
         "Metadata". Default value is None.
        :paramtype result_type: str
        :keyword metricnamespace: Metric namespace where the metrics you want reside. Default value is
         None.
        :paramtype metricnamespace: str
        :keyword auto_adjust_timegrain: When set to true, if the timespan passed in is not supported by
         this metric, the API will return the result using the closest supported timespan. When set to
         false, an error is returned for invalid timespan parameters. Defaults to false. Default value
         is None.
        :paramtype auto_adjust_timegrain: bool
        :keyword validate_dimensions: When set to false, invalid filter parameter values will be
         ignored. When set to true, an error is returned for invalid filter parameters. Defaults to
         true. Default value is None.
        :paramtype validate_dimensions: bool
        :keyword rollupby: Dimension name(s) to rollup results by. For example if you only want to see
         metric values with a filter like 'City eq Seattle or City eq Tacoma' but don't want to see
         separate values for each city, you can specify 'RollUpBy=City' to see the results for Seattle
         and Tacoma rolled up into one timeseries. Default value is None.
        :paramtype rollupby: str
        :return: JSON object
        :rtype: JSON
        :raises ~azure.core.exceptions.HttpResponseError:

        Example:
            .. code-block:: python

                # response body for status code(s): 200
                response == {
                    "timespan": "str",
                    "value": [
                        {
                            "id": "str",
                            "name": {
                                "value": "str",
                                "localizedValue": "str"
                            },
                            "timeseries": [
                                {
                                    "data": [
                                        {
                                            "timeStamp": "2020-02-20
                                              00:00:00",
                                            "average": 0.0,
                                            "count": 0.0,
                                            "maximum": 0.0,
                                            "minimum": 0.0,
                                            "total": 0.0
                                        }
                                    ],
                                    "metadatavalues": [
                                        {
                                            "name": {
                                                "value": "str",
                                                "localizedValue":
                                                  "str"
                                            },
                                            "value": "str"
                                        }
                                    ]
                                }
                            ],
                            "type": "str",
                            "unit": "str",
                            "displayDescription": "str",
                            "errorCode": "str",
                            "errorMessage": "str"
                        }
                    ],
                    "cost": 0,
                    "interval": "str",
                    "namespace": "str",
                    "resourceregion": "str"
                }
        """
        error_map: MutableMapping = {
            401: ClientAuthenticationError,
            404: ResourceNotFoundError,
            409: ResourceExistsError,
            304: ResourceNotModifiedError,
        }
        error_map.update(kwargs.pop("error_map", {}) or {})

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

        cls: ClsType[JSON] = kwargs.pop("cls", None)

        _request = build_metrics_list_request(
            resource_uri=resource_uri,
            timespan=timespan,
            interval=interval,
            metricnames=metricnames,
            aggregation=aggregation,
            top=top,
            orderby=orderby,
            filter=filter,
            result_type=result_type,
            metricnamespace=metricnamespace,
            auto_adjust_timegrain=auto_adjust_timegrain,
            validate_dimensions=validate_dimensions,
            rollupby=rollupby,
            api_version=self._config.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)
            raise HttpResponseError(response=response)

        if response.content:
            deserialized = response.json()
        else:
            deserialized = None

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

        return cast(JSON, deserialized)  # type: ignore


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

        Instead, you should access the following operations through
        :class:`~monitor_metrics_client.aio.MonitorMetricsClient`'s
        :attr:`metric_namespaces` attribute.
    """

    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")

    @distributed_trace
    def list(self, resource_uri: str, *, start_time: Optional[str] = None, **kwargs: Any) -> AsyncIterable[JSON]:
        """Lists the metric namespaces for the resource.

        :param resource_uri: The identifier of the resource. Required.
        :type resource_uri: str
        :keyword start_time: The ISO 8601 conform Date start time from which to query for metric
         namespaces. Default value is None.
        :paramtype start_time: str
        :return: An iterator like instance of JSON object
        :rtype: ~azure.core.async_paging.AsyncItemPaged[JSON]
        :raises ~azure.core.exceptions.HttpResponseError:

        Example:
            .. code-block:: python

                # response body for status code(s): 200
                response == {
                    "classification": "str",
                    "id": "str",
                    "name": "str",
                    "properties": {
                        "metricNamespaceName": "str"
                    },
                    "type": "str"
                }
        """
        _headers = kwargs.pop("headers", {}) or {}
        _params = kwargs.pop("params", {}) or {}

        cls: ClsType[JSON] = kwargs.pop("cls", None)

        error_map: MutableMapping = {
            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_metric_namespaces_list_request(
                    resource_uri=resource_uri,
                    start_time=start_time,
                    api_version=self._config.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)

            return _request

        async def extract_data(pipeline_response):
            deserialized = pipeline_response.http_response.json()
            list_of_elem = deserialized["value"]
            if cls:
                list_of_elem = cls(list_of_elem)  # type: ignore
            return 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)
                raise HttpResponseError(response=response)

            return pipeline_response

        return AsyncItemPaged(get_next, extract_data)
