#!/usr/bin/env python3
# -*- coding: utf-8; py-indent-offset: 4 -*-
#
# Author:  Linuxfabrik GmbH, Zurich, Switzerland
# Contact: info (at) linuxfabrik (dot) ch
#          https://www.linuxfabrik.ch/
# License: The Unlicense, see LICENSE file.

# https://github.com/Linuxfabrik/monitoring-plugins/blob/main/CONTRIBUTING.md

"""See the check's README for more details."""

import argparse
import os
import sys

import lib.base
import lib.human
import lib.time
from lib.globals import STATE_OK, STATE_UNKNOWN, STATE_WARN

__author__ = 'Linuxfabrik GmbH, Zurich/Switzerland'
__version__ = '2026041001'

DESCRIPTION = """Checks the status of the last borgbackup run by parsing the borg logfile. Alerts on
non-zero return codes from the create or prune steps, and warns if the last successful
backup is older than a configurable threshold (default: 24 hours). Also detects
active borg mounts in /proc/mounts.
Requires root or sudo."""

DEFAULT_CRIT = None
DEFAULT_WARN = 24


def parse_args():
    """Parse command line arguments using argparse."""
    parser = argparse.ArgumentParser(description=DESCRIPTION)

    parser.add_argument(
        '-V',
        '--version',
        action='version',
        version=f'%(prog)s: v{__version__} by {__author__}',
    )

    parser.add_argument(
        '-c',
        '--critical',
        help='CRIT threshold for the time since the last backup started, in hours. '
        'Default: %(default)s',
        dest='CRIT',
        type=int,
        default=DEFAULT_CRIT,
    )

    parser.add_argument(
        '-w',
        '--warning',
        help='WARN threshold for the time since the last backup started, in hours. '
        'Default: %(default)s',
        dest='WARN',
        type=int,
        default=DEFAULT_WARN,
    )

    args, _ = parser.parse_known_args()
    return args


def main():
    """The main function. This is where the magic happens."""

    # parse the command line
    try:
        args = parse_args()
    except SystemExit:
        sys.exit(STATE_UNKNOWN)

    perfdata = ''
    path = '/var/log/borg/borg.log'

    # content of the log file:
    # start: 2020-04-08 23:00:01
    # prune_retc: 0
    # create_retc: 0
    # end: 2020-04-08 23:00:13

    if os.path.exists(path) <= 0 and not os.path.isfile(path):
        lib.base.cu(f'Logfile {path} not found or empty.')

    mount_lines = ''
    with open('/proc/mounts', encoding='utf-8') as mountlist:
        for line in mountlist:
            if 'borgfs' in line:
                mount_lines += '* ' + line

    if mount_lines:
        lib.base.oao(f'There are active borg mounts.\n\n{mount_lines}', STATE_WARN)

    starttime = None
    endtime = None
    create_retc = None
    prune_retc = None
    with open(path, 'r', encoding='utf-8') as logfile:
        for line in logfile:
            if line.startswith('start'):
                starttime = line.split(': ')[1].strip()

            if line.startswith('end'):
                endtime = line.split(': ')[1].strip()

            if line.startswith('create_retc'):
                create_retc = int(line.split(': ')[1])

            if line.startswith('prune_retc'):
                prune_retc = int(line.split(': ')[1])

    if any(v is None for v in (starttime, endtime, create_retc, prune_retc)):
        lib.base.cu('Could not find all expected values in the logfile.')

    # We ignore retc 1, as it means operation reached its normal end,
    # but there were warnings from borg.
    state = STATE_OK
    create_retc_state = lib.base.get_state(create_retc, 2, None, _operator='ge')
    state = lib.base.get_worst(state, create_retc_state)
    perfdata += lib.base.get_perfdata(
        'create_retc',
        create_retc,
        warn=1,
        crit=2,
        _min=0,
    )

    prune_retc_state = lib.base.get_state(prune_retc, 2, None, _operator='ge')
    state = lib.base.get_worst(state, prune_retc_state)
    perfdata += lib.base.get_perfdata(
        'prune_retc',
        prune_retc,
        warn=1,
        crit=2,
        _min=0,
    )

    now = lib.time.now(as_type='iso')
    delta = lib.time.timestrdiff(now, starttime)

    last_starttime_state = lib.base.get_state(
        delta,
        args.WARN * 60 * 60 if args.WARN is not None else None,
        args.CRIT * 60 * 60 if args.CRIT is not None else None,
    )
    state = lib.base.get_worst(state, last_starttime_state)

    duration = lib.time.timestrdiff(endtime, starttime)  # in seconds
    perfdata += lib.base.get_perfdata(
        'duration',
        duration,
        uom='s',
        _min=0,
    )

    # build the message
    msg = (
        f'Last Backup started {starttime}, ended {endtime},'
        f' took {lib.human.seconds2human(duration)}.\n'
    )
    if state != STATE_OK:
        if last_starttime_state != STATE_OK:
            msg = 'Last backup is too long ago. ' + msg
        else:
            msg = 'One or more errors. ' + msg
    msg += f'* Create retc: {create_retc}, State: {lib.base.state2str(create_retc_state)}\n'
    msg += f'* Prune retc: {prune_retc}, State: {lib.base.state2str(prune_retc_state)}'

    # over and out
    lib.base.oao(msg, state, perfdata)


if __name__ == '__main__':
    try:
        main()
    except Exception:
        lib.base.cu()
