import json
import re
from collections import defaultdict
from typing import Any, Dict, Tuple, Union

from moto.core.responses import BaseResponse
from moto.core.utils import camelcase_to_underscores
from .models import sns_backends, SNSBackend
from .exceptions import InvalidParameterValue, SNSNotFoundError
from .utils import is_e164


class SNSResponse(BaseResponse):
    SMS_ATTR_REGEX = re.compile(
        r"^attributes\.entry\.(?P<index>\d+)\.(?P<type>key|value)$"
    )
    OPT_OUT_PHONE_NUMBER_REGEX = re.compile(r"^\+?\d+$")

    def __init__(self) -> None:
        super().__init__(service_name="sns")

    @property
    def backend(self) -> SNSBackend:
        return sns_backends[self.current_account][self.region]

    def _error(self, code: str, message: str, sender: str = "Sender") -> str:
        template = self.response_template(ERROR_RESPONSE)
        return template.render(code=code, message=message, sender=sender)

    def _get_attributes(self) -> Dict[str, str]:
        attributes = self._get_list_prefix("Attributes.entry")
        return dict((attribute["key"], attribute["value"]) for attribute in attributes)

    def _get_tags(self) -> Dict[str, str]:
        tags = self._get_list_prefix("Tags.member")
        return {tag["key"]: tag["value"] for tag in tags}

    def _parse_message_attributes(self) -> Dict[str, Any]:
        message_attributes = self._get_object_map(
            "MessageAttributes.entry", name="Name", value="Value"
        )
        return self._transform_message_attributes(message_attributes)

    def _transform_message_attributes(
        self, message_attributes: Dict[str, Any]
    ) -> Dict[str, Any]:
        # SNS converts some key names before forwarding messages
        # DataType -> Type, StringValue -> Value, BinaryValue -> Value
        transformed_message_attributes = {}
        for name, value in message_attributes.items():
            # validation
            data_type = value["DataType"]
            if not data_type:
                raise InvalidParameterValue(
                    f"The message attribute '{name}' must contain non-empty message attribute value."
                )

            data_type_parts = data_type.split(".")
            if len(data_type_parts) > 2 or data_type_parts[0] not in [
                "String",
                "Binary",
                "Number",
            ]:
                raise InvalidParameterValue(
                    f"The message attribute '{name}' has an invalid message "
                    "attribute type, the set of supported type prefixes is "
                    "Binary, Number, and String."
                )

            transform_value = None
            if "StringValue" in value:
                transform_value = value["StringValue"]
                if data_type == "Number":
                    try:
                        int(transform_value)
                    except ValueError:
                        try:
                            float(transform_value)
                        except ValueError:
                            raise InvalidParameterValue(
                                "An error occurred (ParameterValueInvalid) "
                                "when calling the Publish operation: "
                                f"Could not cast message attribute '{name}' value to number."
                            )
            elif "BinaryValue" in value:
                transform_value = value["BinaryValue"]
            if transform_value == "":
                raise InvalidParameterValue(
                    f"The message attribute '{name}' must contain non-empty "
                    "message attribute value for message attribute "
                    f"type '{data_type[0]}'."
                )

            # transformation
            transformed_message_attributes[name] = {
                "Type": data_type,
                "Value": transform_value,
            }

        return transformed_message_attributes

    def create_topic(self) -> str:
        name = self._get_param("Name")
        attributes = self._get_attributes()
        tags = self._get_tags()
        topic = self.backend.create_topic(name, attributes, tags)

        if self.request_json:
            return json.dumps(
                {
                    "CreateTopicResponse": {
                        "CreateTopicResult": {"TopicArn": topic.arn},
                        "ResponseMetadata": {
                            "RequestId": "a8dec8b3-33a4-11df-8963-01868b7c937a"
                        },
                    }
                }
            )

        template = self.response_template(CREATE_TOPIC_TEMPLATE)
        return template.render(topic=topic)

    def list_topics(self) -> str:
        next_token = self._get_param("NextToken")
        topics, next_token = self.backend.list_topics(next_token=next_token)

        if self.request_json:
            return json.dumps(
                {
                    "ListTopicsResponse": {
                        "ListTopicsResult": {
                            "Topics": [{"TopicArn": topic.arn} for topic in topics],
                            "NextToken": next_token,
                        }
                    },
                    "ResponseMetadata": {
                        "RequestId": "a8dec8b3-33a4-11df-8963-01868b7c937a"
                    },
                }
            )

        template = self.response_template(LIST_TOPICS_TEMPLATE)
        return template.render(topics=topics, next_token=next_token)

    def delete_topic(self) -> str:
        topic_arn = self._get_param("TopicArn")
        self.backend.delete_topic(topic_arn)

        if self.request_json:
            return json.dumps(
                {
                    "DeleteTopicResponse": {
                        "ResponseMetadata": {
                            "RequestId": "a8dec8b3-33a4-11df-8963-01868b7c937a"
                        }
                    }
                }
            )

        template = self.response_template(DELETE_TOPIC_TEMPLATE)
        return template.render()

    def get_topic_attributes(self) -> str:
        topic_arn = self._get_param("TopicArn")
        topic = self.backend.get_topic(topic_arn)

        if self.request_json:
            attributes = {
                "Owner": topic.account_id,
                "Policy": topic.policy,
                "TopicArn": topic.arn,
                "DisplayName": topic.display_name,
                "SubscriptionsPending": topic.subscriptions_pending,
                "SubscriptionsConfirmed": topic.subscriptions_confimed,
                "SubscriptionsDeleted": topic.subscriptions_deleted,
                "DeliveryPolicy": topic.delivery_policy,
                "EffectiveDeliveryPolicy": topic.effective_delivery_policy,
            }
            if topic.kms_master_key_id:
                attributes["KmsMasterKeyId"] = topic.kms_master_key_id
            if topic.fifo_topic == "true":
                attributes["FifoTopic"] = topic.fifo_topic
                attributes[
                    "ContentBasedDeduplication"
                ] = topic.content_based_deduplication
            response = {
                "GetTopicAttributesResponse": {
                    "GetTopicAttributesResult": {"Attributes": attributes},
                    "ResponseMetadata": {
                        "RequestId": "057f074c-33a7-11df-9540-99d0768312d3"
                    },
                }
            }
            return json.dumps(response)

        template = self.response_template(GET_TOPIC_ATTRIBUTES_TEMPLATE)
        return template.render(topic=topic)

    def set_topic_attributes(self) -> str:
        topic_arn = self._get_param("TopicArn")
        attribute_name = self._get_param("AttributeName")
        attribute_name = camelcase_to_underscores(attribute_name)
        attribute_value = self._get_param("AttributeValue")
        self.backend.set_topic_attribute(topic_arn, attribute_name, attribute_value)

        if self.request_json:
            return json.dumps(
                {
                    "SetTopicAttributesResponse": {
                        "ResponseMetadata": {
                            "RequestId": "a8763b99-33a7-11df-a9b7-05d48da6f042"
                        }
                    }
                }
            )

        template = self.response_template(SET_TOPIC_ATTRIBUTES_TEMPLATE)
        return template.render()

    def subscribe(self) -> str:
        topic_arn = self._get_param("TopicArn")
        endpoint = self._get_param("Endpoint")
        protocol = self._get_param("Protocol")
        attributes = self._get_attributes()

        subscription = self.backend.subscribe(topic_arn, endpoint, protocol)

        if attributes is not None:
            # We need to set the FilterPolicyScope first, as the validation of the FilterPolicy will depend on it
            if "FilterPolicyScope" in attributes:
                filter_policy_scope = attributes.pop("FilterPolicyScope")
                self.backend.set_subscription_attributes(
                    subscription.arn, "FilterPolicyScope", filter_policy_scope
                )

            for attr_name, attr_value in attributes.items():
                self.backend.set_subscription_attributes(
                    subscription.arn, attr_name, attr_value
                )

        if self.request_json:
            return json.dumps(
                {
                    "SubscribeResponse": {
                        "SubscribeResult": {"SubscriptionArn": subscription.arn},
                        "ResponseMetadata": {
                            "RequestId": "a8763b99-33a7-11df-a9b7-05d48da6f042"
                        },
                    }
                }
            )

        template = self.response_template(SUBSCRIBE_TEMPLATE)
        return template.render(subscription=subscription)

    def unsubscribe(self) -> str:
        subscription_arn = self._get_param("SubscriptionArn")
        self.backend.unsubscribe(subscription_arn)

        if self.request_json:
            return json.dumps(
                {
                    "UnsubscribeResponse": {
                        "ResponseMetadata": {
                            "RequestId": "a8763b99-33a7-11df-a9b7-05d48da6f042"
                        }
                    }
                }
            )

        template = self.response_template(UNSUBSCRIBE_TEMPLATE)
        return template.render()

    def list_subscriptions(self) -> str:
        next_token = self._get_param("NextToken")
        subscriptions, next_token = self.backend.list_subscriptions(
            next_token=next_token
        )

        if self.request_json:
            return json.dumps(
                {
                    "ListSubscriptionsResponse": {
                        "ListSubscriptionsResult": {
                            "Subscriptions": [
                                {
                                    "TopicArn": subscription.topic.arn,
                                    "Protocol": subscription.protocol,
                                    "SubscriptionArn": subscription.arn,
                                    "Owner": subscription.topic.account_id,
                                    "Endpoint": subscription.endpoint,
                                }
                                for subscription in subscriptions
                            ],
                            "NextToken": next_token,
                        },
                        "ResponseMetadata": {
                            "RequestId": "384ac68d-3775-11df-8963-01868b7c937a"
                        },
                    }
                }
            )

        template = self.response_template(LIST_SUBSCRIPTIONS_TEMPLATE)
        return template.render(subscriptions=subscriptions, next_token=next_token)

    def list_subscriptions_by_topic(self) -> str:
        topic_arn = self._get_param("TopicArn")
        next_token = self._get_param("NextToken")
        subscriptions, next_token = self.backend.list_subscriptions_by_topic(
            topic_arn, next_token=next_token
        )

        if self.request_json:
            return json.dumps(
                {
                    "ListSubscriptionsByTopicResponse": {
                        "ListSubscriptionsByTopicResult": {
                            "Subscriptions": [
                                {
                                    "TopicArn": subscription.topic.arn,
                                    "Protocol": subscription.protocol,
                                    "SubscriptionArn": subscription.arn,
                                    "Owner": subscription.topic.account_id,
                                    "Endpoint": subscription.endpoint,
                                }
                                for subscription in subscriptions
                            ],
                            "NextToken": next_token,
                        },
                        "ResponseMetadata": {
                            "RequestId": "384ac68d-3775-11df-8963-01868b7c937a"
                        },
                    }
                }
            )

        template = self.response_template(LIST_SUBSCRIPTIONS_BY_TOPIC_TEMPLATE)
        return template.render(subscriptions=subscriptions, next_token=next_token)

    def publish(self) -> Union[str, Tuple[str, Dict[str, int]]]:
        target_arn = self._get_param("TargetArn")
        topic_arn = self._get_param("TopicArn")
        phone_number = self._get_param("PhoneNumber")
        subject = self._get_param("Subject")
        message_group_id = self._get_param("MessageGroupId")
        message_deduplication_id = self._get_param("MessageDeduplicationId")

        message_attributes = self._parse_message_attributes()

        arn = None
        if phone_number is not None:
            # Check phone is correct syntax (e164)
            if not is_e164(phone_number):
                return (
                    self._error(
                        "InvalidParameter", "Phone number does not meet the E164 format"
                    ),
                    dict(status=400),
                )
        elif target_arn is not None:
            arn = target_arn
        else:
            arn = topic_arn

        message = self._get_param("Message")

        try:
            message_id = self.backend.publish(
                message,
                arn=arn,
                phone_number=phone_number,
                subject=subject,
                message_attributes=message_attributes,
                group_id=message_group_id,
                deduplication_id=message_deduplication_id,
            )
        except ValueError as err:
            error_response = self._error("InvalidParameter", str(err))
            return error_response, dict(status=400)

        if self.request_json:
            return json.dumps(
                {
                    "PublishResponse": {
                        "PublishResult": {"MessageId": message_id},
                        "ResponseMetadata": {
                            "RequestId": "384ac68d-3775-11df-8963-01868b7c937a"
                        },
                    }
                }
            )

        template = self.response_template(PUBLISH_TEMPLATE)
        return template.render(message_id=message_id)

    def publish_batch(self) -> str:
        topic_arn = self._get_param("TopicArn")
        publish_batch_request_entries = self._get_multi_param(
            "PublishBatchRequestEntries.member"
        )
        for entry in publish_batch_request_entries:
            if "MessageAttributes" in entry:
                # Convert into the same format as the regular publish-method
                # FROM: [{'Name': 'a', 'Value': {'DataType': 'String', 'StringValue': 'v'}}]
                # TO  : {'name': {'DataType': 'Number', 'StringValue': '123'}}
                msg_attrs = {y["Name"]: y["Value"] for y in entry["MessageAttributes"]}
                # Use the same validation/processing as the regular publish-method
                entry["MessageAttributes"] = self._transform_message_attributes(
                    msg_attrs
                )
        successful, failed = self.backend.publish_batch(
            topic_arn=topic_arn,
            publish_batch_request_entries=publish_batch_request_entries,
        )
        template = self.response_template(PUBLISH_BATCH_TEMPLATE)
        return template.render(successful=successful, failed=failed)

    def create_platform_application(self) -> str:
        name = self._get_param("Name")
        platform = self._get_param("Platform")
        attributes = self._get_attributes()
        platform_application = self.backend.create_platform_application(
            name, platform, attributes
        )

        if self.request_json:
            return json.dumps(
                {
                    "CreatePlatformApplicationResponse": {
                        "CreatePlatformApplicationResult": {
                            "PlatformApplicationArn": platform_application.arn
                        },
                        "ResponseMetadata": {
                            "RequestId": "384ac68d-3775-11df-8963-01868b7c937b"
                        },
                    }
                }
            )

        template = self.response_template(CREATE_PLATFORM_APPLICATION_TEMPLATE)
        return template.render(platform_application=platform_application)

    def get_platform_application_attributes(self) -> str:
        arn = self._get_param("PlatformApplicationArn")
        attributes = self.backend.get_platform_application_attributes(arn)

        if self.request_json:
            return json.dumps(
                {
                    "GetPlatformApplicationAttributesResponse": {
                        "GetPlatformApplicationAttributesResult": {
                            "Attributes": attributes
                        },
                        "ResponseMetadata": {
                            "RequestId": "384ac68d-3775-11df-8963-01868b7c937f"
                        },
                    }
                }
            )

        template = self.response_template(GET_PLATFORM_APPLICATION_ATTRIBUTES_TEMPLATE)
        return template.render(attributes=attributes)

    def set_platform_application_attributes(self) -> str:
        arn = self._get_param("PlatformApplicationArn")
        attributes = self._get_attributes()

        self.backend.set_platform_application_attributes(arn, attributes)

        if self.request_json:
            return json.dumps(
                {
                    "SetPlatformApplicationAttributesResponse": {
                        "ResponseMetadata": {
                            "RequestId": "384ac68d-3775-12df-8963-01868b7c937f"
                        }
                    }
                }
            )

        template = self.response_template(SET_PLATFORM_APPLICATION_ATTRIBUTES_TEMPLATE)
        return template.render()

    def list_platform_applications(self) -> str:
        applications = self.backend.list_platform_applications()

        if self.request_json:
            return json.dumps(
                {
                    "ListPlatformApplicationsResponse": {
                        "ListPlatformApplicationsResult": {
                            "PlatformApplications": [
                                {
                                    "PlatformApplicationArn": application.arn,
                                    "attributes": application.attributes,
                                }
                                for application in applications
                            ],
                            "NextToken": None,
                        },
                        "ResponseMetadata": {
                            "RequestId": "384ac68d-3775-11df-8963-01868b7c937c"
                        },
                    }
                }
            )

        template = self.response_template(LIST_PLATFORM_APPLICATIONS_TEMPLATE)
        return template.render(applications=applications)

    def delete_platform_application(self) -> str:
        platform_arn = self._get_param("PlatformApplicationArn")
        self.backend.delete_platform_application(platform_arn)

        if self.request_json:
            return json.dumps(
                {
                    "DeletePlatformApplicationResponse": {
                        "ResponseMetadata": {
                            "RequestId": "384ac68d-3775-11df-8963-01868b7c937e"
                        }
                    }
                }
            )

        template = self.response_template(DELETE_PLATFORM_APPLICATION_TEMPLATE)
        return template.render()

    def create_platform_endpoint(self) -> str:
        application_arn = self._get_param("PlatformApplicationArn")
        application = self.backend.get_application(application_arn)

        custom_user_data = self._get_param("CustomUserData")
        token = self._get_param("Token")
        attributes = self._get_attributes()

        platform_endpoint = self.backend.create_platform_endpoint(
            application, custom_user_data, token, attributes
        )

        if self.request_json:
            return json.dumps(
                {
                    "CreatePlatformEndpointResponse": {
                        "CreatePlatformEndpointResult": {
                            "EndpointArn": platform_endpoint.arn
                        },
                        "ResponseMetadata": {
                            "RequestId": "384ac68d-3779-11df-8963-01868b7c937b"
                        },
                    }
                }
            )

        template = self.response_template(CREATE_PLATFORM_ENDPOINT_TEMPLATE)
        return template.render(platform_endpoint=platform_endpoint)

    def list_endpoints_by_platform_application(self) -> str:
        application_arn = self._get_param("PlatformApplicationArn")
        endpoints = self.backend.list_endpoints_by_platform_application(application_arn)

        if self.request_json:
            return json.dumps(
                {
                    "ListEndpointsByPlatformApplicationResponse": {
                        "ListEndpointsByPlatformApplicationResult": {
                            "Endpoints": [
                                {
                                    "Attributes": endpoint.attributes,
                                    "EndpointArn": endpoint.arn,
                                }
                                for endpoint in endpoints
                            ],
                            "NextToken": None,
                        },
                        "ResponseMetadata": {
                            "RequestId": "384ac68d-3775-11df-8963-01868b7c937a"
                        },
                    }
                }
            )

        template = self.response_template(
            LIST_ENDPOINTS_BY_PLATFORM_APPLICATION_TEMPLATE
        )
        return template.render(endpoints=endpoints)

    def get_endpoint_attributes(self) -> Union[str, Tuple[str, Dict[str, int]]]:
        arn = self._get_param("EndpointArn")
        try:
            attributes = self.backend.get_endpoint_attributes(arn)

            if self.request_json:
                return json.dumps(
                    {
                        "GetEndpointAttributesResponse": {
                            "GetEndpointAttributesResult": {"Attributes": attributes},
                            "ResponseMetadata": {
                                "RequestId": "384ac68d-3775-11df-8963-01868b7c937f"
                            },
                        }
                    }
                )

            template = self.response_template(GET_ENDPOINT_ATTRIBUTES_TEMPLATE)
            return template.render(attributes=attributes)
        except SNSNotFoundError:
            error_response = self._error("NotFound", "Endpoint does not exist")
            return error_response, dict(status=404)

    def set_endpoint_attributes(self) -> Union[str, Tuple[str, Dict[str, int]]]:
        arn = self._get_param("EndpointArn")
        attributes = self._get_attributes()

        self.backend.set_endpoint_attributes(arn, attributes)

        if self.request_json:
            return json.dumps(
                {
                    "SetEndpointAttributesResponse": {
                        "ResponseMetadata": {
                            "RequestId": "384bc68d-3775-12df-8963-01868b7c937f"
                        }
                    }
                }
            )

        template = self.response_template(SET_ENDPOINT_ATTRIBUTES_TEMPLATE)
        return template.render()

    def delete_endpoint(self) -> str:
        arn = self._get_param("EndpointArn")
        self.backend.delete_endpoint(arn)

        if self.request_json:
            return json.dumps(
                {
                    "DeleteEndpointResponse": {
                        "ResponseMetadata": {
                            "RequestId": "384bc68d-3775-12df-8963-01868b7c937f"
                        }
                    }
                }
            )

        template = self.response_template(DELETE_ENDPOINT_TEMPLATE)
        return template.render()

    def get_subscription_attributes(self) -> str:
        arn = self._get_param("SubscriptionArn")
        attributes = self.backend.get_subscription_attributes(arn)
        template = self.response_template(GET_SUBSCRIPTION_ATTRIBUTES_TEMPLATE)
        return template.render(attributes=attributes)

    def set_subscription_attributes(self) -> str:
        arn = self._get_param("SubscriptionArn")
        attr_name = self._get_param("AttributeName")
        attr_value = self._get_param("AttributeValue")
        self.backend.set_subscription_attributes(arn, attr_name, attr_value)
        template = self.response_template(SET_SUBSCRIPTION_ATTRIBUTES_TEMPLATE)
        return template.render()

    def set_sms_attributes(self) -> str:
        # attributes.entry.1.key
        # attributes.entry.1.value
        # to
        # 1: {key:X, value:Y}
        temp_dict: Dict[str, Any] = defaultdict(dict)
        for key, value in self.querystring.items():
            match = self.SMS_ATTR_REGEX.match(key)
            if match is not None:
                temp_dict[match.group("index")][match.group("type")] = value[0]

        # 1: {key:X, value:Y}
        # to
        # X: Y
        # All of this, just to take into account when people provide invalid stuff.
        result = {}
        for item in temp_dict.values():
            if "key" in item and "value" in item:
                result[item["key"]] = item["value"]

        self.backend.set_sms_attributes(result)

        template = self.response_template(SET_SMS_ATTRIBUTES_TEMPLATE)
        return template.render()

    def get_sms_attributes(self) -> str:
        filter_list = set()
        for key, value in self.querystring.items():
            if key.startswith("attributes.member.1"):
                filter_list.add(value[0])

        result = self.backend.get_sms_attributes(filter_list)

        template = self.response_template(GET_SMS_ATTRIBUTES_TEMPLATE)
        return template.render(attributes=result)

    def check_if_phone_number_is_opted_out(
        self,
    ) -> Union[str, Tuple[str, Dict[str, int]]]:
        number = self._get_param("phoneNumber")
        if self.OPT_OUT_PHONE_NUMBER_REGEX.match(number) is None:
            error_response = self._error(
                code="InvalidParameter",
                message="Invalid parameter: PhoneNumber Reason: input incorrectly formatted",
            )
            return error_response, dict(status=400)

        x = self.backend.check_if_phone_number_is_opted_out(number)
        template = self.response_template(CHECK_IF_OPTED_OUT_TEMPLATE)
        return template.render(opt_out=str(x).lower())

    def list_phone_numbers_opted_out(self) -> str:
        numbers = self.backend.list_phone_numbers_opted_out()
        template = self.response_template(LIST_OPTOUT_TEMPLATE)
        return template.render(opt_outs=numbers)

    def opt_in_phone_number(self) -> str:
        number = self._get_param("phoneNumber")

        self.backend.opt_in_phone_number(number)

        template = self.response_template(OPT_IN_NUMBER_TEMPLATE)
        return template.render()

    def add_permission(self) -> str:
        topic_arn = self._get_param("TopicArn")
        label = self._get_param("Label")
        aws_account_ids = self._get_multi_param("AWSAccountId.member.")
        action_names = self._get_multi_param("ActionName.member.")

        self.backend.add_permission(topic_arn, label, aws_account_ids, action_names)

        template = self.response_template(ADD_PERMISSION_TEMPLATE)
        return template.render()

    def remove_permission(self) -> str:
        topic_arn = self._get_param("TopicArn")
        label = self._get_param("Label")

        self.backend.remove_permission(topic_arn, label)

        template = self.response_template(DEL_PERMISSION_TEMPLATE)
        return template.render()

    def confirm_subscription(self) -> Union[str, Tuple[str, Dict[str, int]]]:
        arn = self._get_param("TopicArn")

        if arn not in self.backend.topics:
            error_response = self._error("NotFound", "Topic does not exist")
            return error_response, dict(status=404)

        # Once Tokens are stored by the `subscribe` endpoint and distributed
        # to the client somehow, then we can check validity of tokens
        # presented to this method. The following code works, all thats
        # needed is to perform a token check and assign that value to the
        # `already_subscribed` variable.
        #
        # token = self._get_param('Token')
        # auth = self._get_param('AuthenticateOnUnsubscribe')
        # if already_subscribed:
        #     error_response = self._error(
        #         code='AuthorizationError',
        #         message='Subscription already confirmed'
        #     )
        #     return error_response, dict(status=400)

        template = self.response_template(CONFIRM_SUBSCRIPTION_TEMPLATE)
        return template.render(sub_arn=f"{arn}:68762e72-e9b1-410a-8b3b-903da69ee1d5")

    def list_tags_for_resource(self) -> str:
        arn = self._get_param("ResourceArn")

        result = self.backend.list_tags_for_resource(arn)

        template = self.response_template(LIST_TAGS_FOR_RESOURCE_TEMPLATE)
        return template.render(tags=result)

    def tag_resource(self) -> str:
        arn = self._get_param("ResourceArn")
        tags = self._get_tags()

        self.backend.tag_resource(arn, tags)

        return self.response_template(TAG_RESOURCE_TEMPLATE).render()

    def untag_resource(self) -> str:
        arn = self._get_param("ResourceArn")
        tag_keys = self._get_multi_param("TagKeys.member")

        self.backend.untag_resource(arn, tag_keys)

        return self.response_template(UNTAG_RESOURCE_TEMPLATE).render()


CREATE_TOPIC_TEMPLATE = """<CreateTopicResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
     <CreateTopicResult>
       <TopicArn>{{ topic.arn }}</TopicArn>
     </CreateTopicResult>
     <ResponseMetadata>
       <RequestId>a8dec8b3-33a4-11df-8963-01868b7c937a</RequestId>
     </ResponseMetadata>
   </CreateTopicResponse>"""

LIST_TOPICS_TEMPLATE = """<ListTopicsResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
  <ListTopicsResult>
    <Topics>
    {% for topic in topics %}
      <member>
        <TopicArn>{{ topic.arn }}</TopicArn>
      </member>
    {% endfor %}
    </Topics>
    {% if next_token  %}
    <NextToken>{{ next_token }}</NextToken>
    {% endif %}
  </ListTopicsResult>
  <ResponseMetadata>
    <RequestId>3f1478c7-33a9-11df-9540-99d0768312d3</RequestId>
  </ResponseMetadata>
</ListTopicsResponse>"""

DELETE_TOPIC_TEMPLATE = """<DeleteTopicResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
  <ResponseMetadata>
    <RequestId>f3aa9ac9-3c3d-11df-8235-9dab105e9c32</RequestId>
  </ResponseMetadata>
</DeleteTopicResponse>"""

GET_TOPIC_ATTRIBUTES_TEMPLATE = """<GetTopicAttributesResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
  <GetTopicAttributesResult>
    <Attributes>
      <entry>
        <key>Owner</key>
        <value>{{ topic.account_id }}</value>
      </entry>
      <entry>
        <key>Policy</key>
        <value>{{ topic.policy }}</value>
      </entry>
      <entry>
        <key>TopicArn</key>
        <value>{{ topic.arn }}</value>
      </entry>
      <entry>
        <key>DisplayName</key>
        <value>{{ topic.display_name }}</value>
      </entry>
      <entry>
        <key>SubscriptionsPending</key>
        <value>{{ topic.subscriptions_pending }}</value>
      </entry>
      <entry>
        <key>SubscriptionsConfirmed</key>
        <value>{{ topic.subscriptions_confimed }}</value>
      </entry>
      <entry>
        <key>SubscriptionsDeleted</key>
        <value>{{ topic.subscriptions_deleted }}</value>
      </entry>
      <entry>
        <key>DeliveryPolicy</key>
        <value>{{ topic.delivery_policy }}</value>
      </entry>
      <entry>
        <key>EffectiveDeliveryPolicy</key>
        <value>{{ topic.effective_delivery_policy }}</value>
      </entry>
      {% if topic.kms_master_key_id %}
      <entry>
        <key>KmsMasterKeyId</key>
        <value>{{ topic.kms_master_key_id }}</value>
      </entry>
      {% endif %}
      {% if topic.fifo_topic == 'true' %}
      <entry>
        <key>FifoTopic</key>
        <value>{{ topic.fifo_topic }}</value>
      </entry>
      <entry>
        <key>ContentBasedDeduplication</key>
        <value>{{ topic.content_based_deduplication }}</value>
      </entry>
      {% endif %}
    </Attributes>
  </GetTopicAttributesResult>
  <ResponseMetadata>
    <RequestId>057f074c-33a7-11df-9540-99d0768312d3</RequestId>
  </ResponseMetadata>
</GetTopicAttributesResponse>"""

SET_TOPIC_ATTRIBUTES_TEMPLATE = """<SetTopicAttributesResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
  <ResponseMetadata>
    <RequestId>a8763b99-33a7-11df-a9b7-05d48da6f042</RequestId>
  </ResponseMetadata>
</SetTopicAttributesResponse>"""

CREATE_PLATFORM_APPLICATION_TEMPLATE = """<CreatePlatformApplicationResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
  <CreatePlatformApplicationResult>
    <PlatformApplicationArn>{{ platform_application.arn }}</PlatformApplicationArn>
  </CreatePlatformApplicationResult>
  <ResponseMetadata>
    <RequestId>b6f0e78b-e9d4-5a0e-b973-adc04e8a4ff9</RequestId>
  </ResponseMetadata>
</CreatePlatformApplicationResponse>"""

CREATE_PLATFORM_ENDPOINT_TEMPLATE = """<CreatePlatformEndpointResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
  <CreatePlatformEndpointResult>
    <EndpointArn>{{ platform_endpoint.arn }}</EndpointArn>
  </CreatePlatformEndpointResult>
  <ResponseMetadata>
    <RequestId>6613341d-3e15-53f7-bf3c-7e56994ba278</RequestId>
  </ResponseMetadata>
</CreatePlatformEndpointResponse>"""

LIST_PLATFORM_APPLICATIONS_TEMPLATE = """<ListPlatformApplicationsResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
  <ListPlatformApplicationsResult>
    <PlatformApplications>
    {% for application in applications %}
      <member>
      <PlatformApplicationArn>{{ application.arn }}</PlatformApplicationArn>
        <Attributes>
        {% for attribute in application.attributes %}
          <entry>
            <key>{{ attribute }}</key>
            <value>{{ application.attributes[attribute] }}</value>
          </entry>
        {% endfor %}
        </Attributes>
      </member>
    {% endfor %}
    </PlatformApplications>
  </ListPlatformApplicationsResult>
  <ResponseMetadata>
    <RequestId>315a335e-85d8-52df-9349-791283cbb529</RequestId>
  </ResponseMetadata>
</ListPlatformApplicationsResponse>"""

DELETE_PLATFORM_APPLICATION_TEMPLATE = """<DeletePlatformApplicationResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
  <ResponseMetadata>
    <RequestId>097dac18-7a77-5823-a8dd-e65476dcb037</RequestId>
  </ResponseMetadata>
</DeletePlatformApplicationResponse>"""

GET_ENDPOINT_ATTRIBUTES_TEMPLATE = """<GetEndpointAttributesResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
  <GetEndpointAttributesResult>
    <Attributes>
    {% for attribute in attributes %}
      <entry>
        <key>{{ attribute }}</key>
        <value>{{ attributes[attribute] }}</value>
      </entry>
    {% endfor %}
    </Attributes>
  </GetEndpointAttributesResult>
  <ResponseMetadata>
    <RequestId>6c725a19-a142-5b77-94f9-1055a9ea04e7</RequestId>
  </ResponseMetadata>
</GetEndpointAttributesResponse>"""

LIST_ENDPOINTS_BY_PLATFORM_APPLICATION_TEMPLATE = """<ListEndpointsByPlatformApplicationResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
  <ListEndpointsByPlatformApplicationResult>
    <Endpoints>
      {% for endpoint in endpoints %}
      <member>
        <EndpointArn>{{ endpoint.arn }}</EndpointArn>
        <Attributes>
          {% for attribute in endpoint.attributes %}
          <entry>
            <key>{{ attribute }}</key>
            <value>{{ endpoint.attributes[attribute] }}</value>
          </entry>
          {% endfor %}
        </Attributes>
      </member>
      {% endfor %}
    </Endpoints>
  </ListEndpointsByPlatformApplicationResult>
  <ResponseMetadata>
    <RequestId>9a48768c-dac8-5a60-aec0-3cc27ea08d96</RequestId>
  </ResponseMetadata>
</ListEndpointsByPlatformApplicationResponse>"""

GET_PLATFORM_APPLICATION_ATTRIBUTES_TEMPLATE = """<GetPlatformApplicationAttributesResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
  <GetPlatformApplicationAttributesResult>
    <Attributes>
    {% for attribute in attributes %}
      <entry>
        <key>{{ attribute }}</key>
        <value>{{ attributes[attribute] }}</value>
      </entry>
    {% endfor %}
    </Attributes>
  </GetPlatformApplicationAttributesResult>
  <ResponseMetadata>
    <RequestId>74848df2-87f6-55ed-890c-c7be80442462</RequestId>
  </ResponseMetadata>
</GetPlatformApplicationAttributesResponse>"""

PUBLISH_TEMPLATE = """<PublishResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
  <PublishResult>
    <MessageId>{{ message_id }}</MessageId>
  </PublishResult>
  <ResponseMetadata>
    <RequestId>f187a3c1-376f-11df-8963-01868b7c937a</RequestId>
  </ResponseMetadata>
</PublishResponse>"""

SET_ENDPOINT_ATTRIBUTES_TEMPLATE = """<SetEndpointAttributesResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
  <ResponseMetadata>
    <RequestId>2fe0bfc7-3e85-5ee5-a9e2-f58b35e85f6a</RequestId>
  </ResponseMetadata>
</SetEndpointAttributesResponse>"""

DELETE_ENDPOINT_TEMPLATE = """<DeleteEndpointResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
 <ResponseMetadata>
 <RequestId>c1d2b191-353c-5a5f-8969-fbdd3900afa8</RequestId>
 </ResponseMetadata>
</DeleteEndpointResponse>"""


SET_PLATFORM_APPLICATION_ATTRIBUTES_TEMPLATE = """<SetPlatformApplicationAttributesResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
  <ResponseMetadata>
    <RequestId>cf577bcc-b3dc-5463-88f1-3180b9412395</RequestId>
  </ResponseMetadata>
</SetPlatformApplicationAttributesResponse>"""

SUBSCRIBE_TEMPLATE = """<SubscribeResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
  <SubscribeResult>
    <SubscriptionArn>{{ subscription.arn }}</SubscriptionArn>
  </SubscribeResult>
  <ResponseMetadata>
    <RequestId>c4407779-24a4-56fa-982c-3d927f93a775</RequestId>
  </ResponseMetadata>
</SubscribeResponse>"""

UNSUBSCRIBE_TEMPLATE = """<UnsubscribeResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
  <ResponseMetadata>
    <RequestId>18e0ac39-3776-11df-84c0-b93cc1666b84</RequestId>
  </ResponseMetadata>
</UnsubscribeResponse>"""

LIST_SUBSCRIPTIONS_TEMPLATE = """<ListSubscriptionsResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
  <ListSubscriptionsResult>
    <Subscriptions>
    {% for subscription in subscriptions %}
      <member>
        <TopicArn>{{ subscription.topic.arn }}</TopicArn>
        <Protocol>{{ subscription.protocol }}</Protocol>
        <SubscriptionArn>{{ subscription.arn }}</SubscriptionArn>
        <Owner>{{ subscription.account_id }}</Owner>
        <Endpoint>{{ subscription.endpoint }}</Endpoint>
      </member>
    {% endfor %}
    </Subscriptions>
    {% if next_token  %}
    <NextToken>{{ next_token }}</NextToken>
    {% endif %}
  </ListSubscriptionsResult>
  <ResponseMetadata>
    <RequestId>384ac68d-3775-11df-8963-01868b7c937a</RequestId>
  </ResponseMetadata>
</ListSubscriptionsResponse>"""

LIST_SUBSCRIPTIONS_BY_TOPIC_TEMPLATE = """<ListSubscriptionsByTopicResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
  <ListSubscriptionsByTopicResult>
    <Subscriptions>
    {% for subscription in subscriptions %}
      <member>
        <TopicArn>{{ subscription.topic.arn }}</TopicArn>
        <Protocol>{{ subscription.protocol }}</Protocol>
        <SubscriptionArn>{{ subscription.arn }}</SubscriptionArn>
        <Owner>{{ subscription.account_id }}</Owner>
        <Endpoint>{{ subscription.endpoint }}</Endpoint>
      </member>
    {% endfor %}
    </Subscriptions>
    {% if next_token  %}
    <NextToken>{{ next_token }}</NextToken>
    {% endif %}
  </ListSubscriptionsByTopicResult>
  <ResponseMetadata>
    <RequestId>384ac68d-3775-11df-8963-01868b7c937a</RequestId>
  </ResponseMetadata>
</ListSubscriptionsByTopicResponse>"""


# Not responding aws system attribetus like 'Owner' and 'SubscriptionArn'
GET_SUBSCRIPTION_ATTRIBUTES_TEMPLATE = """<GetSubscriptionAttributesResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
  <GetSubscriptionAttributesResult>
    <Attributes>
      {% for name, value in attributes.items() %}
      <entry>
        <key>{{ name }}</key>
        <value>{{ value }}</value>
      </entry>
      {% endfor %}
    </Attributes>
  </GetSubscriptionAttributesResult>
  <ResponseMetadata>
    <RequestId>057f074c-33a7-11df-9540-99d0768312d3</RequestId>
  </ResponseMetadata>
</GetSubscriptionAttributesResponse>"""


SET_SUBSCRIPTION_ATTRIBUTES_TEMPLATE = """<SetSubscriptionAttributesResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
  <ResponseMetadata>
    <RequestId>a8763b99-33a7-11df-a9b7-05d48da6f042</RequestId>
  </ResponseMetadata>
</SetSubscriptionAttributesResponse>"""

SET_SMS_ATTRIBUTES_TEMPLATE = """<SetSMSAttributesResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
  <SetSMSAttributesResult/>
  <ResponseMetadata>
    <RequestId>26332069-c04a-5428-b829-72524b56a364</RequestId>
  </ResponseMetadata>
</SetSMSAttributesResponse>"""

GET_SMS_ATTRIBUTES_TEMPLATE = """<GetSMSAttributesResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
  <GetSMSAttributesResult>
    <attributes>
      {% for name, value in attributes.items() %}
      {% if value %}
      <entry>
        <key>{{ name }}</key>
        <value>{{ value }}</value>
      </entry>
      {% endif %}
      {% endfor %}
    </attributes>
  </GetSMSAttributesResult>
  <ResponseMetadata>
    <RequestId>287f9554-8db3-5e66-8abc-c76f0186db7e</RequestId>
  </ResponseMetadata>
</GetSMSAttributesResponse>"""

CHECK_IF_OPTED_OUT_TEMPLATE = """<CheckIfPhoneNumberIsOptedOutResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
  <CheckIfPhoneNumberIsOptedOutResult>
    <isOptedOut>{{ opt_out }}</isOptedOut>
  </CheckIfPhoneNumberIsOptedOutResult>
  <ResponseMetadata>
    <RequestId>287f9554-8db3-5e66-8abc-c76f0186db7e</RequestId>
  </ResponseMetadata>
</CheckIfPhoneNumberIsOptedOutResponse>"""

ERROR_RESPONSE = """<ErrorResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
  <Error>
    <Type>{{ sender }}</Type>
    <Code>{{ code }}</Code>
    <Message>{{ message }}</Message>
  </Error>
  <RequestId>9dd01905-5012-5f99-8663-4b3ecd0dfaef</RequestId>
</ErrorResponse>"""

LIST_OPTOUT_TEMPLATE = """<ListPhoneNumbersOptedOutResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
  <ListPhoneNumbersOptedOutResult>
    <phoneNumbers>
      {% for item in opt_outs %}
      <member>{{ item }}</member>
      {% endfor %}
    </phoneNumbers>
  </ListPhoneNumbersOptedOutResult>
  <ResponseMetadata>
    <RequestId>985e196d-a237-51b6-b33a-4b5601276b38</RequestId>
  </ResponseMetadata>
</ListPhoneNumbersOptedOutResponse>"""

OPT_IN_NUMBER_TEMPLATE = """<OptInPhoneNumberResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
  <OptInPhoneNumberResult/>
  <ResponseMetadata>
    <RequestId>4c61842c-0796-50ef-95ac-d610c0bc8cf8</RequestId>
  </ResponseMetadata>
</OptInPhoneNumberResponse>"""

ADD_PERMISSION_TEMPLATE = """<AddPermissionResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
  <ResponseMetadata>
    <RequestId>c046e713-c5ff-5888-a7bc-b52f0e4f1299</RequestId>
  </ResponseMetadata>
</AddPermissionResponse>"""

DEL_PERMISSION_TEMPLATE = """<RemovePermissionResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
  <ResponseMetadata>
    <RequestId>e767cc9f-314b-5e1b-b283-9ea3fd4e38a3</RequestId>
  </ResponseMetadata>
</RemovePermissionResponse>"""

CONFIRM_SUBSCRIPTION_TEMPLATE = """<ConfirmSubscriptionResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
  <ConfirmSubscriptionResult>
    <SubscriptionArn>{{ sub_arn }}</SubscriptionArn>
  </ConfirmSubscriptionResult>
  <ResponseMetadata>
    <RequestId>16eb4dde-7b3c-5b3e-a22a-1fe2a92d3293</RequestId>
  </ResponseMetadata>
</ConfirmSubscriptionResponse>"""

LIST_TAGS_FOR_RESOURCE_TEMPLATE = """<ListTagsForResourceResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
  <ListTagsForResourceResult>
    <Tags>
      {% for name, value in tags.items() %}
      <member>
        <Key>{{ name }}</Key>
        <Value>{{ value }}</Value>
      </member>
      {% endfor %}
    </Tags>
  </ListTagsForResourceResult>
  <ResponseMetadata>
    <RequestId>97fa763f-861b-5223-a946-20251f2a42e2</RequestId>
  </ResponseMetadata>
</ListTagsForResourceResponse>"""

TAG_RESOURCE_TEMPLATE = """<TagResourceResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
    <TagResourceResult/>
    <ResponseMetadata>
        <RequestId>fd4ab1da-692f-50a7-95ad-e7c665877d98</RequestId>
    </ResponseMetadata>
</TagResourceResponse>"""

UNTAG_RESOURCE_TEMPLATE = """<UntagResourceResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
    <UntagResourceResult/>
    <ResponseMetadata>
        <RequestId>14eb7b1a-4cbd-5a56-80db-2d06412df769</RequestId>
    </ResponseMetadata>
</UntagResourceResponse>"""

PUBLISH_BATCH_TEMPLATE = """<PublishBatchResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
  <ResponseMetadata>
    <RequestId>1549581b-12b7-11e3-895e-1334aEXAMPLE</RequestId>
  </ResponseMetadata>
  <PublishBatchResult>
    <Successful>
{% for successful in successful %}
      <member>
        <Id>{{ successful["Id"] }}</Id>
        <MessageId>{{ successful["MessageId"] }}</MessageId>
      </member>
{% endfor %}
    </Successful>
    <Failed>
{% for failed in failed %}
      <member>
        <Id>{{ failed["Id"] }}</Id>
        <Code>{{ failed["Code"] }}</Code>
        <Message>{{ failed["Message"] }}</Message>
        <SenderFault>{{'true' if failed["SenderFault"] else 'false'}}</SenderFault>
      </member>
{% endfor %}
    </Failed>
  </PublishBatchResult>
</PublishBatchResponse>"""
