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

import json
from typing import Iterable

# 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.graylog import *  # pylint: disable=wildcard-import,unused-wildcard-import

# <<<graylog_failures>>>


def inventory_graylog_failures(parsed):
    failure_details = parsed.get("failures")
    if failure_details is not None:
        return [(None, {})]


def _failure_message_to_human_readable(message_serialized: str) -> Iterable[str]:
    try:
        message_deserialized = json.loads(message_serialized)
    except json.JSONDecodeError:
        # the graylog API does not specify the scheme of the endpoint providing this data (it only
        # says 'anyMap'), see SUP-11170
        yield f"Message: {message_serialized}"
        return
    yield from (
        f"{field.title()}: {field_value}"
        for field in [
            "type",
            "reason",
        ]
        if (field_value := message_deserialized.get(field)) is not None
    )


def check_graylog_failures(_no_item, params, parsed):
    failure_details = parsed.get("failures")
    if failure_details is None:
        return

    failure_total = parsed.get("total")
    if failure_total is not None:
        yield check_levels(
            failure_total,
            "failures",
            params.get("failures"),
            human_readable_func=int,
            infoname="Total number of failures",
        )

    failure_count = parsed.get("count")
    if failure_count is not None:
        yield check_levels(
            failure_count,
            None,
            params.get("failures_last"),
            human_readable_func=int,
            infoname="Failures in last %s" % get_age_human_readable(parsed["ds_param_since"]),
        )

        if failure_count:
            index_affected = []
            long_output = []
            for failure in sorted(
                failure_details,
                key=lambda k: (k["timestamp"], k["index"]),
            ):

                long_output_str = ""

                timestamp = failure.get("timestamp")
                if timestamp is not None:
                    long_output_str = "Timestamp: %s" % timestamp

                index = failure.get("index")
                if index is not None:
                    if index not in index_affected:
                        index_affected.append(index)
                    long_output_str += ", Index: %s" % index

                long_output.append(
                    ", ".join(
                        [
                            long_output_str,
                            *(
                                _failure_message_to_human_readable(message_serialized)
                                if (message_serialized := failure.get("message")) is not None
                                else []
                            ),
                        ]
                    )
                )

            if long_output:
                yield 0, "Affected indices: %d, " "See long output for further information" % len(
                    index_affected
                )

                yield 0, "\n%s" % "\n".join(long_output)


check_info["graylog_failures"] = {
    "parse_function": parse_graylog_agent_data,
    "check_function": check_graylog_failures,
    "inventory_function": inventory_graylog_failures,
    "service_description": "Graylog Index Failures",
    "has_perfdata": True,
    "group": "graylog_failures",
}
