#!/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 sys
from email.utils import make_msgid
from socket import gethostname

import lib.args
import lib.base
import lib.icinga
import lib.mail
from lib.globals import STATE_UNKNOWN

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

DESCRIPTION = """Sends notifications for services using mail."""

TIMEOUT = 8  # seconds
DEFAULT_MAIL_PORT = 25
DEFAULT_MAIL_SERVER = 'localhost'


def parse_args():
    """Parse command line arguments using argparse."""
    parser = argparse.ArgumentParser(
        description=DESCRIPTION,
        epilog=lib.args.epilog(__file__, section='notification-plugins'),
        formatter_class=lib.args.HelpFormatter,
    )

    parser.add_argument(
        '-V',
        '--version',
        action='version',
        version=f'%(prog)s: v{__version__} by {__author__}',
    )

    parser.add_argument(
        '--datetime',
        help='Set the message timestamp.',
        dest='DATETIME',
        required=True,
    )

    parser.add_argument(
        '--host-address',
        help='Set the IPv4 address of the host.',
        dest='HOST_ADDRESS',
    )

    parser.add_argument(
        '--host-displayname',
        help='Set the display name of the host.',
        dest='HOST_DISPLAYNAME',
        required=True,
    )

    parser.add_argument(
        '--hostname',
        help='Set the hostname.',
        dest='HOSTNAME',
    )

    parser.add_argument(
        '--icingaweb2-url',
        help='Set the Icinga Web 2 URL, for example "https://example.com/icingaweb2".',
        dest='ICINGAWEB2_URL',
    )

    parser.add_argument(
        '--mail-password',
        help='Set the mail server login password.',
        dest='MAIL_PASSWORD',
    )

    parser.add_argument(
        '--mail-port',
        help='Set the mail server port. Default: %(default)s.',
        dest='MAIL_PORT',
        default=DEFAULT_MAIL_PORT,
    )

    parser.add_argument(
        '--mail-recipient',
        help='Set the mail recipient.',
        dest='MAIL_RECIPIENT',
        required=True,
    )

    parser.add_argument(
        '--mail-sender',
        help='Set the mail sender.',
        dest='MAIL_SENDER',
        required=True,
    )

    parser.add_argument(
        '--mail-server',
        help='Set the mail server. Default: %(default)s.',
        dest='MAIL_SERVER',
        default=DEFAULT_MAIL_SERVER,
    )

    parser.add_argument(
        '--mail-user',
        help='Set the mail server login user.',
        dest='MAIL_USER',
    )

    parser.add_argument(
        '--notes',
        help='Set the notes.',
        dest='NOTES',
    )

    parser.add_argument(
        '--notes-url',
        help='Set the notes url.',
        dest='NOTES_URL',
    )

    parser.add_argument(
        '--notification-author',
        help='Set the author of the comment.',
        dest='NOTIFICATION_AUTHOR',
    )

    parser.add_argument(
        '--notification-comment',
        help='Set the comment.',
        dest='NOTIFICATION_COMMENT',
    )

    parser.add_argument(
        '--notification-type',
        help='Set the type of notification like "PROBLEM" or "RECOVERY".',
        dest='NOTIFICATION_TYPE',
    )

    parser.add_argument(
        '--perfdata',
        help='Set the perfdata.',
        dest='PERFDATA',
    )

    parser.add_argument(
        '--service-displayname',
        help='Set the display name of the service.',
        dest='SERVICE_DISPLAYNAME',
        required=True,
    )

    parser.add_argument(
        '--service-output',
        help='Set the service output.',
        dest='SERVICE_OUTPUT',
    )

    parser.add_argument(
        '--service-state',
        help='Set the service state.',
        dest='SERVICE_STATE',
        required=True,
    )

    parser.add_argument(
        '--servicename',
        help='Set the servicename.',
        dest='SERVICENAME',
    )

    parser.add_argument(
        '--short',
        help='Send a short message. This can be useful when using a SMS relay, for example.',
        dest='SHORT',
        action='store_true',
        default=False,
    )

    args, _ = parser.parse_known_args()
    return args


def generate_mail_content(args, logo_cid):
    """Build the plain-text and HTML representation of the notification mail."""
    colors = {
        'ACKNOWLEDGEMENT': '#FFFF80',
        'CRITICAL': '#FF99AA',
        'DOWNTIMECANCELLED': '#FFFF80',
        'DOWNTIMEEND': '#80FF80',
        'DOWNTIMESTART': '#80FFFF',
        'FLAPPINGDISABLED': '#FFFF80',
        'FLAPPINGSTART': '#FF8080',
        'FLAPPINGSTOP': '#80FF80',
        'OK': '#80FF80',
        'PROBLEM': '#FF8080',
        'RECOVERY': '#80FF80',
        'TEST': '#80FFFF',
        'UNKNOWN': '#CC77FF',
        'WARNING': '#FFFF80',
    }

    rows = []

    if args.NOTIFICATION_TYPE:
        rows += [
            {
                'left_column': 'Notification Type:',
                'right_column': args.NOTIFICATION_TYPE,
                'right_column_attributes': f'style="background-color: {colors.get(args.NOTIFICATION_TYPE)}"',
            },
        ]

    rows += [
        {
            'left_column': 'Host:',
            'right_column': args.HOST_DISPLAYNAME,
        }
    ]

    rows += [
        {
            'left_column': 'Service:',
            'right_column': args.SERVICE_DISPLAYNAME,
        },
        {
            'left_column': 'Service State:',
            'right_column': args.SERVICE_STATE,
            'right_column_attributes': f'style="background-color: {colors.get(args.SERVICE_STATE)}"',
        },
    ]

    if args.SERVICE_OUTPUT:
        rows += [
            {
                'left_column': 'Service Output:',
                'right_column': args.SERVICE_OUTPUT,
            },
        ]

    if args.HOSTNAME:
        rows += [
            {
                'left_column': 'Hostname:',
                'right_column': args.HOSTNAME,
            },
        ]

    if args.HOST_ADDRESS:
        rows += [
            {
                'left_column': 'IP Address:',
                'right_column': args.HOST_ADDRESS,
            },
        ]

    rows += [
        {
            'left_column': 'Event Time:',
            'right_column': args.DATETIME,
        },
    ]

    if args.PERFDATA:
        rows += [
            {
                'left_column': 'Perfdata:',
                'right_column': args.PERFDATA,
            },
        ]

    if args.NOTIFICATION_COMMENT:
        rows += [
            {
                'left_column': 'Author:',
                'right_column': args.NOTIFICATION_AUTHOR,
            },
            {
                'left_column': 'Comment:',
                'right_column': args.NOTIFICATION_COMMENT,
            },
        ]

    web_url = lib.icinga.build_icingaweb2_url(
        args.ICINGAWEB2_URL, args.HOSTNAME, args.SERVICENAME
    )
    if web_url:
        rows += [
            {
                'left_column': 'IcingaWeb2 URL:',
                'right_column': web_url,
            },
        ]

    if args.NOTES:
        rows += [
            {
                'left_column': 'Notes:',
                'right_column': args.NOTES,
            },
        ]

    if args.NOTES_URL:
        rows += [
            {
                'left_column': 'Notes URL:',
                'right_column': args.NOTES_URL,
            },
        ]

    return lib.icinga.render_notification_mail(rows, logo_cid, gethostname())


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)

    # build the message
    subject = ''
    html = ''
    images = None

    if args.SHORT:
        plain = (
            f'{args.SERVICE_STATE:.4}:'
            f' {args.SERVICE_DISPLAYNAME}\n'
            f'HOST: {args.HOST_DISPLAYNAME}'
            f' ({args.DATETIME})'
        )

        if args.NOTIFICATION_COMMENT:
            plain += (
                f'\nCOMMENT: {args.NOTIFICATION_COMMENT} ({args.NOTIFICATION_AUTHOR})'
            )
    else:
        # Pin the domain to the short hostname so make_msgid() does not
        # fall back to socket.getfqdn(). On hosts with a long FQDN the
        # resulting Content-ID header overflows 80 characters and gets
        # wrapped by Python's quoted-printable encoder, which breaks the
        # cid:<...> reference in the HTML body and turns the inline logo
        # into an attachment (see python/cpython#100293 and issue #790).
        logo_cid = make_msgid(domain=gethostname())
        plain, html = generate_mail_content(args, logo_cid)

        subject = (
            f'Service "{args.SERVICE_DISPLAYNAME}"'
            f' on "{args.HOST_DISPLAYNAME}" is {args.SERVICE_STATE}'
        )
        if args.NOTIFICATION_TYPE:
            subject = f'[{args.NOTIFICATION_TYPE}] {subject}'

        images = [
            {
                'data': lib.icinga.get_logo(),
                'maintype': 'icinga',
                'subtype': 'png',
                'cid': logo_cid,
            }
        ]

    # over and out
    lib.base.coe(
        lib.mail.send(
            args.MAIL_SERVER,
            args.MAIL_SENDER,
            args.MAIL_RECIPIENT,
            subject=subject,
            plain=plain,
            html=html,
            images=images,
            port=args.MAIL_PORT,
            username=args.MAIL_USER,
            password=args.MAIL_PASSWORD,
            timeout=TIMEOUT,
        )
    )


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