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

__contracts__ = ["resource"]


TREQ = {
    "absent": {
        "require": [
            "aws.iam.role.absent",
        ],
    },
}


async def present(
    hub,
    ctx,
    name: str,
    resource_id: str = None,
    path: str = None,
    roles: List = None,
    tags: Dict[str, Any]
    or List[
        make_dataclass(
            "Tag",
            [("Key", str, field(default=None)), ("Value", str, field(default=None))],
        )
    ] = 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): AWS Instance Profile Name.
        resource_id(Text, Optional): AWS Instance Profile Name.
        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(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 instance profile. 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.

    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)

    before = await hub.exec.aws.iam.instance_profile.get(
        ctx, name=name, resource_id=resource_id
    )
    if not before["result"]:
        result["result"] = False
        result["comment"] = before["comment"]
        return result
    if isinstance(tags, List):
        tags = hub.tool.aws.tag_utils.convert_tag_list_to_dict(tags)
    if before["ret"]:
        result["old_state"] = before["ret"]
        result["new_state"] = copy.deepcopy(before["ret"])
        result["comment"] = (f"'{name}' already exists",)
        # Update key tags if tags are specified
        if tags is not None and 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=result["old_state"].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"]
    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"] = hub.tool.aws.comment_utils.would_create_comment(
                resource_type="aws.iam.instance_profile", name=name
            )
            return result
        try:
            ret = await hub.exec.boto3.client.iam.create_instance_profile(
                ctx,
                InstanceProfileName=name,
                Path=path,
                Tags=hub.tool.aws.tag_utils.convert_tag_dict_to_list(tags)
                if tags
                else None,
            )
            result["result"] = ret["result"]
            if not ret["result"]:
                result["comment"] = ret["comment"]
                return result
            result["comment"] = hub.tool.aws.comment_utils.create_comment(
                resource_type="aws.iam.instance_profile", name=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["ret"] and before["ret"].get("roles", []):
            curr_role = before["ret"].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["ret"] and update_ret is None:
        result["new_state"] = copy.deepcopy(result["old_state"])
    else:
        after = await hub.exec.aws.iam.instance_profile.get(
            ctx, name=name, resource_id=resource_id
        )
        if not after["result"]:
            result["result"] = False
            result["comment"] = after["comment"]
            return result
        result["new_state"] = after["ret"]

    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): AWS Instance Profile Name.
        resource_id(Text, Optional): AWS Instance Profile Name. If not specified, Idem will use "name"
         parameter to identify the role policy on AWS.

    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)
    before = await hub.exec.aws.iam.instance_profile.get(
        ctx, name=name, resource_id=resource_id
    )
    if not before["result"]:
        result["comment"] = before["comment"]
        result["result"] = False
        return result
    if not before["ret"]:
        result["comment"] = hub.tool.aws.comment_utils.already_absent_comment(
            resource_type="aws.iam.instance_profile", name=name
        )
        return result
    else:
        try:
            result["old_state"] = before["ret"]
            if ctx.get("test", False):
                result["comment"] = hub.tool.aws.comment_utils.would_delete_comment(
                    resource_type="aws.iam.instance_profile", name=name
                )
                return result

            ret = await hub.exec.boto3.client.iam.delete_instance_profile(
                ctx,
                InstanceProfileName=name,
            )
            if not ret["result"]:
                result["comment"] = ret["comment"]
                result["result"] = False
                return result
            result["comment"] = hub.tool.aws.comment_utils.delete_comment(
                resource_type="aws.iam.instance_profile", name=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
        resource = await hub.exec.aws.iam.instance_profile.get(
            ctx,
            name=instance_profile["InstanceProfileName"],
            resource_id=instance_profile["InstanceProfileName"],
        )
        if not resource["result"]:
            hub.log.info(
                f"Failed to retrieve aws.iam.instance_profile {instance_profile['InstanceProfileName']}: {resource['comment']}"
            )
            continue

        resource_key = f"iam-instance-profile-{resource['ret']['name']}"
        result[resource_key] = {
            "aws.iam.instance_profile.present": [
                {parameter_key: parameter_value}
                for parameter_key, parameter_value in resource["ret"].items()
            ]
        }

    return result


async def search(hub, ctx, name, resource_id: str = None):
    """
    Provides details about a specific instance profile as a data-source. This function has been deprecated.
    Please use exec.run with aws.iam.instance_profile.get instead.

    Args:
        name(Text): AWS Instance Profile name to identify the resource.
        resource_id(Text, Optional): AWS Instance Profile name to identify the resource.

    Request Syntax:
        [Idem-state-name]:
           aws.iam.instance_profile.search:
              - name: 'string'
              - resource_id: 'string'

    Response Syntax:
        The following fields will be returned to the `new_state` of the response.

          - name: 'string'
          - resource_id: 'string'
          - arn: 'string'
          - path: 'string'
          - roles:
            - RoleName: 'string'
            - RoleArn: 'string'
            - RoleId: 'string'
          - Tags:
            - Key: 'string'
              Value: 'string'

    Examples:

        Input state file:

        .. code-block:: sls

            instance-profile-test-search:
              aws.iam.instance_profile.search:
                - name: instance-profile-test-search
                - resource_id: instance-profile-test-search

        Sample response:
              - name: inst-profile-1
              - resource_id: inst-profile-1
              - arn:
              - path: /aws-service-inst-profile/
              - roles:
                - RoleName: role-1
                - RoleArn: arn:aws:iam::000000000000:role/idem-test-role-09e61fc9-d078-48a6-b297-8969a509c7cd
                - RoleId: tpx1mo6cjpsvh95ex5mq
              - Tags:
                - Key: test-1
                  Value: test-1-val
    """

    hub.log.warning(
        f"aws.iam.instance_profile.search '{name}' state has been deprecated. Please use exec.run with aws.iam.instance_profile.get instead."
    )
    result = dict(comment=(), old_state=None, new_state=None, name=name, result=True)
    ret = await hub.exec.aws.iam.instance_profile.get(
        ctx=ctx, name=name, resource_id=resource_id
    )
    result["result"] = ret["result"]
    result["comment"] = ret["comment"]
    if result["result"]:
        result["old_state"] = ret["ret"]
        # Populate both "old_state" and "new_state" with the same data
        result["new_state"] = copy.deepcopy(result["old_state"])
    return result
