#!/usr/bin/python
# -*- coding: utf-8 -*-

# Copyright (c) 2023, Pedro Nascimento <apecnascimento@gmail.com>
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later

from __future__ import absolute_import, division, print_function

__metaclass__ = type

DOCUMENTATION = '''
---
module: nomad_token
author: Pedro Nascimento (@apecnascimento)
version_added: "8.1.0"
short_description: Manage Nomad ACL tokens
description:
    - This module allows to create Bootstrap tokens, create ACL tokens, update ACL tokens, and delete ACL tokens.
requirements:
    - python-nomad
extends_documentation_fragment:
    - community.general.nomad
    - community.general.attributes
attributes:
    check_mode:
        support: none
    diff_mode:
        support: none
options:
    name:
        description:
            - Name of ACL token to create.
        type: str
    token_type:
        description:
            - The type of the token can be V(client), V(management), or V(bootstrap).
        choices: ["client", "management", "bootstrap"]
        type: str
        default: "client"
    policies:
        description:
            - A list of the policies assigned to the token.
        type: list
        elements: str
        default: []
    global_replicated:
        description:
            - Indicates whether or not the token was created with the C(--global).
        type: bool
        default: false
    state:
        description:
            - Create or remove ACL token.
        choices: ["present", "absent"]
        required: true
        type: str

seealso:
  - name: Nomad ACL documentation
    description: Complete documentation for Nomad API ACL.
    link: https://developer.hashicorp.com/nomad/api-docs/acl/tokens
'''

EXAMPLES = '''
- name: Create boostrap token
  community.general.nomad_token:
    host: localhost
    token_type: bootstrap
    state: present

- name: Create ACL token
  community.general.nomad_token:
    host: localhost
    name: "Dev token"
    token_type: client
    policies:
        - readonly
    global_replicated: false
    state: absent

- name: Update ACL token Dev token
  community.general.nomad_token:
    host: localhost
    name: "Dev token"
    token_type: client
    policies:
        - readonly
        - devpolicy
    global_replicated: false
    state: absent

- name: Delete ACL token
  community.general.nomad_token:
    host: localhost
    name: "Dev token"
    state: absent
'''

RETURN = '''
result:
    description: Result returned by nomad.
    returned: always
    type: dict
    sample: {
        "accessor_id": "0d01c55f-8d63-f832-04ff-1866d4eb594e",
        "create_index": 14,
        "create_time": "2023-11-12T18:48:34.248857001Z",
        "expiration_time": null,
        "expiration_ttl": "",
        "global": true,
        "hash": "eSn8H8RVqh8As8WQNnC2vlBRqXy6DECogc5umzX0P30=",
        "modify_index": 836,
        "name": "devs",
        "policies": [
            "readonly"
        ],
        "roles": null,
        "secret_id": "12e878ab-e1f6-e103-b4c4-3b5173bb4cea",
        "type": "client"
    }
'''

from ansible.module_utils.basic import AnsibleModule, missing_required_lib
from ansible.module_utils.common.text.converters import to_native

import_nomad = None

try:
    import nomad

    import_nomad = True
except ImportError:
    import_nomad = False


def get_token(name, nomad_client):
    tokens = nomad_client.acl.get_tokens()
    token = next((token for token in tokens
                  if token.get('Name') == name), None)
    return token


def transform_response(nomad_response):
    transformed_response = {
        "accessor_id": nomad_response['AccessorID'],
        "create_index": nomad_response['CreateIndex'],
        "create_time": nomad_response['CreateTime'],
        "expiration_ttl": nomad_response['ExpirationTTL'],
        "expiration_time": nomad_response['ExpirationTime'],
        "global": nomad_response['Global'],
        "hash": nomad_response['Hash'],
        "modify_index": nomad_response['ModifyIndex'],
        "name": nomad_response['Name'],
        "policies": nomad_response['Policies'],
        "roles": nomad_response['Roles'],
        "secret_id": nomad_response['SecretID'],
        "type": nomad_response['Type']
    }

    return transformed_response


argument_spec = dict(
    host=dict(required=True, type='str'),
    port=dict(type='int', default=4646),
    state=dict(required=True, choices=['present', 'absent']),
    use_ssl=dict(type='bool', default=True),
    timeout=dict(type='int', default=5),
    validate_certs=dict(type='bool', default=True),
    client_cert=dict(type='path'),
    client_key=dict(type='path'),
    namespace=dict(type='str'),
    token=dict(type='str', no_log=True),
    name=dict(type='str'),
    token_type=dict(choices=['client', 'management', 'bootstrap'], default='client'),
    policies=dict(type='list', elements='str', default=[]),
    global_replicated=dict(type='bool', default=False),
)


def setup_module_object():
    module = AnsibleModule(
        argument_spec=argument_spec,
        supports_check_mode=False,
        required_one_of=[
            ['name', 'token_type']
        ],
        required_if=[
            ('token_type', 'client', ('name',)),
            ('token_type', 'management', ('name',)),
        ],
    )
    return module


def setup_nomad_client(module):
    if not import_nomad:
        module.fail_json(msg=missing_required_lib("python-nomad"))

    certificate_ssl = (module.params.get('client_cert'), module.params.get('client_key'))

    nomad_client = nomad.Nomad(
        host=module.params.get('host'),
        port=module.params.get('port'),
        secure=module.params.get('use_ssl'),
        timeout=module.params.get('timeout'),
        verify=module.params.get('validate_certs'),
        cert=certificate_ssl,
        namespace=module.params.get('namespace'),
        token=module.params.get('token')
    )

    return nomad_client


def run(module):
    nomad_client = setup_nomad_client(module)

    msg = ""
    result = {}
    changed = False
    if module.params.get('state') == "present":

        if module.params.get('token_type') == 'bootstrap':
            try:
                current_token = get_token('Bootstrap Token', nomad_client)
                if current_token:
                    msg = "ACL bootstrap already exist."
                else:
                    nomad_result = nomad_client.acl.generate_bootstrap()
                    msg = "Boostrap token created."
                    result = transform_response(nomad_result)
                    changed = True

            except nomad.api.exceptions.URLNotAuthorizedNomadException:
                try:
                    nomad_result = nomad_client.acl.generate_bootstrap()
                    msg = "Boostrap token created."
                    result = transform_response(nomad_result)
                    changed = True

                except Exception as e:
                    module.fail_json(msg=to_native(e))
        else:
            try:
                token_info = {
                    "Name": module.params.get('name'),
                    "Type": module.params.get('token_type'),
                    "Policies": module.params.get('policies'),
                    "Global": module.params.get('global_replicated')
                }

                current_token = get_token(token_info['Name'], nomad_client)

                if current_token:
                    token_info['AccessorID'] = current_token['AccessorID']
                    nomad_result = nomad_client.acl.update_token(current_token['AccessorID'], token_info)
                    msg = "ACL token updated."
                    result = transform_response(nomad_result)
                    changed = True

                else:
                    nomad_result = nomad_client.acl.create_token(token_info)
                    msg = "ACL token Created."
                    result = transform_response(nomad_result)
                    changed = True

            except Exception as e:
                module.fail_json(msg=to_native(e))

    if module.params.get('state') == "absent":

        if not module.params.get('name'):
            module.fail_json(msg="name is needed to delete token.")

        if module.params.get('token_type') == 'bootstrap' or module.params.get('name') == 'Bootstrap Token':
            module.fail_json(msg="Delete ACL bootstrap token is not allowed.")

        try:
            token = get_token(module.params.get('name'), nomad_client)
            if token:
                nomad_client.acl.delete_token(token.get('AccessorID'))
                msg = 'ACL token deleted.'
                changed = True
            else:
                msg = "No token with name '{0}' found".format(module.params.get('name'))

        except Exception as e:
            module.fail_json(msg=to_native(e))

    module.exit_json(changed=changed, msg=msg, result=result)


def main():
    module = setup_module_object()
    run(module)


if __name__ == "__main__":
    main()