#!/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.db_sqlite
import lib.human
import lib.jitsi
import lib.lftest
import lib.txt
import lib.version
from lib.globals import STATE_OK, STATE_UNKNOWN

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

DESCRIPTION = """Monitors Jitsi Videobridge performance via the COLIBRI REST API. Reports conference
count, participant count, video channels, bitrates, packet rates, and other bridge
metrics.
The figures are reported for trending and never alert on their own."""

DEFAULT_INSECURE = False
DEFAULT_NO_PROXY = False
DEFAULT_TIMEOUT = 3
DEFAULT_URL = 'http://localhost:8080'


def parse_args():
    """Parse command line arguments using argparse."""
    parser = argparse.ArgumentParser(
        description=DESCRIPTION,
        epilog=lib.args.epilog(__file__),
        formatter_class=lib.args.HelpFormatter,
    )

    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(
        '-c',
        '--critical',
        help=argparse.SUPPRESS,  # removed / deprecated parameter
        dest='CRIT',
        type=int,
    )

    parser.add_argument(
        '--insecure',
        help=lib.args.help('--insecure'),
        dest='INSECURE',
        action='store_true',
        default=DEFAULT_INSECURE,
    )

    parser.add_argument(
        '--no-perfdata',
        help=lib.args.help('--no-perfdata'),
        dest='NO_PERFDATA',
        action='store_true',
        default=False,
    )

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

    parser.add_argument(
        '-p',
        '--password',
        help='Jitsi API password.',
        dest='PASSWORD',
        default=None,
    )

    parser.add_argument(
        '--proxy',
        help=lib.args.help('--proxy'),
        dest='PROXY',
        default=None,
    )

    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='Jitsi API URL. Default: %(default)s',
        dest='URL',
        default=DEFAULT_URL,
    )

    parser.add_argument(
        '--username',
        help='Jitsi API username. Default: %(default)s',
        dest='USERNAME',
        default=None,
    )

    parser.add_argument(
        '-w',
        '--warning',
        help=argparse.SUPPRESS,  # removed / deprecated parameter
        dest='WARN',
        type=int,
    )

    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 the `/colibri/stats` endpoint.
        args.URL += '/colibri/stats'
        result = lib.base.coe(lib.jitsi.get_data(args))
    else:
        # do not call the command, put in test data
        stdout, _, _ = lib.lftest.test(args.TEST)
        import json

        result = {}
        result['response_json'] = json.loads(stdout)

    # init some vars
    msg = ''
    state = STATE_OK
    perfdata = ''

    # build the message
    perfdata += lib.base.get_perfdata(
        'bit_rate_download',
        result['response_json'].get('bit_rate_download', 0),
        _min=0,
    )
    perfdata += lib.base.get_perfdata(
        'bit_rate_upload',
        result['response_json'].get('bit_rate_upload', 0),
        _min=0,
    )
    perfdata += lib.base.get_perfdata(
        'conferences',
        result['response_json'].get('conferences', 0),
        _min=0,
    )
    perfdata += lib.base.get_perfdata(
        'current_timestamp',
        result['response_json'].get('current_timestamp', 0),
        _min=0,
    )
    perfdata += lib.base.get_perfdata(
        'endpoints',
        result['response_json'].get('endpoints', 0),
        _min=0,
    )
    perfdata += lib.base.get_perfdata(
        'endpoints_sending_audio',
        result['response_json'].get('endpoints_sending_audio', 0),
        _min=0,
    )
    perfdata += lib.base.get_perfdata(
        'endpoints_sending_video',
        result['response_json'].get('endpoints_sending_video', 0),
        _min=0,
    )
    perfdata += lib.base.get_perfdata(
        # a gauge (current count), not a cumulative counter, unlike the other
        # "total"/"failed" stats; keep it as a plain value (issue #320)
        'endpoints_with_spurious_remb',
        result['response_json'].get('endpoints_with_spurious_remb', 0),
        uom=None,
        _min=0,
    )
    perfdata += lib.base.get_perfdata(
        'graceful_shutdown',
        result['response_json'].get('graceful_shutdown', 0),
        _min=0,
    )
    perfdata += lib.base.get_perfdata(
        'inactive_conferences',
        result['response_json'].get('inactive_conferences', 0),
        _min=0,
    )
    perfdata += lib.base.get_perfdata(
        'inactive_endpoints',
        result['response_json'].get('inactive_endpoints', 0),
        _min=0,
    )
    perfdata += lib.base.get_perfdata(
        'largest_conference',
        result['response_json'].get('largest_conference', 0),
        _min=0,
    )
    perfdata += lib.base.get_perfdata(
        'local_active_endpoints',
        result['response_json'].get('local_active_endpoints', 0),
        _min=0,
    )
    perfdata += lib.base.get_perfdata(
        'local_endpoints',
        result['response_json'].get('local_endpoints', 0),
        _min=0,
    )
    perfdata += lib.base.get_perfdata(
        'num_eps_oversending',
        result['response_json'].get('num_eps_oversending', 0),
        _min=0,
    )
    perfdata += lib.base.get_perfdata(
        'octo_conferences',
        result['response_json'].get('octo_conferences', 0),
        _min=0,
    )
    perfdata += lib.base.get_perfdata(
        'octo_endpoints',
        result['response_json'].get('octo_endpoints', 0),
        _min=0,
    )
    perfdata += lib.base.get_perfdata(
        'octo_receive_bitrate',
        result['response_json'].get('octo_receive_bitrate', 0),
        _min=0,
    )
    perfdata += lib.base.get_perfdata(
        'octo_receive_packet_rate',
        result['response_json'].get('octo_receive_packet_rate', 0),
        _min=0,
    )
    perfdata += lib.base.get_perfdata(
        'octo_send_bitrate',
        result['response_json'].get('octo_send_bitrate', 0),
        _min=0,
    )
    perfdata += lib.base.get_perfdata(
        'octo_send_packet_rate',
        result['response_json'].get('octo_send_packet_rate', 0),
        _min=0,
    )
    perfdata += lib.base.get_perfdata(
        'p2p_conferences',
        result['response_json'].get('p2p_conferences', 0),
        _min=0,
    )
    perfdata += lib.base.get_perfdata(
        'packet_rate_download',
        result['response_json'].get('packet_rate_download', 0),
        _min=0,
    )
    perfdata += lib.base.get_perfdata(
        'packet_rate_upload',
        result['response_json'].get('packet_rate_upload', 0),
        _min=0,
    )
    perfdata += lib.base.get_perfdata(
        'receive_only_endpoints',
        result['response_json'].get('receive_only_endpoints', 0),
        _min=0,
    )
    perfdata += lib.base.get_perfdata(
        'region',
        result['response_json'].get('region', 0),
        _min=0,
    )
    perfdata += lib.base.get_perfdata(
        'rtt_aggregate',
        result['response_json'].get('rtt_aggregate', 0),
        uom='ms',
        _min=0,
    )
    perfdata += lib.base.get_perfdata(
        'stress_level',
        result['response_json'].get('stress_level', 0),
        _min=0,
    )
    perfdata += lib.base.get_perfdata(
        'threads',
        result['response_json'].get('threads', 0),
        _min=0,
    )
    perfdata += lib.base.get_perfdata(
        'version',
        lib.version.version2float(result['response_json']['version']),
        _min=0,
    )

    # Convert the cumulative /colibri/stats counters to per-second rates
    # against the previous run, stored in this plugin's own SQLite cache,
    # instead of emitting uom='c' continuous counters (issue #320).
    counter_fields = [
        'dtls_failed_endpoints',
        'preemptive_kfr_sent',
        'total_bytes_received',
        'total_bytes_received_octo',
        'total_bytes_sent',
        'total_bytes_sent_octo',
        'total_colibri_web_socket_messages_received',
        'total_colibri_web_socket_messages_sent',
        'total_conference_seconds',
        'total_conferences_completed',
        'total_conferences_created',
        'total_data_channel_messages_received',
        'total_data_channel_messages_sent',
        'total_dominant_speaker_changes',
        'total_failed_conferences',
        'total_ice_failed',
        'total_ice_succeeded',
        'total_ice_succeeded_relayed',
        'total_packets_dropped_octo',
        'total_packets_received',
        'total_packets_received_octo',
        'total_packets_sent',
        'total_packets_sent_octo',
        'total_partially_failed_conferences',
        'total_participants',
    ]
    rates = lib.db_sqlite.per_second_deltas(
        'linuxfabrik-monitoring-plugins-jitsi-videobridge-stats.db',
        'jitsi-videobridge-stats',
        {f: int(result['response_json'].get(f, 0)) for f in counter_fields},
    )
    if rates is not None:
        bytes_fields = {
            'total_bytes_received',
            'total_bytes_received_octo',
            'total_bytes_sent',
            'total_bytes_sent_octo',
        }
        for field in counter_fields:
            perfdata += lib.base.get_perfdata(
                f'{field}_per_second',
                round(rates[field], 2),
                uom='B' if field in bytes_fields else None,
                _min=0,
            )

    total_p = result['response_json']['total_participants']
    confs = result['response_json']['conferences']
    msg += (
        f'{total_p} total'
        f' {lib.txt.pluralize("participant", total_p)},'
        f' {confs}'
        f' {lib.txt.pluralize("conference", confs)}'
    )
    if result['response_json']['total_participants'] > 0:
        msg += f', Stress Level {result["response_json"]["stress_level"]}'
        msg += f', {result["response_json"]["threads"]} JVM threads'
        dl = lib.human.bps2human(
            result['response_json']['bit_rate_download'] * 1000,
        )
        ul = lib.human.bps2human(
            result['response_json']['bit_rate_upload'] * 1000,
        )
        msg += f', {dl} download, {ul} upload'

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


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