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

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

DESCRIPTION = """Counts the number of active DAHDI, SIP, and other channels on a Starface PBX via its
monitoring module on port 6556. Alerts when channel usage exceeds the configured
percentage threshold. Supports both IPv4 and IPv6. Data is cached to avoid overloading
the PBX when multiple checks run in parallel."""

DEFAULT_CACHE_EXPIRE = 1  # minutes
DEFAULT_CRIT = 90  # %
DEFAULT_HOSTNAME = 'localhost'
DEFAULT_IPV6 = False
DEFAULT_PORT = 6556
DEFAULT_TIMEOUT = 8  # seconds
DEFAULT_WARN = 80  # %


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(
        '--critical',
        help=lib.args.help('--critical') + ' Default: >= %(default)s',
        dest='CRIT',
        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(
        '--warning',
        help=lib.args.help('--warning') + ' Default: >= %(default)s',
        dest='WARN',
        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_channels>>>', '<<<').splitlines()

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

    max_calls = 0
    cnt_dahdi, cnt_sip, cnt_other = 0, 0, 0

    # build the message
    for row in result:
        if not row:
            continue

        if row.endswith('max calls'):
            max_calls = row.replace(' max calls', '')
        else:
            if row.startswith('DAHDI/'):
                cnt_dahdi += 1
            elif row.startswith('SIP/'):
                cnt_sip += 1
            else:
                cnt_other += 1

    if int(max_calls) != 0:
        # there is a maximum call limit, warn on overusage
        used_percent = round(
            float(cnt_dahdi + cnt_sip + cnt_other) / float(max_calls) * 100, 1
        )
        state = lib.base.get_state(used_percent, args.WARN, args.CRIT)
        msg += (
            f'{used_percent}% call usage '
            f'({cnt_dahdi + cnt_sip + cnt_other}/{max_calls})'
            f'{lib.base.state2str(state, prefix=" ")}. '
        )
        perfdata += lib.base.get_perfdata(
            'used_percent',
            used_percent,
            uom='%',
            warn=args.WARN,
            crit=args.CRIT,
            _min=0,
            _max=100,
        )
        perfdata += lib.base.get_perfdata(
            'max_calls',
            max_calls,
            _min=0,
        )

    if cnt_dahdi or cnt_sip or cnt_other:
        msg += 'Current channels: '
        msg += f'{cnt_dahdi}x DAHDI, ' if cnt_dahdi else ''
        msg += f'{cnt_sip}x SIP, ' if cnt_sip else ''
        msg += f'{cnt_other}x other, ' if cnt_other else ''
    else:
        msg += 'No channels active.  '
    perfdata += lib.base.get_perfdata(
        'channel_dahdi',
        cnt_dahdi,
        _min=0,
        _max=max_calls if max_calls else None,
    )
    perfdata += lib.base.get_perfdata(
        'channel_sip',
        cnt_sip,
        _min=0,
        _max=max_calls if max_calls else None,
    )
    perfdata += lib.base.get_perfdata(
        'channel_other',
        cnt_other,
        _min=0,
        _max=max_calls if max_calls else None,
    )

    # over and out
    lib.base.oao(msg[:-2], state, perfdata, always_ok=args.ALWAYS_OK)


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