"""
Autogenerated using `pop-create-idem <https://gitlab.com/saltstack/pop/pop-create-idem>`__

"""
import copy
from typing import Any
from typing import Dict
from typing import List

__contracts__ = ["resource"]

TREQ = {
    "present": {"require": ["aws.s3.bucket.present"]},
}

BUCKET_NOT_EXISTS_ERROR_MESSAGE = "ClientError: An error occurred (404) when calling the HeadBucket operation: Not Found"


async def present(
    hub,
    ctx,
    name: str,
    notifications: Dict[str, List],
    resource_id: str = None,
) -> Dict[str, Any]:
    r"""
    Bucket Notification allows user to receive notifications when certain events happen in the S3 bucket. To enable
    notifications, add a notification configuration that identifies the events that you want Amazon S3 to publish.
    Make sure that it also identifies the destinations where you want Amazon S3 to send the notifications. You store
    this configuration in the notification subresource that's associated with a bucket
    Amazon S3 can publish notifications for the following events:

    a) New object created events
    b) Object removal events
    c) Restore object events
    d) Reduced Redundancy Storage (RRS) object lost events
    e) Replication events
    f) S3 Lifecycle expiration events
    g) S3 Lifecycle transition events
    h) S3 Intelligent-Tiering automatic archival events
    i) Object tagging events
    j) Object ACL PUT events

    Amazon S3 can send event notification messages to the following destinations. User can specify the Amazon Resource
    Name (ARN) value of these destinations in the notification configuration.

    1) Amazon Simple Notification Service (Amazon SNS) topics
    2) Amazon Simple Queue Service (Amazon SQS) queues
    3) AWS Lambda function

    Args:
        hub:
        ctx:
        name(Text): Name of the bucket on which notification needs to be configured.
        notifications(List): List of notification configurations of all the types.
        resource_id(Text, optional): Name of the bucket and 'notifications' keyword with a seperator '-'.

    Returns:
        Dict[str, Any]

    Request Syntax:
        [bucket_name]-notifications:
              aws.s3.bucket_notifications.present:
                - name: [string]
                - resource_id: [string]
                - notifications
                    -LambdaFunctionConfigurations:
                        - Events:
                          - [string]
                          Filter:
                            Key:
                              FilterRules:
                              - Name: Prefix
                                Value: ''
                              - Name: Suffix
                                Value: [string]
                          Id: [string]
                          LambdaFunctionArn: [string]
                    -TopicConfigurations:

    Examples:

        .. code-block:: sls

            test-bucket-notifications:
              aws.s3.bucket_notifications.present:
                - name: test-bucket
                - resource_id: test-bucket-notifications
                - notifications
                    -LambdaFunctionConfigurations:
                        - Events:
                          - s3:ObjectCreated:*
                          Filter:
                            Key:
                              FilterRules:
                              - Name: Prefix
                                Value: ''
                              - Name: Suffix
                                Value: .jpeg
                          Id: test
                          LambdaFunctionArn: arn:aws:lambda:us-west-1:000000000000:function:test
                    -TopicConfigurations:
    """

    result = dict(comment=(), old_state=None, new_state=None, name=name, result=True)

    if resource_id:
        name = resource_id.split("-notifications")[0]

    # Check if bucket exists
    ret = await hub.exec.boto3.client.s3.head_bucket(ctx, Bucket=name)
    if not ret["result"]:
        if BUCKET_NOT_EXISTS_ERROR_MESSAGE in ret["comment"]:
            result["comment"] = (f"aws.s3.bucket with name: {name} does not exists",)
        else:
            result["comment"] = ret["comment"]
        result["result"] = False
        return result

    # Get current notification configurations for the bucket
    bucket_notification_response = (
        await hub.exec.boto3.client.s3.get_bucket_notification_configuration(
            ctx, Bucket=name
        )
    )
    if bucket_notification_response["result"]:
        result[
            "old_state"
        ] = hub.tool.aws.s3.conversion_utils.convert_raw_bucket_notification_to_present(
            bucket_notification_response["ret"], name
        )
    else:
        result["comment"] = ret["comment"]
        result["result"] = False
        return result

    # check and create payload for creating notification configurations to avoid redundant calls
    notifications_payload = hub.tool.aws.s3.utils.compare_and_get_notification_payload(
        notifications, result["old_state"]
    )

    if notifications_payload:
        if ctx.get("test", False):
            result["new_state"] = hub.tool.aws.test_state_utils.generate_test_state(
                enforced_state={},
                desired_state={
                    "name": name,
                    "resource_id": name + "-notifications",
                    "notifications": notifications_payload,
                },
            )
            if resource_id:
                result["comment"] = (
                    f"Would update aws.s3.bucket_notifications in bucket:'{name}'.",
                )
            else:
                result["comment"] = (
                    f"Would create aws.s3.bucket_notifications in bucket:'{name}'.",
                )
            return result
        notification_ret = (
            await hub.exec.boto3.client.s3.put_bucket_notification_configuration(
                ctx, Bucket=name, NotificationConfiguration=notifications_payload
            )
        )
        if not notification_ret["result"]:
            result["result"] = False
            result["comment"] = notification_ret["comment"]
            return result

        bucket_notification_result = (
            await hub.exec.boto3.client.s3.get_bucket_notification_configuration(
                ctx, Bucket=name
            )
        )
        if bucket_notification_result["result"]:
            result[
                "new_state"
            ] = hub.tool.aws.s3.conversion_utils.convert_raw_bucket_notification_to_present(
                bucket_notification_result["ret"], name
            )
        else:
            result["comment"] = bucket_notification_result["comment"]
            result["result"] = False
    else:
        result["new_state"] = copy.deepcopy(result["old_state"])
    return result


async def absent(
    hub,
    ctx,
    name: str,
    resource_id: str = None,
) -> Dict[str, Any]:
    r"""
    This action enables you to delete multiple notification configurations from a bucket using a single HTTP request.
    If you know the object keys that you want to delete, then this action provides a suitable alternative to sending
    individual delete requests, reducing per-request overhead. For each configuration, Amazon S3 performs a delete
    action and returns the result of that delete, success, or failure, in the response.

    Args:
        hub:
        ctx:
        name(Text): Name of the bucket on which notification needs to be configured.
        resource_id(Text, optional): Name of the bucket and 'notifications' keyword with a seperator '-'.

    Returns:
        Dict[str, Any]

    Request Syntax:
        [bucket_name-notifications]:
              aws.s3.bucket_notifications.absent:
                - name: [string]
                - resource_id: [string]


    Examples:

        .. code-block:: sls

            test-bucket-notifications:
              aws.s3.bucket_notifications.absent:
                - name: test-bucket
                - resource_id: test-bucket-notifications
    """

    result = dict(comment=(), old_state=None, new_state=None, name=name, result=True)

    if resource_id:
        name = resource_id.split("-notifications")[0]

    # check if the bucket exists
    ret = await hub.exec.boto3.client.s3.head_bucket(ctx, Bucket=name)

    if not ret["result"]:
        if BUCKET_NOT_EXISTS_ERROR_MESSAGE in ret["comment"]:
            result["comment"] = (f"aws.s3.bucket with name: {name} does not exists",)
        else:
            result["comment"] = ret["comment"]
        result["result"] = False
        return result

    # Get current notification configurations attached to the bucket
    bucket_notification_response = (
        await hub.exec.boto3.client.s3.get_bucket_notification_configuration(
            ctx, Bucket=name
        )
    )
    if bucket_notification_response["result"]:
        result[
            "old_state"
        ] = hub.tool.aws.s3.conversion_utils.convert_raw_bucket_notification_to_present(
            bucket_notification_response["ret"], name
        )
    else:
        result["comment"] = bucket_notification_response["comment"]
        result["result"] = False
        return result

    if ctx.get("test", False):
        result["comment"] = (
            f"Would remove aws.s3.bucket_notifications from bucket:'{name}'.",
        )
        return result
    if not result["old_state"]:
        result["comment"] = (
            f"aws.s3.bucket_notifications in bucket:'{name}' already absent.",
        )
        return result
    notification_ret = (
        await hub.exec.boto3.client.s3.put_bucket_notification_configuration(
            ctx, Bucket=name, NotificationConfiguration={}
        )
    )
    if not notification_ret["result"]:
        result["result"] = False
        result["comment"] = notification_ret["comment"]
        return result

    after_bucket_notification_response = (
        await hub.exec.boto3.client.s3.get_bucket_notification_configuration(
            ctx, Bucket=name
        )
    )
    if after_bucket_notification_response["result"]:
        result[
            "new_state"
        ] = hub.tool.aws.s3.conversion_utils.convert_raw_bucket_notification_to_present(
            after_bucket_notification_response["ret"], name
        )
    else:
        result["comment"] = after_bucket_notification_response["comment"]
        result["result"] = False
    return result


async def describe(hub, ctx) -> Dict[str, Dict[str, Any]]:
    """
    Obtain S3 bucket notifications for each bucket under the given context for any user.

    Returns:
        Dict[str, Any]

    Examples:

        .. code-block:: bash

            $ idem describe aws.s3.bucket_notification
    """
    result = {}
    # To describe the notification configurations of all the buckets, we first need to list all the buckets,
    # then get the notification configurations of each bucket
    ret = await hub.exec.boto3.client.s3.list_buckets(ctx)

    if not ret["result"]:
        hub.log.debug(f"Could not describe S3 buckets {ret['comment']}")
        return {}

    for bucket in ret["ret"]["Buckets"]:
        bucket_name = bucket.get("Name")
        # get notification configuration for each bucket
        try:
            bucket_notification_response = (
                await hub.exec.boto3.client.s3.get_bucket_notification_configuration(
                    ctx, Bucket=bucket_name
                )
            )
            if bucket_notification_response["result"]:
                translated_resource = hub.tool.aws.s3.conversion_utils.convert_raw_bucket_notification_to_present(
                    bucket_notification_response["ret"], bucket_name
                )

                result[bucket_name + "-notifications"] = {
                    "aws.s3.bucket_notifications.present": [
                        {parameter_key: parameter_value}
                        for parameter_key, parameter_value in translated_resource.items()
                    ]
                }
            else:
                hub.log.warning(
                    f"Could not get attached notification configuration for bucket {bucket_name} with error"
                    f" {bucket_notification_response['comment']} . Describe will skip this bucket and continue."
                )

        except Exception as e:
            hub.log.warning(
                f"Could not get attached notification configuration for bucket {bucket_name} with error"
                f" {e} . Describe will skip this bucket and continue."
            )
    return result
