#!/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 urllib.parse

import lib.args
import lib.base
import lib.human
import lib.url
from lib.globals import STATE_OK, STATE_UNKNOWN

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

DESCRIPTION = """Summarizes traffic statistics for all IPv4 and IPv6 firewall policies on FortiGate
appliances running FortiOS via the REST API. Reports byte and packet counters, active
sessions, and hit counts per policy."""

DEFAULT_INSECURE = False
DEFAULT_NO_PROXY = False
DEFAULT_TIMEOUT = 3


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(
        '-H',
        '--hostname',
        help='FortiOS-based appliance address, optionally including port. '
        'Example: `--hostname 192.168.1.1:443`.',
        dest='HOSTNAME',
        required=True,
    )

    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(
        '--password',
        help='FortiOS REST API single-use access token.',
        dest='PASSWORD',
        required=True,
    )

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

    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
    url = (
        f'https://{args.HOSTNAME}'
        f'/api/v2/monitor/firewall/policy/select/'
        f'?access_token={urllib.parse.quote(args.PASSWORD)}'
    )
    success4, result4 = lib.url.fetch_json(
        url,
        insecure=args.INSECURE,
        no_proxy=args.NO_PROXY,
        timeout=args.TIMEOUT,
    )

    url = (
        f'https://{args.HOSTNAME}'
        f'/api/v2/monitor/firewall/policy6/select/'
        f'?access_token={urllib.parse.quote(args.PASSWORD)}'
    )
    success6, result6 = lib.url.fetch_json(
        url,
        insecure=args.INSECURE,
        no_proxy=args.NO_PROXY,
        timeout=args.TIMEOUT,
    )

    # if both requests fail then exit
    if success4 is False and success6 is False:
        lib.base.oao(
            (
                f'Both requests for IPv4 and IPv6'
                f' policies failed.\n'
                f'* IPv4: "{result4}"\n'
                f'* IPv6: "{result6}"'
            ),
            STATE_UNKNOWN,
        )

    # create an empty dict if IPv4 request fails or is empty
    if not success4:
        result4 = {
            'results': [],
        }

    # create an empty dict if IPv6 request fails or is empty
    if not success6:
        result6 = {
            'results': [],
        }

    # count and compute the total
    policy_count = len(result4['results']) + len(result6['results'])
    total = lib.base.sum_dict(
        lib.base.sum_lod(result4['results']),
        lib.base.sum_lod(result6['results']),
    )
    # {
    #     u'active_sessions': 2
    #     u'asic_bytes': 0
    #     u'asic_packets': 0
    #     u'bytes': 69229872
    #     u'first_used': 3164897177
    #     u'hit_count': 828
    #     u'last_used': 3165744600
    #     u'nturbo_bytes': 0
    #     u'nturbo_packets': 0
    #     u'packets': 1279872
    #     u'policyid': 11
    #     u'session_count': 2
    #     u'session_first_used': 1588147935
    #     u'session_last_used': 1588169820
    #     u'software_bytes': 69229872
    #     u'software_packets': 1279872
    # }

    sessions = total.get('session_count', 0)
    active = total.get('active_sessions', 0)
    hits = total.get('hit_count', 0)
    bytes_hr = lib.human.bytes2human(total.get('bytes', 0))
    sw_hr = lib.human.bytes2human(total.get('software_bytes', 0))
    asic_hr = lib.human.bytes2human(total.get('asic_bytes', 0))
    nturbo_hr = lib.human.bytes2human(total.get('nturbo_bytes', 0))
    msg = (
        f'{policy_count} policies,'
        f' {sessions} sessions ({active} active),'
        f' {hits} hits,'
        f' {bytes_hr} bytes'
        f' ({sw_hr} software,'
        f' {asic_hr} asic, {nturbo_hr} nturbo)'
    )
    state = STATE_OK

    perfdata = ''
    perfdata += lib.base.get_perfdata(
        'total_session_count',
        total.get('session_count', 0),
        _min=0,
    )
    perfdata += lib.base.get_perfdata(
        'total_active_sessions',
        total.get('active_sessions', 0),
        _min=0,
    )
    perfdata += lib.base.get_perfdata(
        'total_hit_count',
        total.get('hit_count', 0),
        _min=0,
    )
    perfdata += lib.base.get_perfdata(
        'total_bytes',
        total.get('bytes', 0),
        uom='B',
        _min=0,
    )
    perfdata += lib.base.get_perfdata(
        'total_software_bytes',
        total.get('software_bytes', 0),
        uom='B',
        _min=0,
    )
    perfdata += lib.base.get_perfdata(
        'total_asic_bytes',
        total.get('asic_bytes', 0),
        uom='B',
        _min=0,
    )
    perfdata += lib.base.get_perfdata(
        'total_nturbo_bytes',
        total.get('nturbo_bytes', 0),
        uom='B',
        _min=0,
    )

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


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