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

hub.exec.boto3.client.iam.create_role
hub.exec.boto3.client.iam.delete_role
hub.exec.boto3.client.iam.get_role
hub.exec.boto3.client.iam.list_roles
hub.exec.boto3.client.iam.tag_role
hub.exec.boto3.client.iam.untag_role
hub.exec.boto3.client.iam.update_role
resource = hub.tool.boto3.resource.create(ctx, "iam", "Role", name)
hub.tool.boto3.resource.exec(resource, attach_policy, *args, **kwargs)
hub.tool.boto3.resource.exec(resource, delete, *args, **kwargs)
hub.tool.boto3.resource.exec(resource, detach_policy, *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,
    assume_role_policy_document: Dict or str,
    resource_id: str = None,
    description: str = None,
    max_session_duration: int = None,
    permissions_boundary: str = None,
    tags: List = None,
) -> Dict[str, Any]:
    r"""
    **Autogenerated function**

    Creates a new role for your Amazon Web Services account. For more information about roles, see IAM roles. For
    information about quotas for role names and the number of roles you can create, see IAM and STS quotas in the
    IAM User Guide.

    Args:
        name(Text): The name of the IAM role.
        assume_role_policy_document(Dict or Text): The trust relationship policy document that grants an entity
         permission to assume the role. This can be either a dictionary or a json string.
        resource_id(Text, Optional): The ID of the IAM role.
        description(Text, Optional): A description of the role. Defaults to None.
        max_session_duration(Integer, Optional): The maximum session duration (in seconds) that you want to set for the
         specified role. If you do not specify a value for this setting, the default maximum of one hour is applied.
          This setting can have a value from 1 hour to 12 hours.
        permissions_boundary(Text, Optional): The ARN of the policy that is used to set the permissions boundary for the role.
        tags(Text, Optional): A list of tags that you want to attach to the new role. Each tag consists of a key name and an associated value.

    Request Syntax:
        [iam-role-name]:
          aws.iam.role.present:
          - name: 'string'
          - resource_id: 'string'
          - assume_role_policy_document: 'dict or string'
          - description: 'string'
          - max_session_duration: 'integer'
          - permissions_boundary: 'string'
          - tags:
            - Key: 'string'
              Value: 'string'

    Returns:
        Dict[str, Any]

    Examples:

        .. code-block:: sls

            AWSServiceRoleForEC2Spot:
              aws.iam.role.present:
                - assume_role_policy_document:
                  Statement:
                    - Action: sts:AssumeRole
                      Effect: Allow
                      Principal:
                        Service: spot.amazonaws.com
                  Version: '2012-10-17'
                - description: Default EC2 Spot Service Linked Role
                - max_session_duration: 3600
                - tags:
                  - Key: tag-key
                    Value: tag-value
    """
    result = dict(comment=(), old_state=None, new_state=None, name=name, result=True)
    resource_updated = False
    resource = hub.tool.boto3.resource.create(
        ctx, "iam", "Role", resource_id if resource_id else name
    )
    before = await hub.tool.boto3.resource.describe(resource)

    # Standardise on the json format of policy
    assume_role_policy_document = hub.tool.aws.state_comparison_utils.standardise_json(
        assume_role_policy_document
    )

    if before:
        result[
            "old_state"
        ] = hub.tool.aws.iam.conversion_utils.convert_raw_role_to_present(before)
        try:
            plan_state = copy.deepcopy(result["old_state"])
            # Update role
            update_ret = await hub.exec.aws.iam.role.update_role(
                ctx,
                old_state=result["old_state"],
                description=description,
                max_session_duration=max_session_duration,
            )
            result["comment"] = result["comment"] + update_ret["comment"]
            result["result"] = update_ret["result"]
            resource_updated = resource_updated or bool(update_ret["ret"])
            if update_ret["ret"] and ctx.get("test", False):
                if "max_session_duration" in update_ret["ret"]:
                    plan_state["max_session_duration"] = update_ret["ret"][
                        "max_session_duration"
                    ]
                if "description" in update_ret["ret"]:
                    plan_state["description"] = update_ret["ret"]["description"]

            # Update tags
            if (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.role.update_role_tags(
                    ctx,
                    role_name=before.get("RoleName"),
                    old_tags=before.get("Tags", []),
                    new_tags=tags,
                )
                result["result"] = result["result"] and update_ret["result"]
                result["comment"] = result["comment"] + update_ret["comment"]
                resource_updated = resource_updated or bool(update_ret["ret"])
                if ctx.get("test", False) and update_ret["ret"] is not None:
                    plan_state["tags"] = update_ret["ret"].get("tags")
            if not resource_updated:
                result["comment"] = result["comment"] + (
                    f"aws.iam.role '{name}' already exists",
                )
            # Update policy which is embedded within the role
            if not hub.tool.aws.state_comparison_utils.is_json_identical(
                result["old_state"]["assume_role_policy_document"],
                assume_role_policy_document,
            ):
                update_ret = await hub.exec.aws.iam.role.update_policy(
                    ctx,
                    role_name=result["old_state"].get("name", None),
                    policy=assume_role_policy_document,
                )
                result["result"] = result["result"] and update_ret["result"]
                result["comment"] = result["comment"] + update_ret["comment"]
                resource_updated = resource_updated or bool(update_ret["ret"])
                if ctx.get("test", False):
                    plan_state[
                        "assume_role_policy_document"
                    ] = assume_role_policy_document

        except hub.tool.boto3.exception.ClientError as e:
            result["comment"] = result["comment"] + (f"{e.__class__.__name__}: {e}",)
            result["result"] = False
    else:
        try:
            if ctx.get("test", False):
                result["new_state"] = hub.tool.aws.test_state_utils.generate_test_state(
                    enforced_state={},
                    desired_state={
                        "name": name,
                        "assume_role_policy_document": assume_role_policy_document,
                        "description": description,
                        "max_session_duration": max_session_duration,
                        "tags": tags,
                    },
                )
                result["comment"] = (f"Would create aws.iam.role '{name}'",)
                return result

            ret = await hub.exec.boto3.client.iam.create_role(
                ctx,
                RoleName=name,
                AssumeRolePolicyDocument=assume_role_policy_document,
                Description=description,
                MaxSessionDuration=max_session_duration,
                PermissionsBoundary=permissions_boundary,
                Tags=tags,
            )
            result["result"] = ret["result"]
            if not result["result"]:
                result["comment"] = result["comment"] + ret["comment"]
                return result
            result["comment"] = result["comment"] + (f"Created aws.iam.role '{name}'",)
        except hub.tool.boto3.exception.ClientError as e:
            result["comment"] = result["comment"] + (f"{e.__class__.__name__}: {e}",)
            result["result"] = False

    try:
        if ctx.get("test", False):
            result["new_state"] = plan_state
        elif (not before) or resource_updated:
            after = await hub.tool.boto3.resource.describe(resource)
            result[
                "new_state"
            ] = hub.tool.aws.iam.conversion_utils.convert_raw_role_to_present(after)
        else:
            result["new_state"] = copy.deepcopy(result["old_state"])
    except Exception as e:
        result["comment"] = 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 role. The role must not have any policies attached. For more information about roles, see
    Working with roles.  Make sure that you do not have any Amazon EC2 instances running with the role you are about
    to delete. Deleting a role or instance profile that is associated with a running instance will break any
    applications running on the instance.

    Args:
        name(Text): The name of the IAM role.
        resource_id(Text, Optional): unique identifier of the resource.

    Returns:
        Dict[str, Any]

    Examples:

        .. code-block:: sls

            resource_is_absent:
              aws.iam.role.absent:
                - resource_id: value
    """

    result = dict(comment=(), old_state=None, new_state=None, name=name, result=True)
    resource = hub.tool.boto3.resource.create(
        ctx, "iam", "Role", resource_id if resource_id else name
    )
    before = await hub.tool.boto3.resource.describe(resource)

    if not before:
        result["comment"] = (f"aws.iam.role '{name}' already absent",)
    elif ctx.get("test", False):
        result[
            "old_state"
        ] = hub.tool.aws.iam.conversion_utils.convert_raw_role_to_present(before)
        result["comment"] = (f"Would delete aws.iam.role '{name}'",)
        return result
    else:
        try:
            result[
                "old_state"
            ] = hub.tool.aws.iam.conversion_utils.convert_raw_role_to_present(before)
            ret = await hub.exec.boto3.client.iam.delete_role(ctx, RoleName=name)
            result["result"] = ret["result"]
            if not result["result"]:
                result["comment"] = ret["comment"]
                result["result"] = False
                return result
            result["comment"] = (f"Deleted aws.iam.role '{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 the IAM roles that have the specified path prefix. If there are none, the operation returns an empty list.
    For more information about roles, see Working with roles.  IAM resource-listing operations return a subset of
    the available attributes for the resource. For example, this operation does not return tags, even though they
    are an attribute of the returned object. To view all of the information for a role, see GetRole.  You can
    paginate the results using the MaxItems and Marker parameters.


    Returns:
        Dict[str, Any]

    Examples:

        .. code-block:: bash

            $ idem describe aws.iam.role
    """

    result = {}
    ret = await hub.exec.boto3.client.iam.list_roles(ctx)

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

    for role in ret["ret"]["Roles"]:
        # This is required to get tags for each role
        boto2_resource = hub.tool.boto3.resource.create(
            ctx, "iam", "Role", role.get("RoleName")
        )
        resource = await hub.tool.boto3.resource.describe(boto2_resource)
        translated_resource = (
            hub.tool.aws.iam.conversion_utils.convert_raw_role_to_present(resource)
        )

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

    return result
