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

hub.exec.boto3.client.eks.create_nodegroup
hub.exec.boto3.client.eks.delete_nodegroup
hub.exec.boto3.client.eks.describe_nodegroup
hub.exec.boto3.client.eks.list_nodegroups
"""
import copy
from dataclasses import field
from dataclasses import make_dataclass
from typing import Any
from typing import Dict
from typing import List

from dict_tools import differ

__contracts__ = ["resource"]

TREQ = {
    "absent": {
        "require": [
            "aws.eks.cluster.absent",
        ],
    },
    "present": {
        "require": [
            "aws.eks.cluster.present",
        ],
    },
}
default_attempts = 120


async def present(
    hub,
    ctx,
    name: str,
    cluster_name: str,
    subnets: List[str],
    node_role: str,
    resource_id: str = None,
    scaling_config: make_dataclass(
        "NodegroupScalingConfig",
        [
            ("minSize", int, field(default=None)),
            ("maxSize", int, field(default=None)),
            ("desiredSize", int, field(default=None)),
        ],
    ) = None,
    disk_size: int = None,
    instance_types: List[str] = None,
    ami_type: str = None,
    remote_access: make_dataclass(
        "RemoteAccessConfig",
        [
            ("ec2SshKey", str, field(default=None)),
            ("sourceSecurityGroups", List[str], field(default=None)),
        ],
    ) = None,
    labels: Dict[str, str] = None,
    taints: List[
        make_dataclass(
            "Taint",
            [
                ("key", str, field(default=None)),
                ("value", str, field(default=None)),
                ("effect", str, field(default=None)),
            ],
        )
    ] = None,
    tags: Dict[str, str] = None,
    client_request_token: str = None,
    launch_template: make_dataclass(
        "LaunchTemplateSpecification",
        [
            ("name", str, field(default=None)),
            ("version", str, field(default=None)),
            ("id", str, field(default=None)),
        ],
    ) = None,
    update_config: make_dataclass(
        "NodegroupUpdateConfig",
        [
            ("maxUnavailable", int, field(default=None)),
            ("maxUnavailablePercentage", int, field(default=None)),
        ],
    ) = None,
    capacity_type: str = None,
    version: str = None,
    release_version: str = None,
    timeout: make_dataclass(
        "Timeout",
        [
            (
                "create",
                make_dataclass(
                    "CreateTimeout",
                    [
                        ("delay", int, field(default=10)),
                        ("max_attempts", int, field(default=60)),
                    ],
                ),
                field(default=None),
            ),
            (
                "update",
                make_dataclass(
                    "UpdateTimeout",
                    [
                        ("delay", int, field(default=10)),
                        ("max_attempts", int, field(default=60)),
                    ],
                ),
                field(default=None),
            ),
        ],
    ) = None,
) -> Dict[str, Any]:
    r"""
    **Autogenerated function**

    Creates a managed node group for an Amazon EKS cluster. You can only create a node group for your cluster that
    is equal to the current Kubernetes version for the cluster. All node groups are created with the latest AMI
    release version for the respective minor Kubernetes version of the cluster, unless you deploy a custom AMI using
    a launch template. For more information about using launch templates, see Launch template support. An Amazon EKS
    managed node group is an Amazon EC2 Auto Scaling group and associated Amazon EC2 instances that are managed by
    Amazon Web Services for an Amazon EKS cluster. Each node group uses a version of the Amazon EKS optimized Amazon
    Linux 2 AMI. For more information, see Managed Node Groups in the Amazon EKS User Guide.

    Args:
        name(Text): An Idem name of EKS Node group.
        cluster_name(Text): The name of the cluster to create the node group in.
        subnets(List[str]): The subnets to use for the Auto Scaling group that is created for your node group. If you
            specify launchTemplate, then don't specify  SubnetId  in your launch template, or the node group
            deployment will fail. For more information about using launch templates with Amazon EKS, see
            Launch template support in the Amazon EKS User Guide.
        node_role(Text): The Amazon Resource Name (ARN) of the IAM role to associate with your node group.
            The Amazon EKS worker node kubelet daemon makes calls to Amazon Web Services APIs on your behalf. Nodes
            receive permissions for these API calls through an IAM instance profile and associated policies. Before you
            can launch nodes and register them into a cluster, you must create an IAM role for those nodes to use when
            they are launched.
        resource_id(Text, optional): AWS EKS Node group name
        scaling_config(Dict[str, Any], optional): The scaling configuration details for the Auto Scaling group that is created for your node
            group. Defaults to None.
            * minSize (int, optional): The minimum number of nodes that the managed node group can scale in to.
            * maxSize (int, optional): The maximum number of nodes that the managed node group can scale out to. For information about
                the maximum number that you can specify, see Amazon EKS service quotas in the Amazon EKS User
                Guide.
            * desiredSize (int, optional): The current number of nodes that the managed node group should maintain.  If you use Cluster
                Autoscaler, you shouldn't change the desiredSize value directly, as this can cause the Cluster
                Autoscaler to suddenly scale up or scale down.  Whenever this parameter changes, the number of
                worker nodes in the node group is updated to the specified size. If this parameter is given a
                value that is smaller than the current number of running worker nodes, the necessary number of
                worker nodes are terminated to match the given value. When using CloudFormation, no action
                occurs if you remove this parameter from your CFN template. This parameter can be different from
                minSize in some cases, such as when starting with extra hosts for testing. This parameter can
                also be different when you want to start with an estimated number of needed hosts, but let
                Cluster Autoscaler reduce the number if there are too many. When Cluster Autoscaler is used, the
                desiredSize parameter is altered by Cluster Autoscaler (but can be out-of-date for short periods
                of time). Cluster Autoscaler doesn't scale a managed node group lower than minSize or higher
                than maxSize.
        capacity_type(Text, optional): The capacity type for your node group
        disk_size(integer, optional): The root device disk size (in GiB) for your node group instances. The default
            disk size is 20 GiB.
        instance_types(List[str], optional): Specify the instance types for a node group. If you specify a GPU instance type, be sure to
            specify AL2_x86_64_GPU with the amiType parameter. If you specify launchTemplate, then you can
            specify zero or one instance type in your launch template or you can specify 0-20 instance types
            for instanceTypes. If however, you specify an instance type in your launch template and specify
            any instanceTypes, the node group deployment will fail. If you don't specify an instance type in
            a launch template or for instanceTypes, then t3.medium is used, by default. If you specify Spot
            for capacityType, then we recommend specifying multiple values for instanceTypes. For more
            information, see Managed node group capacity types and Launch template support in the Amazon EKS
            User Guide. Defaults to None.
        ami_type(Text, optional): The AMI type for your node group. GPU instance types should use the AL2_x86_64_GPU
            AMI type. Non-GPU instances should use the AL2_x86_64 AMI type. Arm instances should use the AL2_ARM_64 AMI
            type.
        remote_access(Dict[str, Any], optional): The remote access (SSH) configuration to use with your node group. If you specify
            launchTemplate, then don't specify remoteAccess, or the node group deployment will fail. For
            more information about using launch templates with Amazon EKS, see Launch template support in
            the Amazon EKS User Guide. Defaults to None.
            * ec2SshKey (str, optional): The Amazon EC2 SSH key that provides access for SSH communication with the nodes in the managed
                node group. For more information, see Amazon EC2 key pairs and Linux instances in the Amazon
                Elastic Compute Cloud User Guide for Linux Instances.
            * sourceSecurityGroups (List[str], optional): The security groups that are allowed SSH access (port 22) to the nodes. If you specify an Amazon
                EC2 SSH key but do not specify a source security group when you create a managed node group,
                then port 22 on the nodes is opened to the internet (0.0.0.0/0). For more information, see
                Security Groups for Your VPC in the Amazon Virtual Private Cloud User Guide.
        labels(Dict[str, str], optional): The Kubernetes labels to be applied to the nodes in the node group when they are created. Defaults to None.
        taints(List[Dict[str, Any]], optional): The Kubernetes taints to be applied to the nodes in the node group. For more information, see
            Node taints on managed node groups. Defaults to None.
            * key (str, optional): The key of the taint.
            * value (str, optional): The value of the taint.
            * effect (str, optional): The effect of the taint.
        tags(Dict, optional): The metadata to apply to the node group to assist with categorization and organization.
            Each tag consists of a key and an optional value. You define both. Node group tags do not propagate to any
            other resources associated with the node group, such as the Amazon EC2 instances or subnets.
        client_request_token(Text, optional): Unique, case-sensitive identifier that you provide to ensure the
            idempotency of the request.
        launch_template(Dict[str, Any], optional): An object representing a node group's launch template specification. If specified, then do not
            specify instanceTypes, diskSize, or remoteAccess and make sure that the launch template meets
            the requirements in launchTemplateSpecification. Defaults to None.
            * name (str, optional): The name of the launch template.
            * version (str, optional): The version of the launch template to use. If no version is specified, then the template's
                default version is used.
            * id (str, optional): The ID of the launch template.
        update_config(Dict[str, Any], optional): The node group update configuration. Defaults to None.
            * maxUnavailable (int, optional): The maximum number of nodes unavailable at once during a version update. Nodes will be updated
                in parallel. This value or maxUnavailablePercentage is required to have a value.The maximum
                number is 100.
            * maxUnavailablePercentage (int, optional): The maximum percentage of nodes unavailable during a version update. This percentage of nodes
                will be updated in parallel, up to 100 nodes at once. This value or maxUnavailable is required
                to have a value.
        version(str, optional): The Kubernetes version to use for your managed nodes. By default, the Kubernetes version of the
            cluster is used, and this is the only accepted specified value. If you specify launchTemplate,
            and your launch template uses a custom AMI, then don't specify version, or the node group
            deployment will fail. For more information about using launch templates with Amazon EKS, see
            Launch template support in the Amazon EKS User Guide. Defaults to None.
        release_version(str, optional): The AMI version of the Amazon EKS optimized AMI to use with your node group. By default, the
            latest available AMI version for the node group's current Kubernetes version is used. For more
            information, see Amazon EKS optimized Amazon Linux 2 AMI versions in the Amazon EKS User Guide.
            If you specify launchTemplate, and your launch template uses a custom AMI, then don't specify
            releaseVersion, or the node group deployment will fail. For more information about using launch
            templates with Amazon EKS, see Launch template support in the Amazon EKS User Guide. Defaults to None.
        timeout(Dict, optional): Timeout configuration for creating or updating nodegroup.
            * create (Dict) -- Timeout configuration for creating nodegroup
                * delay(int, default=30) -- The amount of time in seconds to wait between attempts.
                * max_attempts(int, default=80) -- Customized timeout configuration containing delay and max attempts.
            * update (string) -- Timeout configuration for updating nodegroup
                * delay(int, default=30) -- The amount of time in seconds to wait between attempts.
                * max_attempts(int, default=80) -- Customized timeout configuration containing delay and max attempts.

    Request Syntax:

        [eks-nodegroup-name]:
              aws.eks.nodegroup.present:
              - cluster_name: 'string'
              - node_group_arn: 'string'
              - version: 'string'
              - release_version: 'string'
              - resource_id: 'string'
              - status: 'string'
              - capacity_type: 'ON_DEMAND' | 'SPOT',
              - instance_types:
                - 'string'
              - subnets:
                - 'string'
              - ami_type: 'string'
              - node_role: 'string'
              - disk_size: int
              - scaling_config:
                  desiredSize: int
                  maxSize: int
                  minSize: int
              - update_config:
                  maxUnavailable: int
              - tags:
                  - 'string': 'string'
    Returns:
        Dict[str, Any]

    Examples:

        .. code-block:: sls

            nodes1:
              aws.eks.nodegroup.present:
              - cluster_name: idem-test-cluster
              - node_group_arn: arn:aws:eks:us-west-2:000000000000:nodegroup/prod/idem-test-instance-e1a573b9-4e74-48f0-85c4-c214a8ec8ada/id123
              - version: '1.21'
              - release_version: 1.21.5-20220123
              - resource_id: 'idem-test-cluster-node-group'
              - status: ACTIVE
              - capacity_type: ON_DEMAND
              - instance_types:
                - t1.micro
              - subnets:
                - subnet-31813031
              - ami_type: AL2_x86_64
              - node_role: arn:aws:iam::000000000000:role/idem-test-role-2075a427-24c2-4021-abc3-9b542834addb
              - disk_size: 20
              - scaling_config:
                  desiredSize: 2
                  maxSize: 2
                  minSize: 2
              - update_config:
                  maxUnavailable: 1
              - tags:
                  - Name: idem-test-instance-e1a573b9-4e74-48f0-85c4-c214a8ec8ada



    """

    result = dict(comment=(), old_state=None, new_state=None, name=name, result=True)
    before = None
    resource_updated = False
    if resource_id:
        before = await hub.exec.boto3.client.eks.describe_nodegroup(
            ctx, clusterName=cluster_name, nodegroupName=resource_id
        )
        if not before["result"]:
            if not ("ResourceNotFoundException" in before["comment"][0]):
                result["result"] = False
                result["comment"] = before["comment"]
                return result

    if before and before["ret"]:
        try:
            result[
                "old_state"
            ] = hub.tool.aws.eks.conversion_utils.convert_raw_nodegroup_to_present(
                raw_resource=before["ret"]["nodegroup"], idem_resource_name=name
            )
            plan_state = copy.deepcopy(result["old_state"])
            client_request_token = result["old_state"].get("client_request_token", None)

            # update nodegroup
            update_ret = await hub.exec.aws.eks.nodegroup.update_nodegroup(
                ctx=ctx,
                name=name,
                before=result["old_state"],
                version=version,
                release_version=release_version,
                launch_template=launch_template,
                labels=labels,
                taints=taints,
                scaling_config=scaling_config,
                update_config=update_config,
                client_request_token=client_request_token,
                timeout=timeout,
            )
            result["comment"] = update_ret["comment"]
            result["result"] = update_ret["result"]
            resource_updated = bool(update_ret["ret"])

            if update_ret["ret"] and ctx.get("test", False):
                result["comment"] = result["comment"] + (
                    f"Would update aws.eks.nodegroup '{name}'",
                )
                for nodegroup_param in update_ret["ret"]:
                    plan_state[nodegroup_param] = update_ret["ret"][nodegroup_param]

            # update tags
            update_tags_ret = await hub.exec.aws.eks.tag.update_eks_tags(
                ctx=ctx,
                resource_arn=result["old_state"].get("nodegroup_arn"),
                old_tags=result["old_state"].get("tags"),
                new_tags=tags,
            )
            result["comment"] = result["comment"] + update_tags_ret["comment"]
            result["result"] = result["result"] and update_tags_ret["result"]
            resource_updated = resource_updated or bool(update_tags_ret["ret"])
            if ctx.get("test", False) and update_tags_ret["ret"]:
                plan_state["tags"] = update_tags_ret["ret"].get("tags")
        except Exception as e1:
            result["comment"] = result["comment"] + (f"{e1.__class__.__name__}: {e1}",)
            result["result"] = False
            return result
    else:
        if ctx.get("test", False):
            result["new_state"] = hub.tool.aws.test_state_utils.generate_test_state(
                enforced_state={},
                desired_state={
                    "name": name,
                    "cluster_name": cluster_name,
                    "subnets": subnets,
                    "node_role": node_role,
                    "scaling_config": scaling_config,
                    "disk_size": disk_size,
                    "instance_types": instance_types,
                    "ami_type": ami_type,
                    "remote_access": remote_access,
                    "labels": labels,
                    "taints": taints,
                    "tags": tags,
                    "client_request_token": client_request_token,
                    "launch_template": launch_template,
                    "update_config": update_config,
                    "capacity_type": capacity_type,
                    "version": version,
                    "release_version": release_version,
                },
            )
            result["comment"] = hub.tool.aws.comment_utils.would_create_comment(
                resource_type="aws.eks.nodegroup", name=name
            )
            return result
        try:
            ret = await hub.exec.boto3.client.eks.create_nodegroup(
                ctx,
                clusterName=cluster_name,
                nodegroupName=name,
                scalingConfig=scaling_config,
                diskSize=disk_size,
                subnets=subnets,
                instanceTypes=instance_types,
                amiType=ami_type,
                remoteAccess=remote_access,
                nodeRole=node_role,
                labels=labels,
                taints=taints,
                tags=tags,
                clientRequestToken=client_request_token,
                launchTemplate=launch_template,
                updateConfig=update_config,
                capacityType=capacity_type,
                version=version,
                releaseVersion=release_version,
            )
            result["result"] = ret["result"]
            if not result["result"]:
                result["comment"] = ret["comment"]
                return result

            resource_id = ret["ret"]["nodegroup"]["nodegroupName"]
            waiter_config = hub.tool.aws.waiter_utils.create_waiter_config(
                default_delay=60,
                default_max_attempts=40,
                timeout_config=timeout.get("create") if timeout else None,
            )
            hub.log.debug(f"Waiting on creating aws.eks.nodegroup '{name}'")
            try:
                await hub.tool.boto3.client.wait(
                    ctx,
                    "eks",
                    "nodegroup_active",
                    nodegroupName=resource_id,
                    clusterName=cluster_name,
                    WaiterConfig=waiter_config,
                )
            except Exception as e:
                result["comment"] = result["comment"] + (str(e),)
                result["result"] = False
            result["comment"] = hub.tool.aws.comment_utils.create_comment(
                resource_type="aws.eks.nodegroup", name=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.exec.boto3.client.eks.describe_nodegroup(
                ctx, clusterName=cluster_name, nodegroupName=resource_id
            )
            result[
                "new_state"
            ] = hub.tool.aws.eks.conversion_utils.convert_raw_nodegroup_to_present(
                raw_resource=after["ret"]["nodegroup"], idem_resource_name=name
            )
        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,
    cluster_name: str,
    resource_id: str = None,
    timeout: Dict = None,
) -> Dict[str, Any]:
    r"""
    **Autogenerated function**

    Deletes an Amazon EKS node group for a cluster.

    Args:
        name(Text): An Idem name of EKS Node group.
        cluster_name(Text): The name of the cluster to create the node group in.
        resource_id(Text, optional): AWS EKS Node group name. Idem automatically considers this resource being absent
         if this field is not specified.
        timeout(Dict, optional): Timeout configuration for creating or updating cluster.
            * delete (Dict) -- Timeout configuration for deleting cluster
                * delay -- The amount of time in seconds to wait between attempts.
                * max_attempts -- Customized timeout configuration containing delay and max attempts.
    Request Syntax:
        [nodegroup-name]:
              aws.eks.nodegroup.absent:
                - cluster_name: 'string'
                - resource_id: 'string'

    Returns:
        Dict[str, Any]

    Examples:

        .. code-block:: sls

            nodes1:
              aws.eks.nodegroup.absent:
                - cluster_name: idem-test-cluster
                - resource_id: idem-test-nodegroup
    """

    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.eks.nodegroup", name=name
        )
        return result
    before = await hub.exec.boto3.client.eks.describe_nodegroup(
        ctx, clusterName=cluster_name, nodegroupName=resource_id
    )
    if not before["ret"]:
        result["comment"] = hub.tool.aws.comment_utils.already_absent_comment(
            resource_type="aws.eks.nodegroup", name=name
        )
    elif ctx.get("test", False):
        result[
            "old_state"
        ] = hub.tool.aws.eks.conversion_utils.convert_raw_nodegroup_to_present(
            raw_resource=before["ret"]["nodegroup"], idem_resource_name=name
        )
        result["comment"] = hub.tool.aws.comment_utils.would_delete_comment(
            resource_type="aws.eks.nodegroup", name=name
        )
        return result
    else:
        result[
            "old_state"
        ] = hub.tool.aws.eks.conversion_utils.convert_raw_nodegroup_to_present(
            raw_resource=before["ret"]["nodegroup"], idem_resource_name=name
        )
        try:
            ret = await hub.exec.boto3.client.eks.delete_nodegroup(
                ctx, clusterName=cluster_name, nodegroupName=resource_id
            )
            result["result"] = ret["result"]
            if not result["result"]:
                result["comment"] = ret["comment"]
                result["result"] = False
                return result
            else:
                waiter_config = hub.tool.aws.waiter_utils.create_waiter_config(
                    default_delay=60,
                    default_max_attempts=40,
                    timeout_config=timeout.get("delete") if timeout else None,
                )
                try:
                    await hub.tool.boto3.client.wait(
                        ctx,
                        "eks",
                        "nodegroup_deleted",
                        nodegroupName=resource_id,
                        clusterName=cluster_name,
                        WaiterConfig=waiter_config,
                    )
                except Exception as e:
                    result["comment"] = result["comment"] + (str(e),)
                    result["result"] = False
            result["comment"] = result[
                "comment"
            ] + hub.tool.aws.comment_utils.delete_comment(
                resource_type="aws.eks.nodegroup", name=name
            )
        except Exception as e:
            result["comment"] = 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 Amazon EKS managed node groups associated with the specified cluster in your Amazon Web Services
    account in the specified Region. Self-managed node groups are not listed.


    Returns:
        Dict[str, Any]

    Examples:

        .. code-block:: bash

            $ idem describe aws.eks.nodegroup
    """

    result = {}
    cluster_ret = await hub.exec.boto3.client.eks.list_clusters(ctx)
    if not cluster_ret["result"]:
        hub.log.debug(f"Could not describe cluster {cluster_ret['comment']}")
        return {}
    cluster_node_groups = []
    try:
        for name in cluster_ret["ret"]["clusters"]:
            node_group_ret = await hub.exec.boto3.client.eks.list_nodegroups(
                ctx, clusterName=name
            )
            if not node_group_ret["result"]:
                result["comment"] = node_group_ret["comment"]
                return result
            for group in node_group_ret["ret"]["nodegroups"]:
                cluster_node_groups.append({"name": name, "group": group})
    except Exception as e:
        result["comment"] = str(e)
        result["result"] = False
        return result

    for node_groups in cluster_node_groups:
        cluster_name = node_groups["name"]
        node_group = node_groups["group"]
        describe_ret = await hub.exec.boto3.client.eks.describe_nodegroup(
            ctx, clusterName=cluster_name, nodegroupName=node_group
        )
        nodegroup = describe_ret["ret"]["nodegroup"]
        resource_id = f"{nodegroup['clusterName']}-{nodegroup['nodegroupName']}"
        resource_translated = (
            hub.tool.aws.eks.conversion_utils.convert_raw_nodegroup_to_present(
                raw_resource=nodegroup, idem_resource_name=resource_id
            )
        )
        result[resource_id] = {
            "aws.eks.nodegroup.present": [
                {parameter_key: parameter_value}
                for parameter_key, parameter_value in resource_translated.items()
            ]
        }
    return result
