from __future__ import absolute_import, division, print_function, unicode_literals

import argparse
import functools
import logging
from os import path

import boto3
import jsonschema
from c7n_mailer import deploy, utils
from c7n_mailer.azure.azure_queue_processor import MailerAzureQueueProcessor
from c7n_mailer.azure import deploy as azure_deploy
from c7n_mailer.sqs_queue_processor import MailerSqsQueueProcessor
from c7n_mailer.utils import get_provider, Providers
from ruamel import yaml

CONFIG_SCHEMA = {
    'type': 'object',
    'additionalProperties': False,
    'required': ['queue_url'],
    'properties': {
        'queue_url': {'type': 'string'},
        'from_address': {'type': 'string'},
        'contact_tags': {'type': 'array', 'items': {'type': 'string'}},
        'org_domain': {'type': 'string'},

        # Standard Lambda Function Config
        'region': {'type': 'string'},
        'role': {'type': 'string'},
        'runtime': {'type': 'string'},
        'memory': {'type': 'integer'},
        'timeout': {'type': 'integer'},
        'subnets': {'type': 'array', 'items': {'type': 'string'}},
        'security_groups': {'type': 'array', 'items': {'type': 'string'}},
        'dead_letter_config': {'type': 'object'},
        'lambda_name': {'type': 'string'},
        'lambda_description': {'type': 'string'},
        'lambda_tags': {'type': 'object'},
        'lambda_schedule': {'type': 'string'},

        # Azure Function Config
        'function_properties': {
            'type': 'object',
            'appInsights': {
                'type': 'object',
                'oneOf': [
                    {'type': 'string'},
                    {'type': 'object',
                        'properties': {
                            'name': 'string',
                            'location': 'string',
                            'resourceGroupName': 'string'}
                     }
                ]
            },
            'storageAccount': {
                'type': 'object',
                'oneOf': [
                    {'type': 'string'},
                    {'type': 'object',
                        'properties': {
                            'name': 'string',
                            'location': 'string',
                            'resourceGroupName': 'string'}
                     }
                ]
            },
            'servicePlan': {
                'type': 'object',
                'oneOf': [
                    {'type': 'string'},
                    {'type': 'object',
                        'properties': {
                            'name': 'string',
                            'location': 'string',
                            'resourceGroupName': 'string',
                            'skuTier': 'string',
                            'skuName': 'string'}
                     }
                ]
            },
        },
        'function_schedule': {'type': 'string'},
        'function_skuCode': {'type': 'string'},
        'function_sku': {'type': 'string'},

        # Mailer Infrastructure Config
        'cache_engine': {'type': 'string'},
        'smtp_server': {'type': 'string'},
        'smtp_port': {'type': 'integer'},
        'smtp_ssl': {'type': 'boolean'},
        'smtp_username': {'type': 'string'},
        'smtp_password': {'type': 'string'},
        'ldap_email_key': {'type': 'string'},
        'ldap_uid_tags': {'type': 'array', 'items': {'type': 'string'}},
        'debug': {'type': 'boolean'},
        'ldap_uid_regex': {'type': 'string'},
        'ldap_uri': {'type': 'string'},
        'ldap_bind_dn': {'type': 'string'},
        'ldap_bind_user': {'type': 'string'},
        'ldap_uid_attribute': {'type': 'string'},
        'ldap_manager_attribute': {'type': 'string'},
        'ldap_email_attribute': {'type': 'string'},
        'ldap_bind_password_in_kms': {'type': 'boolean'},
        'ldap_bind_password': {'type': 'string'},
        'cross_accounts': {'type': 'object'},
        'ses_region': {'type': 'string'},
        'redis_host': {'type': 'string'},
        'redis_port': {'type': 'integer'},
        'datadog_api_key': {'type': 'string'},              # TODO: encrypt with KMS?
        'datadog_application_key': {'type': 'string'},      # TODO: encrypt with KMS?
        'slack_token': {'type': 'string'},
        'slack_webhook': {'type': 'string'},
        'sendgrid_api_key': {'type': 'string'},
        'splunk_hec_url': {'type': 'string'},
        'splunk_hec_token': {'type': 'string'},
        'splunk_remove_paths': {
            'type': 'array',
            'items': {'type': 'string'}
        },
        'splunk_actions_list': {'type': 'boolean'},
        'splunk_max_attempts': {'type': 'integer'},
        'splunk_hec_max_length': {'type': 'integer'},

        # SDK Config
        'profile': {'type': 'string'},
        'http_proxy': {'type': 'string'},
        'https_proxy': {'type': 'string'},

        # Mapping account / emails
        'account_emails': {'type': 'object'}
    }
}


def session_factory(mailer_config):
    return boto3.Session(
        region_name=mailer_config['region'],
        profile_name=mailer_config.get('profile', None))


def get_logger(debug=False):
    log_format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
    logging.basicConfig(level=logging.INFO, format=log_format)
    logging.getLogger('botocore').setLevel(logging.WARNING)
    if debug:
        logging.getLogger('botocore').setLevel(logging.DEBUG)
        debug_logger = logging.getLogger('custodian-mailer')
        debug_logger.setLevel(logging.DEBUG)
        return debug_logger
    else:
        return logging.getLogger('custodian-mailer')


def get_and_validate_mailer_config(args):
    with open(args.config) as fh:
        config = yaml.load(fh.read(), Loader=yaml.SafeLoader)
    jsonschema.validate(config, CONFIG_SCHEMA)
    utils.setup_defaults(config)
    return config


def get_c7n_mailer_parser():
    parser = argparse.ArgumentParser()
    parser.add_argument('-c', '--config', required=True, help='mailer.yml config file')
    debug_help_msg = 'sets c7n_mailer logger to debug, for maximum output (the default is INFO)'
    parser.add_argument('--debug', action='store_true', help=debug_help_msg)
    max_num_processes_help_msg = 'will run the mailer in parallel, integer of max processes allowed'
    parser.add_argument('--max-num-processes', type=int, help=max_num_processes_help_msg)
    templates_folder_help_msg = 'message templates folder location'
    parser.add_argument('-t', '--templates', help=templates_folder_help_msg)
    group = parser.add_mutually_exclusive_group(required=True)
    update_lambda_help_msg = 'packages your c7n_mailer, uploads the zip to aws lambda as a function'
    group.add_argument('--update-lambda', action='store_true', help=update_lambda_help_msg)
    run_help_msg = 'run c7n-mailer locally, process sqs messages and send emails or sns messages'
    group.add_argument('--run', action='store_true', help=run_help_msg)
    return parser


def run_mailer_in_parallel(processor, max_num_processes):
    max_num_processes = int(max_num_processes)
    if max_num_processes < 1:
        raise Exception
    processor.max_num_processes = max_num_processes
    processor.run(parallel=True)


def main():
    parser = get_c7n_mailer_parser()
    args = parser.parse_args()
    mailer_config = get_and_validate_mailer_config(args)
    args_dict = vars(args)
    logger = get_logger(debug=args_dict.get('debug', False))

    module_dir = path.dirname(path.abspath(__file__))
    default_templates = [path.abspath(path.join(module_dir, 'msg-templates')),
                         path.abspath(path.join(module_dir, '..', 'msg-templates')),
                         path.abspath('.')]
    templates = args_dict.get('templates', None)
    if templates:
        default_templates.append(path.abspath(path.expanduser(path.expandvars(templates))))

    mailer_config['templates_folders'] = default_templates

    provider = get_provider(mailer_config)
    if args_dict.get('update_lambda'):
        if args_dict.get('debug'):
            print('\n** --debug is only supported with --run, not --update-lambda **\n')
            return
        if args_dict.get('max_num_processes'):
            print('\n** --max-num-processes is only supported '
                  'with --run, not --update-lambda **\n')
            return

        if provider == Providers.Azure:
            azure_deploy.provision(mailer_config)
        elif provider == Providers.AWS:
            deploy.provision(mailer_config, functools.partial(session_factory, mailer_config))

    if args_dict.get('run'):
        max_num_processes = args_dict.get('max_num_processes')

        # Select correct processor
        if provider == Providers.Azure:
            processor = MailerAzureQueueProcessor(mailer_config, logger)
        elif provider == Providers.AWS:
            aws_session = session_factory(mailer_config)
            processor = MailerSqsQueueProcessor(mailer_config, aws_session, logger)

        # Execute
        if max_num_processes:
            run_mailer_in_parallel(processor, max_num_processes)
        else:
            processor.run()


if __name__ == '__main__':
    main()
