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

hub.exec.boto3.client.elasticache.create_cache_parameter_group
hub.exec.boto3.client.elasticache.delete_cache_parameter_group
hub.exec.boto3.client.elasticache.describe_cache_parameter_groups
hub.exec.boto3.client.elasticache.modify_cache_parameter_group
hub.exec.boto3.client.elasticache.reset_cache_parameter_group
"""
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"]


async def present(
    hub,
    ctx,
    name: str,
    cache_parameter_group_family: str,
    description: str,
    resource_id: str = None,
    parameter_name_values: List[
        make_dataclass(
            "ParameterNameValue",
            [("ParameterName", str), ("ParameterValue", str, field(default=None))],
        )
    ] = None,
    tags: Dict[str, Any]
    or List[
        make_dataclass("Tag", [("Key", str), ("Value", str, field(default=None))])
    ] = None,
) -> Dict[str, Any]:
    r"""
    **Autogenerated function**

    Creates a new Amazon ElastiCache cache parameter group. An ElastiCache cache parameter group is a collection of
    parameters and their values that are applied to all of the nodes in any cluster or replication group using the
    CacheParameterGroup. A newly created CacheParameterGroup is an exact duplicate of the default parameter group
    for the CacheParameterGroupFamily. To customize the newly created CacheParameterGroup you can change the values
    of specific parameters. For more information, see:    ModifyCacheParameterGroup in the ElastiCache API
    Reference.    Parameters and Parameter Groups in the ElastiCache User Guide.

    Args:
        name(Text): An Idem name of the resource.
        cache_parameter_group_family (Text): The name of the cache parameter group family that this cache parameter group is compatible with.
        description (Text): The description for this cache parameter group.
        resource_id(Text, optional): AWS Elasticache parameter group name to identify the resource.
        parameter_name_values(List[Dict[str, str], optional): An array of parameter names and values for the parameter update.
        You must supply at least one parameter name and value; subsequent arguments are optional.
        A maximum of 20 parameters may be modified per request.
            * ParameterName (str, optional) -- The name of the parameter.
            * ParameterValue (str, optional) -- The value of the parameter.
        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 cache parameter group.
            Each tag consists of a key name and an associated value. Defaults to None.
            * Key (str, optional): The key of the tag. Constraints: Tag keys are case-sensitive and accept a maximum of 127 Unicode
                characters. May not begin with aws:.
            * Value(str, optional): The value of the tag. Constraints: Tag values are case-sensitive and accept a maximum of 256
                Unicode characters.
    Returns:
        Dict[str, Any]

    Examples:

        .. code-block:: sls
            [cache_parameter_group_name]:
              aws.elasticache.cache_parameter_group.present:
                - cache_parameter_group_family: redis6.x
                - description: Customized default parameter group for redis6.x with cluster mode
                    on
                - resource_id: 'string'
                - parameter_name_values:
                  - parameter_name: acllog-max-len
                    parameter_value: '128'
                - tags:
                  - Key: 'string'
                    Value: 'string'
    """
    result = dict(comment=(), old_state=None, new_state=None, name=name, result=True)
    before = None
    if resource_id:
        before = (
            await hub.exec.boto3.client.elasticache.describe_cache_parameter_groups(
                ctx, CacheParameterGroupName=resource_id
            )
        )
    if isinstance(tags, List):
        tags = hub.tool.aws.tag_utils.convert_tag_list_to_dict(tags)

    is_updated = False
    are_tags_updated = False
    old_cache_parameter_group = {}
    resource_arn = str
    parameters = []

    if before and before["result"]:
        try:
            convert_ret = await hub.tool.aws.elasticache.conversion_utils.convert_raw_elasticache_parameter_group_to_present(
                ctx=ctx,
                raw_resource=before["ret"]["CacheParameterGroups"][0],
                idem_resource_name=resource_id,
            )
            result["result"] = convert_ret["result"]
            if not result["result"]:
                result["comment"] = result["comment"] + convert_ret["comment"]
            result["old_state"] = convert_ret["ret"]

            old_cache_parameter_group = copy.deepcopy(result["old_state"])
            plan_state = old_cache_parameter_group
            parameters = result["old_state"].get("parameter_name_values")
            resource_arn = result["old_state"].get("arn")
            result["comment"] = (
                f"aws.elasticache.cache_parameter_group '{name}' already exists",
            )

            # Update tags
            if tags is not None and tags != result["old_state"].get("tags"):
                update_tags_ret = await hub.exec.aws.elasticache.tag.update_tags(
                    ctx=ctx,
                    resource_arn=resource_arn,
                    old_tags=result["old_state"].get("tags"),
                    new_tags=tags,
                )
                if not update_tags_ret["result"]:
                    result["comment"] = result["comment"] + update_tags_ret["comment"]
                    result["result"] = False
                    return result
                are_tags_updated = bool(update_tags_ret["result"])
                if ctx.get("test", False) and are_tags_updated:
                    plan_state["tags"] = update_tags_ret["ret"]
                    result["comment"] = result["comment"] + (
                        f"Would update tags for aws.elasticache.cache_parameter_group {name}",
                    )
        except Exception as e:
            result["comment"] = result["comment"] + (str(e),)
            result["result"] = False
            return result
    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,
                        "cache_parameter_group_family": cache_parameter_group_family,
                        "description": description,
                        "parameter_name_values": parameter_name_values,
                        "tags": tags,
                    },
                )
                result["comment"] = result["comment"] + (
                    f"Would create aws.elasticache.cache_parameter_group '{name}'",
                )
                return result

            ret = await hub.exec.boto3.client.elasticache.create_cache_parameter_group(
                ctx,
                CacheParameterGroupName=name,
                CacheParameterGroupFamily=cache_parameter_group_family,
                Description=description,
                Tags=hub.tool.aws.tag_utils.convert_tag_dict_to_list(tags)
                if tags
                else None,
            )
            result["result"] = ret["result"]
            if not result["result"]:
                result["comment"] = ret["comment"]
                return result
            resource_id = name
            after_parameters = (
                await hub.exec.boto3.client.elasticache.describe_cache_parameters(
                    ctx, CacheParameterGroupName=resource_id
                )
            )
            if not after_parameters["result"]:
                result["comment"] = result["comment"] + after_parameters["comment"]
                result["result"] = False
                return result
            parameters = after_parameters["ret"].get("Parameters")
        except hub.tool.boto3.exception.ClientError as e:
            result["comment"] = result["comment"] + (f"{e.__class__.__name__}: {e}",)
            result["result"] = False
            return result

    # To add,remove,update the ParameterNameValues.
    # ParameterNameValues are defined automatically when a resource is created,
    # so this needs to be done regardless of whether the resource exists before.
    if parameter_name_values:
        update_parameter = (
            await hub.tool.aws.elasticache.parameters.update_elasticache_parameters(
                ctx=ctx,
                resource_id=resource_id,
                old_parameters=parameters,
                parameter_name_values=parameter_name_values,
            )
        )
        if not update_parameter["result"]:
            result["comment"] = result["comment"] + update_parameter["comment"]
            result["result"] = False
            return result
        is_updated = update_parameter["is_updated"]
        if is_updated:
            after_parameters = (
                await hub.exec.boto3.client.elasticache.describe_cache_parameters(
                    ctx, CacheParameterGroupName=resource_id
                )
            )
            if not after_parameters["result"]:
                result["comment"] = result["comment"] + after_parameters["comment"]
                result["result"] = False
                return result
            parameters = after_parameters.get("ret").get("Parameters")
        if ctx.get("test", False) and is_updated:
            plan_state["parameter_name_values"] = parameters

    try:
        if ctx.get("test", False):
            new_cache_parameter_group = plan_state
        elif (not (before and before["result"])) or is_updated or are_tags_updated:
            after = (
                await hub.exec.boto3.client.elasticache.describe_cache_parameter_groups(
                    ctx, CacheParameterGroupName=resource_id
                )
            )
            if after and after.get("ret"):
                convert_ret = await hub.tool.aws.elasticache.conversion_utils.convert_raw_elasticache_parameter_group_to_present(
                    ctx=ctx,
                    raw_resource=after["ret"]["CacheParameterGroups"][0],
                    idem_resource_name=resource_id,
                )
                result["result"] = convert_ret["result"]
                if not result["result"]:
                    result["comment"] = result["comment"] + convert_ret["comment"]
                new_cache_parameter_group = convert_ret.get("ret")
                if not (before and before["result"]):
                    result["comment"] = result["comment"] + (
                        f"Created aws.elasticache.cache_parameter_group '{name}'",
                    )
                elif is_updated or are_tags_updated:
                    result["comment"] = result["comment"] + (
                        f"Updated aws.elasticache.cache_parameter_group '{name}'",
                    )
            else:
                result["result"] = result["result"] and after["result"]
                if not result["result"]:
                    result["comment"] = result["comment"] + after["comment"]
                    return result
        else:
            new_cache_parameter_group = copy.deepcopy(old_cache_parameter_group)

        result["old_state"] = old_cache_parameter_group
        result["new_state"] = new_cache_parameter_group

    except Exception as e:
        result["comment"] = result["comment"] + (f"{e.__class__.__name__}: {e}",)
        result["result"] = False
    return result


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

    Deletes the specified cache parameter group. You cannot delete a cache parameter group if it is associated with
    any cache clusters. You cannot delete the default cache parameter groups in your account.

    Args:
        name(Text): An Idem name of the resource.
        resource_id(Text): AWS Elasticache parameter group name to identify the resource.

    Returns:
        Dict[str, Any]

    Examples:

        .. code-block:: sls

            resource_is_absent:
              aws.elasticache.cache_parameter_group.absent:
                - resource_id: value
    """

    result = dict(comment=(), old_state=None, new_state=None, name=name, result=True)

    before = await hub.exec.boto3.client.elasticache.describe_cache_parameter_groups(
        ctx,
        CacheParameterGroupName=name,
    )
    if not (before and before["result"]):
        result["comment"] = (
            f"aws.elasticache.cache_parameter_group '{name}' already absent",
        )
        return result
    else:
        convert_ret = await hub.tool.aws.elasticache.conversion_utils.convert_raw_elasticache_parameter_group_to_present(
            ctx=ctx,
            raw_resource=before["ret"]["CacheParameterGroups"][0],
            idem_resource_name=name,
        )
        result["result"] = convert_ret["result"]
        if not result["result"]:
            result["comment"] = result["comment"] + convert_ret["comment"]
        result["old_state"] = convert_ret["ret"]
        if ctx.get("test", False):
            result["comment"] = (
                f"Would delete aws.elasticache.cache_parameter_group '{name}'",
            )
            return result
        else:
            try:
                ret = await hub.exec.boto3.client.elasticache.delete_cache_parameter_group(
                    ctx,
                    CacheParameterGroupName=name,
                )
                result["result"] = ret["result"]
                if not result["result"]:
                    result["comment"] = ret["comment"]
                    return result
                result["comment"] = (
                    f"Deleted aws.elasticache.cache_parameter_group '{name}'",
                )
            except hub.tool.boto3.exception.ClientError 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


    Returns a list of cache parameter group descriptions. If a cache parameter group name is specified, the list
    contains only the descriptions for that group.


    Returns:
        Dict[str, Any]

    Examples:

        .. code-block:: bash

            $ idem describe aws.elasticache.cache_parameter_group
    """
    result = {}
    ret = await hub.exec.boto3.client.elasticache.describe_cache_parameter_groups(ctx)
    if not ret["result"]:
        hub.log.debug(
            f"Could not describe Elasticache parameter groups {ret['comment']}"
        )
        return {}
    for cache_parameter_group in ret["ret"]["CacheParameterGroups"]:
        resource_id = cache_parameter_group.get("CacheParameterGroupName")
        convert_ret = await hub.tool.aws.elasticache.conversion_utils.convert_raw_elasticache_parameter_group_to_present(
            ctx=ctx,
            raw_resource=cache_parameter_group,
            idem_resource_name=resource_id,
        )
        if not convert_ret["result"]:
            hub.log.warning(
                f"Could not describe elasticache parameter group '{resource_id}' with error {convert_ret['comment']}"
            )
            continue
        resource_translated = convert_ret.get("ret")
        result[resource_id] = {
            "aws.elasticache.cache_parameter_group.present": [
                {parameter_key: parameter_value}
                for parameter_key, parameter_value in resource_translated.items()
            ]
        }
    return result
