"""
hub.exec.boto3.client.ec2.allocate_address
hub.exec.boto3.client.ec2.describe_addresses
hub.exec.boto3.client.ec2.release_address
"""
import copy
from collections import OrderedDict
from typing import Any
from typing import Dict
from typing import List

import dict_tools.differ as differ

__contracts__ = ["resource"]


async def present(
    hub,
    ctx,
    name: str,
    domain: str = "standard",
    network_border_group: str = None,
    public_ipv4_pool: str = None,
    customer_owned_ipv4_pool: str = None,
    tags: List = None,
) -> Dict[str, Any]:
    r"""
    **Autogenerated function**

    Allocates an Elastic IP address to your Amazon Web Services account. After you allocate the Elastic IP address
    you can associate it with an instance or network interface. After you release an Elastic IP address,
    it is released to the IP address pool and can be allocated to a different Amazon Web Services account. [EC2-VPC]
    If you release an Elastic IP address, you might be able to recover it. You cannot recover an Elastic IP address
    that you released after it is allocated to another Amazon Web Services account. You cannot recover an Elastic IP
    address for EC2-Classic. To attempt to recover an Elastic IP address that you released, specify it in this
    operation. An Elastic IP address is for use either in the EC2-Classic platform or in a VPC. By default,
    you can allocate 5 Elastic IP addresses for EC2-Classic per Region and 5 Elastic IP addresses for EC2-VPC per
    Region. For more information, see Elastic IP Addresses in the Amazon Elastic Compute Cloud User Guide.

    Args:
        name(Text): A name, ID to identify the resource.
        domain(Text): Indicates whether the Elastic IP address is for use with instances in a VPC or instances in EC2-Classic.
        network_border_group(TEXT): A unique set of Availability Zones, Local Zones, or Wavelength Zones from which Amazon Web Services advertises IP addresses
        public_ipv4_pool(TEXT): The ID of an address pool that you own.
        customer_owned_ipv4_pool(TEXT): The ID of a customer-owned address pool.
        tags(Dict, optional): The tags to apply to the resources during launch.

    Returns:
        Dict[str, Any]

    Examples:

        .. code-block:: sls

            127.15.17.187:
              aws.ec2.elastic_ip.present:
              - domain: vpc
              - tags:
                - Key: name
                  Value: ONE
    """

    result = dict(comment="", old_state=None, new_state=None, name=name, result=True)
    public_ip = None
    allocation_id = None
    try:
        before = await hub.exec.boto3.client.ec2.describe_addresses(
            ctx, PublicIps=[name]
        )
        if ctx.get("test", False):
            if before:
                result["comment"] = f"Would update aws.ec2.elastic_ip {name}"
                result["result"] = True
            else:
                result["comment"] = f"Would create aws.ec2.elastic_ip {name}"
                result["result"] = True
            return result
        if before:
            result["old_state"] = before["ret"]["Addresses"][0]
            public_ip = name
            if domain == "vpc":
                allocation_id = before["ret"]["Addresses"][0]["AllocationId"]
            if tags is not None:
                update_ret = await hub.exec.aws.ec2.tag.update_tags(
                    ctx=ctx,
                    resource_id=allocation_id,
                    old_tags=before["ret"]["Addresses"][0]["Tags"],
                    new_tags=tags,
                )
                if not update_ret["result"]:
                    result["comment"] = update_ret["comment"]
                    result["result"] = False
                    return result
                result["new_state"] = copy.deepcopy(result["old_state"])
                if update_ret["ret"]:
                    result["new_state"]["Tags"] = tags
                    result["comment"] = f"Updated' {name}'"
                else:
                    result["comment"] = f"'{name}' already exists"
        else:
            ret = await hub.exec.boto3.client.ec2.allocate_address(
                ctx,
                Domain=domain,
                PublicIpv4Pool=public_ipv4_pool,
                NetworkBorderGroup=network_border_group,
                CustomerOwnedIpv4Pool=customer_owned_ipv4_pool,
                TagSpecifications=[{"ResourceType": "elastic-ip", "Tags": tags}]
                if tags
                else None,
            )
            if not result["result"]:
                result["result"] = ret["result"]
                result["comment"] = ret["comment"]
                return result
            public_ip = ret["ret"]["PublicIp"]
            result["comment"] = f"Created '{name}'"
    except Exception as e:
        result["comment"] = str(e)
        result["result"] = False
        return result

    try:
        if not before:
            after = await hub.exec.boto3.client.ec2.describe_addresses(
                ctx, PublicIps=[public_ip]
            )
            result["new_state"] = after["ret"]["Addresses"][0]
    except Exception as e:
        result["comment"] = str(e)
        result["result"] = False
    return result


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

    Releases the specified Elastic IP address. [EC2-Classic, default VPC] Releasing an Elastic IP address
    automatically disassociates it from any instance that it's associated with. To disassociate an Elastic IP address
    without releasing it, use DisassociateAddress . [Nondefault VPC] You must use DisassociateAddress to disassociate
    the Elastic IP address before you can release it. After releasing an Elastic IP address, it is released to the IP
    address pool. Be sure to update your DNS records and any servers or devices that communicate with the address. If
    you attempt to release an Elastic IP address that you already released, you'll get an AuthFailure error if the
    address is already allocated to another Amazon Web Services account. [EC2-VPC] After you release an Elastic IP
    address for use in a VPC, you might be able to recover it. For more information, see AllocateAddress.

    Args:
        name(Text): A name, ID to identify the resource.

    Returns:
        Dict[str, Any]

    Examples:

        .. code-block:: sls

            127.15.17.187:
              aws.ec2.elastic_ip.absent:
              - domain: vpc
              - tags:
                - Key: name
                  Value: ONE
    """

    result = dict(comment="", old_state=None, new_state=None, name=name, result=True)
    before = await hub.exec.boto3.client.ec2.describe_addresses(ctx, PublicIps=[name])

    if not before:
        result["comment"] = f"'{name}' already absent"
    elif ctx.get("test", False):
        result["comment"] = f"Would delete aws.ec2.elastic_ip {name}"
        return result
    else:
        try:
            result["old_state"] = before
            ret = await hub.exec.boto3.client.ec2.release_address(ctx, PublicIp=name)
            if not result["result"]:
                result["comment"] = ret["comment"]
                result["result"] = False
                return result
            result["result"] = ret["result"]
            result["comment"] = f"Deleted '{name}'"
        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**

    Describe the resource in a way that can be recreated/managed with the corresponding "present" function
    Describes the specified Elastic IP addresses or all of your Elastic IP addresses.

    Returns:
        Dict[str, Any]

    Examples:

        .. code-block:: bash

            $ idem describe aws.ec2.elastic_ip
    """

    result = {}
    ret = await hub.exec.boto3.client.ec2.describe_addresses(ctx)
    if not ret["result"]:
        hub.log.debug(f"Could not describe elastic_ip {ret['comment']}")
        return {}

    for each_elastic_ip in ret["ret"]["Addresses"]:
        elastic_ip_id = each_elastic_ip.get("PublicIp")
        describe_parameters = OrderedDict(
            {
                "AllocationId": "allocation_id",
                "Domain": "domain",
                "NetworkBorderGroup": "network_border_group",
                "PublicIpv4Pool": "public_ipv4_pool",
                "Tags": "tags",
                "CustomerOwnedIp": "customer_ownedIp",
                "CustomerOwnedIpv4Pool": "customer_owned_ipv4_pool",
                "CarrierIp": "carrier_ip",
            }
        )
        new_elastic_ip = []
        for parameter_old_key, parameter_new_key in describe_parameters.items():
            if each_elastic_ip.get(parameter_old_key) is not None:
                new_elastic_ip.append(
                    {parameter_new_key: each_elastic_ip.get(parameter_old_key)}
                )
        result[elastic_ip_id] = {"aws.ec2.elastic_ip.present": new_elastic_ip}

    return result
