import copy
from typing import Any
from typing import Dict

__contracts__ = ["resource"]

AWS_Org_Not_In_Use_Exception = "AWSOrganizationsNotInUseException"
ALREADY_EXISTS = "already exists"

TREQ = {
    "absent": {
        "require": [
            "aws.organizations.organization_unit.absent",
        ],
    },
}


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

    Creates an AWS organization. The account whose user is calling the CreateOrganization operation
    automatically becomes the management account of the new organization.This operation must be called using
    credentials from the account that is to become the new organization's management account. The principal
    must also have the relevant IAM permissions.By default (or if you set the FeatureSet parameter to ALL ),
    the new organization is created with all features enabled and service control policies automatically
    enabled in the root. If you instead choose to create the organization supporting only the
    consolidated billing features by setting the FeatureSet parameter to CONSOLIDATED_BILLING" ,
    no policy types are enabled by default, and you can't use organization policies

    Args:
        name(Text): Name of the resource.
        feature_set(Text, Default: 'ALL'): optional values: 'ALL'|'CONSOLIDATED_BILLING'

    Request Syntax:
        [org-id]:
          aws.organizations.organization.present:
          - feature_set: 'string'

    Returns:
        Dict[str, Any]

    Examples:

        .. code-block:: sls

            org_is_present:
              aws.organizations.organization.present:
                - feature_set: 'ALL'
    """

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

    before = await hub.exec.boto3.client.organizations.describe_organization(ctx)

    if not before:
        # Org not present , create

        if ctx.get("test", False):
            result["new_state"] = hub.tool.aws.test_state_utils.generate_test_state(
                enforced_state={},
                desired_state={
                    "name": name,
                    "feature_set": feature_set,
                },
            )
            result["comment"] = result["comment"] + (
                f"Would create aws.organizations.organization '{name}'.",
            )
            return result

        try:
            ret = await hub.exec.boto3.client.organizations.create_organization(
                ctx, FeatureSet=feature_set
            )

            result["result"] = ret["result"]
            if not result["result"]:
                result["comment"] = result["comment"] + ret["comment"]
                return result
            result["comment"] = result["comment"] + (
                f"Created aws.organizations.organization {name}.",
            )
            after = await hub.exec.boto3.client.organizations.describe_organization(ctx)
            if after and after.get("ret"):
                result[
                    "new_state"
                ] = hub.tool.aws.organization.conversion_utils.convert_raw_organization_to_present(
                    after["ret"]["Organization"], name
                )
                roots_resp = await hub.exec.boto3.client.organizations.list_roots(ctx)

                if roots_resp and roots_resp["ret"] and roots_resp["ret"]["Roots"]:
                    result["new_state"]["roots"] = roots_resp["ret"]["Roots"]
        except hub.tool.boto3.exception.ClientError as e:
            result["comment"] = result["comment"] + (f"{e.__class__.__name__}: {e}",)
            result["result"] = False
    else:
        # Org present
        result[
            "old_state"
        ] = hub.tool.aws.organization.conversion_utils.convert_raw_organization_to_present(
            before["ret"]["Organization"], name
        )
        roots_resp = await hub.exec.boto3.client.organizations.list_roots(ctx)

        if roots_resp and roots_resp["ret"] and roots_resp["ret"]["Roots"]:
            result["old_state"]["roots"] = roots_resp["ret"]["Roots"]
        result["new_state"] = copy.deepcopy(result["old_state"])
        result["comment"] = result["comment"] + (
            f"aws.organizations.organization '{name}' {ALREADY_EXISTS}.",
        )

    return result


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

    Deletes the organization. You can delete an organization only by using credentials from the
    management account. The organization must be empty of member accounts.

    Args:
        name(Text): Name of the resource.

    Returns:
        Dict[str, Any]

    Examples:

        .. code-block:: sls

            aws.organizations.organization.absent:
                - name: value
    """

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

    before = await hub.exec.boto3.client.organizations.describe_organization(ctx)
    if before:
        result[
            "old_state"
        ] = hub.tool.aws.organization.conversion_utils.convert_raw_organization_to_present(
            before["ret"], name
        )
        if ctx.get("test", False):
            result["comment"] = result["comment"] + (
                f"Would delete aws.organizations.organization '{name}'.",
            )
            return result

    if not before and AWS_Org_Not_In_Use_Exception in str(before["comment"]):
        result["comment"] = result["comment"] + (
            f"aws.organizations.organization '{name}' is already absent",
        )
        return result
    else:
        try:
            ret = await hub.exec.boto3.client.organizations.delete_organization(ctx)

            if not ret["result"]:
                result["comment"] = ret["comment"]
                result["result"] = False
                return result
            result["comment"] = result["comment"] + (
                f"aws.organizations.organization {name} deleted.",
            )
            result["result"] = ret["result"]
        except hub.tool.boto3.exception.ClientError as e:
            result["comment"] = (f"{e.__class__.__name__}: {e}",)

    try:
        after = await hub.exec.boto3.client.organizations.describe_organization(ctx)
        if after.get("ret"):
            result[
                "new_state"
            ] = hub.tool.aws.organization.conversion_utils.convert_raw_organization_to_present(
                after["ret"], name
            )
    except Exception as e:
        result["comment"] = result["comment"] + (str(e),)
        result["result"] = False
    return result


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

    Retrieves information about the organization that the user's account belongs to.
    This operation can be called from any account in the organization..


    Returns:
        Dict[str, Any]

    Examples:

        .. code-block:: bash

            $ idem describe aws.organizations.organization
    """

    result = {}
    ret = await hub.exec.boto3.client.organizations.describe_organization(ctx)
    if not ret["result"]:
        hub.log.debug(f"Could not describe organization with error:  {ret['comment']}")
        return {}

    resource = ret["ret"]["Organization"]

    translated_resource = (
        hub.tool.aws.organization.conversion_utils.convert_raw_organization_to_present(
            resource
        )
    )
    roots_resp = await hub.exec.boto3.client.organizations.list_roots(ctx)

    if roots_resp and roots_resp["ret"] and roots_resp["ret"]["Roots"]:
        translated_resource["roots"] = roots_resp["ret"]["Roots"]
    result[resource["Id"]] = {
        "aws.organizations.organization.present": [
            {parameter_key: parameter_value}
            for parameter_key, parameter_value in translated_resource.items()
        ]
    }

    return result
