#!/usr/bin/python
# (c) 2016, Pierre Jodouin <pjodouin@virtualcomputing.solutions>
#
# Ansible 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.
#
# Ansible 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 Ansible.  If not, see <http://www.gnu.org/licenses/>.

import sys
import json
from hashlib import md5

try:
    import boto3
    import boto              # seems to be needed for ansible.module_utils
    from botocore.exceptions import ClientError, ParamValidationError, MissingParametersError
    HAS_BOTO3 = True
except ImportError:
    HAS_BOTO3 = False


DOCUMENTATION = '''
---
module: lambda_event
short_description: Creates, updates or deletes AWS Lambda function event mappings.
description:
    - This module allows the management of AWS Lambda function event source mappings such as S3 bucket
      events, DynamoDB and Kinesis streaming events via the Ansible framework.
      It is idempotent and supports "Check" mode.  Use module M(lambda) to manage the lambda
      function itself and M(lambda_alias) to manage function aliases.
version_added: "2.1"
author: Pierre Jodouin (@pjodouin)
options:
  lambda_function_arn:
    description:
      - The name or ARN of the lambda function.
    required: true
    aliases: ['function_name', 'function_arn']
  state:
    description:
      - Describes the desired state and defaults to "present".
    required: true
    default: "present"
    choices: ["present", "absent"]
  alias:
    description:
      - Name of the function alias. Mutually exclusive with C(version).
    required: true
  version:
    description:
      -  Version of the Lambda function. Mutually exclusive with C(alias).
    required: false
  event_source:
    description:
      -  Source of the event that triggers the lambda function.
    required: true
    choices: ['s3', 'Kinesis', 'DynamoDB', 'SNS']
  source_params:
    description:
      -  Sub-parameters required for event source.
      -  I(== S3 event source ==)
      -  C(id) Unique ID for this source event.
      -  C(bucket) Name of source bucket.
      -  C(prefix) Bucket prefix (e.g. images/)
      -  C(suffix) Bucket suffix (e.g. log)
      -  C(events) List of events (e.g. ['s3:ObjectCreated:Put'])
      -  I(== stream event source ==)
      -  C(source_arn) The Amazon Resource Name (ARN) of the Kinesis or DynamoDB stream that is the event source.
      -  C(enabled) Indicates whether AWS Lambda should begin polling the event source. Default is True.
      -  C(batch_size) The largest number of records that AWS Lambda will retrieve from your event source at the
         time of invoking your function. Default is 100.
      -  C(starting_position) The position in the stream where AWS Lambda should start reading.
         Choices are TRIM_HORIZON or LATEST.
      -  I(== SNS event source ==)
      -  C(id) Unique ID for this source event.
      -  C(topic_arn) The ARN of the topic to which you want to subscribe the lambda function.
    required: true
requirements:
    - boto3
extends_documentation_fragment:
    - aws

'''

EXAMPLES = '''
---
# Simple example that creates a lambda event notification for an S3 bucket
- hosts: localhost
  gather_facts: no
  vars:
    state: present
  tasks:
  - name: S3 event mapping
    lambda_event:
      state: "{{ state | default('present') }}"
      event_source: s3
      function_name: ingestData
      alias: Dev
      source_params:
        id: lambda-s3-myBucket-create-data-log
        bucket: buzz-scanner
        prefix: twitter
        suffix: log
        events:
        - s3:ObjectCreated:Put

# Example that creates a lambda event notification for a DynamoDB stream
- hosts: localhost
  gather_facts: no
  vars:
    state: present
  tasks:
  - name: DynamoDB stream event mapping
    lambda_event:
      state: "{{ state | default('present') }}"
      event_source: stream
      function_name: "{{ function_name }}"
      alias: Dev
      source_params:
        source_arn: arn:aws:dynamodb:us-east-1:123456789012:table/tableName/stream/2016-03-19T19:51:37.457
        enabled: True
        batch_size: 100
        starting_position: TRIM_HORIZON

  - name: show source event
    debug: var=lambda_stream_events

# example of SNS topic
  - name: SNS event mapping
    lambda_event:
      state: "{{ state | default('present') }}"
      event_source: sns
      function_name: SaveMessage
      alias: Prod
      source_params:
        id: lambda-sns-topic-notify
        topic_arn: arn:aws:sns:us-east-1:123456789012:sns-some-topic

  - name: show SNS event mapping
    debug: var=lambda_sns_event

'''

RETURN = '''
---
lambda_s3_events:
    description: list of dictionaries returned by the API describing S3 event mappings
    returned: success
    type: list
lambda_stream_events:
    description: list of dictionaries returned by the API describing stream event mappings
    returned: success
    type: list
lambda_sns_event:
    description: dictionary returned by the API describing SNS event mapping
    returned: success
    type: dict

'''

# ---------------------------------------------------------------------------------------------------
#
#   Helper Functions & classes
#
# ---------------------------------------------------------------------------------------------------


class AWSConnection:
    """
    Create the connection object and client objects as required.
    """

    def __init__(self, ansible_obj, resources, boto3=True):

        try:
            self.region, self.endpoint, aws_connect_kwargs = get_aws_connection_info(ansible_obj, boto3=boto3)

            self.resource_client = dict()
            if not resources:
                resources = ['lambda']

            resources.append('iam')

            for resource in resources:
                aws_connect_kwargs.update(dict(region=self.region,
                                               endpoint=self.endpoint,
                                               conn_type='client',
                                               resource=resource
                                               ))
                self.resource_client[resource] = boto3_conn(ansible_obj, **aws_connect_kwargs)

            # if region is not provided, then get default profile/session region
            if not self.region:
                self.region = self.resource_client['lambda'].meta.region_name

        except (ClientError, ParamValidationError, MissingParametersError) as e:
            ansible_obj.fail_json(msg="Unable to connect, authorize or access resource: {0}".format(e))

        # set account ID
        try:
            self.account_id = self.resource_client['iam'].get_user()['User']['Arn'].split(':')[4]
        except (ClientError, ValueError, KeyError, IndexError):