#!/usr/bin/python
# Copyright (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

ANSIBLE_METADATA = {'metadata_version': '1.1',
                    'status': ['preview'],
                    'supported_by': 'community'
                    }

DOCUMENTATION = '''
---
module: aws_elasticbeanstalk_app

short_description: create, update, and delete an elastic beanstalk application

version_added: "2.5"

description:
    - "creates, updates, deletes beanstalk applications if app_name is provided"

options:
  app_name:
    description:
      - name of the beanstalk application you wish to manage
    aliases: [ 'name' ]
  description:
    description:
      - the description of the application
  state:
    description:
      - whether to ensure the application is present or absent
    default: present
    choices: ['absent','present']
  terminate_by_force:
    description:
      - when set to true, running environments will be terminated before deleting the application
    default: false
author:
    - Harpreet Singh (@hsingh)
    - Stephen Granger (@viper233)
extends_documentation_fragment:
    - aws
    - ec2
'''

EXAMPLES = '''
# Create or update an application
- aws_elasticbeanstalk_app:
    app_name: Sample_App
    description: "Hello World App"
    state: present

# Delete application
- aws_elasticbeanstalk_app:
    app_name: Sample_App
    state: absent

'''

RETURN = '''
app:
    description: beanstalk application
    returned: always
    type: dict
    sample: {
        "ApplicationName": "app-name",
        "ConfigurationTemplates": [],
        "DateCreated": "2016-12-28T14:50:03.185000+00:00",
        "DateUpdated": "2016-12-28T14:50:03.185000+00:00",
        "Description": "description",
        "Versions": [
            "1.0.0",
            "1.0.1"
        ]
    }
output:
    description: message indicating what change will occur
    returned: in check mode
    type: string
    sample: App is up-to-date
'''

try:
    from botocore.exceptions import BotoCoreError, ClientError
except ImportError:
    pass  # handled by AnsibleAWSModule

from ansible.module_utils.aws.core import AnsibleAWSModule
from ansible.module_utils.ec2 import boto3_conn, ec2_argument_spec, get_aws_connection_info


def describe_app(ebs, app_name, module):
    apps = list_apps(ebs, app_name, module)

    return None if len(apps) != 1 else apps[0]


def list_apps(ebs, app_name, module):
    try:
        if app_name is not None:
            apps = ebs.describe_applications(ApplicationNames=[app_name])
        else:
            apps = ebs.describe_applications()
    except (BotoCoreError, ClientError) as e:
        module.fail_json_aws(e, msg="Could not describe application")

    return apps.get("Applications", [])


def check_app(ebs, app, module):
    app_name = module.params['app_name']
    description = module.params['description']
    state = module.params['state']
    terminate_by_force = module.params['terminate_by_force']

    result = {}

    if state == 'present' and app is None:
        result = dict(changed=True, output="App would be created")
    elif state == 'present' and app.get("Description", None) != description:
        result = dict(changed=True, output="App would be updated", app=app)
    elif state == 'present' and app.get("Description", None) == description:
        result = dict(changed=False, output="App is up-to-date", app=app)
    elif state == 'absent' and app is None:
        result = dict(changed=False, output="App does not exist", app={})
    elif state == 'absent' and app is not None:
        result = dict(changed=True, output="App will be deleted", app=app)
    elif state == 'absent' and app is not None and terminate_by_force is True:
        result = dict(changed=True, output="Running environments terminated before the App will be deleted", app=app)

    module.exit_json(**result)


def filter_empty(**kwargs):
    retval = {}
    for k, v in kwargs.items():
        if v:
            retval[k] = v
    return retval


def main():
    argument_spec = ec2_argument_spec()

    argument_spec.update(
        dict(
            app_name=dict(aliases=['name'], type='str', required=False),
            description=dict(),
            state=dict(choices=['present', 'absent'], default='present'),
            terminate_by_force=dict(type='bool', default=False, required=False)
        )
    )

    module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True)

    app_name = module.params['app_name']
    description = module.params['description']
    state = module.params['state']
    terminate_by_force = module.params['terminate_by_force']

    if app_name is None:
        module.fail_json(msg='Module parameter "app_name" is required')

    result = {}
    region, ec2_url, aws_connect_params = get_aws_connection_info(module, boto3=True)
    ebs = boto3_conn(module, conn_type='client', resource='elasticbeanstalk',
                     region=region, endpoint=ec2_url, **aws_connect_params)

    app = describe_app(ebs, app_name, module)

    if module.check_mode:
        check_app(ebs, app, module)
        module.fail_json(msg='ASSERTION FAILURE: check_app() should not return control.')

    if state == 'present':
        if app is None:
            try:
                create_app = ebs.create_application(**filter_empty(ApplicationName=app_name,
                                                    Description=description))
            except (BotoCoreError, ClientError) as e:
                module.fail_json_aws(e, msg="Could not create application")

            app = describe_app(ebs, app_name, module)

            result = dict(changed=True, app=app)
        else:
            if app.get("Description", None) != description:
                try:
                    if not description:
                        ebs.update_application(ApplicationName=app_name)
                    else:
                        ebs.update_application(ApplicationName=app_name, Description=description)
                except (BotoCoreError, ClientError) as e:
                    module.fail_json_aws(e, msg="Could not update application")

                app = describe_app(ebs, app_name, module)

                result = dict(changed=True, app=app)
            else:
                result = dict(changed=False, app=app)

    else:
        if app is None:
            result = dict(changed=False, output='Application not found', app={})
        else:
            try:
                if terminate_by_force:
                    # Running environments will be terminated before deleting the application
                    ebs.delete_application(ApplicationName=app_name, TerminateEnvByForce=terminate_by_force)
                else:
                    ebs.delete_application(ApplicationName=app_name)
                changed = True
            except BotoCoreError as e:
                module.fail_json_aws(e, msg="Cannot terminate app")
            except ClientError as e:
                if 'It is currently pending deletion.' not in e.response['Error']['Message']:
                    module.fail_json_aws(e, msg="Cannot terminate app")
                else:
                    changed = False

            result = dict(changed=changed, app=app)

    module.exit_json(**result)

if __name__ == '__main__':
    main()
