"""State module for managing IAM Groups."""
import copy
from typing import Any
from typing import Dict

__contracts__ = ["resource"]

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


async def present(
    hub, ctx, name: str, group_name: str, resource_id: str = None, path: str = None
) -> Dict[str, Any]:
    """Create/Update the IAM Group.

    Creates a new group.  For information about the number of groups you can create, see IAM and STS quotas in the
    IAM User Guide.

    Args:
        name(str): An Idem name of the resource. This is also used as the name of the IAM group.

        resource_id(str, optional): An identifier of the resource in the provider. Name of Group.

        path(str, optional): The path to the group. This parameter is optional. If it is not included,
            it defaults to a slash (/). This parameter allows (through its regex pattern) a string of
            characters consisting of either a forward slash (/) by itself or a string that must begin and
            end with forward slashes. For more information about paths, see IAM identifiers in the IAM User Guide.

        group_name(str): The name of the group to create. Do not include the path in this value.
            IAM user, group, role, and policy names must be unique within the account. Names are not distinguished by
            case. For example, you cannot create resources named both "MyResource" and "myresource".

    Request Syntax:
        .. code-block:: sls

            [group_name]:
              aws.iam.group.present:
                - name: "string"
                - group_name: "string"
                - resource_id: "string"
                - path: "string"

    Returns:
        Dict[str, Any]

    Examples:
        .. code-block:: sls

            test-group:
              aws.iam.group.present:
                - name: test-group
                - resource_id: test-group
                - group_name: test-group
                - path: /

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

    if resource_id:
        before = await hub.exec.aws.iam.group.get(
            ctx, resource_id=resource_id, name=name
        )
        if not before["result"] or not before["ret"]:
            result["result"] = False
            result["comment"] = before["comment"]
            return result

        result["comment"] += hub.tool.aws.comment_utils.already_exists_comment(
            resource_type="aws.iam.group", name=name
        )
        result["old_state"] = copy.deepcopy(before["ret"])
        result["new_state"] = copy.deepcopy(result["old_state"])

        update_ret = await hub.tool.aws.iam.group.update(
            ctx,
            resource_id=resource_id,
            before=before["ret"],
            group_name=group_name,
            path=path,
        )
        result["comment"] += update_ret["comment"]
        if not update_ret["result"]:
            result["result"] = False
            return result

        if update_ret["ret"] and "group_name" in update_ret["ret"]:
            resource_id = group_name
        resource_updated = bool(update_ret["ret"])

        if update_ret["ret"] and ctx.get("test", False):
            result["new_state"].update(update_ret["ret"])
            result["comment"] += hub.tool.aws.comment_utils.would_update_comment(
                resource_type="aws.iam.group", name=name
            )
        if resource_updated and ctx.get("test", 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,
                    "resource_id": name,
                    "group_name": group_name,
                    "path": path,
                },
            )
            result["comment"] = hub.tool.aws.comment_utils.would_create_comment(
                resource_type="aws.iam.group", name=name
            )
            return result
        create_ret = await hub.exec.boto3.client.iam.create_group(
            ctx, GroupName=group_name, Path=path
        )
        if not create_ret["result"]:
            result["result"] = False
            result["comment"] = create_ret["comment"]
            return result

        result["comment"] = hub.tool.aws.comment_utils.create_comment(
            resource_type="aws.iam.group", name=name
        )
        resource_id = group_name

    if (not result["old_state"]) or resource_updated:
        after_ret = await hub.exec.aws.iam.group.get(
            ctx, name=group_name, resource_id=resource_id
        )
        if not after_ret["result"] or not after_ret["ret"]:
            result["result"] = False
            result["comment"] += tuple(after_ret["comment"])
            return result
        result["new_state"] = after_ret["ret"]

    return result


async def absent(hub, ctx, name: str, resource_id: str = None) -> Dict[str, Any]:
    """Deletes the specified IAM group.

    The group must not contain any users or have any attached policies.

    Args:
        name(str): An Idem name of the resource.
        resource_id(str): Name of IAM group.

    Request Syntax:
        .. code-block:: sls

            [group_name]:
              aws.iam.group.absent:
                - name: "string"
                - resource_id: "string"

    Returns:
        Dict[str, Any]

    Examples:
        .. code-block:: sls

            test-group:
              aws.iam.group.absent:
                - name: test-group
                - resource_id: test-group
    """
    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.iam.group", name=name
        )
        return result

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

        result["comment"] = hub.tool.aws.comment_utils.delete_comment(
            resource_type="aws.iam.group", name=name
        )

    return result


async def describe(hub, ctx) -> Dict[str, Dict[str, Any]]:
    """Gets information about the IAM Group.

    Describe the resource in a way that can be recreated/managed with the corresponding
    present function.

    Returns:
        Dict[str, Dict[str, Any]]

    Examples:
        .. code-block:: bash

            $ idem describe aws.iam.group

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

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

    for group in ret["ret"]["Groups"]:
        group_name = group.get("GroupName")

        translated_resource = (
            hub.tool.aws.iam.conversion_utils.convert_raw_group_to_present(
                raw_resource=group,
                idem_resource_name=group_name,
            )
        )
        resource_key = f"iam-group-{group_name}"
        result[resource_key] = {
            "aws.iam.group.present": [
                {parameter_key: parameter_value}
                for parameter_key, parameter_value in translated_resource.items()
            ]
        }

    return result
