#!/usr/bin/env python3
""" Write out a puppet cosmos-modules.conf """

import hashlib
import os
import os.path
import sys
import socket
import re

try:
    from configobj import ConfigObj

    OS_INFO = ConfigObj("/etc/os-release")
except (IOError, ModuleNotFoundError):
    OS_INFO = None

try:
    fqdn = socket.getfqdn()
    hostname = fqdn.split('.')[0]
except OSError:
    host_info = None
else:
    domainname =  '.'.join(fqdn.split(".")[1:])
    environ = "prod"
    pattern = r"(.+test.*\.komreg\.net)|(.+\.test\.sveidas\.se)"

    if re.match(pattern, fqdn):
       environ = "test"

    host_info = {
        "domainname": domainname,
        "environment": environ,
        "fqdn": fqdn,
        "hostname": hostname,
    }


def get_file_hash(modulesfile):
    """
    Based on https://github.com/python/cpython/pull/31930: should use
    hashlib.file_digest() but it is only available in python 3.11
    """
    try:
        with open(modulesfile, "rb") as fileobj:
            digestobj = hashlib.sha256()
            _bufsize = 2**18
            buf = bytearray(_bufsize)  # Reusable buffer to reduce allocations.
            view = memoryview(buf)
            while True:
                size = fileobj.readinto(buf)
                if size == 0:
                    break  # EOF
                digestobj.update(view[:size])
    except FileNotFoundError:
        return ""

    return digestobj.hexdigest()


def get_list_hash(file_lines):
    """Get hash of list contents"""

    file_lines_hash = hashlib.sha256()
    for line in file_lines:
        file_lines_hash.update(line)

    return file_lines_hash.hexdigest()


def create_file_content(modules):
    """
    Write out the expected file contents to a list so we can check the
    expected checksum before writing anything
    """
    file_lines = []
    file_lines.append(
        "# Generated by {}\n".format(  # pylint: disable=consider-using-f-string
            os.path.basename(sys.argv[0])
        ).encode("utf-8")
    )
    for key in modules:
        file_lines.append(
            "{0:11} {1} {2} {3}\n".format(  # pylint: disable=consider-using-f-string
                key,
                modules[key]["repo"],
                modules[key]["upgrade"],
                modules[key]["tag"],
            ).encode("utf-8")
        )

    return file_lines


def main():
    """Starting point of the program"""

    modulesfile: str = "/etc/puppet/cosmos-modules.conf"
    modulesfile_tmp: str = modulesfile + ".tmp"

    modules: dict = {
        "concat": {
            "repo": "https://github.com/SUNET/puppetlabs-concat.git",
            "upgrade": "yes",
            "tag": f"eidas-{host_info['environment']}-2*",
        },
        "stdlib": {
            "repo": "https://github.com/SUNET/puppetlabs-stdlib.git",
            "upgrade": "yes",
            "tag": f"eidas-{host_info['environment']}-2*",
        },
        "cosmos": {
            "repo": "https://github.com/SUNET/puppet-cosmos.git",
            "upgrade": "yes",
            "tag": f"eidas-{host_info['environment']}-2*",
        },
        "ufw": {
            "repo": "https://github.com/SUNET/puppet-module-ufw.git",
            "upgrade": "yes",
            "tag": f"eidas-{host_info['environment']}-2*",
        },
        "apt": {
            "repo": "https://github.com/SUNET/puppetlabs-apt.git",
            "upgrade": "yes",
            "tag": f"eidas-{host_info['environment']}-2*",
        },
        "vcsrepo": {
            "repo": "https://github.com/SUNET/puppetlabs-vcsrepo.git",
            "upgrade": "yes",
            "tag": f"eidas-{host_info['environment']}-2*",
        },
        "xinetd": {
            "repo": "https://github.com/SUNET/puppetlabs-xinetd.git",
            "upgrade": "yes",
            "tag": f"eidas-{host_info['environment']}-2*",
        },
        "python": {
            "repo": "https://github.com/SUNET/puppet-python.git",
            "upgrade": "yes",
            "tag": f"eidas-{host_info['environment']}-2*",
        },
        "hiera-gpg": {
            "repo": "https://github.com/SUNET/hiera-gpg.git",
            "upgrade": "yes",
            "tag": f"eidas-{host_info['environment']}-2*",
        },
        "pound": {
            "repo": "https://github.com/SUNET/puppet-pound.git",
            "upgrade": "yes",
            "tag": f"eidas-{host_info['environment']}-2*",
        },
        "augeas": {
            "repo": "https://github.com/SUNET/puppet-augeas.git",
            "upgrade": "yes",
            "tag": f"eidas-{host_info['environment']}-2*",
        },
        "bastion": {
            "repo": "https://github.com/SUNET/puppet-bastion.git",
            "upgrade": "yes",
            "tag": f"eidas-{host_info['environment']}-2*",
        },
        "pyff": {
            "repo": "https://github.com/SUNET/puppet-pyff.git",
            "upgrade": "yes",
            "tag": f"eidas-{host_info['environment']}-2*",
        },
        "dhcp": {
            "repo": "https://github.com/SUNET/puppetlabs-dhcp.git",
            "upgrade": "yes",
            "tag": f"eidas-{host_info['environment']}-2*",
        },
        "varnish": {
            "repo": "https://github.com/SUNET/puppet-varnish.git",
            "upgrade": "yes",
            "tag": f"eidas-{host_info['environment']}-2*",
        },
        "apparmor": {
            "repo": "https://github.com/SUNET/puppet-apparmor.git",
            "upgrade": "yes",
            "tag": f"eidas-{host_info['environment']}-2*",
        },
        "docker": {
            "repo": "https://github.com/SUNET/garethr-docker.git",
            "upgrade": "yes",
            "tag": f"eidas-{host_info['environment']}-2*",
        },
        "sunet": {
            "repo": "https://github.com/SUNET/puppet-sunet.git",
            "upgrade": "yes",
            "tag": f"eidas-{host_info['environment']}-2*",
        },
        "sysctl": {
            "repo": "https://github.com/SUNET/puppet-sysctl.git",
            "upgrade": "yes",
            "tag": f"eidas-{host_info['environment']}-2*",
        },
        "nagioscfg": {
            "repo": "https://github.com/SUNET/puppet-nagioscfg.git",
            "upgrade": "yes",
            "tag": f"eidas-{host_info['environment']}-2*",
        },
    }

    # When/if we want we can do stuff to modules here
    if OS_INFO:
        if OS_INFO["VERSION_CODENAME"] == "bullseye":
            pass

        if OS_INFO["VERSION_CODENAME"] == "noble":
            del modules["apparmor"]
            del modules["apt"]
            del modules["augeas"]
            del modules["bastion"]
            del modules["concat"]
            del modules["cosmos"]
            del modules["dhcp"]
            del modules["docker"]
            del modules["hiera-gpg"]
            del modules["pound"]
            del modules["pyff"]
            del modules["python"]
            del modules["stdlib"]
            del modules["sysctl"]
            del modules["ufw"]
            del modules["varnish"]
            del modules["vcsrepo"]
            del modules["xinetd"]

    if re.match(r'^idm-sto\d-qa-', host_info["fqdn"]):
        modules["sunet"]["tag"] = "jocar-idm-*"

    if re.match(r'^eumdsigner-test-', host_info["fqdn"]):
        modules["sunet"]["tag"] = "jocar-fetch-mdqp-image*"

    if re.match(r'^connector-test-', host_info["fqdn"]):
        modules["sunet"]["tag"] = "pahol-redict*"

    qa_exceptions = ['nic.komreg.net', 'jmp.komreg.net', 'md1.komreg.net']

    if ".qa." in host_info["fqdn"] or host_info["fqdn"] in qa_exceptions:
        modules["sunet"]["tag"] = "eidas-qa-2*"

    # Build list of expected file content
    file_lines = create_file_content(modules)

    # Get hash of the list
    list_hash = get_list_hash(file_lines)

    # Get hash of the existing file on disk
    file_hash = get_file_hash(modulesfile)

    # Update the file if necessary
    if list_hash != file_hash:
        # Since we are reading the file with 'rb' when computing our hash use 'wb' when
        # writing so we dont end up creating a file that does not match the
        # expected hash
        with open(modulesfile_tmp, "wb") as fileobj:
            for line in file_lines:
                fileobj.write(line)

        # Rename it in place so the update is atomic for anything else trying to
        # read the file
        os.rename(modulesfile_tmp, modulesfile)


if __name__ == "__main__":
    main()