#!/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  # pylint: disable=C0413
import sys  # pylint: disable=C0413
from pathlib import Path  # pylint: disable=C0413

import lib.base  # pylint: disable=C0413

try:
    import lib.smb  # pylint: disable=C0413
    HAVE_SMB = True
except ModuleNotFoundError as e:
    HAVE_SMB = False
    missing_lib = e.name
import lib.time  # pylint: disable=C0413
import lib.txt  # pylint: disable=C0413
from lib.globals import (STATE_CRIT, STATE_OK,  # pylint: disable=C0413
                          STATE_UNKNOWN, STATE_WARN)

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

DESCRIPTION = 'Checks the number of matching files.'

DEFAULT_PATTERN = '*'
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='%(prog)s: v{} by {}'.format(__version__, __author__)
    )

    parser.add_argument(
        '--always-ok',
        help='Always returns OK.',
        dest='ALWAYS_OK',
        action='store_true',
        default=False,
    )

    parser.add_argument(
        '-c', '--critical',
        help='Set the critical number of files. Supports ranges.',
        dest='CRIT',
    )

    parser.add_argument(
        '--filename',
        help='File (or directory) name to check. Supports glob in accordance with https://docs.python.org/3/library/pathlib.html#pathlib.Path.glob. Beware of using recursive globs. This is mutually exclusive with -u / --url.',
        dest='FILENAME',
    )

    parser.add_argument(
        '--only-dirs',
        help='Only consider directories.',
        dest='ONLY_DIRS',
        action='store_true',
        default=False,
    )

    parser.add_argument(
        '--only-files',
        help='Only consider files.',
        dest='ONLY_FILES',
        action='store_true',
        default=False,
    )

    parser.add_argument(
        '--password',
        help='SMB Password.',
        dest='PASSWORD',
    )

    parser.add_argument(
        '--pattern',
        help="The search string to match against the names of SMB directories or files. This pattern can use '*' as a wildcard for multiple chars and '?' as a wildcard for a single char. Does not support regex patterns. Default: %(default)s.",
        dest='PATTERN',
        default=DEFAULT_PATTERN,
    )

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

    parser.add_argument(
        '--timerange',
        help='Set the timerange (seconds) in which the files should be considered. Supports ranges.',
        dest='TIMERANGE',
    )

    parser.add_argument(
        '-u', '--url',
        help='Set the url of the file (or directory) to check, starting with "smb://". This is mutually exclusive with --filename.',
        dest='URL',
        type=str,
    )

    parser.add_argument(
        '--username',
        help='SMB Username.',
        dest='USERNAME',
    )

    parser.add_argument(
        '-w', '--warning',
        help='Set the warning number of files. Supports ranges.',
        dest='WARN',
    )

    return parser.parse_args()


def main():
    """The main function. Hier spielt die Musik.
    """

    # parse the command line, exit with UNKNOWN if it fails
    try:
        args = parse_args()
    except SystemExit:
        sys.exit(STATE_UNKNOWN)

    # fetch data
    if args.FILENAME and args.URL:
        lib.base.cu('The --filename and -u / --url parameter are mutually exclusive. Please only use one.')

    now = lib.time.now()
    msg = ''
    file_count = 0

    if args.FILENAME:
        path = Path(args.FILENAME)
        for item in sorted(Path(path.anchor).glob(str(path.relative_to(path.anchor)))):
            if item.is_file() and args.ONLY_DIRS:
                continue
            if item.is_dir() and args.ONLY_FILES:
                continue
            if args.TIMERANGE:
                age = (now - item.stat().st_mtime)
                if not lib.base.coe(lib.base.match_range(age, args.TIMERANGE)):
                    continue

            file_count += 1

    if args.URL:
        split_url = args.URL.split('://')
        if len(split_url) != 2:
            lib.base.cu('Could not parse the protocol of the url "{}".'.format(args.URL))
        proto, url = split_url

        if proto == 'smb':
            if not HAVE_SMB:
                lib.base.cu('Python module "{}" is not installed.'.format(missing_lib))
            for item in lib.base.coe(lib.smb.glob(url, args.USERNAME, args.PASSWORD, args.TIMEOUT, pattern=args.PATTERN)):
                if item.is_file() and args.ONLY_DIRS:
                    continue
                if item.is_dir() and args.ONLY_FILES:
                    continue
                if args.TIMERANGE:
                    age = (now - item.stat().st_mtime)
                    if not lib.base.coe(lib.base.match_range(age, args.TIMERANGE)):
                        continue

                file_count += 1
        else:
            lib.base.cu('The protocol "{}" is not supported.'.format(proto))

    if not lib.base.coe(lib.base.match_range(file_count, args.CRIT)):
        state = STATE_CRIT
    elif not lib.base.coe(lib.base.match_range(file_count, args.WARN)):
        state = STATE_WARN
    else:
        state = STATE_OK

    msg = 'Found {} matching {} (thresholds {}/{})'.format(
        file_count,
        lib.txt.pluralize('file', file_count),
        args.WARN,
        args.CRIT,
    )
    perfdata = lib.base.get_perfdata('file_count', file_count, None, args.WARN, args.CRIT, 0, None)

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


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