"""
xml parser

This is the xml parser for use with the cli_parse module and action plugin
https://github.com/martinblech/xmltodict
"""
from __future__ import absolute_import, division, print_function

__metaclass__ = type

DOCUMENTATION = """
    author: Bradley Thornton (@cidrblock)
    name: xml
    short_description: Define configurable options for C(xml) sub-plugin of M(ansible.utils.cli_parse) module
    description:
    - This plugin documentation provides the configurable options that can be passed
      to the M(ansible.utils.cli_parse) plugins when I(ansible.utils.xml) is used as a value for
      I(name) option.
    version_added: 1.0.0
"""

EXAMPLES = r"""
- name: "Run command and parse with xml"
  ansible.utils.cli_parse:
    command: "show interface | xml"
    parser:
      name: ansible.utils.xml
  register: nxos_xml_command

- name: "Pass text and parse with xml"
  ansible.utils.cli_parse:
    text: "{{ lookup('ansible.builtin.file', '/home/user/files/nxos_show_interface.xml') }}"
    parser:
      name: ansible.utils.xml
      os: nxos
  register: nxos_xml_text
"""

from ansible.module_utils._text import to_native
from ansible.module_utils.basic import missing_required_lib
from ansible_collections.ansible.utils.plugins.plugin_utils.base.cli_parser import (
    CliParserBase,
)


try:
    import xmltodict

    HAS_XMLTODICT = True
except ImportError:
    HAS_XMLTODICT = False


class CliParser(CliParserBase):
    """ The xml parser class
    Convert an xml string to structured data using xmltodict
    """

    DEFAULT_TEMPLATE_EXTENSION = None
    PROVIDE_TEMPLATE_CONTENTS = False

    @staticmethod
    def _check_reqs():
        """ Check the prerequisites for the xml parser
        """
        errors = []
        if not HAS_XMLTODICT:
            errors.append(missing_required_lib("xmltodict"))

        return errors

    def parse(self, *_args, **_kwargs):
        """ Std entry point for a cli_parse parse execution

        :return: Errors or parsed text as structured data
        :rtype: dict

        :example:

        The parse function of a parser should return a dict:
        {"errors": [a list of errors]}
        or
        {"parsed": obj}
        """
        errors = self._check_reqs()
        if errors:
            return {"errors": errors}

        cli_output = self._task_args.get("text")

        network_os = self._task_args.get("parser").get(
            "os"
        ) or self._task_vars.get("ansible_network_os")
        # the nxos | xml includes a odd garbage line at the end, so remove it
        if not network_os:
            self._debug("network_os value is not set")

        if network_os and "nxos" in network_os:
            splitted = cli_output.splitlines()
            if splitted[-1] == "]]>]]>":
                cli_output = "\n".join(splitted[:-1])

        try:
            parsed = xmltodict.parse(cli_output)
            return {"parsed": parsed}
        except Exception as exc:
            msg = "XML parser returned an error while parsing. Error: {err}"
            return {"errors": [msg.format(err=to_native(exc))]}
