#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright 2019 Red Hat
# 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

ANSIBLE_METADATA = {'metadata_version': '1.1',
                    'status': ['preview'],
                    'supported_by': 'community'}

DOCUMENTATION = '''
---

module: ce_mdn_interface
short_description: Manages MDN configuration on HUAWEI CloudEngine switches.
description:
    - Manages MDN configuration on HUAWEI CloudEngine switches.
author: xuxiaowei0512 (@CloudEngine-Ansible)
options:
  lldpenable:
    description:
      - Set global LLDP enable state.
    type: str
    choices: ['enabled', 'disabled']
  mdnstatus:
    description:
      - Set interface MDN enable state.
    type: str
    choices: ['rxOnly', 'disabled']
  ifname:
    description:
      - Interface name.
    type: str
  state:
    description:
      - Manage the state of the resource.
    default: present
    type: str
    choices: ['present','absent']
notes:
  - This module requires the netconf system service be enabled on
    the remote device being managed.
  - This module works with connection C(netconf).
'''

EXAMPLES = '''
  - name: "Configure global LLDP enable state"
    ce_mdn_interface:
      lldpenable: enabled

  - name: "Configure interface MDN enable state"
    ce_mdn_interface:
      ifname: 10GE1/0/1
      mdnstatus: rxOnly
'''

RETURN = '''
proposed:
    description: k/v pairs of parameters passed into module
    returned: always
    type: dict
    sample: {
                "lldpenable": "enabled",
                "ifname": "10GE1/0/1",
                "mdnstatus": "rxOnly",
                "state":"present"
            }
existing:
    description: k/v pairs of existing global LLDP configration
    returned: always
    type: dict
    sample: {
                "lldpenable": "enabled",
                "ifname": "10GE1/0/1",
                "mdnstatus": "disabled"
            }
end_state:
    description: k/v pairs of global LLDP configration after module execution
    returned: always
    type: dict
    sample: {
                "lldpenable": "enabled",
                "ifname": "10GE1/0/1",
                "mdnstatus": "rxOnly"
            }
updates:
    description: command sent to the device
    returned: always
    type: list
    sample: [
                "interface 10ge 1/0/1",
                "lldp mdn enable",
            ]
changed:
    description: check to see if a change was made on the device
    returned: always
    type: bool
    sample: true
'''

import copy
import re
from xml.etree import ElementTree
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import set_nc_config, get_nc_config, execute_nc_action

CE_NC_GET_GLOBAL_LLDPENABLE_CONFIG = """
<filter type="subtree">
  <lldp xmlns="http://www.huawei.com/netconf/vrp" content-version="1.0" format-version="1.0">
    <lldpSys>
      <lldpEnable></lldpEnable>
    </lldpSys>
  </lldp>
</filter>
"""

CE_NC_MERGE_GLOBA_LLDPENABLE_CONFIG = """
<config>
  <lldp xmlns="http://www.huawei.com/netconf/vrp" content-version="1.0" format-version="1.0">
    <lldpSys operation="merge">
      <lldpEnable>%s</lldpEnable>
    </lldpSys>
  </lldp>
</config>
"""

CE_NC_GET_INTERFACE_MDNENABLE_CONFIG = """
<filter type="subtree">
  <lldp xmlns="http://www.huawei.com/netconf/vrp" content-version="1.0" format-version="1.0">
    <mdnInterfaces>
      <mdnInterface>
        <ifName></ifName>
        <mdnStatus></mdnStatus>
      </mdnInterface>
    </mdnInterfaces>
  </lldp>
</filter>
"""

CE_NC_MERGE_INTERFACE_MDNENABLE_CONFIG = """
<config>
  <lldp xmlns="http://www.huawei.com/netconf/vrp" content-version="1.0" format-version="1.0">
    <mdnInterfaces>
      <mdnInterface operation="merge">
        <ifName>%s</ifName>
        <mdnStatus>%s</mdnStatus>
      </mdnInterface>
    </mdnInterfaces>
  </lldp>
</config>
"""


def get_interface_type(interface):
    """Gets the type of interface, such as 10GE, ..."""

    if interface is None:
        return None

    iftype = None

    if interface.upper().startswith('GE'):
        iftype = 'ge'
    elif interface.upper().startswith('10GE'):
        iftype = '10ge'
    elif interface.upper().startswith('25GE'):
        iftype = '25ge'
    elif interface.upper().startswith('40GE'):
        iftype = '40ge'
    elif interface.upper().startswith('100GE'):
        iftype = '100ge'
    elif interface.upper().startswith('PORT-GROUP'):
        iftype = 'stack-Port'
    elif interface.upper().startswith('NULL'):
        iftype = 'null'
    else:
        return None
    return iftype.lower()


class Interface_mdn(object):
    """Manage global lldp enable configration"""

    def __init__(self, argument_spec):
        self.spec = argument_spec
        self.module = None
        self.init_module()

        # LLDP global configration info
        self.lldpenable = self.module.params['lldpenable'] or None
        self.ifname = self.module.params['ifname']
        self.mdnstatus = self.module.params['mdnstatus'] or None
        self.state = self.module.params['state']
        self.lldp_conf = dict()
        self.conf_exsit = False
        self.enable_flag = 0
        self.check_params()

        # state
        self.changed = False
        self.proposed_changed = dict()
        self.updates_cmd = list()
        self.results = dict()
        self.proposed = dict()
        self.existing = dict()
        self.end_state = dict()

    def check_params(self):
        """Check all input params"""

        if self.ifname:
            intf_type = get_interface_type(self.ifname)
            if not intf_type:
                self.module.fail_json(
                    msg='Error: ifname name of %s '
                        'is error.' % self.ifname)
            if (len(self.ifname) < 1) or (len(self.ifname) > 63):
                self.module.fail_json(
                    msg='Error: Ifname length is beetween 1 and 63.')

    def init_module(self):
        """Init module object"""

        self.module = AnsibleModule(
            argument_spec=self.spec, supports_check_mode=True)

    def check_response(self, xml_str, xml_name):
        """Check if response message is already succeed"""

        if "<ok/>" not in xml_str:
            self.module.fail_json(msg='Error: %s failed.' % xml_name)

    def config_interface_mdn(self):
        """Configure lldp enabled and interface mdn enabled parameters"""

        if self.state == 'present':
            if self.enable_flag == 0 and self.lldpenable == 'enabled':
                xml_str = CE_NC_MERGE_GLOBA_LLDPENABLE_CONFIG % self.lldpenable
                ret_xml = set_nc_config(self.module, xml_str)
                self.check_response(ret_xml, "LLDP_ENABLE_CONFIG")
                self.changed = True
            elif self.enable_flag == 1 and self.lldpenable == 'disabled':
                xml_str = CE_NC_MERGE_GLOBA_LLDPENABLE_CONFIG % self.lldpenable
                ret_xml = set_nc_config(self.module, xml_str)
                self.check_response(ret_xml, "LLDP_ENABLE_CONFIG")
                self.changed = True
            elif self.enable_flag == 1 and self.conf_exsit:
                xml_str = CE_NC_MERGE_INTERFACE_MDNENABLE_CONFIG % (self.ifname, self.mdnstatus)
                ret_xml = set_nc_config(self.module, xml_str)
                self.check_response(ret_xml, "INTERFACE_MDN_ENABLE_CONFIG")
                self.changed = True

    def show_result(self):
        """Show result"""

        self.results['changed'] = self.changed
        self.results['proposed'] = self.proposed
        self.results['existing'] = self.existing
        self.results['end_state'] = self.end_state
        if self.changed:
            self.results['updates'] = self.updates_cmd
        else:
            self.results['updates'] = list()

        self.module.exit_json(**self.results)

    def get_interface_mdn_exist_config(self):
        """Get lldp existed configure"""

        lldp_config = list()
        lldp_dict = dict()
        conf_enable_str = CE_NC_GET_GLOBAL_LLDPENABLE_CONFIG
        conf_enable_obj = get_nc_config(self.module, conf_enable_str)
        xml_enable_str = conf_enable_obj.replace('\r', '').replace('\n', '').\
            replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\
            replace('xmlns="http://www.huawei.com/netconf/vrp"', "")

        # get lldp enable config info
        root_enable = ElementTree.fromstring(xml_enable_str)
        ntpsite_enable = root_enable.findall("lldp/lldpSys")
        for nexthop_enable in ntpsite_enable:
            for ele_enable in nexthop_enable:
                if ele_enable.tag in ["lldpEnable"]:
                    lldp_dict[ele_enable.tag] = ele_enable.text

            if self.state == "present":
                if lldp_dict['lldpEnable'] == 'enabled':
                    self.enable_flag = 1
            lldp_config.append(dict(lldpenable=lldp_dict['lldpEnable']))

        if self.enable_flag == 1:
            conf_str = CE_NC_GET_INTERFACE_MDNENABLE_CONFIG
            conf_obj = get_nc_config(self.module, conf_str)
            if "<data/>" in conf_obj:
                return lldp_config
            xml_str = conf_obj.replace('\r', '').replace('\n', '').\
                replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\
                replace('xmlns="http://www.huawei.com/netconf/vrp"', "")
            # get all ntp config info
            root = ElementTree.fromstring(xml_str)
            ntpsite = root.findall("lldp/mdnInterfaces/mdnInterface")
            for nexthop in ntpsite:
                for ele in nexthop:
                    if ele.tag in ["ifName", "mdnStatus"]:
                        lldp_dict[ele.tag] = ele.text
                if self.state == "present":
                    cur_interface_mdn_cfg = dict(ifname=lldp_dict['ifName'], mdnstatus=lldp_dict['mdnStatus'])
                    exp_interface_mdn_cfg = dict(ifname=self.ifname, mdnstatus=self.mdnstatus)
                    if self.ifname == lldp_dict['ifName']:
                        if cur_interface_mdn_cfg != exp_interface_mdn_cfg:
                            self.conf_exsit = True
                            lldp_config.append(dict(ifname=lldp_dict['ifName'], mdnstatus=lldp_dict['mdnStatus']))
                            return lldp_config
                        lldp_config.append(dict(ifname=lldp_dict['ifName'], mdnstatus=lldp_dict['mdnStatus']))
        return lldp_config

    def get_existing(self):
        """Get existing info"""

        self.existing = self.get_interface_mdn_exist_config()

    def get_proposed(self):
        """Get proposed info"""

        if self.lldpenable:
            self.proposed = dict(lldpenable=self.lldpenable)
        if self.enable_flag == 1:
            if self.ifname:
                self.proposed = dict(ifname=self.ifname, mdnstatus=self.mdnstatus)

    def get_end_state(self):
        """Get end state info"""

        self.end_state = self.get_interface_mdn_exist_config()

    def get_update_cmd(self):
        """Get updated commands"""

        update_list = list()
        if self.state == "present":
            if self.lldpenable == "enabled":
                cli_str = "lldp enable"
                update_list.append(cli_str)
                if self.ifname:
                    cli_str = "%s %s" % ("interface", self.ifname)
                    update_list.append(cli_str)
                if self.mdnstatus:
                    if self.mdnstatus == "rxOnly":
                        cli_str = "lldp mdn enable"
                        update_list.append(cli_str)
                    else:
                        cli_str = "undo lldp mdn enable"
                        update_list.append(cli_str)

            elif self.lldpenable == "disabled":
                cli_str = "undo lldp enable"
                update_list.append(cli_str)
            else:
                if self.enable_flag == 1:
                    if self.ifname:
                        cli_str = "%s %s" % ("interface", self.ifname)
                        update_list.append(cli_str)
                    if self.mdnstatus:
                        if self.mdnstatus == "rxOnly":
                            cli_str = "lldp mdn enable"
                            update_list.append(cli_str)
                        else:
                            cli_str = "undo lldp mdn enable"
                            update_list.append(cli_str)

        self.updates_cmd.append(update_list)

    def work(self):
        """Excute task"""
        self.check_params()
        self.get_existing()
        self.get_proposed()
        self.config_interface_mdn()
        self.get_update_cmd()
        self.get_end_state()
        self.show_result()


def main():
    """Main function entry"""

    argument_spec = dict(
        lldpenable=dict(type='str', choices=['enabled', 'disabled']),
        mdnstatus=dict(type='str', choices=['rxOnly', 'disabled']),
        ifname=dict(type='str'),
        state=dict(choices=['absent', 'present'], default='present'),
    )
    lldp_obj = Interface_mdn(argument_spec)
    lldp_obj.work()


if __name__ == '__main__':
    main()