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

# Agent output examples:# #   .

# Pre-V15 agent output:

# <<<suseconnect:sep(58)>>>
# identifier: SLES
# version: 12.1
# arch: x86_64
# status: Registered
# regcode: banana001
# starts_at: 2015-12-01 00:00:00 UTC
# expires_at: 2019-12-31 00:00:00 UTC
# subscription_status: ACTIVE
# _type: full

# V15+ agent output

# <<<suseconnect:sep(58)>>>
# Installed Products:

#   advanced Systems Management Module
#   (sle-module-adv-systems-management/12/x86_64)

#   Registered

#   sUSE Linux Enterprise Server for SAP Applications 12 SP5
#   (SLES_SAP/12.5/x86_64)

#   Registered

#     Subscription:

#     Regcode: banana005
#     Starts at: 2018-07-01 00:00:00 UTC
#     Expires at: 2021-06-30 00:00:00 UTC
#     Status: ACTIVE
#     Type: full

#   SUSE Package Hub 12
#   (PackageHub/12.5/x86_64)

#   Registered
##.

factory_settings["sles_license_default_levels"] = {
    "status": "Registered",
    "subscription_status": "ACTIVE",
    "days_left": (14, 7),
}


def _join_line(line):
    return ":".join(line).strip()


def _parse_header(header):

    return dict(zip(["identifier", "version", "architecture"], header[1:-1].split("/")))


def _parse_suseconnect_v15(info):
    map_keys = {
        "Regcode": "registration_code",
        "Starts at": "starts_at",
        "Expires at": "expires_at",
        "Status": "subscription_status",
        "Type": "subscription_type",
    }
    parsed = {}
    specs = {}
    iter_info = iter(info)

    for line in iter_info:
        if line[0].startswith("(") and line[0].endswith(")"):
            parsed_header = _parse_header(line[0])
            specs = parsed.setdefault(parsed_header["identifier"], parsed_header)
            specs["registration_status"] = next(iter_info)[0]
            continue
        if len(line) > 1:
            key, value = line[0], _join_line(line[1:])
            if key in map_keys:
                specs[map_keys[key]] = value

    return parsed


def _parse_suseconnect_pre_v15(info):
    map_keys = {
        "identifier": "identifier",
        "version": "version",
        "arch": "architecture",
        "status": "registration_status",
        "type": "subscription_type",
        "starts_at": "starts_at",
        "expires_at": "expires_at",
        "subscription_status": "subscription_status",
        "regcode": "registration_code",
    }

    parsed = {
        map_keys[line[0]]: _join_line(line[1:])
        for line in info
        if line[0] in map_keys and len(line) > 1
    }

    # Normalise to get data in the format  {identifier: {specs}}
    return {parsed["identifier"]: parsed}


def parse_suseconnect(info):

    try:
        first = info[0][0]
    except IndexError:
        return {}

    if first == "identifier":
        return _parse_suseconnect_pre_v15(info)

    return _parse_suseconnect_v15(info)


def inventory_suseconnect(parsed):
    if any("SLES" in identifier for identifier in parsed):
        return [(None, {})]


def check_suseconnect(_no_item, params, parsed):
    # we assume here that the parsed data contains all required keys

    specs = None

    for identifier, these_specs in parsed.items():
        if "SLES" in identifier:
            specs = these_specs
            break

    if specs is None:
        return

    state, infotext = 0, "Status: %(registration_status)s" % specs
    if params["status"] != "Ignore" and params["status"] != specs["registration_status"]:
        state = 2
    yield state, infotext

    state, infotext = 0, "Subscription: %(subscription_status)s" % specs
    if (
        params["subscription_status"] != "Ignore"
        and params["subscription_status"] != specs["subscription_status"]
    ):
        state = 2
    yield state, infotext

    yield 0, (
        "Subscription type: %(subscription_type)s, Registration code: %(registration_code)s, "
        "Starts at: %(starts_at)s, Expires at: %(expires_at)s"
    ) % specs

    expiration_date = time.strptime(specs["expires_at"], "%Y-%m-%d %H:%M:%S %Z")
    expiration_time = time.mktime(expiration_date) - time.time()

    if expiration_time > 0:
        warn, crit = params["days_left"]
        days2seconds = 24 * 60 * 60

        if expiration_time <= crit * days2seconds:
            state = 2
        elif expiration_time <= warn * days2seconds:
            state = 1
        else:
            state = 0

        infotext = "Expires in: %s" % get_age_human_readable(expiration_time)
        if state:
            infotext += " (warn/crit at %d/%d days)" % (warn, crit)

        yield state, infotext
    else:
        yield 2, "Expired since: %s" % get_age_human_readable(-1.0 * expiration_time)


check_info["suseconnect"] = {
    "service_description": "SLES license",
    "parse_function": parse_suseconnect,
    "inventory_function": inventory_suseconnect,
    "check_function": check_suseconnect,
    "group": "sles_license",
    "default_levels_variable": "sles_license_default_levels",
}
