#!/usr/bin/env python3

r"""
This module contains functions having to do with redfish path walking.
"""

import json
import os
import subprocess

ERROR_RESPONSE = {
    "404": "Response Error: status_code: 404 -- Not Found",
    "500": "Response Error: status_code: 500 -- Internal Server Error",
}

# Variable to hold enumerated data.
result = {}

# Variable to hold the pending list of resources for which enumeration.
# is yet to be obtained.
pending_enumeration = set()


def execute_redfish_cmd(parms, json_type="json"):
    r"""
    Run CLI standard redfish tool.

    Description of variable:
    parms_string         Command to execute from the current SHELL.
    quiet                do not print tool error message if True
    """
    resp = subprocess.run(
        [parms],
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        shell=True,
        universal_newlines=True,
    )

    if resp.stderr:
        print("\n\t\tERROR with %s " % parms)
        print("\t\t" + resp.stderr)
        return resp.stderr
    elif json_type == "json":
        json_data = json.loads(resp.stdout)
        return json_data
    else:
        return resp.stdout


def enumerate_request(hostname, username, password, url, return_json="json"):
    r"""
    Perform a GET enumerate request and return available resource paths.

    Description of argument(s):
    url               URI resource absolute path (e.g.
                      "/redfish/v1/SessionService/Sessions").
    return_json       Indicates whether the result should be
                      returned as a json string or as a
                      dictionary.
    """
    parms = (
        "redfishtool -u "
        + username
        + " -p "
        + password
        + " -r "
        + hostname
        + " -S Always raw GET "
    )

    pending_enumeration.add(url)

    # Variable having resources for which enumeration is completed.
    enumerated_resources = set()

    resources_to_be_enumerated = (url,)

    while resources_to_be_enumerated:
        for resource in resources_to_be_enumerated:
            # JsonSchemas, SessionService or URLs containing # are not
            # required in enumeration.
            # Example: '/redfish/v1/JsonSchemas/' and sub resources.
            #          '/redfish/v1/SessionService'
            #          '/redfish/v1/Managers/${MANAGER_ID}#/Oem'
            if (
                ("JsonSchemas" in resource)
                or ("SessionService" in resource)
                or ("PostCodes" in resource)
                or ("Registries" in resource)
                or ("#" in resource)
            ):
                continue

            response = execute_redfish_cmd(parms + resource)
            # Enumeration is done for available resources ignoring the
            # ones for which response is not obtained.
            if "Error getting response" in response:
                continue

            walk_nested_dict(response, url=resource)

        enumerated_resources.update(set(resources_to_be_enumerated))
        resources_to_be_enumerated = tuple(
            pending_enumeration - enumerated_resources
        )

    if return_json == "json":
        return json.dumps(
            result, sort_keys=True, indent=4, separators=(",", ": ")
        )
    else:
        return result


def walk_nested_dict(data, url=""):
    r"""
    Parse through the nested dictionary and get the resource id paths.

    Description of argument(s):
    data    Nested dictionary data from response message.
    url     Resource for which the response is obtained in data.
    """
    url = url.rstrip("/")

    for key, value in data.items():
        # Recursion if nested dictionary found.
        if isinstance(value, dict):
            walk_nested_dict(value)
        else:
            # Value contains a list of dictionaries having member data.
            if "Members" == key:
                if isinstance(value, list):
                    for memberDict in value:
                        if isinstance(memberDict, str):
                            pending_enumeration.add(memberDict)
                        else:
                            pending_enumeration.add(memberDict["@odata.id"])

            if "@odata.id" == key:
                value = value.rstrip("/")
                # Data for the given url.
                if value == url:
                    result[url] = data
                # Data still needs to be looked up,
                else:
                    pending_enumeration.add(value)


def get_key_value_nested_dict(data, key):
    r"""
    Parse through the nested dictionary and get the searched key value.

    Description of argument(s):
    data    Nested dictionary data from response message.
    key     Search dictionary key element.
    """

    for k, v in data.items():
        if isinstance(v, dict):
            get_key_value_nested_dict(v, key)

        if k == key:
            target_list.append(v)