import argparse
import logging
import pathlib
import re

import zonefilegen
import zonefilegen.parsing
import zonefilegen.core

# First line of each generated zone file should be a comment with the 
# SHA-1 hex digest of the input toml file:
# ; Generated by zonefilegen, INPUT_SHA1: ea03443f2d9f8c580e73d2f8cd1016dc7174bddc
FIRST_LINE_PATTERN = re.compile(r'^\s*;.*INPUT_DIGEST:\s+(?P<digest>[0-9a-f]+)')
SOA_PATTERN = re.compile(r'^.+SOA[ \t]+(?P<mname>[\.\w]+)[ \t]+(?P<rname>[\.\w]+)[ \t]+\(\s*(?P<serial>[0-9]+)', re.MULTILINE)

def gen_zone(zone: zonefilegen.core.Zone, output_dir: pathlib.Path, soa_dict: dict, input_digest: str):
    out_filepath: pathlib.Path = output_dir / f"{zone.name}zone"
    logging.info(f"Generating zone file {out_filepath}")
    serial_number = None
    if out_filepath.exists():
        with open(out_filepath, 'r') as f:
            first_line_matches = FIRST_LINE_PATTERN.match(f.readline())
            soa_matches = SOA_PATTERN.search(f.read(), )
            old_digest = None
            old_serial = None
            if first_line_matches:
                old_digest = first_line_matches.group('digest')
            else:
                logging.error(f"Existing zone file {out_filepath} was not generated by this tool. Aborting.") 
                exit(1)

            if soa_matches:
                old_serial = soa_matches.group('serial')
            else:
                logging.warning(f"Didn't find or recognize SOA record in existing zone file {out_filepath}. Serial number will be reset.")
            
            if old_serial and old_digest:
                if old_digest != input_digest:
                    serial_number =  (int(old_serial) + 1) % pow(2, 32)
                    logging.info(f"Changes detected, updating serial to {serial_number}")
                else:
                    serial_number = int(old_serial)
                    logging.info(f"No changes detected, serial remains at {serial_number}")
    
    if serial_number is None:
        serial_number = 1
    soa_rec = zonefilegen.generation.build_soa_record(soa_dict, serial_number)

    with open(out_filepath, 'w') as f:
        f.write(zonefilegen.generation.generate_header(input_digest) + '\n')
        f.write(zone.generate_origin() + '\n')
        f.write(zone.generate_ttl() + '\n')
        f.write(soa_rec.to_line() + '\n')
        for rec in zone.records:
            f.write(rec.to_line() + '\n')

def generate():
    parser = argparse.ArgumentParser(description='Generate DNS zone files.')
    parser.add_argument('input_file', type=pathlib.Path, help='Input file in TOML format')
    parser.add_argument('output_dir', type=pathlib.Path, help='Output directory where generated zone files will be placed')
    parser.add_argument('--verbose', '-v', action='count', default=0)
    parser.add_argument('--version', action='version', version=zonefilegen.__version__)
    args = parser.parse_args()

    log_level = logging.WARNING
    if args.verbose > 1:
        log_level = logging.DEBUG
    elif args.verbose > 0:
        log_level = logging.INFO
    logging.basicConfig(format='%(levelname)s: %(message)s',level=log_level)


    (fwd_zone, reverse_zones, soa_dict, input_digest) = zonefilegen.parsing.parse_toml_file(args.input_file)

    for zone in reverse_zones:
        gen_zone(zone, args.output_dir, soa_dict, input_digest)

    gen_zone(fwd_zone, args.output_dir, soa_dict, input_digest)
