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

hub.exec.boto3.client.organizations.attach_policy
hub.exec.boto3.client.organizations.create_policy
hub.exec.boto3.client.organizations.delete_policy
hub.exec.boto3.client.organizations.describe_policy
hub.exec.boto3.client.organizations.detach_policy
hub.exec.boto3.client.organizations.list_policies
hub.exec.boto3.client.organizations.update_policy
"""
import copy
from collections import OrderedDict
from typing import Any
from typing import Dict
from typing import List

__contracts__ = ["resource"]

TREQ = {
    "absent": {
        "require": [
            "aws.organizations.policy_attachment.absent",
        ],
    },
    "present": {
        "require": [
            "aws.organizations.organization.present",
            "aws.organizations.organization_unit.present",
            "aws.organizations.account.present",
        ],
    },
}


async def present(
    hub,
    ctx,
    name: str,
    policy_name: str = None,
    description: str = None,
    policy_type: str = None,
    content: str = None,
    tags: List = None,
) -> Dict[str, Any]:
    r"""
    **Autogenerated function**

    Creates a policy of a specified type that you can attach to a root, an organizational unit (OU), or an
    individual AWS account. For more information about policies and their use, see Managing Organization Policies.
    If the request includes tags, then the requester must have the organizations:TagResource permission. This
    operation can be called only from the organization's management account.

    Args:
        name(Text): An ID to identify the resource.
        policy_name: The friendly name to assign to the policy.
        description: An optional description to assign to the policy.
        policy_type : The type of policy to create. Only supported type currently is SERVICE_CONTROL_POLICY
        content : The policy text content to add to the new policy. The text that you supply must adhere to
                    the rules of the policy type you specify in the Type parameter
        tags(Optional): A list of tags that you want to attach to the newly created policy

    Request Syntax:
        [policy-id]:
          aws.organizations.policy.present:
          - policy_name: 'string'
          - description: 'string'
          - policy_type: 'string'
          - content: 'string'
          - tags:
            - Key: 'string'
              Value: 'string'

    Returns:
        Dict[str, Any]

    Examples:

        .. code-block:: sls

            p-id:
              aws.organizations.policy.present:
                - policy_name: AllowAllS3Actions
                - description: Enables admins of attached accounts to delegate all S3 permissions
                - policy_type : SERVICE_CONTROL_POLICY
                - content : '{\"Version\":\"2012-10-17\",\"Statement\":{\"Effect\":\"Allow\",\"Action\":\"s3:*\"}}'
                - tags:
                  - Key: org
                    Value: idem-aws
    """

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

    before = await hub.exec.boto3.client.organizations.describe_policy(
        ctx, PolicyId=name
    )

    if ctx.get("test", False):
        if before:
            result["comment"] = f"Would update aws.organizations.policy {name}."
            result["result"] = True
        else:
            result["comment"] = f"Would create aws.oranizations.policy {name}."
            result["result"] = True
        return result

    change_dict = dict(Name=None, Description=None, Content=None)

    update_tag = False
    policy_id = None
    if before:
        # Policy exists , update
        result["comment"] = f"aws.organizations.policy '{name}' exists."
        before["ret"].pop("ResponseMetadata", None)
        result["old_state"] = before["ret"]["Policy"]["PolicySummary"]
        result["old_state"]["Content"] = before["ret"]["Policy"]["Content"]

        policy_id = before["ret"]["Policy"]["PolicySummary"].get("Id")

        try:

            if before["ret"]["Policy"]["PolicySummary"].get("Name") != policy_name:
                change_dict["Name"] = policy_name

            if (
                before["ret"]["Policy"]["PolicySummary"].get("Description")
                != description
            ):
                change_dict["Description"] = description

            if before["ret"]["Policy"]["Content"] != content:
                change_dict["Content"] = content

            if any(value is not None for value in change_dict.values()):

                update_policy_ret = (
                    await hub.exec.boto3.client.organizations.update_policy(
                        ctx,
                        PolicyId=policy_id,
                        Name=change_dict.get("Name"),
                        Description=change_dict.get("Description"),
                        Content=change_dict.get("Content"),
                    )
                )
                result["result"] = update_policy_ret["result"]
                if not update_policy_ret:
                    result["comment"] = update_policy_ret["comment"]
                    return result

                after = update_policy_ret

                after["ret"].pop("ResponseMetadata", None)
                result["new_state"] = after["ret"]["Policy"]["PolicySummary"]
                result["new_state"]["Content"] = after["ret"]["Policy"]["Content"]

                result["comment"] = f"Updated aws.organizations.policy '{name}'"

            else:
                result["new_state"] = copy.deepcopy(result["old_state"])

            old_tags = await hub.exec.boto3.client.organizations.list_tags_for_resource(
                ctx, ResourceId=policy_id
            )

            if old_tags:
                before["ret"]["Policy"]["Tags"] = old_tags["ret"]["Tags"]
                result["old_state"]["Tags"] = old_tags["ret"]["Tags"]

                # If no update in tags , then new_state will contain same tags as old
                if tags is None:
                    result["new_state"]["Tags"] = result["old_state"]["Tags"]

            if tags is not None and old_tags["result"]:

                update_tags_ret = (
                    await hub.exec.aws.organizations.organization.update_tags(
                        ctx, policy_id, old_tags["ret"].get("Tags", []), tags
                    )
                )
                if not update_tags_ret["result"]:
                    result[
                        "comment"
                    ] = f" {result['comment']} , {str(update_tags_ret['comment'])}"
                    result["result"] = False
                    return result

                if update_tags_ret["ret"] is not None:
                    update_tag = True

                    old_comment = result["comment"]
                    result[
                        "comment"
                    ] = f"'{old_comment}'. Updated tags on aws.organizations.account '{name}'."

            result["result"] = True
        except hub.tool.boto3.exception.ClientError as e:
            result["result"] = False
            result["comment"] = f"{e.__class__.__name__}: {e}"

    else:

        # Policy does not exist , create
        try:
            create_policy_ret = await hub.exec.boto3.client.organizations.create_policy(
                ctx,
                Name=policy_name,
                Description=description,
                Type=policy_type,
                Content=content,
                Tags=tags,
            )
            result["result"] = create_policy_ret["result"]
            if not result["result"]:
                result["comment"] = create_policy_ret["comment"]
                return result

            after = create_policy_ret

            after["ret"].pop("ResponseMetadata", None)
            result["new_state"] = after["ret"]["Policy"]["PolicySummary"]
            result["new_state"]["Content"] = after["ret"]["Policy"]["Content"]

            policy_id = result["new_state"]["Id"]
            if tags is not None:
                list_tags = (
                    await hub.exec.boto3.client.organizations.list_tags_for_resource(
                        ctx, ResourceId=policy_id
                    )
                )
                if list_tags:
                    result["new_state"]["Tags"] = list_tags["ret"]["Tags"]

            result["comment"] = f"Created aws.organizations.policy '{name}'."
        except hub.tool.boto3.exception.ClientError as e:
            result["comment"] = f"{e.__class__.__name__}: {e}"
            result["result"] = False

    if before and update_tag:
        try:
            updated_tags = (
                await hub.exec.boto3.client.organizations.list_tags_for_resource(
                    ctx, ResourceId=policy_id
                )
            )

            if updated_tags:
                result["new_state"]["Tags"] = updated_tags["ret"]["Tags"]

        except hub.tool.boto3.exception.ClientError as e:
            result["comment"] = f"{e.__class__.__name__}: {e}"
            result["result"] = False

    return result


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

    Deletes the specified policy from your organization. Before you perform this operation, you must first detach
    the policy from all organizational units (OUs), roots, and accounts. This operation can be called only from the
    organization's management account.

    Args:
        name(Text): An Identifier to identify the resource.

    Returns:
        Dict[str, Any]

    Examples:

        .. code-block:: sls
            p-iauqv2gg:
                aws.organizations.policy.absent:
                - name: p-iauqv2gg
    """

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

    before = await hub.exec.boto3.client.organizations.describe_policy(
        ctx, PolicyId=name
    )

    policy_id = name

    if not before:
        result["comment"] = f"aws.organizations.policy {name} already absent"
        result["result"] = True
    else:
        try:

            delete_ret = await hub.exec.boto3.client.organizations.delete_policy(
                ctx, PolicyId=policy_id
            )

            result["result"] = delete_ret["result"]

            if not result["result"]:
                result["comment"] = delete_ret["comment"]
                return result

            before["ret"].pop("ResponseMetadata", None)

            result["old_state"] = before["ret"]["Policy"]["PolicySummary"]
            result["old_state"]["Content"] = before["ret"]["Policy"]["Content"]

            result["comment"] = f"aws.organizations.policy {name} deleted."
        except hub.tool.boto3.exception.ClientError as e:
            result["comment"] = f"{e.__class__.__name__}: {e}"
            result["result"] = False

    return result


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

    Retrieves information about a policy.

    This operation can be called only from the organization's management account
    or by a member account that is a delegated administrator for an AWS service.

    Currently IDEM AWS supports only SERVICE_CONTROL_POLICY.

    Returns:
        Dict[str, Any]

    Examples:

        .. code-block:: bash

            $ idem describe aws.organizations.policy
    """

    result = {}

    list_all_policies = await hub.exec.boto3.client.organizations.list_policies(
        ctx, Filter="SERVICE_CONTROL_POLICY"
    )

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

    describe_parameters = OrderedDict(
        {
            "Name": "policy_name",
            "Description": "description",
            "Type": "policy_type",
            "Content": "content",
            "Tags": "tags",
        }
    )

    for policy in list_all_policies["ret"]["Policies"]:
        policy_id = policy["Id"]
        policy_ret = await hub.exec.boto3.client.organizations.describe_policy(
            ctx, PolicyId=policy_id
        )

        if not policy_ret and not policy_ret["ret"]:
            hub.log.debug(f"Could not describe '{policy_id}'")
            continue

        translated_resource = []

        policy_desc = policy_ret["ret"]["Policy"]

        for camel_case_key, snake_case_key in describe_parameters.items():
            if policy_desc["PolicySummary"].get(camel_case_key) is not None:
                translated_resource.append(
                    {snake_case_key: policy_desc["PolicySummary"].get(camel_case_key)}
                )
            elif policy_desc.get(camel_case_key) is not None:
                translated_resource.append(
                    {snake_case_key: policy_desc.get(camel_case_key)}
                )

        tags_ret = await hub.exec.boto3.client.organizations.list_tags_for_resource(
            ctx, ResourceId=policy_id
        )
        if tags_ret:
            translated_resource.append({"tags": tags_ret["ret"]["Tags"]})

        result[policy["Id"]] = {"aws.organizations.policy.present": translated_resource}

    return result
