#!/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.lftest
import lib.time
import lib.url
from lib.globals import STATE_CRIT, STATE_OK, STATE_UNKNOWN, STATE_WARN

try:
    from bs4 import BeautifulSoup
except ImportError:
    print('Python module "BeautifulSoup4" is not installed.')
    sys.exit(STATE_UNKNOWN)


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

DESCRIPTION = """Monitors a StatusIQ (by Site24x7) status page via its RSS feed. Returns OK for
operational or informational messages, WARN for maintenance windows, and CRIT for
service disruptions or degraded performance."""

DEFAULT_INSECURE = False
DEFAULT_NO_PROXY = False
DEFAULT_TIMEOUT = 8
DEFAULT_URL = 'https://status.trustid.ch'


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(
        '--insecure',
        help=lib.args.help('--insecure'),
        dest='INSECURE',
        action='store_true',
        default=DEFAULT_INSECURE,
    )

    parser.add_argument(
        '--no-proxy',
        help=lib.args.help('--no-proxy'),
        dest='NO_PROXY',
        action='store_true',
        default=DEFAULT_NO_PROXY,
    )

    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(
        '--url',
        help='StatusIQ status page URL. Default: %(default)s',
        dest='URL',
        default=DEFAULT_URL,
    )

    args, _ = parser.parse_known_args()
    return args


def statusiqstate2state(siqs):
    """Convert StatusIQs incident level to the Nagios world."""
    siqs = siqs.lower()
    if siqs.endswith('major outage'):
        return STATE_CRIT
    if (
        siqs.endswith('partial outage')
        or siqs.endswith('degraded performance')
        or siqs.endswith('under maintenance')
    ):
        return STATE_WARN
    return STATE_OK


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:
        xml = lib.base.coe(
            lib.url.fetch(
                args.URL + ('rss' if args.URL.endswith('/') else '/rss'),
                insecure=args.INSECURE,
                no_proxy=args.NO_PROXY,
                timeout=args.TIMEOUT,
            )
        )
    else:
        # do not call the command, put in test data
        xml, _, _ = lib.lftest.test(args.TEST)

    try:
        soup = BeautifulSoup(xml, 'xml')
    except Exception as e:
        print(f'{e}')
        sys.exit(STATE_UNKNOWN)

    # init some vars
    msg = ''
    state = STATE_OK
    perfdata = ''
    table_data = []
    cnt_warn, cnt_crit = 0, 0

    # analyze data
    for item in soup.find_all('item'):
        title = item.find('title')
        if not title:
            continue
        title = title.text.strip().replace(' - Operational', '')
        pubdate = item.find('pubDate')
        pubdate = pubdate.text.strip()

        item_state = statusiqstate2state(title)
        if item_state == STATE_WARN:
            cnt_warn += 1
        if item_state == STATE_CRIT:
            cnt_crit += 1
        state = lib.base.get_worst(state, item_state)
        table_data.append(
            {
                'title': title,
                'pubdate': lib.time.timestr2datetime(
                    pubdate, pattern='%a, %d %b %Y %H:%M:%S %z'
                ),  # Thu, 06 Mar 2025 14:44:59 +0100  pylint: disable=C0301
                'state_hr': lib.base.state2str(item_state, empty_ok=False),
            }
        )
    if not table_data:
        state = STATE_UNKNOWN

    # build the message
    msg += {
        STATE_CRIT: 'Major incidents',
        STATE_WARN: 'Partial outages, degraded performance or under maintenance',
        STATE_UNKNOWN: 'RSS feed disabled for this page?',
    }.get(state, 'Everything is ok')
    msg += f' @ {args.URL}'
    if table_data:
        msg += '\n\n' + lib.base.get_table(
            table_data,
            ['title', 'pubdate', 'state_hr'],
            header=['Component Name', 'Published', 'State'],
        )
    perfdata += lib.base.get_perfdata(
        'cnt_warn',
        cnt_warn,
        uom=None,
        warn=None,
        crit=None,
        _min=0,
        _max=None,
    )
    perfdata += lib.base.get_perfdata(
        'cnt_crit',
        cnt_crit,
        uom=None,
        warn=None,
        crit=None,
        _min=0,
        _max=None,
    )

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


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