#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright (C) 2019 tribe29 GmbH - License: GNU General Public License v2
# This file is part of Checkmk (https://checkmk.com). It is subject to the terms and
# conditions defined in the file COPYING, which is part of this source code package.

# NOTE: Careful when replacing the *-import below with a more specific import. This can cause
# problems because it might remove variables from the check-context which are necessary for
# resolving legacy discovery results such as [("SUMMARY", "diskstat_default_levels")]. Furthermore,
# it might also remove variables needed for accessing discovery rulesets.
from cmk.base.check_legacy_includes.brocade import *  # pylint: disable=wildcard-import,unused-wildcard-import

# NOTE: Careful when replacing the *-import below with a more specific import. This can cause
# problems because it might remove variables from the check-context which are necessary for
# resolving legacy discovery results such as [("SUMMARY", "diskstat_default_levels")]. Furthermore,
# it might also remove variables needed for accessing discovery rulesets.
from cmk.base.check_legacy_includes.temperature import *  # pylint: disable=wildcard-import,unused-wildcard-import


def parse_brocade_sfp(info):
    parsed = {}

    isl_ports = [int(x[0]) for x in info[1]]

    for fcport_info, values in zip(info[0], info[2]):

        # Observed in the wild: Either all of the values are present
        # or none of them.
        if values[0] == "NA":
            continue

        port_index = int(fcport_info[0])

        parsed[port_index] = {
            "port_name": fcport_info[4],
            "temp": int(values[0]),  # °C
            "phystate": int(fcport_info[1]),
            "opstate": int(fcport_info[2]),
            "admstate": int(fcport_info[3]),
            "voltage": as_float(values[1]) / 1000,  # mV -> V
            "current": as_float(values[2]) / 1000,  # mA -> A
            "rx_power": as_float(values[3]),  # dBm
            "tx_power": as_float(values[4]),  # dBm
            "is_isl": bool(port_index in isl_ports),
        }

    return parsed


def inventory_brocade_sfp(parsed):
    settings = host_extra_conf_merged(host_name(), brocade_fcport_inventory)
    number_of_ports = len(parsed)
    for port_index, port_info in parsed.items():
        if brocade_fcport_inventory_this_port(
            admstate=port_info["admstate"],
            phystate=port_info["phystate"],
            opstate=port_info["opstate"],
            settings=settings,
        ):
            yield brocade_fcport_getitem(
                number_of_ports=number_of_ports,
                index=port_index,
                portname=port_info["port_name"],
                is_isl=port_info["is_isl"],
                settings=settings,
            ), {}


#   .--Temperature---------------------------------------------------------.
#   |     _____                                   _                        |
#   |    |_   _|__ _ __ ___  _ __   ___ _ __ __ _| |_ _   _ _ __ ___       |
#   |      | |/ _ \ '_ ` _ \| '_ \ / _ \ '__/ _` | __| | | | '__/ _ \      |
#   |      | |  __/ | | | | | |_) |  __/ | | (_| | |_| |_| | | |  __/      |
#   |      |_|\___|_| |_| |_| .__/ \___|_|  \__,_|\__|\__,_|_|  \___|      |
#   |                       |_|                                            |
#   '----------------------------------------------------------------------'


def check_brocade_sfp_temp(item, params, parsed):

    # TODO: Move this magical plucking apart of the
    #       item to brocade.include and do the same
    #       for brocade.fcport.
    port_index = int(item.split()[0]) + 1
    if port_index not in parsed:
        return None
    port_info = parsed[port_index]

    return check_temperature(port_info["temp"], params, item)


check_info["brocade_sfp.temp"] = {
    "inventory_function": inventory_brocade_sfp,
    "check_function": check_brocade_sfp_temp,
    "service_description": "SFP Temperature %s",
    "has_perfdata": True,
    "group": "temperature",
}

# .
#   .--Power level - Main check--------------------------------------------.
#   |          ____                          _                _            |
#   |         |  _ \ _____      _____ _ __  | | _____   _____| |           |
#   |         | |_) / _ \ \ /\ / / _ \ '__| | |/ _ \ \ / / _ \ |           |
#   |         |  __/ (_) \ V  V /  __/ |    | |  __/\ V /  __/ |           |
#   |         |_|   \___/ \_/\_/ \___|_|    |_|\___| \_/ \___|_|           |
#   |                                                                      |
#   +----------------------------------------------------------------------+
#   | Also includes information about current and voltage                  |
#   '----------------------------------------------------------------------'


def check_brocade_sfp(item, params, parsed):

    # TODO: Move this magical plucking apart of the
    #       item to brocade.include and do the same
    #       for brocade.fcport.
    port_index = int(item.split()[0]) + 1
    if port_index not in parsed:
        return
    port_info = parsed[port_index]

    def sort_params(key):
        # levels are given in an uncommon order at the rulespec
        # We have crit_lower, warn_lower, warn_upper, crit_upper,
        # but we need warn_upper, warn_upper, warn_lower, crit_lower
        if key not in params:
            return None

        return params[key][2], params[key][3], params[key][1], params[key][0]

    yield check_levels(
        port_info["rx_power"],
        "input_signal_power_dbm",
        sort_params("rx_power"),
        infoname="Rx",
        unit="dBm",
    )
    yield check_levels(
        port_info["tx_power"],
        "output_signal_power_dbm",
        sort_params("tx_power"),
        infoname="Tx",
        unit="dBm",
    )
    yield check_levels(
        port_info["current"], "current", sort_params("current"), infoname="Current", unit="A"
    )
    yield check_levels(
        port_info["voltage"], "voltage", sort_params("voltage"), infoname="Voltage", unit="V"
    )


check_info["brocade_sfp"] = {
    "parse_function": parse_brocade_sfp,
    "inventory_function": inventory_brocade_sfp,
    "check_function": check_brocade_sfp,
    "service_description": "SFP %s",
    "snmp_scan_function": lambda oid: oid(".1.3.6.1.2.1.1.2.0").startswith(
        ".1.3.6.1.4.1.1588.2.1.1"
    )
    and oid(".1.3.6.1.4.1.1588.2.1.1.1.6.2.1.*") is not None,
    "snmp_info": [
        (
            ".1.3.6.1.4.1.1588.2.1.1.1.6.2.1",
            [
                CACHED_OID("1"),  # swFCPortIndex
                CACHED_OID("3"),  # swFCPortPhyState
                CACHED_OID("4"),  # swFCPortOpStatus
                CACHED_OID("5"),  # swFCPortAdmStatus
                CACHED_OID("36"),  # swFCPortName  (not supported by all devices)
            ],
        ),
        # Information about Inter-Switch-Links (contains baud rate of port)
        (
            ".1.3.6.1.4.1.1588.2.1.1.1.2.9.1",
            [
                CACHED_OID("2"),  # swNbMyPort
            ],
        ),
        # NOTE: It appears that the port name and index in connUnitPortEntry
        #       are identical to the ones in the table used by
        #       brocade_fcport. We work on this assumption for the time being,
        #       meaning we use the same table as in brocade_fcport (see above)
        #       which we need anyway for PhyState, OpStatus and AdmStatus.
        #       Please check the connUnitPortEntry table (.1.3.6.1.3.94.1.10.1)
        #       should you come across a device for which this assumption
        #       does not hold.
        (
            ".1.3.6.1.4.1.1588.2.1.1.1.28.1.1",
            [  # FA-EXT-MIB::swSfpStatEntry
                # AUGMENTS {connUnitPortEntry}
                "1",  # swSfpTemperature
                "2",  # swSfpVoltage
                "3",  # swSfpCurrent
                "4",  # swSfpRxPower
                "5",  # swSfpTxPower
            ],
        ),
    ],
    "has_perfdata": True,
    "group": "brocade_sfp",
}
