#!/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 os
from collections import defaultdict
from typing import Counter, Dict, Literal, Mapping, NamedTuple

# <<<inotify:sep(9)>>>
# configured folder    /tmp/noti
# configured file  /tmp/noti/test
# configured file  /tmp/noti/other
# 1465470055  modify  /tmp/noti/test  5   1465470055
# 1465470055  open    /tmp/noti/test  5   1465470055
# 1465470055  modify  /tmp/noti/test  5   1465470055
# 1465470056  modify  /tmp/noti/test  5   1465470056
# 1465470056  open    /tmp/noti/test  5   1465470056

_Mode2Time = Mapping[str, int]


class Section(NamedTuple):
    warnings: Counter[str]
    configured: Mapping[str, Literal["file", "folder"]]
    stats: Mapping[str, _Mode2Time]


def parse_inotify(info) -> Section:

    warnings: Counter[str] = Counter()
    configured: Dict[str, Literal["file", "folder"]] = {}
    stats: Dict[str, Dict[str, int]] = defaultdict(dict)

    for line in info:
        if line[0].startswith("warning"):
            warnings[line[1]] += 1
            continue
        if line[0].startswith("configured"):
            configured[line[2]] = "file" if line[1] == "file" else "folder"
            continue

        time, mode, filepath, *_unused = line

        stats[filepath][mode] = int(time)
        stats[os.path.dirname(filepath)][mode] = int(time)

    return Section(warnings, configured, stats)


def discover_inotify(section: Section):
    yield from ((f"{type_.title()} {path}", {}) for path, type_ in section.configured.items())


def check_inotify(item, params, section: Section):
    last_status = get_item_state("last_operations", default={})
    now = time.time()
    yield from _check_inotify(item, params, section, last_status, now)
    set_item_state("last_operations", last_status)


def _check_inotify(item, params, section: Section, last_status, now):
    type_, path = item.split(" ", 1)
    if section.configured.get(path) != type_.lower():
        return

    last_status.update(section.stats.get(path, {}))

    levels = {mode: (warn, crit) for mode, warn, crit in params.get("age_last_operation", [])}

    for mode, timestamp in sorted(last_status.items()):
        yield check_levels(
            now - timestamp,
            None,
            levels.get(mode),
            human_readable_func=get_age_human_readable,
            infoname=f"Time since last {mode}",
        )

    for mode in set(levels) - set(last_status):
        yield 3, f"Time since last {mode}: unknown"

    if section.warnings:
        yield 1, "Incomplete data!"
        yield from ((1, f"{count} warning(s): {msg}") for msg, count in section.warnings.items())

    if not last_status:
        yield 0, "No data available yet"


check_info["inotify"] = {
    "parse_function": parse_inotify,
    "inventory_function": discover_inotify,
    "check_function": check_inotify,
    "service_description": "INotify %s",
    "group": "inotify",
}
