# Copyright (c) 2021 Cisco and/or its affiliates.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Telemetry utility."""

from robot.api import logger

from resources.libraries.python.Constants import Constants
from resources.libraries.python.OptionString import OptionString
from resources.libraries.python.ssh import exec_cmd, exec_cmd_no_error
from resources.libraries.python.topology import NodeType

__all__ = [u"TelemetryUtil"]


class TelemetryUtil:
    """Class contains methods for telemetry utility."""

    @staticmethod
    def perf_stat(node, cpu_list=None, duration=1):
        """Get perf stat read for duration.

        :param node: Node in the topology.
        :param cpu_list: CPU List as a string separated by comma.
        :param duration: Measure time in seconds.
        :type node: dict
        :type cpu_list: str
        :type duration: int
        """
        if cpu_list:
            cpu_list = list(dict.fromkeys(cpu_list.split(u",")))
            cpu_list = ",".join(str(cpu) for cpu in cpu_list)

        cmd_opts = OptionString(prefix=u"--")
        cmd_opts.add(u"no-aggr")
        cmd_opts.add_with_value_if(
            u"cpu", cpu_list, cpu_list
        )
        cmd_opts.add_if(
            u"all-cpus", not(cpu_list)
        )
        cmd_opts.add_with_value_if(
            u"event", f"'{{{Constants.PERF_STAT_EVENTS}}}'",
            Constants.PERF_STAT_EVENTS
        )
        cmd_opts.add_with_value(
            u"interval-print", 1000
        )
        cmd_opts.add_with_value(
            u"field-separator", u"';'"
        )

        cmd_base = OptionString()
        cmd_base.add(f"perf stat")
        cmd_base.extend(cmd_opts)
        cmd_base.add(u"--")
        cmd_base.add_with_value(u"sleep", int(duration))

        exec_cmd(node, cmd_base, sudo=True)

    @staticmethod
    def perf_stat_on_all_duts(nodes, cpu_list=None, duration=1):
        """Get perf stat read for duration on all DUTs.

        :param nodes: Nodes in the topology.
        :param cpu_list: CPU List.
        :param duration: Measure time in seconds.
        :type nodes: dict
        :type cpu_list: str
        :type duration: int
        """
        for node in nodes.values():
            if node[u"type"] == NodeType.DUT:
                TelemetryUtil.perf_stat(
                    node, cpu_list=cpu_list, duration=duration
                )

    @staticmethod
    def run_telemetry(node, profile, hook=None):
        """Get telemetry stat read for duration.

        :param node: Node in the topology.
        :param profile: Telemetry configuration profile.
        :param hook: Process ID or socket path (optional).
        :type node: dict
        :type profile: str
        :type hook: str
        """
        config = u""
        config += f"{Constants.REMOTE_FW_DIR}/"
        config += f"{Constants.RESOURCES_TPL_TELEMETRY}/"
        config += f"{profile}"

        cd_cmd = u""
        cd_cmd += f"sh -c \"cd {Constants.REMOTE_FW_DIR}/"
        cd_cmd += f"{Constants.RESOURCES_TOOLS}"

        bin_cmd = f"python3 -m telemetry --config {config} --hook {hook}\""
        hostname = node[u"host"]

        exec_cmd_no_error(node, f"{cd_cmd} && {bin_cmd}", sudo=True)
        stdout, _ = exec_cmd_no_error(
            node, u"cat /tmp/metric.prom", sudo=True, log_stdout_err=False
        )
        logger.info(
            u"# TYPE target info\n"
            u"# HELP target Target metadata\n"
            f"target_info{{hostname=\"{hostname}\",hook=\"{hook}\"}} 1\n"
            f"{stdout}"
        )

    @staticmethod
    def run_telemetry_on_all_duts(nodes, profile):
        """Get telemetry stat read on all DUTs.

        :param nodes: Nodes in the topology.
        :param profile: Telemetry configuration profile.
        :param hooks: Dict of Process IDs or socket paths (optional).
        :type nodes: dict
        :type profile: str
        :type hooks: dict
        """
        for node in nodes.values():
            if node[u"type"] == NodeType.DUT:
                try:
                    for socket in node[u"sockets"][u"PAPI"].values():
                        TelemetryUtil.run_telemetry(
                            node, profile=profile, hook=socket
                        )
                except IndexError:
                    pass