#!/usr/bin/python
#
# Copyright Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later

from __future__ import annotations

DOCUMENTATION = r"""
module: lldp_facts
requirements:
  - C(lldpctl), usually provided by the C(lldpd) package.
  - The module was renamed from C(lldp) to C(lldp_facts) in community.general 13.0.0.
    The old name is available as an alias. Eventually a deprecation message will be shown
    when the old name is used.
short_description: Get details reported by LLDP
description:
  - Reads LLDP data from C(lldpd) using the CLI tool C(lldpctl).
extends_documentation_fragment:
  - community.general._attributes
  - community.general._attributes.facts
  - community.general._attributes.facts_module
options:
  multivalues:
    description: If C(lldpctl) outputs an attribute multiple time represent all values as a list.
    type: bool
    default: false
author: "Andy Hill (@andyhky)"
notes:
  - Requires C(lldpd) running and LLDP enabled on switches.
"""

EXAMPLES = r"""
# Retrieve switch/port information
- name: Gather information from LLDP
  community.general.lldp:

- name: Print each switch/port
  ansible.builtin.debug:
    msg: "{{ lldp[item]['chassis']['name'] }} / {{ lldp[item]['port']['ifname'] }}"
  with_items: "{{ lldp.keys() }}"

# TASK: [Print each switch/port] ***********************************************************
# ok: [10.13.0.22] => (item=eth2) => {"item": "eth2", "msg": "switch1.example.com / Gi0/24"}
# ok: [10.13.0.22] => (item=eth1) => {"item": "eth1", "msg": "switch2.example.com / Gi0/3"}
# ok: [10.13.0.22] => (item=eth0) => {"item": "eth0", "msg": "switch3.example.com / Gi0/3"}
"""

RESULTS = r"""
ansible_facts:
  description: The returned facts.
  returned: success
  type: dict
  contains:
    lldp:
      descrition:
        - The parsed output.
      type: dict
"""

from ansible.module_utils.basic import AnsibleModule


def gather_lldp(module):
    cmd = [module.get_bin_path("lldpctl"), "-f", "keyvalue"]
    rc, output, err = module.run_command(cmd)
    if output:
        output_dict = {}
        current_dict = {}
        lldp_entries = output.strip().split("\n")

        final = ""
        for entry in lldp_entries:
            if entry.startswith("lldp"):
                path, value = entry.strip().split("=", 1)
                path = path.split(".")
                path_components, final = path[:-1], path[-1]
            elif final in current_dict and isinstance(current_dict[final], str):
                current_dict[final] += f"\n{entry}"
                continue
            elif final in current_dict and isinstance(current_dict[final], list):
                current_dict[final][-1] += f"\n{entry}"
                continue
            else:
                continue

            current_dict = output_dict
            for path_component in path_components:
                current_dict[path_component] = current_dict.get(path_component, {})
                if not isinstance(current_dict[path_component], dict):
                    current_dict[path_component] = {"value": current_dict[path_component]}
                current_dict = current_dict[path_component]

            if final in current_dict and isinstance(current_dict[final], dict) and module.params["multivalues"]:
                current_dict = current_dict[final]
                final = "value"

            if final not in current_dict or not module.params["multivalues"]:
                current_dict[final] = value
            elif isinstance(current_dict[final], str):
                current_dict[final] = [current_dict[final], value]
            elif isinstance(current_dict[final], list):
                current_dict[final].append(value)

        return output_dict


def main():
    module_args = dict(multivalues=dict(type="bool", default=False))
    module = AnsibleModule(module_args, supports_check_mode=True)
    module.run_command_environ_update = {"LANGUAGE": "C", "LC_ALL": "C"}

    lldp_output = gather_lldp(module)
    try:
        data = {"lldp": lldp_output["lldp"]}
        module.exit_json(ansible_facts=data)
    except TypeError:
        module.fail_json(msg="lldpctl command failed. is lldpd running?")


if __name__ == "__main__":
    main()
