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

"""
from collections import OrderedDict
from typing import Any
from typing import Dict

import dict_tools.differ as differ

__contracts__ = ["resource"]


async def present(
    hub,
    ctx,
    name: str,
    acl: str = None,
    create_bucket_configuration: dict = None,
    grant_full_control: str = None,
    grant_read: str = None,
    grant_read_acp: str = None,
    grant_write: str = None,
    grant_write_acp: str = None,
    object_lock_enabled_for_bucket: bool = None,
    object_ownership: str = None,
) -> Dict[str, Any]:
    r"""
    **Autogenerated function**
        ACL (string) -- The canned ACL to apply to the bucket.

        name (string) --
        [REQUIRED]

        The name of the bucket to create.

        CreateBucketConfiguration (dict) --
            The configuration information for the bucket.

        LocationConstraint (string) --
            Specifies the Region where the bucket will be created.
            If you don't specify a Region, the bucket is created
            in the US East (N. Virginia) Region (us-east-1).

        GrantFullControl (string) --
            Allows grantee the read, write, read ACP, and write ACP permissions on the bucket.

        GrantRead (string) --
            Allows grantee to list the objects in the bucket.

        GrantReadACP (string) --
            Allows grantee to read the bucket ACL.

        GrantWrite (string) --
            Allows grantee to create new objects in the bucket.

        For the bucket and object owners of existing objects, also allows deletions and overwrites of those objects.

        GrantWriteACP (string) --
            Allows grantee to write the ACL for the applicable bucket.

        ObjectLockEnabledForBucket (boolean) --
            Specifies whether you want S3 Object Lock to be enabled for the new bucket.

        ObjectOwnership (string) --
            The container element for object ownership for a bucket's ownership controls.

        BucketOwnerPreferred -
            Objects uploaded to the bucket change ownership to the bucket owner if
            the objects are uploaded with the bucket-owner-full-control canned ACL.

        ObjectWriter -
            The uploading account will own the object if the object is uploaded
            with the bucket-owner-full-control canned ACL.

        BucketOwnerEnforced -
            Access control lists (ACLs) are disabled and no longer affect permissions.
            The bucket owner automatically owns and has full control over every object
            in the bucket. The bucket only accepts PUT requests that don't specify an
            ACL or bucket owner full control ACLs, such as the bucket-owner-full-control
            canned ACL or an equivalent form of this ACL expressed in the XML format.


    Request Syntax:
        resource:
          aws.s3.bucket.present:
            - ACL: 'private'|'public-read'|'public-read-write'|'authenticated-read'
            - Bucket: string
            - CreateBucketConfiguration: {
                'af-south-1'|'ap-east-1'|'ap-northeast-1'|'ap-northeast-2'|
                'ap-northeast-3'|'ap-south-1'|'ap-southeast-1'|'ap-southeast-2'|'ca-central-1'|
                'cn-north-1'|'cn-northwest-1'|'EU'|'eu-central-1'|'eu-north-1'|'eu-south-1'|
                'eu-west-1'|'eu-west-2'|'eu-west-3'|'me-south-1'|'sa-east-1'|'us-east-2'|
                'us-gov-east-1'|'us-gov-west-1'|'us-west-1'|'us-west-2'}
            - GrantFullControl: string
            - GrantRead: string
            - GrantReadACP: string
            - GrantWrite: string
            - GrantWriteACP: string
            - ObjectLockEnabledForBucket: True|False
            - ObjectOwnership: BucketOwnerPreferred'|'ObjectWriter'|'BucketOwnerEnforced'

    Returns:
        Dict[str, Any]

    Examples:

        .. code-block:: sls

            test_bucket-bb7bb32e9533:
              aws.s3.bucket.present:
                - name: test_bucket-bb7bb32e9533
                - acl: 'private'
                - create_bucket_configuration: 'us-west-1'
                - object_lock_enabled_for_bucket: False
                - object_ownership: 'BucketOwnerEnforced'


    """
    result = dict(comment="", old_state=None, new_state=None, name=name, result=True)
    resource = hub.tool.boto3.resource.create(ctx, "s3", "Bucket", name)
    before = await hub.tool.boto3.resource.describe(resource)
    result["new_state"] = before

    if ctx.get("test", False):
        if before:
            result["comment"] = f"Would update aws.s3.bucket {name}"
            result["result"] = True
        else:
            result["comment"] = f"Would create aws.s3.bucket {name}"
            result["result"] = True
        return result

    if not before:
        try:
            # Build out S3 bucket creation here
            update_ret = await hub.exec.boto3.client.s3.create_bucket(
                ctx,
                **{
                    "ACL": acl,
                    "Bucket": name,
                    "CreateBucketConfiguration": create_bucket_configuration,
                    "GrantFullControl": grant_full_control,
                    "GrantRead": grant_read,
                    "GrantReadACP": grant_read_acp,
                    "GrantWrite": grant_write,
                    "GrantWriteACP": grant_write_acp,
                    "ObjectLockEnabledForBucket": object_lock_enabled_for_bucket,
                    "ObjectOwnership": object_ownership,
                },
            )
            if not update_ret["result"]:
                result["comment"] = update_ret["comment"]
                result["result"] = False
                return result
        except hub.tool.boto3.exception.ClientError as e:
            result["comment"] = f"{e.__class__.__name__}: {e}"
            result["result"] = False
            return result
        result["comment"] = f"'Created {name}'"
    else:
        result["comment"] = f"Resource {name} already exists."

    if not before:
        try:
            after = await hub.tool.boto3.resource.describe(resource)
        except Exception as e:
            result["comment"] = str(e)
            result["result"] = False
        else:
            result["old_state"] = before
            result["new_state"] = after
    return result


async def absent(hub, ctx, name: str) -> Dict[str, Any]:
    r"""
    **Autogenerated function**

    Deletes the specified s3 bucket.

    Args:
        name(Text): The name of the s3 bucket.

    Returns:
        Dict[str, Any]

    Examples:

        .. code-block:: sls

            bucket-5435423646-456464:
              aws.s3.bucket.absent:
                - name: value
    """

    result = dict(comment="", old_state=None, new_state=None, name=name, result=True)
    resource = hub.tool.boto3.resource.create(ctx, "s3", "Bucket", name)
    before = await hub.tool.boto3.resource.describe(resource)

    if not before:
        result["comment"] = f"'{name}' already absent"
    elif ctx.get("test", False):
        result["comment"] = f"Would delete aws.s3.bucket {name}"
        return result
    else:
        try:
            ret = await hub.exec.boto3.client.s3.delete_bucket(ctx, **{"Bucket": name})
            result["result"] = ret["result"]
            if not result["result"]:
                result["comment"] = ret["comment"]
                result["result"] = False
                return result
            result["comment"] = f"Deleted '{name}'"
        except hub.tool.boto3.exception.ClientError as e:
            result["comment"] = f"{e.__class__.__name__}: {e}"
        result["old_state"] = before

    return result


async def describe(hub, ctx) -> Dict[str, Dict[str, Any]]:
    """
    Obtain S3 buckets under the given context for any user.
    :param hub:
    :param ctx:
    :return:
    """
    result = {}
    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"]:
        resource_id = bucket.get("Name")
        describe_parameters = OrderedDict(
            {
                "Name": "name",
                "CreationDate": "creation_date",
            }
        )
        resource_translated = []
        for parameter_old_key, parameter_new_key in describe_parameters.items():
            if bucket.get(parameter_old_key) is not None:
                resource_translated.append(
                    {parameter_new_key: bucket.get(parameter_old_key)}
                )
        result[resource_id] = {"aws.s3.bucket.present": resource_translated}
    return result
