from typing import Dict, List, Any
import logging
import argparse
import traceback
import boto3
from zenyatta.common.util import parse_config_file, check_response
from zenyatta.aws import boto_client, boto_resource, get_aws_metadata, get_instance
from airflow.settings import conf


def lauch_zenyatta_worker(airflow_conf: Any, ec2_conf: Dict, team: str, region: str, subnet_id: str,
                          security_groups: List[str], env: str, bucket: str, key: str) -> List:
    try:
        ec2 = boto3.resource('ec2', region_name=region)
        update_config_command = """/opt/virtualenvs/airflow/bin/python /opt/twitch/zenyatta/current/scripts/push_pull_config.py \
--bucket {bucket} --key {key} pull""".format(**locals())
        instances = ec2.create_instances(
            BlockDeviceMappings=[
                {
                    'DeviceName': '/dev/xvdb',
                    'Ebs': {
                        'Encrypted': False,
                        'DeleteOnTermination': True,
                        'VolumeSize': int(airflow_conf.get('ec2', 'ebs_size')),
                        'VolumeType': 'gp2',
                    },
                },
            ],
            ImageId=airflow_conf.get('ec2', 'worker_ami'),
            MinCount=ec2_conf['ec2']['num_worker_nodes'],
            MaxCount=ec2_conf['ec2']['num_worker_nodes'],
            SecurityGroupIds=security_groups,
            SubnetId=subnet_id,
            InstanceType=ec2_conf['ec2']['worker_node_type'],
            UserData="""
#cloud-config
mounts:
 - [ /dev/xvdb, /mnt, "ext4", "defaults,nofail,comment=cloudconfig", "0", "2" ]
fs_setup:
 - label: None
   filesystem: 'ext4'
   device: '/dev/xvdb'
   partition: 'auto'
runcmd:
 - /etc/zenyatta/attempt_update.sh
 - {update_config_command}
 - chown -R airflow:airflow /etc/zenyatta
 - chmod a+x /opt/twitch/zenyatta/current/worker.sh
 - chown airflow:airflow -R /opt/twitch/zenyatta
 - chown airflow:airflow -R /var/log/zenyatta/
 - chown airflow:airflow -R /mnt
 - echo "*/15 * * * * airflow {update_config_command}" >> /etc/crontab
 - svc -d /var/lib/service/airflow-worker
 - svc -u /var/lib/service/airflow-worker
 - puppet agent
""".format(**locals()),
            IamInstanceProfile={
                'Name': 'iam_profile_rds_buddy_{team}_{env}'.format(**locals()),
            })
        # wait for them to be ready
        for instance in instances:
            instance.wait_until_running()
        # tag them so we can easily find and classify them
        for instance in instances:
            instance.create_tags(Tags=[{'Key': 'Name',
                                        'Value': '{team}-zenyatta-worker'.format(**locals())},
                                       {'Key': 'Owner',
                                        'Value': '{team}@twitch.tv'.format(**locals())},
                                       {'Key': 'Service',
                                        'Value': '{team}/zenyatta/zenyatta-worker'.format(**locals())}])
        return [instance.instance_id for instance in instances]
    except Exception:
            logging.error(traceback.format_exc())


def launch_worker_node(team: str, region: str, subnet_id: str, security_groups: List[str],
                       env: str, bucket: str, key: str):
    try:
        ec2_client = boto3.client('ec2', region)
        filters = [
            {'Name': 'tag:Name', 'Values': ['{team}-zenyatta-worker'.format(**locals())]},
            {'Name': 'instance-state-name', 'Values': ['running', 'pending']}
            ]
        response = ec2_client.describe_instances(Filters=filters)

        if check_response(response) and len(response['Reservations']) > 0:
            logging.info("buddy found zenyatta worker nodes for {team}".format(**locals()))
        else:
            logging.info("buddy did not find zenyatta worker node for {team}. buddy will create a worker."
                         .format(**locals()))
            cfg_home = '/etc/zenyatta/'
            cfg_name = 'hardware_cfg.yaml'
            cfg = parse_config_file(cfg_home+cfg_name)
            new_instances = lauch_zenyatta_worker(conf, cfg, team, region, subnet_id,
                                                  security_groups, env, bucket, key)
            logging.info("zenyatta worker nodes are created: {new_instances}".format(**locals()))
    except Exception:
            logging.error(traceback.format_exc())


if __name__ == '__main__':
    # buddy needs to provide team name, enviornment, subnet group, and a list of
    # security group in the form of calling
    # python setup_redis.py -n teamA -e dev or prod -s zenyatta-production -l security-group-1
    #   security-group-2 -z availability_zone
    parser = argparse.ArgumentParser(description="set up ec2 worker instance for airflow")
    parser.add_argument('-n', '--name', required=True, dest='team', action='store',
                        help='buddy team name used as ec2 worker nodes tag')
    parser.add_argument('-r', '--region', required=True, dest='region', action='store',
                        help='aws region to launch in')
    parser.add_argument('-s', '--subnet-id', required=False, dest='subnet_id', action='store',
                        help='subnet ids to set up ec2 worker nodes')
    parser.add_argument('-l', '--security-group-ids-list', required=False, dest='security_group_list',
                        type=str, nargs='*', action='store',
                        help='security group ids to set up ec2 worker nodes')
    parser.add_argument('-e', '--env', required=True, dest='env', action='store',
                        help='environment name (staging, production, etc.)')
    parser.add_argument('--bucket', required=False, dest='bucket', action='store',
                        help='s3 bucket to pull config from')
    parser.add_argument('--key', required=True, dest='key', action='store',
                        help='s3 key for bucket to pull config from')
    args = parser.parse_args()

    if not args.bucket:
        from zenyatta.common import get_connections
        args.bucket = get_connections()['aws']['s3-input']['bucket']

    if not args.subnet_id or not args.security_group_list or not args.availability_zone:
        # use master node settings
        myself = get_instance(args.region)
        args.security_group_list = [sg['GroupId'] for sg in myself.security_groups] \
            if not args.security_group_list else args.security_group_list
        args.subnet_id = myself.subnet_id if not args.subnet_id else args.subnet_id
    launch_worker_node(args.team, args.region, args.subnet_id,
                       args.security_group_list, args.env, args.bucket, args.key)
