"""AWS Route 53 Lambda Backup
Summary
Lambda script will parse through your route53 records and dump them to a specified s3 bucket
It will save a .csv and .json file of your record sets
This will run once every 24 hours (this is configured in the terraform cloudwatch event) in a lambda

Parameters:
bucket_name (str): The bucket name where we will write the files to
bucket_region (str): The region where the bucket is 
"""
import os
import csv
import json
import time
from pathlib import Path
from datetime import datetime
import boto3
from botocore.exceptions import ClientError


# Set environmental variables
try:
    s3_bucket_name = os.environ['bucket_name']
    s3_bucket_region = os.environ['bucket_region']
except KeyError as e:
    raise Exception("Environmental variable " + e.args[0] + " not defined")


s3 = boto3.client('s3', region_name= s3_bucket_region)
route53 = boto3.client('route53')


### Functions
def upload_to_s3(folder, filename, bucket_name, key):
    """Upload a file to a folder in an Amazon S3 bucket."""
    key = folder + '/' + key
    s3.upload_file(filename, bucket_name, key)


def get_route53_hosted_zones(next_zone=None):
    """Returns a list of hosted zones in Amazon Route 53."""
    paginator = route53.get_paginator('list_hosted_zones')
    # Create a PageIterator from the Paginator
    page_iterator = paginator.paginate()
    hosted_zones ={}
    for hostedZones in page_iterator:
        for obj in hostedZones['HostedZones']:
            zone_name = (obj['Name'])
            zone_id= (obj['Id']).split("/",2)[2]
            hosted_zones[zone_name] = zone_id
    return hosted_zones


def get_route53_zone_records(zone_id, next_record=None):
    """Returns a list of records of a hosted zone in Route 53."""
    record_list=[]
    new_paginator = route53.get_paginator('list_resource_record_sets')
    record_iterator = new_paginator.paginate(HostedZoneId=zone_id)
    for y in record_iterator:
        for obj in y['ResourceRecordSets']:
            record_list.append(obj)
    return record_list


def try_record(test, record):
    """Return a value for a record"""
    # test for Key and Type errors
    try:
        value = record[test]
    except (KeyError, TypeError):
        value = ''
    return value


def parseResourceRecord(record_list):
    """Pulls the resource records out of the dictionary and formats to string
    Example of data:
    Data in:
    {'Name': 'dev.systems.twitch.a2z.com.', 'Type': 'NS', 'TTL': 172800, 'ResourceRecords': [{'Value': 'ns-736.awsdns-28.net.'}, {'Value': 'ns-1390.awsdns-45.org.'}, 
    {'Value': 'ns-354.awsdns-44.com.'}, {'Value': 'ns-1927.awsdns-48.co.uk.'}]}

    Data out:
    ns-736.awsdns-28.net.
    ns-1390.awsdns-45.org.
    ns-354.awsdns-44.com.
    ns-1927.awsdns-48.co.uk.
    """
    resources = ""
    for x in record_list:
        if x == "ResourceRecords":
            for z in record_list[x]:
                for value in z.values():  
                    resources+=str(value +"\n")
        if x == "AliasTarget":
            for title, value in record_list[x].items():  
                resor = str(title)+ ": " + str(value)
                resources+=str(resor) +"\n"
    return resources


def write_zone_to_csv(zone, zone_records):
    """Write hosted zone records to a csv file in /tmp/."""
    zone_file_name = '/tmp/' + zone + 'csv'
    # write to csv file with zone name
    with open(zone_file_name, 'w', newline='') as csv_file:
        writer = csv.writer(csv_file)
        # write column headers
        writer.writerow([
            'NAME', 'TYPE', 'VALUE',
            'TTL', 'REGION', 'WEIGHT',
            'SETID', 'FAILOVER', 'EVALUATE_HEALTH'
            ])
        # loop through all the records for a given zone
        for record in zone_records:
             writer.writerow([
            record['Name'],
            record['Type'],
            parseResourceRecord(record),
            try_record('TTL', record), 
            try_record('Region', record),
            try_record('Weight', record),
            try_record('SetIdentifier', record),
            try_record('Failover', record),
            try_record('EvaluateTargetHealth', try_record('AliasTarget', record)),
            ])
    return zone_file_name


def write_zone_to_json(zone, zone_records):
    """Write hosted zone records to a json file in /tmp/."""
    zone_file_name = '/tmp/' + zone + 'json'
    # write to json file with zone name
    with open(zone_file_name, 'w') as json_file:
        json.dump(zone_records, json_file, indent=4)
    return zone_file_name

## HANDLER FUNCTION ##

def lambda_handler(event, context):
    """Handler function for AWS Lambda
    The output of this function will write your records to:
    /<youraccountname>-route53-backups/route53-backups/date/time-the-lambda-ran/zone/zone.csv and zone.json

    """
    time_stamp = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
    hosted_zones = get_route53_hosted_zones()
    try:
        for zone in hosted_zones:
            zone_folder = 'route53_backups/{0}/{1}'.format(time_stamp, zone[:-1])
            zone_records = get_route53_zone_records(hosted_zones[zone])
            upload_to_s3(
                zone_folder,
                write_zone_to_csv(zone, zone_records),
                s3_bucket_name,
                (zone + 'csv')
            )
            upload_to_s3(
                zone_folder,
                write_zone_to_json(zone, zone_records),
                s3_bucket_name,
                (zone + 'json')
            )
    except Exception as e:
        raise Exception("Could not evaulate records. Reason: " + str(e))
    return True

if __name__ == "__main__":
    lambda_handler(0, 0)