"""
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 typing import Any
from typing import Dict
from typing import List

__contracts__ = ["resource"]


async def present(
    hub,
    ctx,
    name: str,
    policy_name: str,
    policy_document: Dict or str,
    resource_id: str = None,
    path: str = "/",
    description: str = None,
    tags: List = 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): A name, or the Amazon Resource Name (ARN) to identify the resource.
        policy_name(Text): The friendly name of the policy. Policy names must be unique within the account.
         They are not distinguished by case.
        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): The ID of the resource.
        path(Text, Optional): The path for the policy. Defaults to a slash (/)
        description(Text, Optional): A friendly description of the policy.
        tags(List, Optional): A list of tags that you want to attach to the new IAM customer managed policy.
         Each tag consists of a key name and an associated value.

    Request Syntax:
        [policy-resource-id]:
          aws.iam.policy.present:
          - policy_name: 'string'
          - path: 'string'
          - policy_document: 'dict' or 'string'
          - description: 'string'
          - tags:
            - Key: 'string'
              Value: 'string'

    Returns:
        Dict[str, Any]

    Examples:

        .. code-block:: sls

            arn:aws:iam::53721234567:policy/idem-policy-automation:
              aws.iam.policy.present:
                - policy_name: 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)
    resource = hub.tool.boto3.resource.create(
        ctx, "iam", "Policy", resource_id if resource_id else name
    )
    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:
        try:
            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,
                    )
                    if not update_ret["result"]:
                        result["comment"] = result["comment"] + update_ret["comment"]
                        result["result"] = False

            # Update policy tags
            if (
                result["old_state"] is not None
                and tags is not None
                and not hub.tool.aws.state_comparison_utils.are_lists_identical(
                    tags, result["old_state"].get("tags", None)
                )
            ):
                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["ret"] is not None:
                    plan_state["tags"] = update_ret["ret"].get("tags")
                    result["comment"] = result["comment"] + ("Would update tags.",)
        except hub.tool.boto3.exception.ClientError as e:
            result["comment"] = (f"{e.__class__.__name__}: {e}",)
            result["result"] = False

        if update_ret is not None and result["result"] and not ctx.get("test", False):
            result["comment"] = (f"'Updated policy {name}'",)
    else:
        try:
            if ctx.get("test", False):
                result["new_state"] = hub.tool.aws.test_state_utils.generate_test_state(
                    enforced_state={},
                    desired_state={
                        "policy_name": name,
                        "path": path,
                        "policy_document": policy_document,
                        "description": description,
                        "tags": tags,
                    },
                )
                result["comment"] = (f"Would create aws.iam.policy {name}",)
                return result

            ret = await hub.exec.boto3.client.iam.create_policy(
                ctx,
                PolicyName=policy_name,
                Path=path,
                PolicyDocument=policy_document,
                Description=description,
                Tags=tags,
            )
            result["result"] = ret["result"]
            if not result["result"]:
                result["comment"] = ret["comment"]
                return result
            result["comment"] = (f"Created aws.iam.policy '{name}'",)
            resource_id = ret["ret"]["Policy"]["Arn"]
            resource = hub.tool.boto3.resource.create(ctx, "iam", "Policy", resource_id)
        except hub.tool.boto3.exception.ClientError as e:
            result["comment"] = (f"{e.__class__.__name__}: {e}",)
            result["result"] = False

    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:
            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): A name
        resource_id(Text, Optional): Amazon Resource Name (ARN) to identify the resource.

    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)
    resource = hub.tool.boto3.resource.create(
        ctx, "iam", "Policy", resource_id if resource_id else name
    )
    before = await hub.tool.boto3.resource.describe(resource)

    if not before:
        result["comment"] = (f"aws.iam.policy '{name}' already absent",)
    else:
        try:
            result[
                "old_state"
            ] = await hub.tool.aws.iam.conversion_utils.convert_raw_policy_to_present(
                ctx, raw_resource=before
            )

            if ctx.get("test", False):
                result["comment"] = (f"Would delete aws.iam.policy '{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"] = (f"Deleted aws.iam.policy '{name}'",)
        except hub.tool.boto3.exception.ClientError as e:
            result["comment"] = (f"{e.__class__.__name__}: {e}",)

    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

        result[translated_resource["resource_id"]] = {
            "aws.iam.policy.present": [
                {parameter_key: parameter_value}
                for parameter_key, parameter_value in translated_resource.items()
            ]
        }

    return result
