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

# pylint: disable=invalid-name,use-dict-literal,line-too-long,wrong-import-position

# Copyright: (c) 2024, Infinidat <info@infinidat.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

"""This module creates or modifies SSL certificates on Infinibox."""

from __future__ import absolute_import, division, print_function

__metaclass__ = type

DOCUMENTATION = r"""
---
module: infini_certificate
version_added: 2.16.0
short_description:  Create (present state) or clear (absent state) SSL certificates on Infinibox
description:
    - This module uploads (present state) or clears (absent state) SSL certificates on Infinibox
author: David Ohlemacher (@ohlemacher)
options:
  certificate_file_name:
    description:
      - Name with full path of a certificate file.
    type: str
    required:  false
  state:
    description:
      - Creates/Modifies the systems SSL certificate by uploading one from a file, when using state present.
      - For state absent, the current certificate is removed and a new self-signed certificate is automatically generated by the IBOX.
      - State stat shows the existing certificate's details.
    type: str
    required: false
    default: present
    choices: [ "stat", "present", "absent" ]
extends_documentation_fragment:
    - infinibox
"""

EXAMPLES = r"""
- name: Upload SSL certificate from file
  infini_certificate:
    certificate_file_name: cert.crt
    state: present
    user: admin
    password: secret
    system: ibox001

- name: State SSL certificate
  infini_certificate:
    state: stat
    user: admin
    password: secret
    system: ibox001

- name: Clear SSL certificate
  infini_certificate:
    state: absent
    user: admin
    password: secret
    system: ibox001
"""

# RETURN = r''' # '''

from ansible.module_utils.basic import AnsibleModule, missing_required_lib

from ansible_collections.infinidat.infinibox.plugins.module_utils.infinibox import (
    merge_two_dicts,
    get_system,
    infinibox_argument_spec,
)

HAS_URLLIB3 = True
try:
    from infinisdk.core.exceptions import APICommandFailed
except ImportError:
    HAS_URLLIB3 = False


def handle_stat(module):
    """ Handle the stat state parameter """
    certificate_file_name = module.params['certificate_file_name']
    path = "system/certificates"
    system = get_system(module)
    try:
        cert_result = system.api.get(path=path).get_result()[0]
    except APICommandFailed:
        msg = f"Cannot stat SSL certificate {certificate_file_name}"
        module.fail_json(msg=msg)
    result = dict(
        changed=False,
        msg="SSL certificate stat {certificate_file_name} found"
    )
    result = merge_two_dicts(result, cert_result)
    module.exit_json(**result)


def handle_present(module):
    """ Handle the present state parameter """
    certificate_file_name = module.params['certificate_file_name']
    path = "system/certificates"
    system = get_system(module)

    with open(certificate_file_name, 'rb') as cert_file:
        try:
            try:
                files = {'file': cert_file}
            except FileNotFoundError:
                module.fail_json(msg=f"Cannot find SSL certificate file named {certificate_file_name}")
            except Exception as err:  # pylint: disable=broad-exception-caught
                module.fail_json(msg=f"Cannot open SSL certificate file named {certificate_file_name}: {err}")
            cert_result = system.api.post(path=path, files=files).get_result()
        except APICommandFailed as err:
            msg = f"Cannot upload cert: {err}"
            module.fail_json(msg=msg)

    cert_serial = cert_result['certificate']['serial_number']
    cert_issued_by_cn = cert_result['certificate']['issued_by']['CN']
    cert_issued_to_cn = cert_result['certificate']['issued_to']['CN']
    result = dict(
        changed=True,
        msg="System SSL certificate uploaded successfully. " +
        f"Certificate S/N {cert_serial} issued by CN {cert_issued_by_cn} to CN {cert_issued_to_cn}"
    )
    result = merge_two_dicts(result, cert_result)
    module.exit_json(**result)


def handle_absent(module):
    """ Handle the absent state parameter. Clear existing cert. IBOX will install self signed cert. """
    path = "system/certificates/generate_self_signed?approved=true"
    system = get_system(module)
    try:
        cert_result = system.api.post(path=path).get_result()
    except APICommandFailed as err:
        msg = f"Cannot clear SSL certificate: {err}"
        module.fail_json(msg=msg)
    result = dict(
        changed=True,
        msg="System SSL certificate cleared and a self signed certificate was installed successfully"
    )
    result = merge_two_dicts(result, cert_result)
    module.exit_json(**result)


def execute_state(module):
    """Handle states"""
    state = module.params["state"]
    try:
        if state == "stat":
            handle_stat(module)
        elif state == "present":
            handle_present(module)
        elif state == "absent":
            handle_absent(module)
        else:
            module.fail_json(msg=f"Internal handler error. Invalid state: {state}")
    finally:
        system = get_system(module)
        system.logout()


def check_options(module):
    """Verify module options are sane"""
    certificate_file_name = module.params["certificate_file_name"]
    state = module.params["state"]

    if state in ["stat", "absent"]:
        pass
    if state in ["present"]:
        if not certificate_file_name:
            msg = "Certificate file name parameter must be provided"
            module.fail_json(msg=msg)


def main():
    """ Main """
    argument_spec = infinibox_argument_spec()
    argument_spec.update(
        dict(
            certificate_file_name=dict(required=False, default=None),
            state=dict(default="present", choices=["stat", "present", "absent"]),
        )
    )

    module = AnsibleModule(argument_spec, supports_check_mode=True)

    if not HAS_URLLIB3:
        module.fail_json(msg=missing_required_lib("urllib3"))

    check_options(module)
    execute_state(module)


if __name__ == "__main__":
    main()
