#!/usr/bin/python3

# Copyright 2022 SUSE LLC
#
# This file is part of ec2imgutils
#
# ec2imgutils is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# ec2imgutils is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with ec2removeimg. If not, see <http://www.gnu.org/licenses/>.

import argparse
import os
import sys

import ec2imgutils.ec2utils as utils
import ec2imgutils.ec2removeimg as ec2rmimg
from ec2imgutils.ec2imgutilsExceptions import (
    EC2AccountException,
    EC2RemoveImgException
)


# ----------------------------------------------------------------------------
def parse_args(args):
    """Command argument parsing function."""
    parser = argparse.ArgumentParser(description='Remove images in EC2')
    parser.add_argument(
        '-a', '--account',
        dest='accountName',
        help='Account to use',
        metavar='ACCOUNT_NAME',
    )
    parser.add_argument(
        '--access-id',
        dest='accessKey',
        help='AWS access key (Optional)',
        metavar='AWS_ACCESS_KEY'
    )
    parser.add_argument(
        '--all',
        action='store_true',
        default=False,
        dest='all',
        help='Delete all images that match the search criteria'
    )
    help_msg = 'Do not perform any action, print information about actions '
    help_msg += 'that would be performed instead (Optional)'
    parser.add_argument(
        '-n', '--dry-run',
        action='store_true',
        default=False,
        dest='dryRun',
        help=help_msg
    )
    parser.add_argument(
        '-f', '--file',
        default=os.sep.join(['~', '.ec2utils.conf']),
        dest='configFilePath',
        help='Path to configuration file, default ~/.ec2utils.conf (Optional)',
        metavar='CONFIG_FILE'
    )
    # Note, one of the arguments in the group is required if --version is
    # not specified. However setting this behavior through the parser
    # also requires one argument to be specified even if --version is specified
    # This parser behavior is true even if --version and the group are part
    # of the same subgroup
    remove_image_condition_group = parser.add_mutually_exclusive_group()
    remove_image_condition_group.add_argument(
        '--image-id',
        dest='imageID',
        help='The AMI ID of the image to be removed (Optional)',
        metavar='AMI_ID'
    )
    remove_image_condition_group.add_argument(
        '--image-name',
        dest='imageName',
        help='The image name of the image to be removed (Optional)',
        metavar='IMAGE_NAME'
    )
    help_msg = 'An image name fragment to match the image name of the image '
    help_msg += 'to be removed (Optional)'
    remove_image_condition_group.add_argument(
        '--image-name-frag',
        dest='imageNameFrag',
        help=help_msg,
        metavar='IMAGE_NAME_FRAGMENT'
    )
    help_msg = 'A regular expression to match the image name of the image '
    help_msg += 'to be removed (Optional)'
    remove_image_condition_group.add_argument(
        '--image-name-match',
        dest='imageNameMatch',
        help=help_msg,
        metavar='REGEX'
    )
    parser.add_argument(
        '--confirm',
        action='store_true',
        default=False,
        dest='confirm',
        help='Remove matched images with confirmation of action'
    )
    parser.add_argument(
        '--preserve-snap',
        action='store_true',
        default=False,
        dest='preserveSnap',
        help='Do not remove the snapshot associated with the image'
    )
    help_msg = 'Comma separated list of regions for removing, all integrated '
    help_msg += 'regions if not given (Optional)'
    parser.add_argument(
        '-r', '--regions',
        dest='regions',
        help=help_msg,
        metavar='EC2_REGIONS'
    )
    parser.add_argument(
        '-s', '--secret-key',
        dest='secretKey',
        help='AWS secret access key (Optional)',
        metavar='AWS_SECRET_KEY'
    )
    parser.add_argument(
        '--verbose',
        action='store_true',
        default=False,
        dest='verbose',
        help='Enable on verbose output'
    )
    parser.add_argument(
        '--version',
        action='version',
        version=utils.get_version(),
        help='Program version'
    )

    parsed_args = parser.parse_args(args)
    return parsed_args


# ----------------------------------------------------------------------------
def check_required_arguments(args, logger):
    """This function is used to ensure the additional requirements over
    arguments (which ones are mandatory, etc.) are met.
    """
    check_remove_image_args_present(args, logger)


# ----------------------------------------------------------------------------
def check_remove_image_args_present(args, logger):
    """This function checks that at least one of the possible args to specify
    the image to delete is present
    """
    if (
        not args.imageID and not
        args.imageName and not
        args.imageNameFrag and not
        args.imageNameMatch
    ):
        error_msg = 'ec2removeimg: error: one of the arguments '
        error_msg += '--image-id --image-name --image-name-frag '
        error_msg += '--image-name-match is required'
        logger.error(error_msg)
        sys.exit(1)


# ----------------------------------------------------------------------------
def get_config(args, logger):
    """Function to get the configutation parsed from the configuration file
    provided as an argument (or the default path)
    """
    config_file = os.path.expanduser(args.configFilePath)
    config = None
    if not os.path.isfile(config_file):
        logger.error('Configuration file "%s" not found.' % config_file)
        sys.exit(1)
    try:
        config = utils.get_config(config_file)
    except Exception as e:
        logger.error(e)
        sys.exit(1)
    return config


# ----------------------------------------------------------------------------
def get_access_key(args, config, logger):
    """Function to get the access_key to AWS with the data provided as command
    line args and configuration.
    """
    access_key = args.accessKey
    if not access_key:
        try:
            access_key = utils.get_from_config(
                args.accountName,
                config,
                None,
                'access_key_id',
                '--access-id'
            )
        except EC2AccountException as e:
            logger.error(e)
            sys.exit(1)

    if not access_key:
        logger.error('Could not determine account access key')
        sys.exit(1)
    return access_key


# ----------------------------------------------------------------------------
def get_secret_key(args, config, logger):
    """Function to get the secret_key to AWS with the data provided as command
    line args and configuration
    """
    secret_key = args.secretKey
    if not secret_key:
        try:
            secret_key = utils.get_from_config(
                args.accountName,
                config,
                None,
                'secret_access_key',
                '--secret-key'
            )
        except EC2AccountException as e:
            logger.error(e)
            sys.exit(1)

    if not secret_key:
        logger.error('Could not determine account secret access key')
        sys.exit(1)
    return secret_key


# ----------------------------------------------------------------------------
def get_image_remover(args, access_key, secret_key, logger):
    """Function to get an instance of the ec2imgutils.EC2RemoveImage class"""
    try:
        remover = ec2rmimg.EC2RemoveImage(
            access_key=access_key,
            image_id=args.imageID,
            image_name=args.imageName,
            image_name_fragment=args.imageNameFrag,
            image_name_match=args.imageNameMatch,
            keep_snap=args.preserveSnap,
            confirm=args.confirm,
            remove_all=args.all,
            secret_key=secret_key,
            log_callback=logger
        )
    except EC2RemoveImgException as e:
        logger.error(e)
        sys.exit(1)
    return remover


# ----------------------------------------------------------------------------
def remove_images_in_region(remover, dryRun, region, logger):
    """Function that removes the specified images for a AWS region."""
    try:
        if dryRun:
            logger.info('Dry run, the following images would be removed')
            remover.print_remove_info()
        else:
            remover.remove_images()
    except EC2RemoveImgException as e:
        logger.error(e)
        sys.exit(1)
    except Exception as e:
        logger.error(e)
        sys.exit(1)


# ----------------------------------------------------------------------------
def main(args):
    args = parse_args(args)
    logger = utils.get_logger(args.verbose)
    check_required_arguments(args, logger)

    config = get_config(args, logger)
    access_key = get_access_key(args, config, logger)
    secret_key = get_secret_key(args, config, logger)

    regions = utils.get_regions(args, access_key, secret_key)
    remover = get_image_remover(args, access_key, secret_key, logger)

    for region in regions:
        remover.set_region(region)
        remove_images_in_region(
            remover,
            args.dryRun,
            region,
            logger
        )


# ----------------------------------------------------------------------------
if __name__ == '__main__':
    main(sys.argv[1:])
