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

hub.exec.boto3.client.iam.create_policy
hub.exec.boto3.client.iam.delete_policy
hub.exec.boto3.client.iam.get_policy
hub.exec.boto3.client.iam.list_policies
hub.exec.boto3.client.iam.tag_policy
hub.exec.boto3.client.iam.untag_policy
resource = hub.tool.boto3.resource.create(ctx, "iam", "Policy", name)
hub.tool.boto3.resource.exec(resource, attach_group, *args, **kwargs)
hub.tool.boto3.resource.exec(resource, attach_role, *args, **kwargs)
hub.tool.boto3.resource.exec(resource, attach_user, *args, **kwargs)
hub.tool.boto3.resource.exec(resource, create_version, *args, **kwargs)
hub.tool.boto3.resource.exec(resource, delete, *args, **kwargs)
hub.tool.boto3.resource.exec(resource, detach_group, *args, **kwargs)
hub.tool.boto3.resource.exec(resource, detach_role, *args, **kwargs)
hub.tool.boto3.resource.exec(resource, detach_user, *args, **kwargs)
"""
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,
    policy_document: Dict or str,
    resource_id: str = None,
    path: str = "/",
    description: str = None,
    tags: Dict[str, Any]
    or List[
        make_dataclass(
            "Tag",
            [("Key", str, field(default=None)), ("Value", str, field(default=None))],
        )
    ] = None,
    timeout: make_dataclass(
        "Timeout",
        [
            (
                "create",
                make_dataclass(
                    "CreateTimeout",
                    [
                        ("delay", int, field(default=0)),
                        ("max_attempts", int, field(default=0)),
                    ],
                ),
                field(default=None),
            ),
            (
                "update",
                make_dataclass(
                    "UpdateTimeout",
                    [
                        ("delay", int, field(default=0)),
                        ("max_attempts", int, field(default=0)),
                    ],
                ),
                field(default=None),
            ),
        ],
    ) = None,
) -> Dict[str, Any]:
    r"""
    **Autogenerated function**

    Create or update a managed policy for your Amazon Web Services account. When creating a policy, this operation
     creates a policy version with a version identifier of v1 and sets v1 as the policy's default version. When updating
      a policy, this operation creates a new policy version, sets the new policy to the default version, and
       deletes the old policy. As a best practice, you can validate your IAM policies. To learn more, see Validating
        IAM policies in AWS's IAM User Guide. For more information about managed policies in general,
         see Managed policies and inline policies in AWS's IAM User Guide.

    Args:
        name(Text): IAM policy name.
        policy_document(Dict or Text): The JSON policy document that you want to use as the content for the new policy.
         You must provide policies in JSON format in IAM. However, for CloudFormation templates formatted in YAML,
          you can provide the policy in JSON or YAML format. CloudFormation always converts a YAML policy to JSON format
           before submitting it to IAM.
        resource_id(Text, optional): Amazon Resource Name (ARN) to identify IAM policy.
         This is a required field for Idem to manage a brown-field resource.
        path(Text, optional): The path for the policy. Defaults to a slash (/)
        description(Text, optional): A friendly description of the policy.
        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 IAM customer managed policy.
            Each tag consists of a key name and an associated value. Defaults to None.
            * (Key): The key name that can be used to look up or retrieve the associated value. For example,
                Department or Cost Center are common choices.
            * (Value): The value associated with this tag. For example, tags with a key name of Department could have
                values such as Human Resources, Accounting, and Support. Tags with a key name of Cost Center
                might have values that consist of the number associated with the different cost centers in your
                company. Typically, many resources have tags with the same key name but with different values.
                Amazon Web Services always interprets the tag Value as a single string. If you need to store an
                array, you can store comma-separated values in the string. However, you must interpret the value
                in your code.
        timeout(Dict, optional): Timeout configuration for create/update/deletion of AWS IAM Policy.
            * create (Dict): Timeout configuration for creating AWS IAM Policy
                * delay (int, Optional): The amount of time in seconds to wait between attempts.
                * max_attempts (int, Optional): Customized timeout configuration containing delay and max attempts.
            * update(Dict, optional): Timeout configuration for updating AWS IAM Policy
                * delay (int, Optional): The amount of time in seconds to wait between attempts.
                * max_attempts: (int, Optional) Customized timeout configuration containing delay and max attempts.

    Request Syntax:
        [policy-idem-state]:
          aws.iam.policy.present:
          - name: 'string'
          - resource_id: 'string'
          - path: 'string'
          - policy_document: 'dict' or 'string'
          - description: 'string'
          - tags:
            - Key: 'string'
              Value: 'string'
          - timeout:
              create:
                delay: 'int'
                max_attempts: 'int'
              update:
                delay: 'int'
                max_attempts: 'int'

    Returns:
        Dict[str, Any]

    Examples:

        .. code-block:: sls

            my-iam-policy:
              aws.iam.policy.present:
                - name: idem-policy-automation
                - resource_id: arn:aws:iam::53721234567:policy/idem-policy-automation
                - path: /
                - policy_document: '{"Statement": [{"Action": ["ec2:CreateSubnet"], "Effect": "Allow", "Resource": "*"}], "Version": "2012-10-17"}'
                - description: "Idem IAM policy description example"
                - tags:
                  - Key: Name
                    Value: Idem-test-policy
    """

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

    tags = (
        hub.tool.aws.tag_utils.convert_tag_list_to_dict(tags)
        if isinstance(tags, List)
        else tags
    )

    if resource_id:
        resource = hub.tool.boto3.resource.create(ctx, "iam", "Policy", resource_id)
        before = await hub.tool.boto3.resource.describe(resource)

    # Standardise on the json format
    policy_document = hub.tool.aws.state_comparison_utils.standardise_json(
        policy_document
    )

    update_ret = None
    if before:
        result[
            "old_state"
        ] = await hub.tool.aws.iam.conversion_utils.convert_raw_policy_to_present(
            ctx, before
        )
        result["comment"] = (f"'{name}' already exists",)
        plan_state = copy.deepcopy(result["old_state"])
        # Update policy document if needed
        if (
            result["old_state"] is not None
            and result["old_state"]["policy_document"]
            and not hub.tool.aws.state_comparison_utils.is_json_identical(
                result["old_state"]["policy_document"], policy_document
            )
        ):
            if ctx.get("test", False):
                plan_state["policy_document"] = policy_document
                result["comment"] = result["comment"] + (
                    f"Would update policy document.",
                )
            else:
                update_ret = await hub.exec.aws.iam.policy.update_policy(
                    ctx=ctx,
                    policy_arn=resource_id,
                    policy_version_id=result["old_state"].get("default_version_id"),
                    new_policy_document=policy_document,
                    timeout=timeout,
                )
                if not update_ret["result"]:
                    result["comment"] = result["comment"] + update_ret["comment"]
                    result["result"] = False

        if (
            result["old_state"] is not None
            and tags is not None
            and tags != result["old_state"].get("tags")
        ):
            update_ret = await hub.exec.aws.iam.policy.update_policy_tags(
                ctx=ctx,
                police_arn=resource_id,
                old_tags=result["old_state"].get("tags"),
                new_tags=tags,
            )
            if not update_ret["result"]:
                result["comment"] = result["comment"] + update_ret["comment"]
                result["result"] = False
            if ctx.get("test", False) and update_ret["result"]:
                plan_state["tags"] = update_ret["ret"]
                result["comment"] = result["comment"] + ("Would update tags.",)

        if update_ret is not None and result["result"] and not ctx.get("test", False):
            result["comment"] = result[
                "comment"
            ] + hub.tool.aws.comment_utils.update_comment(
                resource_type="aws.iam.policy", name=name
            )
    else:
        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": f"policy-{name}-resource_id",
                    "id": f"policy-{name}-id",
                    "path": path,
                    "policy_document": policy_document,
                    "description": description,
                    "tags": tags,
                },
            )
            result["comment"] = hub.tool.aws.comment_utils.would_create_comment(
                resource_type="aws.iam.policy", name=name
            )
            return result

        ret = await hub.exec.boto3.client.iam.create_policy(
            ctx,
            PolicyName=name,
            Path=path,
            PolicyDocument=policy_document,
            Description=description,
            Tags=hub.tool.aws.tag_utils.convert_tag_dict_to_list(tags)
            if tags
            else None,
        )
        result["result"] = ret["result"]
        if not result["result"]:
            result["comment"] = ret["comment"]
            return result
        waiter_config = hub.tool.aws.waiter_utils.create_waiter_config(
            default_delay=1,
            default_max_attempts=40,
            timeout_config=timeout.get("create") if timeout else None,
        )
        hub.log.debug(f"Waiting on creation of aws.iam.policy '{name}'")
        try:
            await hub.tool.boto3.client.wait(
                ctx,
                "iam",
                "policy_exists",
                PolicyArn=ret["ret"].get("Policy")["Arn"],
                WaiterConfig=waiter_config,
            )
        except Exception as e:
            result["comment"] = result["comment"] + (str(e),)
            result["result"] = False
            return result
        result["comment"] = hub.tool.aws.comment_utils.create_comment(
            resource_type="aws.iam.policy", name=name
        )
        resource_id = ret["ret"]["Policy"]["Arn"]

    try:
        if ctx.get("test", False):
            result["new_state"] = plan_state
        elif before and (update_ret is None):
            result["new_state"] = copy.deepcopy(result["old_state"])
        else:
            resource = hub.tool.boto3.resource.create(ctx, "iam", "Policy", resource_id)
            after = await hub.tool.boto3.resource.describe(resource)
            result[
                "new_state"
            ] = await hub.tool.aws.iam.conversion_utils.convert_raw_policy_to_present(
                ctx, after
            )
    except Exception as e:
        result["comment"] = (str(e),)
        result["result"] = False
    return result


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

    Deletes the specified managed policy. Before you can delete a managed policy, you must first detach the policy
    from all users, groups, and roles that it is attached to. In addition, you must delete all the policy's
    versions. The following steps describe the process for deleting a managed policy:   Detach the policy from all
    users, groups, and roles that the policy is attached to, using DetachUserPolicy, DetachGroupPolicy, or
    DetachRolePolicy. To list all the users, groups, and roles that a policy is attached to, use
    ListEntitiesForPolicy.   Delete all versions of the policy using DeletePolicyVersion. To list the policy's
    versions, use ListPolicyVersions. You cannot use DeletePolicyVersion to delete the version that is marked as the
    default version. You delete the policy's default version in the next step of the process.   Delete the policy
    (this automatically deletes the policy's default version) using this operation.   For information about managed
    policies, see Managed policies and inline policies in the IAM User Guide.

    Args:
        name(Text): IAM policy name.
        resource_id(Text, optional): Amazon Resource Name (ARN) to identify IAM policy. If not specified, Idem will use "name"
         parameter to identify the IAM policy on AWS.

    Returns:
        Dict[str, Any]

    Examples:

        .. code-block:: sls

            resource_is_absent:
              aws.iam.policy.absent:
                - name: value
    """

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

    if not before:
        result["comment"] = hub.tool.aws.comment_utils.already_absent_comment(
            resource_type="aws.iam.policy", name=name
        )
    else:
        try:
            result[
                "old_state"
            ] = await hub.tool.aws.iam.conversion_utils.convert_raw_policy_to_present(
                ctx, raw_resource=before
            )
        except hub.tool.boto3.exception.ClientError as e:
            result["comment"] = (f"{e.__class__.__name__}: {e}",)

        if ctx.get("test", False):
            result["comment"] = hub.tool.aws.comment_utils.would_delete_comment(
                resource_type="aws.iam.policy", name=name
            )
            return result

        ret = await hub.exec.boto3.client.iam.delete_policy(
            ctx, PolicyArn=resource_id if resource_id else name
        )
        result["result"] = ret["result"]
        if not result["result"]:
            result["comment"] = ret["comment"]
            result["result"] = False
            return result
        result["comment"] = hub.tool.aws.comment_utils.delete_comment(
            resource_type="aws.iam.policy", name=name
        )

    return result


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

    Describe the resource in a way that can be recreated/managed with the corresponding "present" function


    Lists all of your own customer-defined managed policies.


    Returns:
        Dict[str, Any]

    Examples:

        .. code-block:: bash

            $ idem describe aws.iam.policy
    """

    result = {}
    # Set scope to local to only list the customer-defined managed policies.
    ret_policies = await hub.exec.boto3.client.iam.list_policies(ctx, Scope="Local")

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

    for policy in ret_policies["ret"]["Policies"]:
        translated_resource = (
            await hub.tool.aws.iam.conversion_utils.convert_raw_policy_to_present(
                ctx, policy
            )
        )
        if translated_resource is None:
            continue
        resource_key = f"iam-policy-{translated_resource['resource_id']}"
        result[resource_key] = {
            "aws.iam.policy.present": [
                {parameter_key: parameter_value}
                for parameter_key, parameter_value in translated_resource.items()
            ]
        }

    return result
