#!/usr/bin/python
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.
#
from __future__ import absolute_import, division, print_function

__metaclass__ = type
DOCUMENTATION = """
module: ios_ntp
extends_documentation_fragment:
- cisco.ios.ios
short_description: Manages core NTP configuration.
description:
- Manages core NTP configuration.
version_added: 1.0.0
author:
- Federico Olivieri (@Federico87)
options:
  server:
    description:
    - Network address of NTP server.
    type: str
  source_int:
    description:
    - Source interface for NTP packets.
    type: str
  acl:
    description:
    - ACL for peer/server access restricition.
    type: str
  logging:
    description:
    - Enable NTP logs. Data type boolean.
    type: bool
    default: false
  auth:
    description:
    - Enable NTP authentication. Data type boolean.
    type: bool
    default: false
  auth_key:
    description:
    - md5 NTP authentication key of tye 7.
    type: str
  key_id:
    description:
    - auth_key id. Data type string
    type: str
  state:
    description:
    - Manage the state of the resource.
    default: present
    choices:
    - present
    - absent
    type: str
"""
EXAMPLES = """
# Set new NTP server and source interface
- cisco.ios.ios_ntp:
    server: 10.0.255.10
    source_int: Loopback0
    logging: false
    state: present

# Remove NTP ACL and logging
- cisco.ios.ios_ntp:
    acl: NTP_ACL
    logging: true
    state: absent

# Set NTP authentication
- cisco.ios.ios_ntp:
    key_id: 10
    auth_key: 15435A030726242723273C21181319000A
    auth: true
    state: present

# Set new NTP configuration
- cisco.ios.ios_ntp:
    server: 10.0.255.10
    source_int: Loopback0
    acl: NTP_ACL
    logging: true
    key_id: 10
    auth_key: 15435A030726242723273C21181319000A
    auth: true
    state: present
"""
RETURN = """
commands:
    description: command sent to the device
    returned: always
    type: list
    sample: ["no ntp server 10.0.255.10", "no ntp source Loopback0"]
"""
import re
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import (
    get_config,
    load_config,
)
from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import (
    ios_argument_spec,
)


def parse_server(line, dest):
    if dest == "server":
        match = re.search(
            "(ntp server )(\\d+\\.\\d+\\.\\d+\\.\\d+)", line, re.M
        )
        if match:
            server = match.group(2)
            return server


def parse_source_int(line, dest):
    if dest == "source":
        match = re.search("(ntp source )(\\S+)", line, re.M)
        if match:
            source = match.group(2)
            return source


def parse_acl(line, dest):
    if dest == "access-group":
        match = re.search(
            "ntp access-group (?:peer|serve)(?:\\s+)(\\S+)", line, re.M
        )
        if match:
            acl = match.group(1)
            return acl


def parse_logging(line, dest):
    if dest == "logging":
        logging = dest
        return logging


def parse_auth_key(line, dest):
    if dest == "authentication-key":
        match = re.search(
            "(ntp authentication-key \\d+ md5 )(\\w+)", line, re.M
        )
        if match:
            auth_key = match.group(2)
            return auth_key


def parse_key_id(line, dest):
    if dest == "trusted-key":
        match = re.search("(ntp trusted-key )(\\d+)", line, re.M)
        if match:
            auth_key = match.group(2)
            return auth_key


def parse_auth(dest):
    if dest == "authenticate":
        return dest


def map_config_to_obj(module):
    obj_dict = {}
    obj = []
    server_list = []
    config = get_config(module, flags=["| include ntp"])
    for line in config.splitlines():
        match = re.search("ntp (\\S+)", line, re.M)
        if match:
            dest = match.group(1)
            server = parse_server(line, dest)
            source_int = parse_source_int(line, dest)
            acl = parse_acl(line, dest)
            logging = parse_logging(line, dest)
            auth = parse_auth(dest)
            auth_key = parse_auth_key(line, dest)
            key_id = parse_key_id(line, dest)
            if server:
                server_list.append(server)
            if source_int:
                obj_dict["source_int"] = source_int
            if acl:
                obj_dict["acl"] = acl
            if logging:
                obj_dict["logging"] = True
            if auth:
                obj_dict["auth"] = True
            if auth_key:
                obj_dict["auth_key"] = auth_key
            if key_id:
                obj_dict["key_id"] = key_id
    obj_dict["server"] = server_list
    obj.append(obj_dict)
    return obj


def map_params_to_obj(module):
    obj = []
    obj.append(
        {
            "state": module.params["state"],
            "server": module.params["server"],
            "source_int": module.params["source_int"],
            "logging": module.params["logging"],
            "acl": module.params["acl"],
            "auth": module.params["auth"],
            "auth_key": module.params["auth_key"],
            "key_id": module.params["key_id"],
        }
    )
    return obj


def map_obj_to_commands(want, have, module):
    commands = list()
    server_have = have[0].get("server", None)
    source_int_have = have[0].get("source_int", None)
    acl_have = have[0].get("acl", None)
    logging_have = have[0].get("logging", None)
    auth_have = have[0].get("auth", None)
    auth_key_have = have[0].get("auth_key", None)
    key_id_have = have[0].get("key_id", None)
    for w in want:
        server = w["server"]
        source_int = w["source_int"]
        acl = w["acl"]
        logging = w["logging"]
        state = w["state"]
        auth = w["auth"]
        auth_key = w["auth_key"]
        key_id = w["key_id"]
        if state == "absent":
            if server_have and server in server_have:
                commands.append("no ntp server {0}".format(server))
            if source_int and source_int_have:
                commands.append("no ntp source {0}".format(source_int))
            if acl and acl_have:
                commands.append("no ntp access-group peer {0}".format(acl))
            if logging is True and logging_have:
                commands.append("no ntp logging")
            if auth is True and auth_have:
                commands.append("no ntp authenticate")
            if key_id and key_id_have:
                commands.append("no ntp trusted-key {0}".format(key_id))
            if auth_key and auth_key_have:
                if key_id and key_id_have:
                    commands.append(
                        "no ntp authentication-key {0} md5 {1} 7".format(
                            key_id, auth_key
                        )
                    )
        elif state == "present":
            if server is not None and server not in server_have:
                commands.append("ntp server {0}".format(server))
            if source_int is not None and source_int != source_int_have:
                commands.append("ntp source {0}".format(source_int))
            if acl is not None and acl != acl_have:
                commands.append("ntp access-group peer {0}".format(acl))
            if (
                logging is not None
                and logging != logging_have
                and logging is not False
            ):
                commands.append("ntp logging")
            if auth is not None and auth != auth_have and auth is not False:
                commands.append("ntp authenticate")
            if key_id is not None and key_id != key_id_have:
                commands.append("ntp trusted-key {0}".format(key_id))
            if auth_key is not None and auth_key != auth_key_have:
                if key_id is not None:
                    commands.append(
                        "ntp authentication-key {0} md5 {1} 7".format(
                            key_id, auth_key
                        )
                    )
    return commands


def main():
    argument_spec = dict(
        server=dict(),
        source_int=dict(),
        acl=dict(),
        logging=dict(type="bool", default=False),
        auth=dict(type="bool", default=False),
        auth_key=dict(),
        key_id=dict(),
        state=dict(choices=["absent", "present"], default="present"),
    )
    argument_spec.update(ios_argument_spec)
    module = AnsibleModule(
        argument_spec=argument_spec, supports_check_mode=True
    )
    result = {"changed": False}
    warnings = list()
    if warnings:
        result["warnings"] = warnings
    want = map_params_to_obj(module)
    have = map_config_to_obj(module)
    commands = map_obj_to_commands(want, have, module)
    result["commands"] = commands
    if commands:
        if not module.check_mode:
            load_config(module, commands)
        result["changed"] = True
    module.exit_json(**result)


if __name__ == "__main__":
    main()
