#!/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.

# TODO: Totally ignore type issues for this file. The check is totally broken, because it has not
# been updated since the TimeSeries refactoring. This will be cleaned up with CMK-3556
# type: ignore[arg-type,assignment,call-overload]

import getopt
import sys
import time
from typing import Optional

import cmk.utils.prediction as prediction
from cmk.utils.exceptions import MKGeneralException


def usage():
    sys.stderr.write(
        """== check_cpu_peaks ==
This checks raises an alert if the CPU utilization over a configurable
timeperiod (default is "-30 days until now") does NOT exceed the configured thresholds.
It can be used to detect unchallenged hosts. The check is done by evaluating the
rrd data of the given host and must therefore run in the same site where the rrd data
of this host is located."

USAGE: check_cpu_peaks [OPTIONS] HOST

ARGUMENTS:
  HOST                          Host

OPTIONS:
  -w   Return WARN if this value (integer) is never breached in the check period
  -c   Return CRIT if this value (integer) is never breached in the check period
  -u   Until time (unixtimestamp)
  -f   From time (unixtimestamp), default is until time minus 30 days
  -v   Verbose mode, Outputs RRD data
  -d   Debug mode - let Python exceptions come through
"""
    )


if len(sys.argv) < 2:
    usage()
    sys.exit(1)

short_options = "vw:c:f:u:d"
opts, args = getopt.getopt(sys.argv[1:], short_options)

opt_verbose = False
opt_debug = False
host = None
warn_level = None
crit_level = None
fromtime: Optional[float] = None
untiltime: Optional[float] = None

for o, a in opts:
    if o in ["-v"]:
        opt_verbose = True
    elif o in ["-d"]:
        opt_debug = True
    elif o in ["-w"]:
        warn_level = int(a)
    elif o in ["-c"]:
        crit_level = int(a)
    elif o in ["-f"]:
        fromtime = int(a)
    elif o in ["-u"]:
        untiltime = int(a)

if len(args) == 1:
    host = args[0]
else:
    usage()
    sys.exit(2)

if not untiltime:
    untiltime = time.time()
if not fromtime:
    fromtime = untiltime - 86400 * 30

try:
    datasets = []
    step_secs = 0
    util_total = []
    is_windows = True
    try:
        data = prediction.get_rrd_data(host, "CPU utilization", "util", "MAX", fromtime, untiltime)
        datasets.append(data[1])
        step_secs = data[0]
        # util_total = [ x for x in datasets[0] if x is not None ]
        util_total = datasets[0]
    except Exception as e:
        if opt_debug:
            raise
        # looks like this is not a windows rrd...
        is_windows = False
        for key in ["system", "user", "wait"]:
            data = prediction.get_rrd_data(host, "CPU utilization", key, "MAX", fromtime, untiltime)
            datasets.append(data[1])
            step_secs = data[0]

        def sumsum(_a):
            if None in _a:
                return None
            return int(sum(y for y in _a))

        # util_total = map(lambda x: int(sum([y for y in x if y is not None])),
        util_total = [sumsum(x) for x in zip(datasets[0], datasets[1], datasets[2])]

    cpu_peak = max(util_total)
    peak_time = time.localtime(fromtime + step_secs * util_total.index(cpu_peak))

    if opt_verbose:
        for idx, x in enumerate(datasets[0]):
            sys.stdout.write("idx: %d " % idx)
            sys.stdout.write("%s\t" % int(datasets[0][idx] or -1))
            sys.stdout.write("%s\t" % int(datasets[1][idx] or -1))
            sys.stdout.write("%s\n" % int(datasets[2][idx] or -1))

    if is_windows:
        info = [
            "CPU utilization from %s until %s"
            % (
                time.strftime("%b %d %H:%M:%S", time.localtime(fromtime)),
                time.strftime("%b %d %H:%M:%S", time.localtime(untiltime)),
            )
        ]
    else:
        info = [
            "CPU utilization (user, system, wait) from %s until %s"
            % (
                time.strftime("%b %d %H:%M:%S", time.localtime(fromtime)),
                time.strftime("%b %d %H:%M:%S", time.localtime(untiltime)),
            )
        ]

    if cpu_peak:
        info.append(
            "Highest Peak of %d%% at %s" % (cpu_peak, time.strftime("%b %d %H:%M:%S", peak_time))
        )
        state = 0
        if crit_level and cpu_peak <= crit_level:
            info.append("less than %d (!!)" % crit_level)
            state = 2
        elif warn_level and cpu_peak <= warn_level:
            info.append("less than %d (!)" % warn_level)
            state = 1
        sys.stdout.write("%s\n" % ", ".join(info))
        sys.exit(state)
    else:
        info.append("No data available in time period")
        state = 0
        sys.stdout.write("%s\n" % ": ".join(info))
        sys.exit(state)

except MKGeneralException as e:
    if opt_debug:
        raise
    sys.stdout.write("OK - %s\n" % e)
    sys.exit(0)

except Exception as e:
    if opt_debug:
        raise
    # TODO: Exception handling could be improved.
    # Right now no handling of MKGeneralException, etc. is implemented
    sys.stdout.write("UNKNOWN - %s\n" % e)
    sys.exit(3)
