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

"""
import copy
from dataclasses import field
from dataclasses import make_dataclass
from typing import Any
from typing import Dict
from typing import List

__contracts__ = ["resource"]


async def present(
    hub,
    ctx,
    name: str,
    resource_id: str = None,
    acl: str = None,
    create_bucket_configuration: make_dataclass(
        "CreateBucketConfiguration", [("LocationConstraint", str, field(default=None))]
    ) = 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,
    object_lock_configuration: dict = None,
    tags: Dict[str, Any]
    or List[
        make_dataclass("Tag", [("Key", str), ("Value", str, field(default=None))])
    ] = None,
) -> Dict[str, Any]:
    r"""
        name (Text) -- An Idem name of the resource.
        create_bucket_configuration(Dict[str, Any], optional): The configuration information for the bucket. Defaults to None.
            LocationConstraint (str, optional): 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).
        resource_id(Text, optional): AWS S3 Bucket name
        acl (Text, optional) -- The canned ACL to apply to the bucket.
        grant_full_control (Text, optional) --
            Allows grantee the read, write, read ACP, and write ACP permissions on the bucket.
        grant_read (Text, optional) --
            Allows grantee to list the objects in the bucket.
        grant_read_acp (Text, optional) --
            Allows grantee to read the bucket ACL.
        grant_write (Text, optional) --
            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.
        grant_write_acp (Text, optional) --
            Allows grantee to write the ACL for the applicable bucket.
        object_lock_enabled_for_bucket (boolean, optional) --
            Specifies whether you want S3 Object Lock to be enabled for the new bucket.
            ObjectLockConfiguration (dict) --
        The Object Lock configuration that you want to apply to the specified bucket.
            ObjectLockEnabled (string) -- Indicates whether this bucket has an Object Lock configuration enabled. Enable ObjectLockEnabled
                                        when you apply ObjectLockConfiguration to a bucket.
            Rule (dict) -- Specifies the Object Lock rule for the specified object. Enable the this rule when you apply ObjectLockConfiguration
                            to a bucket. Bucket settings require both a mode and a period. The period can be either Days or Years but you must select one.
                            You cannot specify Days and Years at the same time.
                DefaultRetention (dict) -- The default Object Lock retention mode and period that you want to apply to new objects placed
                                    in the specified bucket. Bucket settings require both a mode and a period. The period can be either Days or Years
                                    but you must select one. You cannot specify Days and Years at the same time.
                    Mode (string) -- The default Object Lock retention mode you want to apply to new objects placed in the specified bucket.
                                    Must be used with either Days or Years .
                    Days (integer) -- The number of days that you want to specify for the default retention period. Must be used with Mode .
                    Years (integer) -- The number of years that you want to specify for the default retention period. Must be used with Mode .
        object_ownership (Text, optional) --
            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.
        tags(Dict or List, optional): Dict in the format of {tag-key: tag-value} or List of tags in the format of
            [{"Key": tag-key, "Value": tag-value}] to associate with the bucket.
            Each tag consists of a key name and an associated value. Defaults to None.
            * Key (str, optional): The key of the tag. Constraints: Tag keys are case-sensitive and accept a maximum of 127 Unicode
                characters. May not begin with aws:.
            * Value(str, optional): The value of the tag. Constraints: Tag values are case-sensitive and accept a maximum of 256
                Unicode characters.

    Request Syntax:
        [s3-resource-id]::
          aws.s3.bucket.present:
            - acl: 'private'|'public-read'|'public-read-write'|'authenticated-read'
            - bucket: string
            - create_bucket_configuration:
                LocationConstraint: string
            - grant_full_control: string
            - grant_read: string
            - grant_read_acp: string
            - grant_write: string
            - grant_write_acp: string
            - object_lock_enabled_for_bucket: True|False
            - object_lock_configuration: 'dict'
                ObjectLockEnabled: 'string'
                  Rule:
                    DefaultRetention: 'dict'
                      Mode: 'string'
                      Days: 'integer'
            - object_ownership: BucketOwnerPreferred'|'ObjectWriter'|'BucketOwnerEnforced'
            - tags:
                - Key: 'string'
                  Value: 'string'

    Returns:
        Dict[str, Any]

    Examples:

        .. code-block:: sls

            test_bucket-bb7bb32e9533:
              aws.s3.bucket.present:
                - name: test_bucket-bb7bb32e9533
                - acl: 'private'
                - create_bucket_configuration:
                    LocationConstraint: 'sa-east-1'
                - object_lock_enabled_for_bucket: True
                - object_lock_configuration:
                    ObjectLockEnabled: 'Enabled'
                    Rule:
                      DefaultRetention:
                        Mode: GOVERNANCE
                        Days: 1
                - object_ownership: 'BucketOwnerEnforced'
                - tags:
                    - Key: Name1
                      Value: s3-test1
                    - Key: Name2
                      Value: s3-test2


    """
    result = dict(comment=(), old_state=None, new_state=None, name=name, result=True)
    before = None
    resource_updated = False
    tags_list = None
    tags_dict = None
    if tags is not None:
        if isinstance(tags, Dict):
            tags_list = hub.tool.aws.tag_utils.convert_tag_dict_to_list(tags)
            tags_dict = tags
        else:
            tags_list = tags
            tags_dict = hub.tool.aws.tag_utils.convert_tag_list_to_dict(tags)
    if resource_id:
        resource = hub.tool.boto3.resource.create(ctx, "s3", "Bucket", resource_id)
        before = await hub.tool.boto3.resource.describe(resource)
    if before:
        convert_ret = await hub.tool.aws.s3.conversion_utils.convert_raw_s3_to_present(
            ctx, idem_resource_name=name
        )
        result["result"] = convert_ret["result"]
        if not result["result"]:
            result["comment"] = result["comment"] + convert_ret["comment"]
            return result
        result["old_state"] = convert_ret["ret"]

        plan_state = copy.deepcopy(result["old_state"])
        result["comment"] = hub.tool.aws.comment_utils.already_exists_comment(
            "aws.s3.bucket", name
        )
        if (
            object_lock_enabled_for_bucket is not None
            and object_lock_enabled_for_bucket
            != result["old_state"].get("object_lock_enabled_for_bucket")
        ):
            if not ctx.get("test", False):
                update_ret = (
                    await hub.exec.boto3.client.s3.put_object_lock_configuration(
                        ctx,
                        Bucket=resource_id,
                        ObjectLockConfiguration=object_lock_configuration,
                    )
                )
                if not update_ret["result"]:
                    result["comment"] = update_ret["comment"]
                    result["result"] = False
                    return result
                result["comment"] = result["comment"] + update_ret["comment"]
                result["result"] = update_ret["result"]
                resource_updated = resource_updated or bool(update_ret["ret"])
            else:
                plan_state[
                    "object_lock_enabled_for_bucket"
                ] = object_lock_enabled_for_bucket
                plan_state["object_lock_configuration"] = object_lock_configuration
        if (
            tags_dict is not None
            and not hub.tool.aws.state_comparison_utils.compare_dicts(
                result["old_state"].get("tags"), tags_dict
            )
        ):
            if not ctx.get("test", False):
                update_ret = await hub.exec.boto3.client.s3.put_bucket_tagging(
                    ctx,
                    Bucket=resource_id,
                    Tagging={"TagSet": tags_list},
                )
                result["comment"] = result["comment"] + (update_ret["comment"])
                result["result"] = update_ret["result"]
                resource_updated = resource_updated or bool(update_ret["ret"])
            else:
                plan_state["tags"] = tags_dict
    else:
        if ctx.get("test", False):
            result["new_state"] = hub.tool.aws.test_state_utils.generate_test_state(
                enforced_state={},
                desired_state={
                    "acl": acl,
                    "name": name,
                    "resource_id": resource_id,
                    "create_bucket_configuration": create_bucket_configuration,
                    "grant_full_control": grant_full_control,
                    "grant_read": grant_read,
                    "grant_read_acp": grant_read_acp,
                    "grant_write": grant_write,
                    "grant_write_acp": grant_write_acp,
                    "object_lock_enabled_for_bucket": object_lock_enabled_for_bucket,
                    "object_ownership": object_ownership,
                    "tags": tags_dict,
                },
            )
            result["comment"] = hub.tool.aws.comment_utils.would_create_comment(
                resource_type="aws.s3.bucket", name=name
            )
            return result
        # Build out S3 bucket creation here
        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 ret["result"]:
            result["comment"] = ret["comment"]
            result["result"] = False
            return result
        result["comment"] = hub.tool.aws.comment_utils.create_comment(
            resource_type="aws.s3.bucket", name=name
        )
        resource_id = name
        if tags_list is not None:
            ret = await hub.exec.boto3.client.s3.put_bucket_tagging(
                ctx,
                Bucket=name,
                Tagging={"TagSet": tags_list},
            )
            if not ret["result"]:
                result["comment"] = result["comment"] + ret["comment"]
                result["result"] = False
                return result

    if ctx.get("test", False):
        result["new_state"] = plan_state
    elif (not before) or resource_updated:
        convert_ret = await hub.tool.aws.s3.conversion_utils.convert_raw_s3_to_present(
            ctx, idem_resource_name=resource_id
        )
        result["result"] = convert_ret["result"]
        if not result["result"]:
            result["comment"] = result["comment"] + convert_ret["comment"]
        result["new_state"] = convert_ret["ret"]
    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"""
    Deletes the specified s3 bucket.

    Args:
        name(Text): The Idem name of the s3 bucket.
        resource_id(Text, Optional): AWS S3 Bucket name

    Returns:
        Dict[str, Any]

    Examples:

        .. code-block:: sls

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

    result = dict(comment=(), old_state=None, new_state=None, name=name, result=True)
    if not resource_id:
        result["comment"] = result[
            "comment"
        ] + hub.tool.aws.comment_utils.already_absent_comment(
            resource_type="aws.s3.bucket", name=name
        )
        return result
    resource = hub.tool.boto3.resource.create(ctx, "s3", "Bucket", resource_id)
    before = await hub.tool.boto3.resource.describe(resource)

    if not before:
        result["comment"] = result[
            "comment"
        ] + hub.tool.aws.comment_utils.already_absent_comment(
            resource_type="aws.s3.bucket", name=name
        )
        return result
    else:
        convert_ret = await hub.tool.aws.s3.conversion_utils.convert_raw_s3_to_present(
            ctx, idem_resource_name=name
        )
        result["result"] = convert_ret["result"]
        if not result["result"]:
            result["comment"] = result["comment"] + convert_ret["comment"]
            return result
        result["old_state"] = convert_ret["ret"]
        if ctx.get("test", False):
            result["comment"] = hub.tool.aws.comment_utils.would_delete_comment(
                resource_type="aws.s3.bucket", name=name
            )
            return result
        ret = await hub.exec.boto3.client.s3.delete_bucket(ctx, Bucket=resource_id)
        result["result"] = ret["result"]
        if not result["result"]:
            result["comment"] = ret["comment"]
            return result
        result["comment"] = hub.tool.aws.comment_utils.delete_comment(
            resource_type="aws.s3.bucket", name=name
        )
    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")
        convert_ret = await hub.tool.aws.s3.conversion_utils.convert_raw_s3_to_present(
            ctx, idem_resource_name=resource_id
        )
        if not convert_ret["result"]:
            hub.log.warning(
                f"Could not describe s3 bucket '{resource_id}' with error {convert_ret['comment']}"
            )
            continue
        translated_resource = convert_ret["ret"]
        result[resource_id] = {
            "aws.s3.bucket.present": [
                {parameter_key: parameter_value}
                for parameter_key, parameter_value in translated_resource.items()
            ]
        }
    return result
