from pprint import pprint

## The various errors we use.
from deployment_helpers.aws.boto_helpers import create_iam_client, create_iam_resource
from deployment_helpers.constants import (EB_INSTANCE_PROFILE_ROLE, EB_INSTANCE_PROFILE_NAME,
    EB_SERVICE_ROLE, get_automation_policy, BEIWE_AUTOMATION_POLICY_NAME, get_aws_access_policy)


class PythonPlatformDiscoveryError(Exception): pass
class EnvironmentDeploymentFailure(Exception): pass
class IamEntityMissingError(Exception): pass


## There are a bunch of IAM calls that were particularly verbose using the inline variable names,
## factored those out and stuck them here.

def iam_attach_role_policy(iam_client, role_name, arn):
    # This call appears to be idempotent; it returns None.
    iam_client.attach_role_policy(RoleName=role_name, PolicyArn=arn)


def iam_add_role_to_instance_profile(iam_client, instance_profile_name, role_name):
    iam_client.add_role_to_instance_profile(InstanceProfileName=instance_profile_name, RoleName=role_name)


def iam_create_role(iam_client, role_name, trust_document_json):
    return iam_client.create_role(RoleName=role_name, AssumeRolePolicyDocument=trust_document_json)


def iam_find_role(iam_client, role_name):
    """ We would use iam_client.get_iam_role, but it throws several different errors that are
    literally more specific than any error we can import from boto and catch explicitly because
    it is generated by an error factory. """
    for role in iam_client.list_roles()['Roles']:
        if role['RoleName'] == role_name:
            return role
    raise IamEntityMissingError("IAM could not find Role %s" % role_name)


def get_or_create_automation_policy():
    iam_client = create_iam_client()
    
    for policy in iam_client.list_policies(MaxItems=1000)["Policies"]:
        if BEIWE_AUTOMATION_POLICY_NAME == policy['PolicyName']:
            return policy
    
    return iam_client.create_policy(
            PolicyName="beiwe_automation_policy",
            PolicyDocument=get_automation_policy(),
            Description="permissions the beiwe elastic beanstalk application."
    )['Policy']


def iam_find_instance_profile(iam_client, instance_profile_name):
    """ We would use iam_client.get_iam_instance_profile, but it throws several different errors
    that are literally more specific than any error we can import from boto and catch explicitly
    because it is generated by an error factory. """
    # pprint(iam_client.list_instance_profiles())
    for instance_profile in iam_client.list_instance_profiles()['InstanceProfiles']:
        if instance_profile['InstanceProfileName'] == instance_profile_name:
            return instance_profile
    raise IamEntityMissingError("IAM could not find Instance Profile %s" % instance_profile_name)


def iam_purge_instance_profiles():
    """ Instance profiles are not well-exposed in the AWS Console (the online management tool),
    this command will delete the instance profiles that are defined in this codebase. Note: you
    will probably have to go and manually delete a service role, though the error message should
    provide sufficient information for you to work out what to do. """
    iam_client = create_iam_client()
    try:
        iam_client.delete_instance_profile(InstanceProfileName=EB_INSTANCE_PROFILE_ROLE)
        print("Deleted", EB_INSTANCE_PROFILE_ROLE)
    except Exception as e:
        print(e)
    
    try:
        iam_client.delete_instance_profile(InstanceProfileName=EB_INSTANCE_PROFILE_NAME)
        print("Deleted", EB_INSTANCE_PROFILE_NAME)
    except Exception as e:
        print(e)
    
    try:
        iam_client.delete_role(RoleName=EB_INSTANCE_PROFILE_ROLE)
        print("Deleted", EB_INSTANCE_PROFILE_ROLE)
    except Exception as e:
        print(e)
    
    try:
        iam_client.delete_role(RoleName=EB_SERVICE_ROLE)
        print("Deleted", EB_SERVICE_ROLE)
    except Exception as e:
        print(e)
    
   
def get_or_create_s3_access_policy(s3_bucket_name):
    iam_client = create_iam_client()
    
    policy_name = "s3-data-access-" + s3_bucket_name
    for policy in iam_client.list_policies(MaxItems=1000)["Policies"]:
        if policy_name == policy['PolicyName']:
            return policy

    policy = get_aws_access_policy() % s3_bucket_name
    
    return iam_client.create_policy(
            PolicyName=policy_name,
            PolicyDocument=policy,
            Description="allows read and write access to s3 bucket %s" % s3_bucket_name
    )['Policy']


def create_server_access_credentials(s3_bucket_name):
    iam_client = create_iam_client()
    user_name = "s3-data-access-user-" + s3_bucket_name
    user_name = user_name[:63] # limited to 63 characters
    user_info = iam_client.create_user(UserName=user_name)
    s3_policy = get_or_create_s3_access_policy(s3_bucket_name)
    iam_client.attach_user_policy(UserName=user_name, PolicyArn=s3_policy['Arn'])

    iam_resource = create_iam_resource()
    iam_user = iam_resource.User(user_name)
    access_key_pair = iam_user.create_access_key_pair()
    return {
        "BEIWE_SERVER_AWS_ACCESS_KEY_ID": access_key_pair.access_key_id,
        "BEIWE_SERVER_AWS_SECRET_ACCESS_KEY": access_key_pair.secret_access_key,
    }
