#!/usr/bin/python

# based on
# https://github.com/ansible-collections/community.general/blob/main/plugins/modules/system/alternatives.py

import os
import re
import subprocess
from ansible.module_utils.basic import AnsibleModule

def main():

    fields = {
      "name": {"required": True, "type": "str"},
      "path": {"required": True, "type": "path"},
      "link": {"type": "path"},
      "priority": {"type": "int", "default": 50},
      "state": {"default": "set", "choices": ["present", "set", "auto"], "type": "str"},
      "slaves": {"type": "list", "default": []}
    }
    module = AnsibleModule(argument_spec=fields, supports_check_mode=True)

    params = module.params
    name = params['name']
    path = params['path']
    link = params['link']
    priority = params['priority']
    state = params["state"]
    slaves = params['slaves']

    UPDATE_ALTERNATIVES = module.get_bin_path('update-alternatives', True)

    current_path = None
    all_alternatives = []

    # Run `update-alternatives --display <name>` to find existing alternatives
    (rc, display_output, dummy) = module.run_command(
        ['env', 'LC_ALL=C', UPDATE_ALTERNATIVES, '--display', name]
    )

    if rc == 0:
        # Alternatives already exist for this link group
        # Parse the output to determine the current path of the symlink and
        # available alternatives
        current_path_regex = re.compile(r'^\s*link currently points to (.*)$',
                                        re.MULTILINE)
        alternative_regex = re.compile(r'^(\/.*)\s-\s(?:family\s\S+\s)?priority', re.MULTILINE)

        match = current_path_regex.search(display_output)
        if match:
            current_path = match.group(1)
        all_alternatives = alternative_regex.findall(display_output)

        if not link:
            # Read the current symlink target from `update-alternatives --query`
            # in case we need to install the new alternative before setting it.
            #
            # This is only compatible on Debian-based systems, as the other
            # alternatives don't have --query available
            rc, query_output, dummy = module.run_command(
                ['env', 'LC_ALL=C', UPDATE_ALTERNATIVES, '--query', name]
            )
            if rc == 0:
                for line in query_output.splitlines():
                    if line.startswith('Link:'):
                        link = line.split()[1]
                        break

    if current_path != path or len(slaves):  # TODO: properly check changed status in presence of slaves
        if module.check_mode:
            module.exit_json(changed=True, current_path=current_path, slaves=slaves)
        try:
            # install the requested path if necessary
            if path not in all_alternatives or len(slaves):
                if not os.path.exists(path):
                    module.fail_json(msg="Specified path %s does not exist" % path)
                if not link:
                    module.fail_json(msg="Needed to install the alternative, but unable to do so as we are missing the link")

                cmd_slaves = []
                SLAVE_KEYS = ["link", "name", "path"]
                for slave in slaves:
                    for key in SLAVE_KEYS:
                        if key not in slave:
                            module.fail_json(msg="Key %s missing from slave list, required keys are: %s"
                                             % (key, ", ".join(SLAVE_KEYS)))
                    cmd_slaves.extend(["--slave", slave["link"], slave["name"], slave["path"]])

                module.run_command(
                    [UPDATE_ALTERNATIVES, '--install', link, name, path, str(priority)] + cmd_slaves,
                    check_rc=True
                )

            if state == "set":
                # select the requested path
                module.run_command(
                    [UPDATE_ALTERNATIVES, '--set', name, path],
                    check_rc=True
                )

            elif state == "auto":
                # set the path to auto mode
                module.run_command(
                    [UPDATE_ALTERNATIVES, '--auto', name],
                    check_rc=True
                )

            module.exit_json(changed=True, slaves=slaves)
        except subprocess.CalledProcessError as cpe:
            module.fail_json(msg=str(dir(cpe)))
    else:
        module.exit_json(changed=False, slaves=slaves)


if __name__ == '__main__':
    main()
