#!/usr/lib64/linuxfabrik-monitoring-plugins/venv/bin/python
# -*- 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 os
import re
import sys

import lib.args
import lib.base
import lib.disk
import lib.version
from lib.globals import STATE_UNKNOWN

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

DESCRIPTION = """Checks the installed Moodle version against the endoflife.date API and alerts if the
version is end-of-life or if newer major, minor, or patch releases are available. By
default, alerts 30 days before the official EOL date. The offset is configurable."""

DEFAULT_CHECK_MAJOR = False
DEFAULT_CHECK_MINOR = False
DEFAULT_CHECK_PATCH = False
DEFAULT_INSECURE = False
DEFAULT_NO_PROXY = False
DEFAULT_OFFSET_EOL = -30  # days
DEFAULT_PATH = '/var/www/html/moodle'
DEFAULT_TIMEOUT = 3
DEFAULT_UNREACHABLE_SEVERITY = 'ok'


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(
        '--check-major',
        help=lib.args.help('--check-major'),
        dest='CHECK_MAJOR',
        action='store_true',
        default=DEFAULT_CHECK_MAJOR,
    )

    parser.add_argument(
        '--check-minor',
        help=lib.args.help('--check-minor'),
        dest='CHECK_MINOR',
        action='store_true',
        default=DEFAULT_CHECK_MINOR,
    )

    parser.add_argument(
        '--check-patch',
        help=lib.args.help('--check-patch'),
        dest='CHECK_PATCH',
        action='store_true',
        default=DEFAULT_CHECK_PATCH,
    )

    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(
        '--offset-eol',
        help=lib.args.help('--offset-eol') + ' Default: %(default)s days',
        dest='OFFSET_EOL',
        type=int,
        default=DEFAULT_OFFSET_EOL,
    )

    parser.add_argument(
        '--path',
        help="Local path to your Moodle installation, typically within your Webserver's "
        'Document Root. Default: %(default)s',
        dest='PATH',
        default=DEFAULT_PATH,
    )

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

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

    parser.add_argument(
        '--unreachable-severity',
        help=lib.args.help('--unreachable-severity') + ' Default: %(default)s',
        dest='UNREACHABLE_SEVERITY',
        choices=['ok', 'warn', 'crit', 'unknown'],
        default=DEFAULT_UNREACHABLE_SEVERITY,
    )

    args, _ = parser.parse_known_args()
    return args


def get_installed_version(path):
    # Moodle up to 5.0 keeps version.php in the installation directory, Moodle 5.1+
    # in its `public/` subdirectory.
    for version_file in (
        os.path.join(path, 'version.php'),
        os.path.join(path, 'public', 'version.php'),
    ):
        # A file that is not there means Moodle is not installed here, which the
        # caller says in one sentence. Handing the reader's I/O error to the caller
        # instead made that error the answer of the check.
        if not lib.disk.file_exists(version_file):
            continue
        try:
            # version.php: `$release  = '4.4.1 (Build: 20240610)';`. Weeklies and
            # pre-releases append a suffix (`3.11.18+`, `4.5dev`, `4.5dev+`,
            # `4.0beta+`, `4.5rc1`, `2.0 RC1`), Moodle 1.x has no build part and
            # partly uses double quotes (`"1.4"`, `'1.4.5 +'`, `'1.0.9 Beta 1'`,
            # `'1.5 unstable development'`). The token runs up to ` (Build: ...)` or
            # the closing quote.
            # Verified against Moodle v1.0.4 to v5.3.0 (all 590 tags) plus weekly
            # builds on main and the MOODLE_*_STABLE branches
            # (github.com/moodle/moodle).
            success, version = lib.disk.grep_file(
                version_file,
                r'\$release\s*=\s*[\'"](\d+(?:\.\d+)+[^\'"(]*?)\s*'
                r'(?:\(Build:[^\'"]*)?[\'"]',
            )
        except Exception:
            return (False, 'Unable to get Moodle version information.')
        if not success:
            return (success, version)
        if version:
            return (True, version)
    return (True, False)


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
    installed_version = lib.base.coe(get_installed_version(args.PATH))
    if not installed_version:
        lib.base.cu(
            f'Moodle not found at `{args.PATH}`. '
            'Use --path to point at the installation directory.'
        )

    # init some vars
    perfdata = ''
    # lib.version reads a suffix as version parts (4.5rc1 -> 4.51), use the core
    installed_core = re.match(r'\d+(?:\.\d+)*', installed_version).group()

    # analyze data
    state, eol = lib.version.check_eol(
        'https://endoflife.date/api/moodle.json',
        installed_core,
        offset_eol=args.OFFSET_EOL,
        check_major=args.CHECK_MAJOR,
        check_minor=args.CHECK_MINOR,
        check_patch=args.CHECK_PATCH,
        insecure=args.INSECURE,
        no_proxy=args.NO_PROXY,
        proxy=args.PROXY,
        timeout=args.TIMEOUT,
        unreachable_severity=args.UNREACHABLE_SEVERITY,
    )

    # build the message
    msg = f'Moodle v{installed_version} ({eol})'

    perfdata += lib.base.get_perfdata(
        'moodle-version',
        lib.version.version2float(installed_core),
        uom=None,
        _min=0,
    )

    # 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()
