# 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 typing import Any, AsyncIterable, Callable, Dict, Optional, TypeVar

from azure.core.async_paging import AsyncItemPaged, AsyncList
from azure.core.exceptions import (
    ClientAuthenticationError,
    HttpResponseError,
    ResourceExistsError,
    ResourceNotFoundError,
    map_error,
)
from azure.core.pipeline import PipelineResponse
from azure.core.pipeline.transport import AsyncHttpResponse, HttpRequest
from azure.mgmt.core.exceptions import ARMErrorFormat

from ... import models

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


class TagsOperations:
    """TagsOperations async operations.

    You should not instantiate this class directly. Instead, you should create a Client instance that
    instantiates it for you and attaches it as an attribute.

    :ivar models: Alias to model classes used in this operation group.
    :type models: ~azure.mgmt.resource.resources.v2020_06_01.models
    :param client: Client for service requests.
    :param config: Configuration of service client.
    :param serializer: An object model serializer.
    :param deserializer: An object model deserializer.
    """

    models = models

    def __init__(self, client, config, serializer, deserializer) -> None:
        self._client = client
        self._serialize = serializer
        self._deserialize = deserializer
        self._config = config

    async def delete_value(self, tag_name: str, tag_value: str, **kwargs) -> None:
        """Deletes a predefined tag value for a predefined tag name.

        This operation allows deleting a value from the list of predefined values for an existing
        predefined tag name. The value being deleted must not be in use as a tag value for the given
        tag name for any resource.

        :param tag_name: The name of the tag.
        :type tag_name: str
        :param tag_value: The value of the tag to delete.
        :type tag_value: str
        :keyword callable cls: A custom type or function that will be passed the direct response
        :return: None, or the result of cls(response)
        :rtype: None
        :raises: ~azure.core.exceptions.HttpResponseError
        """
        cls = kwargs.pop("cls", None)  # type: ClsType[None]
        error_map = {401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError}
        error_map.update(kwargs.pop("error_map", {}))
        api_version = "2020-06-01"
        accept = "application/json"

        # Construct URL
        url = self.delete_value.metadata["url"]  # type: ignore
        path_format_arguments = {
            "tagName": self._serialize.url("tag_name", tag_name, "str"),
            "tagValue": self._serialize.url("tag_value", tag_value, "str"),
            "subscriptionId": self._serialize.url("self._config.subscription_id", self._config.subscription_id, "str"),
        }
        url = self._client.format_url(url, **path_format_arguments)

        # Construct parameters
        query_parameters = {}  # type: Dict[str, Any]
        query_parameters["api-version"] = self._serialize.query("api_version", api_version, "str")

        # Construct headers
        header_parameters = {}  # type: Dict[str, Any]
        header_parameters["Accept"] = self._serialize.header("accept", accept, "str")

        request = self._client.delete(url, query_parameters, header_parameters)
        pipeline_response = await self._client._pipeline.run(request, stream=False, **kwargs)
        response = pipeline_response.http_response

        if response.status_code not in [200, 204]:
            map_error(status_code=response.status_code, response=response, error_map=error_map)
            raise HttpResponseError(response=response, error_format=ARMErrorFormat)

        if cls:
            return cls(pipeline_response, None, {})

    delete_value.metadata = {"url": "/subscriptions/{subscriptionId}/tagNames/{tagName}/tagValues/{tagValue}"}  # type: ignore

    async def create_or_update_value(self, tag_name: str, tag_value: str, **kwargs) -> "models.TagValue":
        """Creates a predefined value for a predefined tag name.

        This operation allows adding a value to the list of predefined values for an existing
        predefined tag name. A tag value can have a maximum of 256 characters.

        :param tag_name: The name of the tag.
        :type tag_name: str
        :param tag_value: The value of the tag to create.
        :type tag_value: str
        :keyword callable cls: A custom type or function that will be passed the direct response
        :return: TagValue, or the result of cls(response)
        :rtype: ~azure.mgmt.resource.resources.v2020_06_01.models.TagValue
        :raises: ~azure.core.exceptions.HttpResponseError
        """
        cls = kwargs.pop("cls", None)  # type: ClsType["models.TagValue"]
        error_map = {401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError}
        error_map.update(kwargs.pop("error_map", {}))
        api_version = "2020-06-01"
        accept = "application/json"

        # Construct URL
        url = self.create_or_update_value.metadata["url"]  # type: ignore
        path_format_arguments = {
            "tagName": self._serialize.url("tag_name", tag_name, "str"),
            "tagValue": self._serialize.url("tag_value", tag_value, "str"),
            "subscriptionId": self._serialize.url("self._config.subscription_id", self._config.subscription_id, "str"),
        }
        url = self._client.format_url(url, **path_format_arguments)

        # Construct parameters
        query_parameters = {}  # type: Dict[str, Any]
        query_parameters["api-version"] = self._serialize.query("api_version", api_version, "str")

        # Construct headers
        header_parameters = {}  # type: Dict[str, Any]
        header_parameters["Accept"] = self._serialize.header("accept", accept, "str")

        request = self._client.put(url, query_parameters, header_parameters)
        pipeline_response = await self._client._pipeline.run(request, stream=False, **kwargs)
        response = pipeline_response.http_response

        if response.status_code not in [200, 201]:
            map_error(status_code=response.status_code, response=response, error_map=error_map)
            raise HttpResponseError(response=response, error_format=ARMErrorFormat)

        if response.status_code == 200:
            deserialized = self._deserialize("TagValue", pipeline_response)

        if response.status_code == 201:
            deserialized = self._deserialize("TagValue", pipeline_response)

        if cls:
            return cls(pipeline_response, deserialized, {})

        return deserialized

    create_or_update_value.metadata = {"url": "/subscriptions/{subscriptionId}/tagNames/{tagName}/tagValues/{tagValue}"}  # type: ignore

    async def create_or_update(self, tag_name: str, **kwargs) -> "models.TagDetails":
        """Creates a predefined tag name.

        This operation allows adding a name to the list of predefined tag names for the given
        subscription. A tag name can have a maximum of 512 characters and is case-insensitive. Tag
        names cannot have the following prefixes which are reserved for Azure use: 'microsoft',
        'azure', 'windows'.

        :param tag_name: The name of the tag to create.
        :type tag_name: str
        :keyword callable cls: A custom type or function that will be passed the direct response
        :return: TagDetails, or the result of cls(response)
        :rtype: ~azure.mgmt.resource.resources.v2020_06_01.models.TagDetails
        :raises: ~azure.core.exceptions.HttpResponseError
        """
        cls = kwargs.pop("cls", None)  # type: ClsType["models.TagDetails"]
        error_map = {401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError}
        error_map.update(kwargs.pop("error_map", {}))
        api_version = "2020-06-01"
        accept = "application/json"

        # Construct URL
        url = self.create_or_update.metadata["url"]  # type: ignore
        path_format_arguments = {
            "tagName": self._serialize.url("tag_name", tag_name, "str"),
            "subscriptionId": self._serialize.url("self._config.subscription_id", self._config.subscription_id, "str"),
        }
        url = self._client.format_url(url, **path_format_arguments)

        # Construct parameters
        query_parameters = {}  # type: Dict[str, Any]
        query_parameters["api-version"] = self._serialize.query("api_version", api_version, "str")

        # Construct headers
        header_parameters = {}  # type: Dict[str, Any]
        header_parameters["Accept"] = self._serialize.header("accept", accept, "str")

        request = self._client.put(url, query_parameters, header_parameters)
        pipeline_response = await self._client._pipeline.run(request, stream=False, **kwargs)
        response = pipeline_response.http_response

        if response.status_code not in [200, 201]:
            map_error(status_code=response.status_code, response=response, error_map=error_map)
            raise HttpResponseError(response=response, error_format=ARMErrorFormat)

        if response.status_code == 200:
            deserialized = self._deserialize("TagDetails", pipeline_response)

        if response.status_code == 201:
            deserialized = self._deserialize("TagDetails", pipeline_response)

        if cls:
            return cls(pipeline_response, deserialized, {})

        return deserialized

    create_or_update.metadata = {"url": "/subscriptions/{subscriptionId}/tagNames/{tagName}"}  # type: ignore

    async def delete(self, tag_name: str, **kwargs) -> None:
        """Deletes a predefined tag name.

        This operation allows deleting a name from the list of predefined tag names for the given
        subscription. The name being deleted must not be in use as a tag name for any resource. All
        predefined values for the given name must have already been deleted.

        :param tag_name: The name of the tag.
        :type tag_name: str
        :keyword callable cls: A custom type or function that will be passed the direct response
        :return: None, or the result of cls(response)
        :rtype: None
        :raises: ~azure.core.exceptions.HttpResponseError
        """
        cls = kwargs.pop("cls", None)  # type: ClsType[None]
        error_map = {401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError}
        error_map.update(kwargs.pop("error_map", {}))
        api_version = "2020-06-01"
        accept = "application/json"

        # Construct URL
        url = self.delete.metadata["url"]  # type: ignore
        path_format_arguments = {
            "tagName": self._serialize.url("tag_name", tag_name, "str"),
            "subscriptionId": self._serialize.url("self._config.subscription_id", self._config.subscription_id, "str"),
        }
        url = self._client.format_url(url, **path_format_arguments)

        # Construct parameters
        query_parameters = {}  # type: Dict[str, Any]
        query_parameters["api-version"] = self._serialize.query("api_version", api_version, "str")

        # Construct headers
        header_parameters = {}  # type: Dict[str, Any]
        header_parameters["Accept"] = self._serialize.header("accept", accept, "str")

        request = self._client.delete(url, query_parameters, header_parameters)
        pipeline_response = await self._client._pipeline.run(request, stream=False, **kwargs)
        response = pipeline_response.http_response

        if response.status_code not in [200, 204]:
            map_error(status_code=response.status_code, response=response, error_map=error_map)
            raise HttpResponseError(response=response, error_format=ARMErrorFormat)

        if cls:
            return cls(pipeline_response, None, {})

    delete.metadata = {"url": "/subscriptions/{subscriptionId}/tagNames/{tagName}"}  # type: ignore

    def list(self, **kwargs) -> AsyncIterable["models.TagsListResult"]:
        """Gets a summary of tag usage under the subscription.

        This operation performs a union of predefined tags, resource tags, resource group tags and
        subscription tags, and returns a summary of usage for each tag name and value under the given
        subscription. In case of a large number of tags, this operation may return a previously cached
        result.

        :keyword callable cls: A custom type or function that will be passed the direct response
        :return: An iterator like instance of either TagsListResult or the result of cls(response)
        :rtype: ~azure.core.async_paging.AsyncItemPaged[~azure.mgmt.resource.resources.v2020_06_01.models.TagsListResult]
        :raises: ~azure.core.exceptions.HttpResponseError
        """
        cls = kwargs.pop("cls", None)  # type: ClsType["models.TagsListResult"]
        error_map = {401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError}
        error_map.update(kwargs.pop("error_map", {}))
        api_version = "2020-06-01"
        accept = "application/json"

        def prepare_request(next_link=None):
            # Construct headers
            header_parameters = {}  # type: Dict[str, Any]
            header_parameters["Accept"] = self._serialize.header("accept", accept, "str")

            if not next_link:
                # Construct URL
                url = self.list.metadata["url"]  # type: ignore
                path_format_arguments = {
                    "subscriptionId": self._serialize.url(
                        "self._config.subscription_id", self._config.subscription_id, "str"
                    ),
                }
                url = self._client.format_url(url, **path_format_arguments)
                # Construct parameters
                query_parameters = {}  # type: Dict[str, Any]
                query_parameters["api-version"] = self._serialize.query("api_version", api_version, "str")

                request = self._client.get(url, query_parameters, header_parameters)
            else:
                url = next_link
                query_parameters = {}  # type: Dict[str, Any]
                request = self._client.get(url, query_parameters, header_parameters)
            return request

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

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

            pipeline_response = await self._client._pipeline.run(request, stream=False, **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, error_format=ARMErrorFormat)

            return pipeline_response

        return AsyncItemPaged(get_next, extract_data)

    list.metadata = {"url": "/subscriptions/{subscriptionId}/tagNames"}  # type: ignore

    async def create_or_update_at_scope(
        self, scope: str, parameters: "models.TagsResource", **kwargs
    ) -> "models.TagsResource":
        """Creates or updates the entire set of tags on a resource or subscription.

        This operation allows adding or replacing the entire set of tags on the specified resource or
        subscription. The specified entity can have a maximum of 50 tags.

        :param scope: The resource scope.
        :type scope: str
        :param parameters:
        :type parameters: ~azure.mgmt.resource.resources.v2020_06_01.models.TagsResource
        :keyword callable cls: A custom type or function that will be passed the direct response
        :return: TagsResource, or the result of cls(response)
        :rtype: ~azure.mgmt.resource.resources.v2020_06_01.models.TagsResource
        :raises: ~azure.core.exceptions.HttpResponseError
        """
        cls = kwargs.pop("cls", None)  # type: ClsType["models.TagsResource"]
        error_map = {401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError}
        error_map.update(kwargs.pop("error_map", {}))
        api_version = "2020-06-01"
        content_type = kwargs.pop("content_type", "application/json")
        accept = "application/json"

        # Construct URL
        url = self.create_or_update_at_scope.metadata["url"]  # type: ignore
        path_format_arguments = {
            "scope": self._serialize.url("scope", scope, "str", skip_quote=True),
        }
        url = self._client.format_url(url, **path_format_arguments)

        # Construct parameters
        query_parameters = {}  # type: Dict[str, Any]
        query_parameters["api-version"] = self._serialize.query("api_version", api_version, "str")

        # Construct headers
        header_parameters = {}  # type: Dict[str, Any]
        header_parameters["Content-Type"] = self._serialize.header("content_type", content_type, "str")
        header_parameters["Accept"] = self._serialize.header("accept", accept, "str")

        body_content_kwargs = {}  # type: Dict[str, Any]
        body_content = self._serialize.body(parameters, "TagsResource")
        body_content_kwargs["content"] = body_content
        request = self._client.put(url, query_parameters, header_parameters, **body_content_kwargs)
        pipeline_response = await self._client._pipeline.run(request, stream=False, **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, error_format=ARMErrorFormat)

        deserialized = self._deserialize("TagsResource", pipeline_response)

        if cls:
            return cls(pipeline_response, deserialized, {})

        return deserialized

    create_or_update_at_scope.metadata = {"url": "/{scope}/providers/Microsoft.Resources/tags/default"}  # type: ignore

    async def update_at_scope(
        self, scope: str, parameters: "models.TagsPatchResource", **kwargs
    ) -> "models.TagsResource":
        """Selectively updates the set of tags on a resource or subscription.

        This operation allows replacing, merging or selectively deleting tags on the specified resource
        or subscription. The specified entity can have a maximum of 50 tags at the end of the
        operation. The 'replace' option replaces the entire set of existing tags with a new set. The
        'merge' option allows adding tags with new names and updating the values of tags with existing
        names. The 'delete' option allows selectively deleting tags based on given names or name/value
        pairs.

        :param scope: The resource scope.
        :type scope: str
        :param parameters:
        :type parameters: ~azure.mgmt.resource.resources.v2020_06_01.models.TagsPatchResource
        :keyword callable cls: A custom type or function that will be passed the direct response
        :return: TagsResource, or the result of cls(response)
        :rtype: ~azure.mgmt.resource.resources.v2020_06_01.models.TagsResource
        :raises: ~azure.core.exceptions.HttpResponseError
        """
        cls = kwargs.pop("cls", None)  # type: ClsType["models.TagsResource"]
        error_map = {401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError}
        error_map.update(kwargs.pop("error_map", {}))
        api_version = "2020-06-01"
        content_type = kwargs.pop("content_type", "application/json")
        accept = "application/json"

        # Construct URL
        url = self.update_at_scope.metadata["url"]  # type: ignore
        path_format_arguments = {
            "scope": self._serialize.url("scope", scope, "str", skip_quote=True),
        }
        url = self._client.format_url(url, **path_format_arguments)

        # Construct parameters
        query_parameters = {}  # type: Dict[str, Any]
        query_parameters["api-version"] = self._serialize.query("api_version", api_version, "str")

        # Construct headers
        header_parameters = {}  # type: Dict[str, Any]
        header_parameters["Content-Type"] = self._serialize.header("content_type", content_type, "str")
        header_parameters["Accept"] = self._serialize.header("accept", accept, "str")

        body_content_kwargs = {}  # type: Dict[str, Any]
        body_content = self._serialize.body(parameters, "TagsPatchResource")
        body_content_kwargs["content"] = body_content
        request = self._client.patch(url, query_parameters, header_parameters, **body_content_kwargs)
        pipeline_response = await self._client._pipeline.run(request, stream=False, **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, error_format=ARMErrorFormat)

        deserialized = self._deserialize("TagsResource", pipeline_response)

        if cls:
            return cls(pipeline_response, deserialized, {})

        return deserialized

    update_at_scope.metadata = {"url": "/{scope}/providers/Microsoft.Resources/tags/default"}  # type: ignore

    async def get_at_scope(self, scope: str, **kwargs) -> "models.TagsResource":
        """Gets the entire set of tags on a resource or subscription.

        Gets the entire set of tags on a resource or subscription.

        :param scope: The resource scope.
        :type scope: str
        :keyword callable cls: A custom type or function that will be passed the direct response
        :return: TagsResource, or the result of cls(response)
        :rtype: ~azure.mgmt.resource.resources.v2020_06_01.models.TagsResource
        :raises: ~azure.core.exceptions.HttpResponseError
        """
        cls = kwargs.pop("cls", None)  # type: ClsType["models.TagsResource"]
        error_map = {401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError}
        error_map.update(kwargs.pop("error_map", {}))
        api_version = "2020-06-01"
        accept = "application/json"

        # Construct URL
        url = self.get_at_scope.metadata["url"]  # type: ignore
        path_format_arguments = {
            "scope": self._serialize.url("scope", scope, "str", skip_quote=True),
        }
        url = self._client.format_url(url, **path_format_arguments)

        # Construct parameters
        query_parameters = {}  # type: Dict[str, Any]
        query_parameters["api-version"] = self._serialize.query("api_version", api_version, "str")

        # Construct headers
        header_parameters = {}  # type: Dict[str, Any]
        header_parameters["Accept"] = self._serialize.header("accept", accept, "str")

        request = self._client.get(url, query_parameters, header_parameters)
        pipeline_response = await self._client._pipeline.run(request, stream=False, **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, error_format=ARMErrorFormat)

        deserialized = self._deserialize("TagsResource", pipeline_response)

        if cls:
            return cls(pipeline_response, deserialized, {})

        return deserialized

    get_at_scope.metadata = {"url": "/{scope}/providers/Microsoft.Resources/tags/default"}  # type: ignore

    async def delete_at_scope(self, scope: str, **kwargs) -> None:
        """Deletes the entire set of tags on a resource or subscription.

        Deletes the entire set of tags on a resource or subscription.

        :param scope: The resource scope.
        :type scope: str
        :keyword callable cls: A custom type or function that will be passed the direct response
        :return: None, or the result of cls(response)
        :rtype: None
        :raises: ~azure.core.exceptions.HttpResponseError
        """
        cls = kwargs.pop("cls", None)  # type: ClsType[None]
        error_map = {401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError}
        error_map.update(kwargs.pop("error_map", {}))
        api_version = "2020-06-01"
        accept = "application/json"

        # Construct URL
        url = self.delete_at_scope.metadata["url"]  # type: ignore
        path_format_arguments = {
            "scope": self._serialize.url("scope", scope, "str", skip_quote=True),
        }
        url = self._client.format_url(url, **path_format_arguments)

        # Construct parameters
        query_parameters = {}  # type: Dict[str, Any]
        query_parameters["api-version"] = self._serialize.query("api_version", api_version, "str")

        # Construct headers
        header_parameters = {}  # type: Dict[str, Any]
        header_parameters["Accept"] = self._serialize.header("accept", accept, "str")

        request = self._client.delete(url, query_parameters, header_parameters)
        pipeline_response = await self._client._pipeline.run(request, stream=False, **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, error_format=ARMErrorFormat)

        if cls:
            return cls(pipeline_response, None, {})

    delete_at_scope.metadata = {"url": "/{scope}/providers/Microsoft.Resources/tags/default"}  # type: ignore
