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

# Copyright: (c) 2022, 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_pull
short_description: download a chart from a repository and (optionally) unpack it in local directory.
version_added: 2.4.0
author:
  - Aubin Bikouo (@abikouo)
description:
  - Retrieve a package from a package repository, and download it locally.
  - It can also be used to perform cryptographic verification of a chart without installing the chart.
  - There are options for unpacking the chart after download.

requirements:
  - "helm >= 3.0 (https://github.com/helm/helm/releases)"

options:
  chart_ref:
    description:
      - chart name on chart repository.
      - absolute URL.
    required: true
    type: str
  chart_version:
    description:
      - Specify a version constraint for the chart version to use.
      - This constraint can be a specific tag (e.g. 1.1.1) or it may reference a valid range (e.g. ^2.0.0).
      - Mutually exclusive with C(chart_devel).
    type: str
  verify_chart:
    description:
      - Verify the package before using it.
    default: False
    type: bool
  verify_chart_keyring:
    description:
      - location of public keys used for verification.
    type: path
  provenance:
    description:
      - Fetch the provenance file, but don't perform verification.
    type: bool
    default: False
  repo_url:
    description:
      - chart repository url where to locate the requested chart.
    type: str
    aliases: [ url, chart_repo_url ]
  repo_username:
    description:
      - Chart repository username where to locate the requested chart.
      - Required if C(repo_password) is specified.
    type: str
    aliases: [ username, chart_repo_username ]
  repo_password:
    description:
      - Chart repository password where to locate the requested chart.
      - Required if C(repo_username) is specified.
    type: str
    aliases: [ password, chart_repo_password ]
  pass_credentials:
    description:
      - Pass credentials to all domains.
    default: False
    type: bool
  skip_tls_certs_check:
    description:
    - Whether or not to check tls certificate for the chart download.
    - Requires helm >= 3.3.0.
    type: bool
    default: False
  chart_devel:
    description:
    - Use development versions, too. Equivalent to version '>0.0.0-0'.
    - Mutually exclusive with C(chart_version).
    type: bool
  untar_chart:
    description:
    - if set to true, will untar the chart after downloading it.
    type: bool
    default: False
  destination:
    description:
    - location to write the chart.
    type: path
    required: True
  chart_ca_cert:
    description:
    - Verify certificates of HTTPS-enabled servers using this CA bundle.
    - Requires helm >= 3.1.0.
    type: path
  chart_ssl_cert_file:
    description:
    - Identify HTTPS client using this SSL certificate file.
    - Requires helm >= 3.1.0.
    type: path
  chart_ssl_key_file:
    description:
    - Identify HTTPS client using this SSL key file
    - Requires helm >= 3.1.0.
    type: path
  binary_path:
    description:
      - The path of a helm binary to use.
    required: false
    type: path
"""

EXAMPLES = r"""
- name: Download chart using chart url
  kubernetes.core.helm_pull:
    chart_ref: https://github.com/grafana/helm-charts/releases/download/grafana-5.6.0/grafana-5.6.0.tgz
    destination: /path/to/chart

- name: Download Chart using chart_name and repo_url
  kubernetes.core.helm_pull:
    chart_ref: redis
    repo_url: https://charts.bitnami.com/bitnami
    untar_chart: yes
    destination: /path/to/chart

- name: Download Chart (skip tls certificate check)
  kubernetes.core.helm_pull:
    chart_ref: redis
    repo_url: https://charts.bitnami.com/bitnami
    untar_chart: yes
    destination: /path/to/chart
    skip_tls_certs_check: yes

- name: Download Chart using chart registry credentials
  kubernetes.core.helm_pull:
    chart_ref: redis
    repo_url: https://charts.bitnami.com/bitnami
    untar_chart: yes
    destination: /path/to/chart
    username: myuser
    password: mypassword123
"""

RETURN = r"""
stdout:
  type: str
  description: Full `helm pull` command stdout, in case you want to display it or examine the event log
  returned: always
  sample: ''
stderr:
  type: str
  description: Full `helm pull` command stderr, in case you want to display it or examine the event log
  returned: always
  sample: ''
command:
  type: str
  description: Full `helm pull` command built by this module, in case you want to re-run the command outside the module or debug a problem.
  returned: always
  sample: helm pull --repo test ...
rc:
  type: int
  description: Helm pull command return code
  returned: always
  sample: 1
"""

from ansible_collections.kubernetes.core.plugins.module_utils.helm import (
    AnsibleHelmModule,
)
from ansible_collections.kubernetes.core.plugins.module_utils.version import (
    LooseVersion,
)


def main():
    argspec = dict(
        chart_ref=dict(type="str", required=True),
        chart_version=dict(type="str"),
        verify_chart=dict(type="bool", default=False),
        verify_chart_keyring=dict(type="path"),
        provenance=dict(type="bool", default=False),
        repo_url=dict(type="str", aliases=["url", "chart_repo_url"]),
        repo_username=dict(type="str", aliases=["username", "chart_repo_username"]),
        repo_password=dict(
            type="str", no_log=True, aliases=["password", "chart_repo_password"]
        ),
        pass_credentials=dict(type="bool", default=False),
        skip_tls_certs_check=dict(type="bool", default=False),
        chart_devel=dict(type="bool"),
        untar_chart=dict(type="bool", default=False),
        destination=dict(type="path", required=True),
        chart_ca_cert=dict(type="path"),
        chart_ssl_cert_file=dict(type="path"),
        chart_ssl_key_file=dict(type="path"),
        binary_path=dict(type="path"),
    )
    module = AnsibleHelmModule(
        argument_spec=argspec,
        supports_check_mode=True,
        required_by=dict(
            repo_username=("repo_password"),
            repo_password=("repo_username"),
        ),
        mutually_exclusive=[("chart_version", "chart_devel")],
    )

    helm_version = module.get_helm_version()
    if LooseVersion(helm_version) < LooseVersion("3.0.0"):
        module.fail_json(
            msg="This module requires helm >= 3.0.0, current version is {0}".format(
                helm_version
            )
        )

    helm_pull_opt_versionning = dict(
        skip_tls_certs_check="3.3.0",
        chart_ca_cert="3.1.0",
        chart_ssl_cert_file="3.1.0",
        chart_ssl_key_file="3.1.0",
    )

    def test_version_requirement(opt):
        req_version = helm_pull_opt_versionning.get(opt)
        if req_version and LooseVersion(helm_version) < LooseVersion(req_version):
            module.fail_json(
                msg="Parameter {0} requires helm >= {1}, current version is {2}".format(
                    opt, req_version, helm_version
                )
            )

    # Set `helm pull` arguments requiring values
    helm_pull_opts = []

    helm_value_args = dict(
        chart_version="version",
        verify_chart_keyring="keyring",
        repo_url="repo",
        repo_username="username",
        repo_password="password",
        destination="destination",
        chart_ca_cert="ca-file",
        chart_ssl_cert_file="cert-file",
        chart_ssl_key_file="key-file",
    )

    for opt, cmdkey in helm_value_args.items():
        if module.params.get(opt):
            test_version_requirement(opt)
            helm_pull_opts.append("--{0} {1}".format(cmdkey, module.params.get(opt)))

    # Set `helm pull` arguments flags
    helm_flag_args = dict(
        verify_chart=dict(key="verify"),
        provenance=dict(key="prov"),
        pass_credentials=dict(key="pass-credentials"),
        skip_tls_certs_check=dict(key="insecure-skip-tls-verify"),
        chart_devel=dict(key="devel"),
        untar_chart=dict(key="untar"),
    )

    for k, v in helm_flag_args.items():
        if module.params.get(k):
            test_version_requirement(k)
            helm_pull_opts.append("--{0}".format(v["key"]))

    helm_cmd_common = "{0} pull {1} {2}".format(
        module.get_helm_binary(),
        module.params.get("chart_ref"),
        " ".join(helm_pull_opts),
    )
    if not module.check_mode:
        rc, out, err = module.run_helm_command(helm_cmd_common, fails_on_error=False)
    else:
        rc, out, err = (0, "", "")

    if rc == 0:
        module.exit_json(
            failed=False,
            changed=True,
            command=helm_cmd_common,
            stdout=out,
            stderr=err,
            rc=rc,
        )
    else:
        module.fail_json(
            msg="Failure when executing Helm command.",
            command=helm_cmd_common,
            changed=False,
            stdout=out,
            stderr=err,
            rc=rc,
        )


if __name__ == "__main__":
    main()
