"""Handles Route53 API requests, invokes method and returns response."""

import re
from typing import Any
from urllib.parse import parse_qs, unquote

import xmltodict
from jinja2 import Template

from moto.core.common_types import TYPE_RESPONSE
from moto.core.responses import BaseResponse
from moto.core.utils import iso_8601_datetime_with_milliseconds
from moto.route53.exceptions import InvalidChangeBatch, InvalidInput
from moto.route53.models import Route53Backend, route53_backends

XMLNS = "https://route53.amazonaws.com/doc/2013-04-01/"


class Route53(BaseResponse):
    """Handler for Route53 requests and responses."""

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

    @staticmethod
    def _convert_to_bool(bool_str: Any) -> bool:  # type: ignore[misc]
        if isinstance(bool_str, bool):
            return bool_str

        if isinstance(bool_str, str):
            return str(bool_str).lower() == "true"

        return False

    @property
    def backend(self) -> Route53Backend:
        return route53_backends[self.current_account][self.partition]

    def create_hosted_zone(self) -> TYPE_RESPONSE:
        vpcid = None
        vpcregion = None
        elements = xmltodict.parse(self.body)
        zone_request = elements["CreateHostedZoneRequest"]
        if "HostedZoneConfig" in zone_request:
            zone_config = zone_request["HostedZoneConfig"]
            comment = zone_config["Comment"]
            if zone_request.get("VPC", {}).get("VPCId", None):
                private_zone = True
            else:
                private_zone = self._convert_to_bool(
                    zone_config.get("PrivateZone", False)
                )
        else:
            comment = None
            private_zone = False

        # It is possible to create a Private Hosted Zone without
        # associating VPC at the time of creation.
        if self._convert_to_bool(private_zone):
            if zone_request.get("VPC", None) is not None:
                vpcid = zone_request["VPC"].get("VPCId", None)
                vpcregion = zone_request["VPC"].get("VPCRegion", None)

        name = zone_request["Name"]
        caller_reference = zone_request["CallerReference"]

        if name[-1] != ".":
            name += "."
        delegation_set_id = zone_request.get("DelegationSetId")

        new_zone = self.backend.create_hosted_zone(
            name,
            comment=comment,
            private_zone=private_zone,
            caller_reference=caller_reference,
            vpcid=vpcid,
            vpcregion=vpcregion,
            delegation_set_id=delegation_set_id,
        )
        template = Template(CREATE_HOSTED_ZONE_RESPONSE).render(zone=new_zone)
        headers = {
            "Location": f"https://route53.amazonaws.com/2013-04-01/hostedzone/{new_zone.id}",
            "status": 201,
        }
        return 201, headers, template

    def list_hosted_zones(self) -> str:
        max_size = self.querystring.get("maxitems", [None])[0]
        if max_size:
            max_size = int(max_size)
        marker = self.querystring.get("marker", [None])[0]
        zone_page, next_marker = self.backend.list_hosted_zones(
            marker=marker, max_size=max_size
        )
        template = Template(LIST_HOSTED_ZONES_RESPONSE).render(
            zones=zone_page,
            marker=marker,
            next_marker=next_marker,
            max_items=max_size,
        )
        return template

    def list_hosted_zones_by_name(self) -> str:
        query_params = parse_qs(self.parsed_url.query)
        dnsnames = query_params.get("dnsname")

        dnsname, zones = self.backend.list_hosted_zones_by_name(dnsnames)

        template = Template(LIST_HOSTED_ZONES_BY_NAME_RESPONSE)
        return template.render(zones=zones, dnsname=dnsname, xmlns=XMLNS)

    def list_hosted_zones_by_vpc(self) -> str:
        query_params = parse_qs(self.parsed_url.query)
        vpc_id = query_params.get("vpcid")[0]  # type: ignore
        zones = self.backend.list_hosted_zones_by_vpc(vpc_id)
        template = Template(LIST_HOSTED_ZONES_BY_VPC_RESPONSE)
        return template.render(zones=zones, xmlns=XMLNS)

    def get_hosted_zone_count(self) -> str:
        num_zones = self.backend.get_hosted_zone_count()
        template = Template(GET_HOSTED_ZONE_COUNT_RESPONSE)
        return template.render(zone_count=num_zones, xmlns=XMLNS)

    def get_hosted_zone(self) -> str:
        zoneid = self.parsed_url.path.rstrip("/").rsplit("/", 1)[1]
        the_zone = self.backend.get_hosted_zone(zoneid)
        template = Template(GET_HOSTED_ZONE_RESPONSE)
        return template.render(zone=the_zone)

    def delete_hosted_zone(self) -> str:
        zoneid = self.parsed_url.path.rstrip("/").rsplit("/", 1)[1]
        self.backend.delete_hosted_zone(zoneid)
        return DELETE_HOSTED_ZONE_RESPONSE

    def update_hosted_zone_comment(self) -> str:
        zoneid = self.parsed_url.path.rstrip("/").rsplit("/", 1)[1]

        elements = xmltodict.parse(self.body)
        comment = elements.get("UpdateHostedZoneCommentRequest", {}).get(
            "Comment", None
        )
        zone = self.backend.update_hosted_zone_comment(zoneid, comment)
        template = Template(UPDATE_HOSTED_ZONE_COMMENT_RESPONSE)
        return template.render(zone=zone)

    def get_dnssec(self) -> str:
        # TODO: implement enable/disable dnssec apis
        zoneid = self.parsed_url.path.rstrip("/").rsplit("/", 2)[1]

        self.backend.get_dnssec(zoneid)
        return GET_DNSSEC

    def associate_vpc_with_hosted_zone(self) -> str:
        zoneid = self.parsed_url.path.rstrip("/").rsplit("/", 2)[1]

        elements = xmltodict.parse(self.body)
        comment = elements.get("AssociateVPCWithHostedZoneRequest", {}).get(
            "Comment", {}
        )
        vpc = elements.get("AssociateVPCWithHostedZoneRequest", {}).get("VPC", {})
        vpcid = vpc.get("VPCId", None)
        vpcregion = vpc.get("VPCRegion", None)

        self.backend.associate_vpc_with_hosted_zone(zoneid, vpcid, vpcregion)

        template = Template(ASSOCIATE_VPC_RESPONSE)
        return template.render(comment=comment)

    def disassociate_vpc_from_hosted_zone(self) -> str:
        zoneid = self.parsed_url.path.rstrip("/").rsplit("/", 2)[1]

        elements = xmltodict.parse(self.body)
        comment = elements.get("DisassociateVPCFromHostedZoneRequest", {}).get(
            "Comment", {}
        )
        vpc = elements.get("DisassociateVPCFromHostedZoneRequest", {}).get("VPC", {})
        vpcid = vpc.get("VPCId", None)

        self.backend.disassociate_vpc_from_hosted_zone(zoneid, vpcid)

        template = Template(DISASSOCIATE_VPC_RESPONSE)
        return template.render(comment=comment)

    def change_resource_record_sets(self) -> str:
        zoneid = self.parsed_url.path.rstrip("/").rsplit("/", 2)[1]

        elements = xmltodict.parse(self.body)

        change_list = elements["ChangeResourceRecordSetsRequest"]["ChangeBatch"][
            "Changes"
        ]["Change"]
        if not isinstance(change_list, list):
            change_list = [
                elements["ChangeResourceRecordSetsRequest"]["ChangeBatch"]["Changes"][
                    "Change"
                ]
            ]

        # Enforce quotas https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/DNSLimitations.html#limits-api-requests-changeresourcerecordsets
        #  - A request cannot contain more than 1,000 ResourceRecord elements. When the value of the Action element is UPSERT, each ResourceRecord element is counted twice.
        effective_rr_count = 0
        for value in change_list:
            record_set = value["ResourceRecordSet"]
            if "ResourceRecords" not in record_set or not record_set["ResourceRecords"]:
                continue
            resource_records = list(record_set["ResourceRecords"].values())[0]
            effective_rr_count += len(resource_records)
            if value["Action"] == "UPSERT":
                effective_rr_count += len(resource_records)
        if effective_rr_count > 1000:
            raise InvalidChangeBatch

        self.backend.change_resource_record_sets(zoneid, change_list)

        return CHANGE_RRSET_RESPONSE

    def list_resource_record_sets(self) -> TYPE_RESPONSE:
        zoneid = self.parsed_url.path.rstrip("/").rsplit("/", 2)[1]
        querystring = parse_qs(self.parsed_url.query)
        template = Template(LIST_RRSET_RESPONSE)
        start_type = querystring.get("type", [None])[0]
        start_name = querystring.get("name", [None])[0]
        max_items = int(querystring.get("maxitems", ["300"])[0])

        if start_type and not start_name:
            return 400, {"status": 400}, "The input is not valid"

        (
            record_sets,
            next_name,
            next_type,
            is_truncated,
        ) = self.backend.list_resource_record_sets(
            zoneid,
            start_type=start_type,  # type: ignore
            start_name=start_name,  # type: ignore
            max_items=max_items,
        )
        r_template = template.render(
            record_sets=record_sets,
            next_name=next_name,
            next_type=next_type,
            max_items=max_items,
            is_truncated=is_truncated,
        )
        return 200, {}, r_template

    def create_health_check(self) -> TYPE_RESPONSE:
        json_body = xmltodict.parse(self.body)["CreateHealthCheckRequest"]
        caller_reference = json_body["CallerReference"]
        config = json_body["HealthCheckConfig"]
        health_check_args = {
            "ip_address": config.get("IPAddress"),
            "port": config.get("Port"),
            "type": config["Type"],
            "resource_path": config.get("ResourcePath"),
            "fqdn": config.get("FullyQualifiedDomainName"),
            "search_string": config.get("SearchString"),
            "request_interval": config.get("RequestInterval"),
            "failure_threshold": config.get("FailureThreshold"),
            "health_threshold": config.get("HealthThreshold"),
            "measure_latency": config.get("MeasureLatency"),
            "inverted": config.get("Inverted"),
            "disabled": config.get("Disabled"),
            "enable_sni": config.get("EnableSNI"),
            "children": config.get("ChildHealthChecks", {}).get("ChildHealthCheck"),
            "regions": config.get("Regions", {}).get("Region"),
        }
        health_check = self.backend.create_health_check(
            caller_reference, health_check_args
        )
        template = Template(CREATE_HEALTH_CHECK_RESPONSE)
        return (
            201,
            {"status": 201},
            template.render(health_check=health_check, xmlns=XMLNS),
        )

    def list_health_checks(self) -> str:
        template = Template(LIST_HEALTH_CHECKS_RESPONSE)
        health_checks = self.backend.list_health_checks()
        return template.render(health_checks=health_checks, xmlns=XMLNS)

    def get_health_check(self) -> str:
        health_check_id = self.parsed_url.path.split("/")[-1]

        health_check = self.backend.get_health_check(health_check_id)
        template = Template(GET_HEALTH_CHECK_RESPONSE)
        return template.render(health_check=health_check)

    def delete_health_check(self) -> str:
        health_check_id = self.parsed_url.path.split("/")[-1]
        self.backend.delete_health_check(health_check_id)
        template = Template(DELETE_HEALTH_CHECK_RESPONSE)
        return template.render(xmlns=XMLNS)

    def update_health_check(self) -> str:
        health_check_id = self.parsed_url.path.split("/")[-1]
        config = xmltodict.parse(self.body)["UpdateHealthCheckRequest"]
        health_check_args = {
            "ip_address": config.get("IPAddress"),
            "port": config.get("Port"),
            "resource_path": config.get("ResourcePath"),
            "fqdn": config.get("FullyQualifiedDomainName"),
            "search_string": config.get("SearchString"),
            "failure_threshold": config.get("FailureThreshold"),
            "health_threshold": config.get("HealthThreshold"),
            "inverted": config.get("Inverted"),
            "disabled": config.get("Disabled"),
            "enable_sni": config.get("EnableSNI"),
            "children": config.get("ChildHealthChecks", {}).get("ChildHealthCheck"),
            "regions": config.get("Regions", {}).get("Region"),
        }
        health_check = self.backend.update_health_check(
            health_check_id, health_check_args
        )
        template = Template(UPDATE_HEALTH_CHECK_RESPONSE)
        return template.render(health_check=health_check)

    def get_health_check_status(self) -> str:
        health_check_match = re.search(
            r"healthcheck/(?P<health_check_id>[^/]+)/status$", self.parsed_url.path
        )
        health_check_id = health_check_match.group("health_check_id")  # type: ignore[union-attr]

        self.backend.get_health_check(health_check_id)
        template = Template(GET_HEALTH_CHECK_STATUS_RESPONSE)
        return template.render(timestamp=iso_8601_datetime_with_milliseconds())

    def not_implemented_response(
        self, request: Any, full_url: str, headers: Any
    ) -> TYPE_RESPONSE:
        self.setup_class(request, full_url, headers)

        action = ""
        if "tags" in full_url:
            action = "tags"
        elif "trafficpolicyinstances" in full_url:
            action = "policies"
        raise NotImplementedError(
            f"The action for {action} has not been implemented for route 53"
        )

    def list_or_change_tags_for_resource_request(  # type: ignore[return]
        self, request: Any, full_url: str, headers: Any
    ) -> TYPE_RESPONSE:
        self.setup_class(request, full_url, headers)

        id_matcher = re.search(r"/tags/([-a-z]+)/(.+)", self.parsed_url.path)
        assert id_matcher
        type_ = id_matcher.group(1)
        id_ = unquote(id_matcher.group(2))
        if type_ == "hostedzone" and len(id_) > 32:
            # From testing, it looks like Route53 creates ID's that are either 21 or 22 characters long
            # In practice, this error will typically appear when passing in the full ID: `/hostedzone/{id}`
            # Users should pass in {id} instead
            # NOTE: we don't know (yet) what kind of validation (if any) is in place for type_==healthcheck.
            raise InvalidInput(
                f"1 validation error detected: Value '{id_}' at 'resourceId' failed to satisfy constraint: Member must have length less than or equal to 32"
            )

        if request.method == "GET":
            tags = self.backend.list_tags_for_resource(id_)
            template = Template(LIST_TAGS_FOR_RESOURCE_RESPONSE)
            return (
                200,
                headers,
                template.render(resource_type=type_, resource_id=id_, tags=tags),
            )

        if request.method == "POST":
            tags = xmltodict.parse(self.body)["ChangeTagsForResourceRequest"]

            if "AddTags" in tags:
                tags = tags["AddTags"]  # type: ignore
            elif "RemoveTagKeys" in tags:
                tags = tags["RemoveTagKeys"]  # type: ignore

            self.backend.change_tags_for_resource(type_, id_, tags)
            template = Template(CHANGE_TAGS_FOR_RESOURCE_RESPONSE)
            return 200, headers, template.render()

    def list_tags_for_resources(self) -> str:
        if id_matcher := re.search(r"/tags/(.+)", self.parsed_url.path):
            resource_type = unquote(id_matcher.group(1))
        else:
            resource_type = ""
        resource_ids = xmltodict.parse(self.body)["ListTagsForResourcesRequest"][
            "ResourceIds"
        ]["ResourceId"]
        # If only one resource id is passed, it is a string, not a list
        if not isinstance(resource_ids, list):
            resource_ids = [resource_ids]
        tag_sets = self.backend.list_tags_for_resources(resource_ids=resource_ids)
        template = Template(LIST_TAGS_FOR_RESOURCES_RESPONSE)
        return template.render(tag_sets=tag_sets, resource_type=resource_type)

    def get_change(self) -> str:
        change_id = self.parsed_url.path.rstrip("/").rsplit("/", 1)[1]
        template = Template(GET_CHANGE_RESPONSE)
        return template.render(change_id=change_id, xmlns=XMLNS)

    def create_query_logging_config(self) -> TYPE_RESPONSE:
        json_body = xmltodict.parse(self.body)["CreateQueryLoggingConfigRequest"]
        hosted_zone_id = json_body["HostedZoneId"]
        log_group_arn = json_body["CloudWatchLogsLogGroupArn"]

        query_logging_config = self.backend.create_query_logging_config(
            hosted_zone_id, log_group_arn
        )

        template = Template(CREATE_QUERY_LOGGING_CONFIG_RESPONSE)
        headers = {"Location": query_logging_config.location, "status": 201}
        return (
            201,
            headers,
            template.render(query_logging_config=query_logging_config, xmlns=XMLNS),
        )

    def list_query_logging_configs(self) -> str:
        hosted_zone_id = self._get_param("hostedzoneid")
        next_token = self._get_param("nexttoken")
        max_results = self._get_int_param("maxresults")

        all_configs, next_token = self.backend.list_query_logging_configs(
            hosted_zone_id=hosted_zone_id,
            next_token=next_token,
            max_results=max_results,
        )

        template = Template(LIST_QUERY_LOGGING_CONFIGS_RESPONSE)
        return template.render(
            query_logging_configs=all_configs, next_token=next_token, xmlns=XMLNS
        )

    def get_query_logging_config(self) -> str:
        query_logging_config_id = self.parsed_url.path.rstrip("/").rsplit("/", 1)[1]

        query_logging_config = self.backend.get_query_logging_config(
            query_logging_config_id
        )
        template = Template(GET_QUERY_LOGGING_CONFIG_RESPONSE)
        return template.render(query_logging_config=query_logging_config, xmlns=XMLNS)

    def delete_query_logging_config(self) -> str:
        query_logging_config_id = self.parsed_url.path.rstrip("/").rsplit("/", 1)[1]
        self.backend.delete_query_logging_config(query_logging_config_id)
        return ""

    def list_reusable_delegation_sets(self) -> str:
        delegation_sets = self.backend.list_reusable_delegation_sets()
        template = self.response_template(LIST_REUSABLE_DELEGATION_SETS_TEMPLATE)
        return template.render(
            delegation_sets=delegation_sets,
            marker=None,
            is_truncated=False,
            max_items=100,
        )

    def create_reusable_delegation_set(self) -> TYPE_RESPONSE:
        elements = xmltodict.parse(self.body)
        root_elem = elements["CreateReusableDelegationSetRequest"]
        caller_reference = root_elem.get("CallerReference")
        hosted_zone_id = root_elem.get("HostedZoneId")
        delegation_set = self.backend.create_reusable_delegation_set(
            caller_reference=caller_reference, hosted_zone_id=hosted_zone_id
        )
        template = self.response_template(CREATE_REUSABLE_DELEGATION_SET_TEMPLATE)
        return (
            201,
            {"Location": delegation_set.location, "status": 201},
            template.render(delegation_set=delegation_set),
        )

    def get_reusable_delegation_set(self) -> str:
        ds_id = self.parsed_url.path.rstrip("/").rsplit("/")[-1]
        delegation_set = self.backend.get_reusable_delegation_set(
            delegation_set_id=ds_id
        )
        template = self.response_template(GET_REUSABLE_DELEGATION_SET_TEMPLATE)
        return template.render(delegation_set=delegation_set)

    def delete_reusable_delegation_set(self) -> str:
        ds_id = self.parsed_url.path.rstrip("/").rsplit("/")[-1]
        self.backend.delete_reusable_delegation_set(delegation_set_id=ds_id)
        template = self.response_template(DELETE_REUSABLE_DELEGATION_SET_TEMPLATE)
        return template.render()


LIST_TAGS_FOR_RESOURCE_RESPONSE = """
<ListTagsForResourceResponse xmlns="https://route53.amazonaws.com/doc/2015-01-01/">
    <ResourceTagSet>
        <ResourceType>{{resource_type}}</ResourceType>
        <ResourceId>{{resource_id}}</ResourceId>
        <Tags>
            {% for key, value in tags.items() %}
            <Tag>
                <Key>{{key}}</Key>
                <Value>{{value}}</Value>
            </Tag>
            {% endfor %}
        </Tags>
    </ResourceTagSet>
</ListTagsForResourceResponse>
"""

LIST_TAGS_FOR_RESOURCES_RESPONSE = """
<ListTagsForResourcesResponse xmlns="https://route53.amazonaws.com/doc/2015-01-01/">
    <ResourceTagSets>
        {% for set in tag_sets %}
        <ResourceTagSet>
            <ResourceType>{{resource_type}}</ResourceType>
            <ResourceId>{{set["ResourceId"]}}</ResourceId>
            <Tags>
                {% for key, value in set["Tags"].items() %}
                <Tag>
                    <Key>{{key}}</Key>
                    <Value>{{value}}</Value>
                </Tag>
                {% endfor %}
            </Tags>
        </ResourceTagSet>
        {% endfor %}
    </ResourceTagSets>
</ListTagsForResourcesResponse>
"""

CHANGE_TAGS_FOR_RESOURCE_RESPONSE = """<ChangeTagsForResourceResponse xmlns="https://route53.amazonaws.com/doc/2015-01-01/">
</ChangeTagsForResourceResponse>
"""

LIST_RRSET_RESPONSE = """<?xml version="1.0" encoding="UTF-8"?>
<ListResourceRecordSetsResponse xmlns="https://route53.amazonaws.com/doc/2012-12-12/">
   <ResourceRecordSets>
       {% for record in record_sets %}
       <ResourceRecordSet>
           <Name>{{ record.name }}</Name>
           <Type>{{ record.type_ }}</Type>
           {% if record.set_identifier %}
               <SetIdentifier>{{ record.set_identifier }}</SetIdentifier>
           {% endif %}
           {% if record.weight %}
               <Weight>{{ record.weight }}</Weight>
           {% endif %}
           {% if record.region %}
               <Region>{{ record.region }}</Region>
           {% endif %}
           {% if record.ttl %}
               <TTL>{{ record.ttl }}</TTL>
           {% endif %}
           {% if record.failover %}
               <Failover>{{ record.failover }}</Failover>
           {% endif %}
           {% if record.multi_value %}
               <MultiValueAnswer>{{ record.multi_value }}</MultiValueAnswer>
           {% endif %}
           {% if record.geo_location %}
           <GeoLocation>
           {% for geo_key in ['ContinentCode','CountryCode','SubdivisionCode'] %}
             {% if record.geo_location[geo_key] %}<{{ geo_key }}>{{ record.geo_location[geo_key] }}</{{ geo_key }}>{% endif %}
           {% endfor %}
           </GeoLocation>
           {% endif %}
           {% if record.alias_target %}
           <AliasTarget>
               <HostedZoneId>{{ record.alias_target['HostedZoneId'] }}</HostedZoneId>
               <DNSName>{{ record.alias_target['DNSName'] }}</DNSName>
               <EvaluateTargetHealth>{{ record.alias_target['EvaluateTargetHealth'] }}</EvaluateTargetHealth>
           </AliasTarget>
           {% else %}
           <ResourceRecords>
               {% for resource in record.records %}
               <ResourceRecord>
                   <Value><![CDATA[{{ resource }}]]></Value>
               </ResourceRecord>
               {% endfor %}
           </ResourceRecords>
           {% endif %}
           {% if record.health_check %}
               <HealthCheckId>{{ record.health_check }}</HealthCheckId>
           {% endif %}
        </ResourceRecordSet>
       {% endfor %}
   </ResourceRecordSets>
   {% if is_truncated %}<NextRecordName>{{ next_name }}</NextRecordName>{% endif %}
   {% if is_truncated %}<NextRecordType>{{ next_type }}</NextRecordType>{% endif %}
   <MaxItems>{{ max_items }}</MaxItems>
   <IsTruncated>{{ 'true' if is_truncated else 'false' }}</IsTruncated>
</ListResourceRecordSetsResponse>"""

CHANGE_RRSET_RESPONSE = """<ChangeResourceRecordSetsResponse xmlns="https://route53.amazonaws.com/doc/2012-12-12/">
   <ChangeInfo>
      <Status>INSYNC</Status>
      <SubmittedAt>2010-09-10T01:36:41.958Z</SubmittedAt>
      <Id>/change/C2682N5HXP0BZ4</Id>
   </ChangeInfo>
</ChangeResourceRecordSetsResponse>"""

DELETE_HOSTED_ZONE_RESPONSE = """<DeleteHostedZoneResponse xmlns="https://route53.amazonaws.com/doc/2012-12-12/">
    <ChangeInfo>
      <Status>INSYNC</Status>
      <SubmittedAt>2010-09-10T01:36:41.958Z</SubmittedAt>
      <Id>/change/C2682N5HXP0BZ4</Id>
   </ChangeInfo>
</DeleteHostedZoneResponse>"""

GET_HOSTED_ZONE_COUNT_RESPONSE = """<GetHostedZoneCountResponse> xmlns="https://route53.amazonaws.com/doc/2012-12-12/">
   <HostedZoneCount>{{ zone_count }}</HostedZoneCount>
</GetHostedZoneCountResponse>"""


GET_HOSTED_ZONE_RESPONSE = """<GetHostedZoneResponse xmlns="https://route53.amazonaws.com/doc/2012-12-12/">
   <HostedZone>
      <Id>/hostedzone/{{ zone.id }}</Id>
      <Name>{{ zone.name }}</Name>
        <CallerReference>{{ zone.caller_reference }}</CallerReference>
      <ResourceRecordSetCount>{{ zone.rrsets|count }}</ResourceRecordSetCount>
      <Config>
        {% if zone.comment %}
            <Comment>{{ zone.comment }}</Comment>
        {% endif %}
        <PrivateZone>{{ 'true' if zone.private_zone else 'false' }}</PrivateZone>
      </Config>
   </HostedZone>
   {% if not zone.private_zone %}
   <DelegationSet>
      <Id>{{ zone.delegation_set.id }}</Id>
      <NameServers>
        {% for name in zone.delegation_set.name_servers %}<NameServer>{{ name }}</NameServer>{% endfor %}
      </NameServers>
   </DelegationSet>
   {% endif %}
   {% if zone.private_zone %}
   <VPCs>
      {% for vpc in zone.vpcs %}
      <VPC>
         <VPCId>{{vpc.vpc_id}}</VPCId>
         <VPCRegion>{{vpc.vpc_region}}</VPCRegion>
      </VPC>
      {% endfor %}
   </VPCs>
   {% endif %}
</GetHostedZoneResponse>"""

CREATE_HOSTED_ZONE_RESPONSE = """<CreateHostedZoneResponse xmlns="https://route53.amazonaws.com/doc/2012-12-12/">
    {% if zone.private_zone %}
    <VPC>
      <VPCId>{{zone.vpcid}}</VPCId>
      <VPCRegion>{{zone.vpcregion}}</VPCRegion>
    </VPC>
    {% endif %}
   <HostedZone>
      <Id>/hostedzone/{{ zone.id }}</Id>
      <Name>{{ zone.name }}</Name>
      <CallerReference>{{ zone.caller_reference }}</CallerReference>
      <ResourceRecordSetCount>{{ zone.rrsets|count }}</ResourceRecordSetCount>
      <Config>
        {% if zone.comment %}
            <Comment>{{ zone.comment }}</Comment>
        {% endif %}
        <PrivateZone>{{ 'true' if zone.private_zone else 'false' }}</PrivateZone>
      </Config>
   </HostedZone>
   {% if not zone.private_zone %}
   <DelegationSet>
      <Id>{{ zone.delegation_set.id }}</Id>
      <NameServers>
         {% for name in zone.delegation_set.name_servers %}<NameServer>{{ name }}</NameServer>{% endfor %}
      </NameServers>
   </DelegationSet>
   {% endif %}
   <ChangeInfo>
      <Id>/change/C1PA6795UKMFR9</Id>
      <Status>INSYNC</Status>
      <SubmittedAt>2017-03-15T01:36:41.958Z</SubmittedAt>
   </ChangeInfo>
</CreateHostedZoneResponse>"""

LIST_HOSTED_ZONES_RESPONSE = """<ListHostedZonesResponse xmlns="https://route53.amazonaws.com/doc/2012-12-12/">
   <HostedZones>
      {% for zone in zones %}
      <HostedZone>
         <Id>/hostedzone/{{ zone.id }}</Id>
         <Name>{{ zone.name }}</Name>
         <CallerReference>{{ zone.caller_reference }}</CallerReference>
         <Config>
            {% if zone.comment %}
                <Comment>{{ zone.comment }}</Comment>
            {% endif %}
           <PrivateZone>{{ 'true' if zone.private_zone else 'false' }}</PrivateZone>
         </Config>
         <ResourceRecordSetCount>{{ zone.rrsets|count  }}</ResourceRecordSetCount>
      </HostedZone>
      {% endfor %}
   </HostedZones>
   {% if marker %}<Marker>{{ marker }}</Marker>{% endif %}
   {%if next_marker %}<NextMarker>{{ next_marker }}</NextMarker>{% endif %}
   {%if max_items %}<MaxItems>{{ max_items }}</MaxItems>{% endif %}
   <IsTruncated>{{ 'true' if next_marker else 'false'}}</IsTruncated>
</ListHostedZonesResponse>"""

LIST_HOSTED_ZONES_BY_NAME_RESPONSE = """<ListHostedZonesByNameResponse xmlns="{{ xmlns }}">
  {% if dnsname %}
  <DNSName>{{ dnsname }}</DNSName>
  {% endif %}
  <HostedZones>
      {% for zone in zones %}
      <HostedZone>
         <Id>/hostedzone/{{ zone.id }}</Id>
         <Name>{{ zone.name }}</Name>
         <CallerReference>{{ zone.caller_reference }}</CallerReference>
         <Config>
            {% if zone.comment %}
                <Comment>{{ zone.comment }}</Comment>
            {% endif %}
           <PrivateZone>{{ 'true' if zone.private_zone else 'false' }}</PrivateZone>
         </Config>
         <ResourceRecordSetCount>{{ zone.rrsets|count  }}</ResourceRecordSetCount>
      </HostedZone>
      {% endfor %}
   </HostedZones>
   <IsTruncated>false</IsTruncated>
</ListHostedZonesByNameResponse>"""

LIST_HOSTED_ZONES_BY_VPC_RESPONSE = """<ListHostedZonesByVpcResponse xmlns="{{xmlns}}">
   <HostedZoneSummaries>
       {% for zone in zones -%}
       <HostedZoneSummary>
           <HostedZoneId>{{zone["HostedZoneId"]}}</HostedZoneId>
           <Name>{{zone["Name"]}}</Name>
           <Owner>
               {% if zone["Owner"]["OwningAccount"] -%}
               <OwningAccount>{{zone["Owner"]["OwningAccount"]}}</OwningAccount>
               {% endif -%}
               {% if zone["Owner"]["OwningService"] -%}
               <OwningService>zone["Owner"]["OwningService"]</OwningService>
               {% endif -%}
           </Owner>
       </HostedZoneSummary>
       {% endfor -%}
   </HostedZoneSummaries>
</ListHostedZonesByVpcResponse>"""

CREATE_HEALTH_CHECK_RESPONSE = """<?xml version="1.0" encoding="UTF-8"?>
<CreateHealthCheckResponse xmlns="{{ xmlns }}">
  {{ health_check.to_xml() }}
</CreateHealthCheckResponse>"""

UPDATE_HEALTH_CHECK_RESPONSE = """<?xml version="1.0" encoding="UTF-8"?>
<UpdateHealthCheckResponse>
  {{ health_check.to_xml() }}
</UpdateHealthCheckResponse>
"""

LIST_HEALTH_CHECKS_RESPONSE = """<?xml version="1.0" encoding="UTF-8"?>
<ListHealthChecksResponse xmlns="{{ xmlns }}">
   <HealthChecks>
   {% for health_check in health_checks %}
      {{ health_check.to_xml() }}
    {% endfor %}
   </HealthChecks>
   <IsTruncated>false</IsTruncated>
   <MaxItems>{{ health_checks|length }}</MaxItems>
</ListHealthChecksResponse>"""

DELETE_HEALTH_CHECK_RESPONSE = """<?xml version="1.0" encoding="UTF-8"?>
    <DeleteHealthCheckResponse xmlns="{{ xmlns }}">
</DeleteHealthCheckResponse>"""

GET_CHANGE_RESPONSE = """<?xml version="1.0" encoding="UTF-8"?>
<GetChangeResponse xmlns="{{ xmlns }}">
   <ChangeInfo>
      <Status>INSYNC</Status>
      <SubmittedAt>2010-09-10T01:36:41.958Z</SubmittedAt>
      <Id>{{ change_id }}</Id>
   </ChangeInfo>
</GetChangeResponse>"""

CREATE_QUERY_LOGGING_CONFIG_RESPONSE = """<?xml version="1.0" encoding="UTF-8"?>
<CreateQueryLoggingConfigResponse xmlns="{{ xmlns }}">
  {{ query_logging_config.to_xml() }}
</CreateQueryLoggingConfigResponse>"""

GET_QUERY_LOGGING_CONFIG_RESPONSE = """<?xml version="1.0" encoding="UTF-8"?>
<CreateQueryLoggingConfigResponse xmlns="{{ xmlns }}">
  {{ query_logging_config.to_xml() }}
</CreateQueryLoggingConfigResponse>"""

LIST_QUERY_LOGGING_CONFIGS_RESPONSE = """<?xml version="1.0" encoding="UTF-8"?>
<ListQueryLoggingConfigsResponse xmlns="{{ xmlns }}">
   <QueryLoggingConfigs>
      {% for query_logging_config in query_logging_configs %}
         {{ query_logging_config.to_xml() }}
      {% endfor %}
   </QueryLoggingConfigs>
   {% if next_token %}
      <NextToken>{{ next_token }}</NextToken>
   {% endif %}
</ListQueryLoggingConfigsResponse>"""


CREATE_REUSABLE_DELEGATION_SET_TEMPLATE = """<CreateReusableDelegationSetResponse>
  <DelegationSet>
      <Id>{{ delegation_set.id }}</Id>
      <CallerReference>{{ delegation_set.caller_reference }}</CallerReference>
      <NameServers>
        {% for name in delegation_set.name_servers %}<NameServer>{{ name }}</NameServer>{% endfor %}
      </NameServers>
  </DelegationSet>
</CreateReusableDelegationSetResponse>
"""


LIST_REUSABLE_DELEGATION_SETS_TEMPLATE = """<ListReusableDelegationSetsResponse>
  <DelegationSets>
    {% for delegation in delegation_sets %}
    <DelegationSet>
  <Id>{{ delegation.id }}</Id>
  <CallerReference>{{ delegation.caller_reference }}</CallerReference>
  <NameServers>
    {% for name in delegation.name_servers %}<NameServer>{{ name }}</NameServer>{% endfor %}
  </NameServers>
</DelegationSet>
    {% endfor %}
  </DelegationSets>
  <Marker>{{ marker }}</Marker>
  <IsTruncated>{{ is_truncated }}</IsTruncated>
  <MaxItems>{{ max_items }}</MaxItems>
</ListReusableDelegationSetsResponse>
"""


DELETE_REUSABLE_DELEGATION_SET_TEMPLATE = """<DeleteReusableDelegationSetResponse>
  <DeleteReusableDelegationSetResponse/>
</DeleteReusableDelegationSetResponse>
"""

GET_REUSABLE_DELEGATION_SET_TEMPLATE = """<GetReusableDelegationSetResponse>
<DelegationSet>
  <Id>{{ delegation_set.id }}</Id>
  <CallerReference>{{ delegation_set.caller_reference }}</CallerReference>
  <NameServers>
    {% for name in delegation_set.name_servers %}<NameServer>{{ name }}</NameServer>{% endfor %}
  </NameServers>
</DelegationSet>
</GetReusableDelegationSetResponse>
"""

GET_DNSSEC = """<?xml version="1.0"?>
<GetDNSSECResponse>
    <Status>
        <ServeSignature>NOT_SIGNING</ServeSignature>
    </Status>
    <KeySigningKeys/>
</GetDNSSECResponse>
"""

GET_HEALTH_CHECK_RESPONSE = """<?xml version="1.0"?>
<GetHealthCheckResponse>
    {{ health_check.to_xml() }}
</GetHealthCheckResponse>
"""

GET_HEALTH_CHECK_STATUS_RESPONSE = """<?xml version="1.0" encoding="UTF-8"?>
<GetHealthCheckStatusResponse>
   <HealthCheckObservations>
      <HealthCheckObservation>
         <IPAddress>127.0.13.37</IPAddress>
         <Region>us-east-1</Region>
         <StatusReport>
            <CheckedTime>{{ timestamp }}</CheckedTime>
            <Status>Success: HTTP Status Code: 200. Resolved IP: 127.0.13.37. OK</Status>
         </StatusReport>
      </HealthCheckObservation>
   </HealthCheckObservations>
</GetHealthCheckStatusResponse>
"""

UPDATE_HOSTED_ZONE_COMMENT_RESPONSE = """<?xml version="1.0" encoding="UTF-8"?>
<UpdateHostedZoneCommentResponse>
   <HostedZone>
      <Config>
         {% if zone.comment %}
         <Comment>{{ zone.comment }}</Comment>
         {% endif %}
         <PrivateZone>{{ 'true' if zone.private_zone else 'false' }}</PrivateZone>
      </Config>
      <Id>/hostedzone/{{ zone.id }}</Id>
      <Name>{{ zone.name }}</Name>
      <ResourceRecordSetCount>{{ zone.rrsets|count }}</ResourceRecordSetCount>
   </HostedZone>
</UpdateHostedZoneCommentResponse>
"""

ASSOCIATE_VPC_RESPONSE = """<?xml version="1.0" encoding="UTF-8"?>
<AssociateVPCWithHostedZoneResponse>
   <ChangeInfo>
      <Comment>{{ comment or "" }}</Comment>
      <Id>/change/a1b2c3d4</Id>
      <Status>INSYNC</Status>
      <SubmittedAt>2017-03-31T01:36:41.958Z</SubmittedAt>
   </ChangeInfo>
</AssociateVPCWithHostedZoneResponse>
"""

DISASSOCIATE_VPC_RESPONSE = """<?xml version="1.0" encoding="UTF-8"?>
<DisassociateVPCFromHostedZoneResponse xmlns="https://route53.amazonaws.com/doc/2013-04-01/">
   <ChangeInfo>
      <Comment>{{ comment or "" }}</Comment>
      <Id>/change/a1b2c3d4</Id>
      <Status>INSYNC</Status>
      <SubmittedAt>2017-03-31T01:36:41.958Z</SubmittedAt>
   </ChangeInfo>
</DisassociateVPCFromHostedZoneResponse>
"""
