#!/usr/bin/python
# -*- encoding: utf-8; py-indent-offset: 4 -*-
# +------------------------------------------------------------------+
# |             ____ _               _        __  __ _  __           |
# |            / ___| |__   ___  ___| | __   |  \/  | |/ /           |
# |           | |   | '_ \ / _ \/ __| |/ /   | |\/| | ' /            |
# |           | |___| | | |  __/ (__|   <    | |  | | . \            |
# |            \____|_| |_|\___|\___|_|\_\___|_|  |_|_|\_\           |
# |                                                                  |
# | Copyright Mathias Kettner 2018             mk@mathias-kettner.de |
# +------------------------------------------------------------------+
#
# This file is part of Check_MK.
# The official homepage is at http://mathias-kettner.de/check_mk.
#
# check_mk is free software;  you can redistribute it and/or modify it
# under the  terms of the  GNU General Public License  as published by
# the Free Software Foundation in version 2.  check_mk is  distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY;  with-
# out even the implied warranty of  MERCHANTABILITY  or  FITNESS FOR A
# PARTICULAR PURPOSE. See the  GNU General Public License for more de-
# tails. You should have  received  a copy of the  GNU  General Public
# License along with GNU Make; see the file  COPYING.  If  not,  write
# to the Free Software Foundation, Inc., 51 Franklin St,  Fifth Floor,
# Boston, MA 02110-1301 USA.
import json

factory_settings['azure_agent_info_levels'] = {
    'warning_levels': (1, 10),
    'exception_levels': (1, 1),
    'remaining_reads_levels_lower': (6000, 3000),
    'remaining_reads_unknown_state': 1,
}


def _update_remaining_reads(parsed, value):
    '''parse remaining API reads

    The key 'remaining-reads' can be present multiple times,
    or not at all.
    Three cases are considered:
     * 'remaining-reads' not present -> not in parsed
     * present, but never an integer -> 'unknown'
     * at least one integer value -> minimum of all values
    '''
    parsed.setdefault('remaining-reads', 'unknown')
    try:
        value = int(value)
    except ValueError:
        return
    # integer wins over string!
    parsed['remaining-reads'] = min(value, parsed['remaining-reads'])


def parse_azure_agent_info(info):

    parsed = {}
    for row in info:
        key = row[0]
        value = _AZURE_AGENT_SEPARATOR.join(row[1:])  # pylint: disable=undefined-variable

        if key == 'remaining-reads':
            _update_remaining_reads(parsed, value)
            continue

        try:
            value = json.loads(value)
        except ValueError:
            pass

        if key == 'issue':
            issues = parsed.setdefault('issues', {})
            issues.setdefault(value['type'], []).append(value)
            continue

        if key in ('monitored-groups', 'monitored-resources'):
            parsed.setdefault(key, []).extend(value)
            continue

        parsed.setdefault(key, []).append(value)

    return parsed


def discovery_azure_agent_info(parsed):
    yield None, {'discovered_resources': parsed.get('monitored-resources', [])}


def agent_bailouts(bailouts):
    now = time.time()
    for status, text in bailouts:
        if text.startswith("Usage client"):
            # Usage API is unreliable.
            # Only use state if this goes on for more than an hour.
            first_seen = get_item_state(text, default=now)
            set_item_state(text, first_seen)
            status = 0 if (now - first_seen < 3600) else status
        yield status, text


def remaining_api_reads(reads, params):
    if not isinstance(reads, int):
        return params['remaining_reads_unknown_state'], "Remaining API reads: %s" % reads

    levels = (None, None) + params.get('remaining_reads_levels_lower', (None, None))
    return check_levels(
        reads,
        'remaining_reads',
        levels,
        infoname="Remaining API reads",
        human_readable_func=lambda i: "%d" % i,
    )


def resource_pinning(present_resources, params):
    if not params.get('resource_pinning'):
        return "", ""

    discovered = params.get('discovered_resources')
    if discovered is None:
        return "", ""

    missing = sorted(set(discovered) - set(present_resources))
    new = sorted(set(present_resources) - set(discovered))
    short_output = []
    long_output = []

    if missing:
        short_output.append("Missing resources: %d" % len(missing))
        long_output.extend("Missing resource: %r(!)" % r for r in missing)
    if new:
        short_output.append("New resources: %d" % len(new))
        long_output.extend("New resource: %r(!)" % r for r in new)

    return ', '.join(short_output), '\n'.join(long_output)


def agent_issues(issues, params):
    for type_ in ('warning', 'exception'):
        count = len(issues.get(type_, ()))
        yield check_levels(
            count,
            None,
            params.get('%s_levels' % type_),
            human_readable_func=lambda i: "%d" % i,
            infoname="%ss" % type_.title(),
        )

    for i in sorted(issues.get('exception', []), key=lambda x: x["msg"]):
        yield 0, "\nIssue in %s: Exception: %s (!!)" % (i["issued_by"], i["msg"])
    for i in sorted(issues.get('warning', []), key=lambda x: x["msg"]):
        yield 0, "\nIssue in %s: Warning: %s (!)" % (i["issued_by"], i["msg"])
    for i in sorted(issues.get('info', []), key=lambda x: x["msg"]):
        yield 0, "\nIssue in %s: Info: %s" % (i["issued_by"], i["msg"])


def check_azure_agent_info(_no_item, params, parsed):

    for subresult in agent_bailouts(parsed.get('agent-bailout', [])):
        yield subresult

    reads = parsed.get('remaining-reads')
    if reads is not None:
        yield remaining_api_reads(reads, params)

    groups = parsed.get('monitored-groups')
    if groups:
        yield 0, "Monitored groups: %s" % ', '.join(groups)

    resources = parsed.get('monitored-resources', [])
    resource_infos = resource_pinning(resources, params)
    if resource_infos[0]:
        yield 1, resource_infos[0]

    for subresult in agent_issues(parsed.get('issues', {}), params):
        yield subresult

    if resource_infos[1]:
        yield 0, "\n%s" % resource_infos[1]


check_info['azure_agent_info'] = {
    'parse_function': parse_azure_agent_info,
    'inventory_function': discovery_azure_agent_info,
    'check_function': check_azure_agent_info,
    'service_description': "Azure Agent Info",
    'default_levels_variable': "azure_agent_info_levels",
    'has_perfdata': True,
    'group': "azure_agent_info",
    'includes': ['azure.include'],
}
