"""
A quick and dirty public DNS script, super tightly coupled to my infrastructure.
"""

import re
from typing import List, Tuple

import jinja2


RECORD_LINE_PATTERN = re.compile(
    r"^(?P<rrset_name>\S+)\s+(?P<rrset_ttl>\d+)\s+IN\s+(?P<rrset_type>\S+)\s+(?P<rrset_values>[^#]+)(?P<comment>\s*#.*?)?$"
)


def update(m, k, f, *args, **kwargs):
    """clojure.core/update for Python's stateful maps."""

    if k in m and m[k] is not None:
        m[k] = f(m[k], *args, **kwargs)

    return m


def parse_zone_record(line):
    if match := RECORD_LINE_PATTERN.match(line):
        dat = match.groupdict()
        dat = update(dat, "rrset_ttl", int)
        dat = update(dat, "rrset_values", lambda x: [x])
        return dat


def same_record(lr, rr):
    """
    A test to see if two records name the same zone entry.
    """

    return lr["rrset_name"] == rr["rrset_name"] and lr["rrset_type"] == rr["rrset_type"]


def records_equate(lr, rr):
    """
    Equality, ignoring rrset_href which is generated by the API.
    """

    if not same_record(lr, rr):
        return False

    elif lr["rrset_ttl"] != rr["rrset_ttl"]:
        return False

    elif set(lr["rrset_values"]) != set(rr["rrset_values"]):
        return False

    else:
        return True


def template_and_parse_zone(template_file, template_bindings):
    assert template_file is not None
    assert template_bindings is not None

    with open(template_file) as fp:
        dat = jinja2.Template(fp.read()).render(**template_bindings)

    uncommitted_records = []
    for line in dat.splitlines():
        if line and not line[0] == "#":
            record = parse_zone_record(line)
            if record:
                uncommitted_records.append(record)
            else:
                print("ERROR, could not parse line %r" % line)

    records = []

    for uncommitted_r in uncommitted_records:
        flag = False
        for committed_r in records:
            if same_record(uncommitted_r, committed_r):
                # Join the two records
                committed_r["rrset_values"].extend(uncommitted_r["rrset_values"])
                flag = True

        if not flag:
            records.append(uncommitted_r)

    sorted(records, key=lambda x: (x["rrset_type"], x["rrset_name"]))

    return records


def diff_zones(left_zone: List, right_zone: List) -> Tuple[List, List]:
    """
    Equality between unordered lists of records constituting a zone.
    """

    in_left_not_right = [it for it in left_zone if it not in right_zone]
    in_right_not_left = [it for it in right_zone if it not in left_zone]

    return in_left_not_right, in_right_not_left
