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

import lib.args
import lib.base
import lib.cache
import lib.human
import lib.lftest
import lib.net
import lib.time
import lib.txt
from lib.globals import STATE_OK, STATE_UNKNOWN, STATE_WARN

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

DESCRIPTION = """Checks the status of the newest backups of a Starface PBX via its monitoring module
on port 6556. Reports backup age, size, and success state. Supports both IPv4 and
IPv6. Data is cached to avoid overloading the PBX when multiple checks run in
parallel.
Alerts when the latest backup has failed or is too old."""

DEFAULT_CACHE_EXPIRE = 1  # minutes
DEFAULT_CRIT = None  # hours
DEFAULT_HOSTNAME = 'localhost'
DEFAULT_IPV6 = False
DEFAULT_PORT = 6556
DEFAULT_TIMEOUT = 8  # seconds
DEFAULT_WARN = 24  # hours


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(
        '--always-ok',
        help=lib.args.help('--always-ok'),
        dest='ALWAYS_OK',
        action='store_true',
        default=False,
    )

    parser.add_argument(
        '--cache-expire',
        help=lib.args.help('--cache-expire') + ' Default: %(default)s',
        dest='CACHE_EXPIRE',
        type=int,
        default=DEFAULT_CACHE_EXPIRE,
    )

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

    parser.add_argument(
        '-H',
        '--hostname',
        help='Starface PBX hostname or IP address. Default: %(default)s',
        dest='HOSTNAME',
        default=DEFAULT_HOSTNAME,
    )

    parser.add_argument(
        '--port',
        help='Starface PBX monitoring port. Default: %(default)s',
        dest='PORT',
        type=int,
        default=DEFAULT_PORT,
    )

    parser.add_argument(
        '--test',
        help=lib.args.help('--test'),
        dest='TEST',
        type=lib.args.csv,
    )

    parser.add_argument(
        '--timeout',
        help=lib.args.help('--timeout') + ' Default: %(default)s (seconds)',
        dest='TIMEOUT',
        type=int,
        default=DEFAULT_TIMEOUT,
    )

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

    parser.add_argument(
        '--ipv6',
        help=lib.args.help('--ipv6'),
        dest='USE_IPV6',
        action='store_true',
        default=DEFAULT_IPV6,
    )

    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)

    # fetch data
    if args.TEST is None:
        # fetch data from PBX, but first from cache
        data = lib.cache.get(
            args.HOSTNAME, filename='linuxfabrik-monitoring-plugins-starface.db'
        )
        if not data:
            data = lib.base.coe(
                lib.net.fetch(args.HOSTNAME, args.PORT, timeout=args.TIMEOUT)
            )
            lib.cache.set(
                args.HOSTNAME,
                data,
                lib.time.now() + args.CACHE_EXPIRE * 60,
                filename='linuxfabrik-monitoring-plugins-starface.db',
            )
    else:
        # do not call the command, put in test data
        stdout, _stderr, retc = lib.lftest.test(args.TEST)
        data = stdout

    if not data:
        lib.base.cu(f'Got no valuable response from {args.HOSTNAME}:{args.PORT}.')

    # extract data
    result = lib.txt.extract_str(data, '<<<starface_backups>>>', '<<<').splitlines()

    # init some vars
    msg = ''
    state = STATE_OK
    perfdata = ''
    old_timestamp = 0
    target = ''
    timestamp = 0
    retc = ''

    # analyze data
    for row in result:
        if not row:
            continue
        # [HDD] 1623885428016 de.vertico.backup.successful'
        # [SMB]192.168.1.6:/Backup_Starface 1556666025340 de.vertico.backup.mountfail
        row = row.split(' ')
        row[1] = int(row[1]) / 1000
        if row[1] > old_timestamp:
            # we found a more recent backup
            target = row[0]
            timestamp = row[1]
            retc = row[2]
            old_timestamp = row[1]

    # get the states
    now = lib.time.now()
    delta = now - timestamp
    delta_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(delta_state, state)

    backup_state = STATE_WARN if not retc.endswith('successful') else STATE_OK
    state = lib.base.get_worst(backup_state, state)

    # build the message
    msg += (
        f'Last Backup to {target} '
        f'at {lib.time.epoch2iso(timestamp)} '
        f'({lib.human.seconds2human(delta)} ago'
        f'{lib.base.state2str(delta_state, prefix=" ")}) '
        f'{"was successful" if backup_state == STATE_OK else f"failed ({retc})"}'
        f'{lib.base.state2str(backup_state, prefix=" ")}.'
    )

    # over and out
    lib.base.oao(msg, state, perfdata, always_ok=args.ALWAYS_OK)


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