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

# Copyright: (c) 2020, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type


DOCUMENTATION = r'''
---
module: helm_repository

short_description: Manage Helm repositories.

version_added: "0.11.0"

author:
  - Lucas Boisserie (@LucasBoisserie)

requirements:
  - "helm (https://github.com/helm/helm/releases)"
  - "yaml (https://pypi.org/project/PyYAML/)"

description:
  -  Manage Helm repositories.

options:
  binary_path:
    description:
      - The path of a helm binary to use.
    required: false
    type: path
  repo_name:
    description:
      - Chart repository name.
    required: true
    type: str
    aliases: [ name ]
  repo_url:
    description:
      - Chart repository url
    type: str
    aliases: [ url ]
  repo_username:
    description:
      - Chart repository username for repository with basic auth.
      - Required if chart_repo_password is specified.
    required: false
    type: str
    aliases: [ username ]
  repo_password:
    description:
      - Chart repository password for repository with basic auth.
      - Required if chart_repo_username is specified.
    required: false
    type: str
    aliases: [ password ]
  repo_state:
    choices: ['present', 'absent']
    description:
      - Desired state of repository.
    required: false
    default: present
    aliases: [ state ]
    type: str
'''

EXAMPLES = r'''
- name: Add a repository
  kubernetes.core.helm_repository:
    name: stable
    repo_url: https://kubernetes.github.io/ingress-nginx

- name: Add Red Hat Helm charts repository
  kubernetes.core.helm_repository:
    name: redhat-charts
    repo_url: https://redhat-developer.github.com/redhat-helm-charts
'''

RETURN = r'''
stdout:
  type: str
  description: Full `helm` command stdout, in case you want to display it or examine the event log
  returned: always
  sample: '"bitnami" has been added to your repositories'
stdout_lines:
  type: list
  description: Full `helm` command stdout in list, in case you want to display it or examine the event log
  returned: always
  sample: ["\"bitnami\" has been added to your repositories"]
stderr:
  type: str
  description: Full `helm` command stderr, in case you want to display it or examine the event log
  returned: always
  sample: ''
stderr_lines:
  type: list
  description: Full `helm` command stderr in list, in case you want to display it or examine the event log
  returned: always
  sample: [""]
command:
  type: str
  description: Full `helm` command built by this module, in case you want to re-run the command outside the module or debug a problem.
  returned: always
  sample: '/usr/local/bin/helm repo add bitnami https://charts.bitnami.com/bitnami'
msg:
  type: str
  description: Error message returned by `helm` command
  returned: on failure
  sample: 'Repository already have a repository named bitnami'
'''

import traceback

try:
    import yaml
    IMP_YAML = True
except ImportError:
    IMP_YAML_ERR = traceback.format_exc()
    IMP_YAML = False

from ansible.module_utils.basic import AnsibleModule, missing_required_lib
from ansible_collections.kubernetes.core.plugins.module_utils.helm import run_helm


# Get repository from all repositories added
def get_repository(state, repo_name):
    if state is not None:
        for repository in state:
            if repository['name'] == repo_name:
                return repository
    return None


# Get repository status
def get_repository_status(module, command, repository_name):
    list_command = command + " repo list --output=yaml"

    rc, out, err = run_helm(module, list_command, fails_on_error=False)

    # no repo => rc=1 and 'no repositories to show' in output
    if rc == 1 and "no repositories to show" in err:
        return None
    elif rc != 0:
        module.fail_json(
            msg="Failure when executing Helm command. Exited {0}.\nstdout: {1}\nstderr: {2}".format(rc, out, err),
            command=list_command
        )

    return get_repository(yaml.safe_load(out), repository_name)


# Install repository
def install_repository(command, repository_name, repository_url, repository_username, repository_password):
    install_command = command + " repo add " + repository_name + " " + repository_url

    if repository_username is not None and repository_password is not None:
        install_command += " --username=" + repository_username
        install_command += " --password=" + repository_password

    return install_command


# Delete repository
def delete_repository(command, repository_name):
    remove_command = command + " repo rm " + repository_name

    return remove_command


def main():
    global module

    module = AnsibleModule(
        argument_spec=dict(
            binary_path=dict(type='path'),
            repo_name=dict(type='str', aliases=['name'], required=True),
            repo_url=dict(type='str', aliases=['url']),
            repo_username=dict(type='str', aliases=['username']),
            repo_password=dict(type='str', aliases=['password'], no_log=True),
            repo_state=dict(default='present', choices=['present', 'absent'], aliases=['state']),
        ),
        required_together=[
            ['repo_username', 'repo_password']
        ],
        required_if=[
            ('repo_state', 'present', ['repo_url']),
        ],
        supports_check_mode=True,
    )

    if not IMP_YAML:
        module.fail_json(msg=missing_required_lib("yaml"), exception=IMP_YAML_ERR)

    changed = False

    bin_path = module.params.get('binary_path')
    repo_name = module.params.get('repo_name')
    repo_url = module.params.get('repo_url')
    repo_username = module.params.get('repo_username')
    repo_password = module.params.get('repo_password')
    repo_state = module.params.get('repo_state')

    if bin_path is not None:
        helm_cmd = bin_path
    else:
        helm_cmd = module.get_bin_path('helm', required=True)

    repository_status = get_repository_status(module, helm_cmd, repo_name)

    if repo_state == "absent" and repository_status is not None:
        helm_cmd = delete_repository(helm_cmd, repo_name)
        changed = True
    elif repo_state == "present":
        if repository_status is None:
            helm_cmd = install_repository(helm_cmd, repo_name, repo_url, repo_username, repo_password)
            changed = True
        elif repository_status['url'] != repo_url:
            module.fail_json(msg="Repository already have a repository named {0}".format(repo_name))

    if module.check_mode:
        module.exit_json(changed=changed)
    elif not changed:
        module.exit_json(changed=False, repo_name=repo_name, repo_url=repo_url)

    rc, out, err = run_helm(module, helm_cmd)

    if repo_password is not None:
        helm_cmd = helm_cmd.replace(repo_password, '******')

    if rc != 0:
        module.fail_json(
            msg="Failure when executing Helm command. Exited {0}.\nstdout: {1}\nstderr: {2}".format(rc, out, err),
            command=helm_cmd
        )

    module.exit_json(changed=changed, stdout=out, stderr=err, command=helm_cmd)


if __name__ == '__main__':
    main()
