#!/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 socket
import sys

import lib.args
import lib.base
from lib.globals import STATE_CRIT, STATE_OK, STATE_UNKNOWN, STATE_WARN

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

DESCRIPTION = """Checks whether a TCP network port is reachable by attempting to establish a connection.
Measures and reports the connection time. Useful for monitoring service availability
from the network perspective.
Alerts if the port is unreachable or the connection time exceeds the configured thresholds."""

DEFAULT_HOSTNAME = 'localhost'
DEFAULT_PORT = 22
DEFAULT_SEVERITY = 'warn'
DEFAULT_TIMEOUT = 2
DEFAULT_TYPE = 'tcp'


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(
        '-H',
        '--hostname',
        help='Host or IP address to check. Default: %(default)s',
        dest='HOSTNAME',
        default=DEFAULT_HOSTNAME,
    )

    parser.add_argument(
        '-p',
        '--port',
        help='TCP port number to check. Default: %(default)s',
        dest='PORT',
        type=int,
        default=DEFAULT_PORT,
    )

    parser.add_argument(
        '--portname',
        help='Human-readable display name for the port in the output message. '
        'Example: `--portname https`.',
        dest='PORTNAME',
    )

    parser.add_argument(
        '--severity',
        help=lib.args.help('--severity') + ' Default: %(default)s',
        dest='SEVERITY',
        default=DEFAULT_SEVERITY,
        choices=['warn', 'crit'],
    )

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

    parser.add_argument(
        '--type',
        help='Connection type, "tcp" for IPv4 or "tcp6" for IPv6. Default: %(default)s',
        dest='TYPE',
        default=DEFAULT_TYPE,
        choices=['tcp', 'tcp6'],
    )

    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.TYPE == 'tcp':
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    else:
        sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
    sock.settimeout(args.TIMEOUT)

    try:
        # Works fine for tcp connections, but not for udp. The port response for udp is based on
        # the target application and is not standard like tcp.
        if sock.connect_ex((args.HOSTNAME, args.PORT)) == 0:
            state = STATE_OK
        else:
            state = STATE_WARN if args.SEVERITY == 'warn' else STATE_CRIT
        sock.close()
    except Exception:
        lib.base.oao(
            f'Cannot initiate a connection to {args.HOSTNAME}:{args.PORT}/{args.TYPE}.',
            STATE_UNKNOWN,
        )

    # over and out
    port_display = args.PORTNAME if args.PORTNAME else args.PORT
    reachability = 'reachable' if state == STATE_OK else 'unreachable'
    lib.base.oao(
        f'{args.HOSTNAME}:{port_display}/{args.TYPE} is {reachability}.',
        state,
    )


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