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

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

__contracts__ = ["resource"]

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


async def present(
    hub,
    ctx,
    name: str,
    bucket: str,
    policy: str,
    confirm_remove_self_bucket_access: bool = None,
    expected_bucket_owner: str = None,
) -> Dict[str, Any]:
    r"""
    **Autogenerated function**

    Applies an Amazon S3 bucket policy to an Amazon S3 bucket. If you are using an identity other than the root user of
    the Amazon Web Services account that owns the bucket, the calling identity must have the PutBucketPolicy permissions
     on the specified bucket and belong to the bucket owner's account in order to use this operation.

    If you don't have PutBucketPolicy permissions, Amazon S3 returns a 403 Access Denied error.
    If you have the correct permissions, but you're not using an identity that belongs to the bucket owner's account,
    Amazon S3 returns a 405 Method Not Allowed error.

    Args:
        hub:
        ctx:
        name(Text): The name of the bucket policy.
        bucket(string):  The name of the bucket
        policy (string): The bucket policy as a JSON document.
        confirm_remove_self_bucket_access (boolean, optional): Set this parameter to true to confirm that you want to remove your permissions to change this bucket policy in the future.
        expected_bucket_owner (string, optional): The account ID of the expected bucket owner. If the bucket is owned by a different account, the request will fail with an HTTP 403 (Access Denied) error.

    Request Syntax:
        resource_name:
          aws.s3.bucket_policy.present:
            - bucket: string
            - policy: string
            - confirm_remove_self_bucket_access: boolean
            - expected_bucket_owner: string

    Returns:
        Dict[str, Any]

    Examples:

        .. code-block:: sls

            test_bucket-bb7bb32e9533-policy:
              aws.s3.bucket_policy.present:
                - bucket: test_bucket-bb7bb32e9533
                - policy: '{"Version":"2012-10-17","Statement":[{"Sid":"PublicReadGetObject","Effect":"Allow","Principal":{"AWS":"arn:aws:iam::418235808912:root"},"Action":"s3:GetObject","Resource":"arn:aws:s3:::thebugbucket3/*"}]}'
                - confirm_remove_self_bucket_access: False
                - expected_bucket_owner: 1239234249


    """
    result = dict(comment="", old_state=None, new_state=None, name=name, result=True)
    resource = hub.tool.boto3.resource.create(ctx, "s3", "Bucket", bucket)
    before = await hub.tool.boto3.resource.describe(resource)
    if not before:
        result["comment"] = f"s3 bucket '{bucket}' does not exist."
        result["result"] = False
    else:
        # get policy of the bucket if exists
        bucket_policy = hub.tool.boto3.resource.create(
            ctx, "s3", "BucketPolicy", bucket
        )
        policy_ret = await hub.tool.boto3.resource.describe(bucket_policy)
        old_policy = policy_ret.get("Policy") if policy_ret else None
        before["policy"] = old_policy
        result["old_state"] = before
        if ctx.get("test", False):
            if old_policy != policy:
                result["comment"] = f"Would update aws.s3.bucket {bucket} policy"
            else:
                result["comment"] = f"No changes were made to policy"
            return result
        # create or update bucket policy only if there is any change in policy
        if old_policy != policy:
            try:
                # create or update the bucket policy
                update_ret = await hub.exec.boto3.client.s3.put_bucket_policy(
                    ctx,
                    Bucket=bucket,
                    Policy=policy,
                    ConfirmRemoveSelfBucketAccess=confirm_remove_self_bucket_access,
                    ExpectedBucketOwner=expected_bucket_owner,
                )
                if not update_ret["result"]:
                    result["comment"] = update_ret["comment"]
                    result["result"] = False
                    return result
            except Exception as e:
                result["comment"] = str(e)
                result["result"] = False
                return result
            try:
                new_policy_ret = await hub.tool.boto3.resource.describe(bucket_policy)
                new_policy = new_policy_ret.get("Policy") if new_policy_ret else None
                after = copy.deepcopy(before)
                after["policy"] = new_policy
                result["new_state"] = after
                result[
                    "comment"
                ] = f"Created bucket policy {bucket}-policy on bucket {bucket}"
            except Exception as e:
                result["comment"] = str(e)
                result["result"] = False
        else:
            result["new_state"] = copy.deepcopy(before)
            result["comment"] = f"No changes were made to policy"
    return result


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

    Deletes the policy of specified s3 bucket.

    Args:
        name(Text): The name of the s3 bucket policy.
        bucket(string): The name of the s3 bucket.
        expected_bucket_owner(string): The account ID of the expected bucket owner. If the bucket is owned by a different account, the request will fail with an HTTP 403 (Access Denied) error.

    Returns:
        Dict[str, Any]

    Request Syntax:
        resource_name:
          aws.s3.bucket_policy.absent:
            - name: string
            - bucket: string
            - expected_bucket_owner: string

    Examples:

        .. code-block:: sls

            bucket-5435423646-456464-policy:
              aws.s3.bucket_policy.absent:
                - name: bucket-5435423646-456464-policy
                - bucket: bucket-5435423646-456464
                - expected_bucket_owner: 1239234249
    """

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

    if not before:
        result["comment"] = f"s3 bucket '{bucket}' does not exist."
        result["result"] = False
    else:
        try:
            resource = hub.tool.boto3.resource.create(ctx, "s3", "BucketPolicy", bucket)
            policy_ret = await hub.tool.boto3.resource.describe(resource)
            old_policy = (
                policy_ret["Policy"]
                if policy_ret and policy_ret.get("Policy")
                else None
            )
            before["policy"] = old_policy
            if ctx.get("test", False):
                if old_policy:
                    result["comment"] = f"Would delete aws.s3.bucket_policy {name}"
                else:
                    result["comment"] = f"Policy is already absent for bucket {bucket}"
                return result
            if old_policy:
                ret = await hub.exec.boto3.client.s3.delete_bucket_policy(
                    ctx, Bucket=bucket, ExpectedBucketOwner=expected_bucket_owner
                )
                result["result"] = ret["result"]
                if not result["result"]:
                    result["comment"] = ret["comment"]
                    result["result"] = False
                    return result
                result["comment"] = f"Deleted policy '{name}' for bucket {bucket}"
            else:
                result["comment"] = f"Policy is already absent for bucket {bucket}"
        except hub.tool.boto3.exception.ClientError as e:
            result["comment"] = f"{e.__class__.__name__}: {e}"
        after = copy.deepcopy(before)
        after["policy"] = None
        result["old_state"] = before
        result["new_state"] = after

    return result


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

    Returns:
        Dict[str, Any]

    Examples:

        .. code-block:: bash

            $ idem describe aws.s3.bucket_policy
    """
    result = {}
    # To describe the policy of all the buckets, we first need to list all the buckets, then get the
    # policy 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"]:
        resource_id = bucket.get("Name")
        resource_translated = [{"bucket": resource_id}]
        resource_name = f"{resource_id}-policy"
        # get policy for each bucket
        try:
            resource = hub.tool.boto3.resource.create(
                ctx, "s3", "BucketPolicy", resource_id
            )
            policy_ret = await hub.tool.boto3.resource.describe(resource)
            if policy_ret and policy_ret["Policy"]:
                resource_translated.append({"policy": policy_ret["Policy"]})
            result[resource_name] = {
                "aws.s3.bucket_policy.present": resource_translated
            }
        except Exception as e:
            hub.log.warning(
                f"Could not get attached policy for bucket {resource_id} with error"
                f" {e} . Describe will skip this bucket and continue."
            )

    return result
