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

hub.exec.boto3.client.iam.create_instance_profile
hub.exec.boto3.client.iam.delete_instance_profile
hub.exec.boto3.client.iam.get_instance_profile
hub.exec.boto3.client.iam.list_instance_profiles
hub.exec.boto3.client.iam.tag_instance_profile
hub.exec.boto3.client.iam.untag_instance_profile
resource = hub.tool.boto3.resource.create(ctx, "iam", "InstanceProfile", name)
hub.tool.boto3.resource.exec(resource, add_role, *args, **kwargs)
hub.tool.boto3.resource.exec(resource, delete, *args, **kwargs)
hub.tool.boto3.resource.exec(resource, remove_role, *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,
    resource_id: str = None,
    path: str = None,
    roles: List = None,
    tags: List = None,
) -> Dict[str, Any]:
    r"""
    **Autogenerated function**

     Creates a new instance profile. For information about instance profiles, see Using roles for applications on
    Amazon EC2 in the IAM User Guide, and Instance profiles in the Amazon EC2 User Guide.  For information about the
    number of instance profiles you can create, see IAM object quotas in the IAM User Guide.
    Tags can be set on a newly created instance profile, or updated on an existing one.
    Roles are set and updated as a day-2 operation.

    Args:
        name(Text): Name of the resource.
        resource_id(Text, Optional): ID of the resource, once created.
        path(Text, Optional): The path to the instance profile.
        roles(Text, Optional): A list with up to one role name. Instance profile can be associated with a single role.
        tags(List, 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.

    Returns:
        Dict[str, Any]

    Request Syntax:
      [iam-instance-profile-name]:
        aws.iam.instance_profile.present:
          - name: 'string'
          - resource_id: 'string'
          - path: 'string'
          - roles:
            - RoleName: 'string'
          - Tags:
            - Key: 'string'
              Value: 'string'

    Example:

        .. code-block:: sls

        inst-profile-1:
          aws.iam.instance_profile.present:
          - name: inst-profile-1
          - path: /aws-service-inst-profile/
          - roles:
            - RoleName: role-1
          - Tags:
            - Key: test-1
              Value: test-1-val

    """

    result = dict(comment=(), old_state=None, new_state=None, name=name, result=True)
    resource = hub.tool.boto3.resource.create(
        ctx, "iam", "InstanceProfile", resource_id if resource_id else name
    )
    before = await hub.tool.boto3.resource.describe(resource)
    if before:
        result[
            "old_state"
        ] = hub.tool.aws.iam.conversion_utils.convert_raw_instance_profile_to_present(
            before
        )
        result["new_state"] = copy.deepcopy(result["old_state"])
        result["comment"] = (f"'{name}' already exists",)
        # Update key tags if tags are specified
        if (
            tags is not None
            and not hub.tool.aws.state_comparison_utils.are_lists_identical(
                tags, result["old_state"].get("tags", None)
            )
        ):
            hub.log.debug(f"aws.iam.instance_profile '{name}' tags update")
            update_ret = (
                await hub.exec.aws.iam.instance_profile.update_instance_profile_tags(
                    ctx=ctx,
                    instance_profile_name=resource_id if resource_id else name,
                    old_tags=before.get("tags", []),
                    new_tags=tags,
                )
            )

            result["result"] = update_ret["result"]
            result["comment"] = result["comment"] + update_ret["comment"]
            if not result["result"]:
                return result
            if ctx.get("test", False) and update_ret["ret"] is not None:
                result["new_state"]["tags"] = update_ret["ret"].get("tags")
    else:
        if ctx.get("test", False):
            result["new_state"] = hub.tool.aws.test_state_utils.generate_test_state(
                enforced_state={},
                desired_state={
                    "name": name,
                    "path": path,
                    "roles": roles,
                    "tags": tags,
                },
            )
            result["comment"] = (f"Would create aws.iam.instance_profile {name}",)
            return result
        try:
            ret = await hub.exec.boto3.client.iam.create_instance_profile(
                ctx,
                InstanceProfileName=name,
                Path=path,
                Tags=tags,
            )
            result["result"] = ret["result"]
            if not ret["result"]:
                result["comment"] = ret["comment"]
                return result
            result["comment"] = (f"Created '{name}'",)
        except hub.tool.boto3.exception.ClientError as e:
            result["comment"] = (f"{e.__class__.__name__}: {e}",)
            result["result"] = False

    update_ret = None
    if roles is not None:
        # Instance profile can be associated only with a single role
        # Remove existing role
        curr_role = None
        new_role = None
        if before and before.get("Roles", []):
            curr_role = before.get("Roles")[0]["RoleName"]
        if roles:
            new_role = roles[0]["RoleName"]
        if curr_role != new_role:
            if curr_role is not None and not ctx.get("test", False):
                hub.log.debug(
                    f"Removing role '{curr_role}' from instance profile '{name}'"
                )
                update_ret = (
                    await hub.exec.boto3.client.iam.remove_role_from_instance_profile(
                        ctx, InstanceProfileName=name, RoleName=curr_role
                    )
                )
                if not update_ret["result"]:
                    result["result"] = False
                    result["comment"] = update_ret["comment"]
                    return result

                result["comment"] = result["comment"] + (
                    f"Role '{curr_role}' was removed from instance profile '{name}'",
                )

            if new_role is not None and not ctx.get("test", False):
                hub.log.debug(f"Adding role '{new_role}' to instance profile '{name}'")
                update_ret = (
                    await hub.exec.boto3.client.iam.add_role_to_instance_profile(
                        ctx, InstanceProfileName=name, RoleName=new_role
                    )
                )
                if not update_ret["result"]:
                    result["result"] = False
                    result["comment"] = update_ret["comment"]
                    return result

                result["comment"] = result["comment"] + (
                    f"Role '{new_role}' was added to instance profile '{name}'",
                )
            # Test context when updating roles
            if ctx.get("test", False):
                result["comment"] = result["comment"] + (
                    f"Updated role in instance profile '{name}'.",
                )
                result["new_state"]["roles"] = (
                    [{"RoleName": new_role}] if new_role is not None else []
                )
                return result

    if ctx.get("test", False):
        return result

    if before and update_ret is None:
        result["new_state"] = copy.deepcopy(result["old_state"])
    else:
        resource = hub.tool.boto3.resource.create(ctx, "iam", "InstanceProfile", name)
        after = await hub.tool.boto3.resource.describe(resource)
        result[
            "new_state"
        ] = hub.tool.aws.iam.conversion_utils.convert_raw_instance_profile_to_present(
            after
        )

    return result


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

    Deletes the specified instance profile. The instance profile must not have an associated role.  Make sure that
    you do not have any Amazon EC2 instances running with the instance profile 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.  For more information about instance profiles, see About instance profiles.

    Args:
        name(Text): Instance profile name
        resource_id(Text, Optional): Instance profile ID

    Returns:
        Dict[str, Any]

    Request Syntax:
      [iam-instance-profile-name]:
        aws.iam.instance_profile.present:
          - name: 'string'

    Examples:

        .. code-block:: sls

            instance-profile-test:
              aws.iam.instance_profile.absent:
                - name: instance-profile-test
    """

    result = dict(comment=(), old_state=None, new_state=None, name=name, result=True)
    resource = hub.tool.boto3.resource.create(
        ctx, "iam", "InstanceProfile", resource_id if resource_id else name
    )
    before = await hub.tool.boto3.resource.describe(resource)
    if not before:
        result["comment"] = (f"'{name}' already absent",)
        return result
    else:
        try:
            result[
                "old_state"
            ] = hub.tool.aws.iam.conversion_utils.convert_raw_instance_profile_to_present(
                before
            )
            if ctx.get("test", False):
                result["comment"] = (f"Would delete aws.iam.instance_profile {name}",)
                return result

            ret = await hub.exec.boto3.client.iam.delete_instance_profile(
                ctx,
                InstanceProfileName=name,
            )
            if not result["result"]:
                result["comment"] = ret["comment"]
                result["result"] = False
                return result
            result["comment"] = (f"Deleted '{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 instance profiles that have the specified path prefix. If there are none, the operation returns an
    empty list. For more information about instance profiles, see About instance profiles.  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 an
    instance profile, see GetInstanceProfile.  You can paginate the results using the MaxItems and Marker
    parameters.


    Returns:
        Dict[str, Any]

    Examples:

        .. code-block:: bash

            $ idem describe aws.iam.instance_profile
    """

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

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

    for instance_profile in ret["ret"]["InstanceProfiles"]:
        # This is required to get tags for each instance profile list_instance_profile_roles is not working
        boto2_resource = hub.tool.boto3.resource.create(
            ctx, "iam", "InstanceProfile", instance_profile["InstanceProfileName"]
        )
        resource = await hub.tool.boto3.resource.describe(boto2_resource)

        translated_resource = (
            hub.tool.aws.iam.conversion_utils.convert_raw_instance_profile_to_present(
                resource
            )
        )
        result[translated_resource["name"]] = {
            "aws.iam.instance_profile.present": [
                {parameter_key: parameter_value}
                for parameter_key, parameter_value in translated_resource.items()
            ]
        }

    return result
