#!/usr/bin/env python3

"""
Command line utility to drive the lava email connector.

Relies on the lava connector subsystem.

"""

import argparse
import logging
import os
import sys

import boto3

from lava.connection import get_email_connection
from lava.lavacore import LOGNAME
from lava.lib.logging import setup_logging
from lava.lib.misc import size_to_bytes
from lava.version import __version__

__author__ = 'Murray Andrews'

PROG = os.path.splitext(os.path.basename(sys.argv[0]))[0]
LOG = logging.getLogger(LOGNAME)
LOGLEVEL = 'info'

MAX_BODY_SIZE = '2MB'


# ------------------------------------------------------------------------------
def process_cli_args() -> argparse.Namespace:
    """
    Process the command line arguments.

    :return:    The args namespace.
    """

    argp = argparse.ArgumentParser(
        prog=PROG, description='Send email using lava email connections.'
    )

    argp.add_argument('--profile', action='store', help='As for AWS CLI.')

    argp.add_argument('-v', '--version', action='version', version=__version__)

    # ------------------------------
    # lava arguments
    lavap = argp.add_argument_group('lava arguments')

    lavap.add_argument(
        '-c',
        '--conn-id',
        dest='conn_id',
        required=True,
        action='store',
        help='Lava connection ID. Required.',
    )

    lavap.add_argument(
        '-r',
        '--realm',
        action='store',
        help=(
            'Lava realm name. If not specified, the environment variable LAVA_REALM must be set.'
        ),
    )

    # ------------------------------
    # Email arguments
    emailp = argp.add_argument_group('email arguments')

    emailp.add_argument(
        '-a',
        '--attach',
        metavar='FILE',
        action='append',
        default=[],
        help=(
            'Add the specified file as an attachment. Can be a local file or an object'
            ' in S3 in the form s3://bucket/key. Can be used multiple times.'
        ),
    )
    emailp.add_argument(
        '--bcc',
        metavar='EMAIL',
        action='append',
        default=[],
        help='Recipients to place on the Bcc: line of the message. Can be used multiple times.',
    )

    emailp.add_argument(
        '--cc',
        metavar='EMAIL',
        action='append',
        default=[],
        help='Recipients to place on the Cc: line of the message. Can be used multiple times.',
    )

    emailp.add_argument(
        '--from',
        metavar='EMAIL',
        dest='sender',
        action='store',
        help=(
            'Message sender. If not specified, a value must be available in either the connection'
            ' specification or the realm specification.'
        ),
    )

    emailp.add_argument(
        '--reply-to',
        metavar='EMAIL',
        action='append',
        help='Reply-to address of the message. Can be used multiple times.',
    )

    emailp.add_argument(
        '--to',
        metavar='EMAIL',
        action='append',
        help='Recipients to place on the To: line of the message. Can be used multiple times.',
    )

    emailp.add_argument(
        '-s', '--subject', action='store', required=True, help='Message subject. Required.'
    )

    # ------------------------------
    # Message source options.

    srcp = argp.add_argument_group(
        'message source arguments',
        description='At most one of the following arguments is permitted.',
    ).add_mutually_exclusive_group()

    srcp.add_argument(
        '--html',
        metavar='FILENAME',
        action='store',
        type=argparse.FileType('r'),
        help='This is a legacy argument for backward compatibility.',
    )
    srcp.add_argument(
        '--text',
        metavar='FILENAME',
        action='store',
        type=argparse.FileType('r'),
        help='This is a legacy argument for backward compatibility.',
    )
    srcp.add_argument(
        'message',
        metavar='FILENAME',
        action='store',
        nargs='?',
        type=argparse.FileType('r'),
        help=(
            'Name of file containing the message body. If not specified or "-", the body will be'
            ' read from stdin. An attempt is made to determine if the message is HTML and send it'
            f' accordingly. Only the first {MAX_BODY_SIZE} is read.'
        ),
    )

    # ------------------------------
    # Logging options

    logp = argp.add_argument_group('logging arguments')
    logp.add_argument(
        '--no-colour',
        '--no-color',
        dest='colour',
        action='store_false',
        default=True,
        help='Don\'t use colour in information messages.',
    )

    logp.add_argument(
        '-l',
        '--level',
        metavar='LEVEL',
        default=LOGLEVEL,
        help=(
            'Print messages of a given severity level or above. The standard logging level names'
            ' are available but debug, info, warning and error are most useful.'
            f' The Default is {LOGLEVEL}.'
        ),
    )

    logp.add_argument(
        '--log',
        action='store',
        help='Log to the specified target. This can be either a file'
        ' name or a syslog facility with an @ prefix (e.g. @local0).',
    )

    logp.add_argument(
        '--tag',
        action='store',
        default=PROG,
        help=f'Tag log entries with the specified value. The default is {PROG}.',
    )

    args = argp.parse_args()

    # Message file object can come from multiple options
    args.message = next((f for f in (args.message, args.html, args.text) if f), sys.stdin)

    if not args.realm:
        try:
            args.realm = os.environ['LAVA_REALM']
        except KeyError:
            argp.error(
                'Lava realm must be specified via -r, --realm or LAVA_REALM environment variable.'
            )

    return args


# ---------------------------------------------------------------------------------------
def main() -> int:
    """
    Show time.

    :return:    status
    """

    setup_logging(LOGLEVEL, name=LOGNAME, prefix=PROG)
    args = process_cli_args()
    setup_logging(args.level, name=LOGNAME, target=args.log, colour=args.colour, prefix=args.tag)

    message = args.message.read(size_to_bytes(MAX_BODY_SIZE))

    aws_session = boto3.Session(profile_name=args.profile)
    email_conn = get_email_connection(args.conn_id, args.realm, aws_session=aws_session)

    email_conn.send(
        subject=args.subject,
        message=message,
        to=args.to,
        cc=args.cc,
        bcc=args.bcc,
        sender=args.sender,
        reply_to=args.reply_to,
        attachments=args.attach,
    )

    return 0


# ------------------------------------------------------------------------------
if __name__ == '__main__':
    # Uncomment for debugging
    # exit(main())  # noqa: ERA001
    try:
        exit(main())
    except Exception as ex:
        print(f'{PROG}: {ex}', file=sys.stderr)
        exit(1)
    except KeyboardInterrupt:
        print('Interrupt', file=sys.stderr)
        exit(2)
