#!/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.ups_generic 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.uptime import *  # pylint: disable=wildcard-import,unused-wildcard-import

# Description of OIDs used from RFC 1628
# OID: 1.3.6.1.2.1.33.1.7.3
# upsTestResultsSummary OBJECT-TYPE
# SYNTAX INTEGER {
#     donePass(1),
#     doneWarning(2),
#     doneError(3),
#     aborted(4),
#     inProgress(5),
#     noTestsInitiated(6)
# }
# MAX-ACCESS read-only
# STATUS current
# DESCRIPTION
# "The results of the current or last UPS diagnostics
# test performed. The values for donePass(1),
# doneWarning(2), and doneError(3) indicate that the
# test completed either successfully, with a warning, or
# with an error, respectively. The value aborted(4) is
# returned for tests which are aborted by setting the
# value of upsTestId to upsTestAbortTestInProgress.
# Tests which have not yet concluded are indicated by
# inProgress(5). The value noTestsInitiated(6)
# indicates that no previous test results are available,
# such as is the case when no tests have been run since
# the last reinitialization of the network management
# subsystem and the system has no provision for non-
# volatile storage of test results."

# OID: 1.3.6.1.2.1.33.1.7.4
# upsTestResultsDetail OBJECT-TYPE
# SYNTAX DisplayString (SIZE (0..255))
# MAX-ACCESS read-only
# STATUS current
# DESCRIPTION
# "Additional information about upsTestResultsSummary.
# If no additional information available, a zero length
# string is returned."

# OID: 1.3.6.1.2.1.33.1.7.5
# Description:
# upsTestStartTime OBJECT-TYPE
# SYNTAX TimeStamp
# MAX-ACCESS read-only
# STATUS current
# DESCRIPTION
# "The value of sysUpTime at the time the test in
# progress was initiated, or, if no test is in progress,
# the time the previous test was initiated. If the
# value of upsTestResultsSummary is noTestsInitiated(6),
# upsTestStartTime has the value 0."

ups_test_default = (0, 0)

_TEST_RESULT_SUMMARY_MAP = {
    "1": (0, "passed"),
    "2": (1, "warning"),
    "3": (2, "error"),
    "4": (2, "aborted"),
    "5": (0, "in progress"),
    "6": (0, "no tests initiated"),
}


def inventory_ups_test(info):
    if info[1]:
        return [(None, "ups_test_default")]


def check_ups_test(_no_item, params, info):
    warn, crit = params

    uptime_info, bat_info = info
    if not uptime_info or not bat_info:
        return

    ResultsSummary, StartTime, upsTestResultsDetail = bat_info[0]
    uptime = parse_snmp_uptime(uptime_info[0][0])
    start_time = parse_snmp_uptime(StartTime)

    details = f" ({upsTestResultsDetail})" if upsTestResultsDetail else ""
    # The MIB dictates a set of possible values for the test result, which are all
    # included in the result mapping.
    # However, the device could still have a test result that is outside the set of
    # possible values (e.g. "0"). In this case, an UNKNOWN state is chosen because
    # it reflects the truth and the check is able to show further check results.
    state, test_result = _TEST_RESULT_SUMMARY_MAP.get(ResultsSummary, (3, "unknown"))
    yield state, f"Last test: {test_result}{details}"

    # Elapsed time since last start of test
    elapsed = uptime - start_time
    state = 0
    if crit and elapsed >= crit * 86400:
        state = 2
    elif warn and elapsed >= warn * 86400:
        state = 1
    if start_time:
        yield state, "time elapsed since start of last test: %s" % get_age_human_readable(elapsed)
    else:
        yield state, "no battery test since start of device (%s ago)" % get_age_human_readable(
            uptime
        )


check_info["ups_test"] = {
    "inventory_function": inventory_ups_test,
    "check_function": check_ups_test,
    "service_description": "Self Test",
    "group": "ups_test",
    "snmp_info": [
        (".1.3.6.1.2.1.1", ["3.0"]),  # sysUptime
        (".1.3.6.1.2.1.33.1.7", [3, 5, 4]),
    ],
    "snmp_scan_function": ups_generic_scan_function,
}
