#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2021, Ansible Project
# Heavily influenced from Fran Fitzpatrick <francis.x.fitzpatrick@gmail.com> ipa_config module
# 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: ipa_otpconfig
author: justchris1 (@justchris1)
short_description: Manage FreeIPA OTP Configuration Settings
version_added: 2.5.0
description:
- Modify global configuration settings of a FreeIPA Server with respect to OTP (One Time Passwords).
options:
  ipatokentotpauthwindow:
    description: TOTP authentication window in seconds.
    aliases: ["totpauthwindow"]
    type: int
  ipatokentotpsyncwindow:
    description: TOTP synchronization window in seconds.
    aliases: ["totpsyncwindow"]
    type: int
  ipatokenhotpauthwindow:
    description: HOTP authentication window in number of hops.
    aliases: ["hotpauthwindow"]
    type: int
  ipatokenhotpsyncwindow:
    description: HOTP synchronization window in hops.
    aliases: ["hotpsyncwindow"]
    type: int
extends_documentation_fragment:
- community.general.ipa.documentation

'''

EXAMPLES = r'''
- name: Ensure the TOTP authentication window is set to 300 seconds
  community.general.ipa_otpconfig:
    ipatokentotpauthwindow: '300'
    ipa_host: localhost
    ipa_user: admin
    ipa_pass: supersecret

- name: Ensure the TOTP syncronization window is set to 86400 seconds
  community.general.ipa_otpconfig:
    ipatokentotpsyncwindow: '86400'
    ipa_host: localhost
    ipa_user: admin
    ipa_pass: supersecret

- name: Ensure the HOTP authentication window is set to 10 hops
  community.general.ipa_otpconfig:
    ipatokenhotpauthwindow: '10'
    ipa_host: localhost
    ipa_user: admin
    ipa_pass: supersecret

- name: Ensure the HOTP syncronization window is set to 100 hops
  community.general.ipa_otpconfig:
    ipatokenhotpsyncwindow: '100'
    ipa_host: localhost
    ipa_user: admin
    ipa_pass: supersecret
'''

RETURN = r'''
otpconfig:
  description: OTP configuration as returned by IPA API.
  returned: always
  type: dict
'''

import traceback

from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.ipa import IPAClient, ipa_argument_spec
from ansible.module_utils.common.text.converters import to_native


class OTPConfigIPAClient(IPAClient):
    def __init__(self, module, host, port, protocol):
        super(OTPConfigIPAClient, self).__init__(module, host, port, protocol)

    def otpconfig_show(self):
        return self._post_json(method='otpconfig_show', name=None)

    def otpconfig_mod(self, name, item):
        return self._post_json(method='otpconfig_mod', name=name, item=item)


def get_otpconfig_dict(ipatokentotpauthwindow=None, ipatokentotpsyncwindow=None,
                       ipatokenhotpauthwindow=None, ipatokenhotpsyncwindow=None):

    config = {}
    if ipatokentotpauthwindow is not None:
        config['ipatokentotpauthwindow'] = str(ipatokentotpauthwindow)
    if ipatokentotpsyncwindow is not None:
        config['ipatokentotpsyncwindow'] = str(ipatokentotpsyncwindow)
    if ipatokenhotpauthwindow is not None:
        config['ipatokenhotpauthwindow'] = str(ipatokenhotpauthwindow)
    if ipatokenhotpsyncwindow is not None:
        config['ipatokenhotpsyncwindow'] = str(ipatokenhotpsyncwindow)

    return config


def get_otpconfig_diff(client, ipa_config, module_config):
    return client.get_diff(ipa_data=ipa_config, module_data=module_config)


def ensure(module, client):
    module_otpconfig = get_otpconfig_dict(
        ipatokentotpauthwindow=module.params.get('ipatokentotpauthwindow'),
        ipatokentotpsyncwindow=module.params.get('ipatokentotpsyncwindow'),
        ipatokenhotpauthwindow=module.params.get('ipatokenhotpauthwindow'),
        ipatokenhotpsyncwindow=module.params.get('ipatokenhotpsyncwindow'),
    )
    ipa_otpconfig = client.otpconfig_show()
    diff = get_otpconfig_diff(client, ipa_otpconfig, module_otpconfig)

    changed = False
    new_otpconfig = {}
    for module_key in diff:
        if module_otpconfig.get(module_key) != ipa_otpconfig.get(module_key, None):
            changed = True
            new_otpconfig.update({module_key: module_otpconfig.get(module_key)})

    if changed and not module.check_mode:
        client.otpconfig_mod(name=None, item=new_otpconfig)

    return changed, client.otpconfig_show()


def main():
    argument_spec = ipa_argument_spec()
    argument_spec.update(
        ipatokentotpauthwindow=dict(type='int', aliases=['totpauthwindow'], no_log=False),
        ipatokentotpsyncwindow=dict(type='int', aliases=['totpsyncwindow'], no_log=False),
        ipatokenhotpauthwindow=dict(type='int', aliases=['hotpauthwindow'], no_log=False),
        ipatokenhotpsyncwindow=dict(type='int', aliases=['hotpsyncwindow'], no_log=False),
    )

    module = AnsibleModule(
        argument_spec=argument_spec,
        supports_check_mode=True
    )

    client = OTPConfigIPAClient(
        module=module,
        host=module.params['ipa_host'],
        port=module.params['ipa_port'],
        protocol=module.params['ipa_prot']
    )

    try:
        client.login(
            username=module.params['ipa_user'],
            password=module.params['ipa_pass']
        )
        changed, otpconfig = ensure(module, client)
    except Exception as e:
        module.fail_json(msg=to_native(e), exception=traceback.format_exc())

    module.exit_json(changed=changed, otpconfig=otpconfig)


if __name__ == '__main__':
    main()
