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

hub.exec.boto3.client.ec2.create_vpc
hub.exec.boto3.client.ec2.delete_vpc
hub.exec.boto3.client.ec2.describe_vpcs
resource = hub.tool.boto3.resource.create(ctx, "ec2", "Vpc", name)
hub.tool.boto3.resource.exec(resource, associate_dhcp_options, *args, **kwargs)
hub.tool.boto3.resource.exec(resource, attach_classic_link_instance, *args, **kwargs)
hub.tool.boto3.resource.exec(resource, attach_internet_gateway, *args, **kwargs)
hub.tool.boto3.resource.exec(resource, create_network_acl, *args, **kwargs)
hub.tool.boto3.resource.exec(resource, create_route_table, *args, **kwargs)
hub.tool.boto3.resource.exec(resource, create_security_group, *args, **kwargs)
hub.tool.boto3.resource.exec(resource, create_subnet, *args, **kwargs)
hub.tool.boto3.resource.exec(resource, create_tags, *args, **kwargs)
hub.tool.boto3.resource.exec(resource, delete, *args, **kwargs)
hub.tool.boto3.resource.exec(resource, describe_attribute, *args, **kwargs)
hub.tool.boto3.resource.exec(resource, detach_classic_link_instance, *args, **kwargs)
hub.tool.boto3.resource.exec(resource, detach_internet_gateway, *args, **kwargs)
hub.tool.boto3.resource.exec(resource, disable_classic_link, *args, **kwargs)
hub.tool.boto3.resource.exec(resource, enable_classic_link, *args, **kwargs)
hub.tool.boto3.resource.exec(resource, modify_attribute, *args, **kwargs)
hub.tool.boto3.resource.exec(resource, request_vpc_peering_connection, *args, **kwargs)
"""
from collections import OrderedDict
from typing import Any
from typing import Dict
from typing import List

__contracts__ = ["resource"]


async def present(
    hub,
    ctx,
    name: str,
    cidr_block_association_set: List = None,
    ipv6_cidr_block_association_set: List = None,
    instance_tenancy: str = None,
    tags: List = None,
) -> Dict[str, Any]:
    r"""
    **Autogenerated function**

    Creates a VPC with the specified IPv4 CIDR block. The smallest VPC you can create uses a /28 netmask (16 IPv4
    addresses), and the largest uses a /16 netmask (65,536 IPv4 addresses). For more information about how large to
    make your VPC, see Your VPC and subnets in the Amazon Virtual Private Cloud User Guide. You can optionally
    request an IPv6 CIDR block for the VPC. You can request an Amazon-provided IPv6 CIDR block from Amazon's pool of
    IPv6 addresses, or an IPv6 CIDR block from an IPv6 address pool that you provisioned through bring your own IP
    addresses (BYOIP). By default, each instance you launch in the VPC has the default DHCP options, which include
    only a default DNS server that we provide (AmazonProvidedDNS). For more information, see DHCP options sets in
    the Amazon Virtual Private Cloud User Guide. You can specify the instance tenancy value for the VPC when you
    create it. You can't change this value for the VPC after you create it. For more information, see Dedicated
    Instances in the Amazon Elastic Compute Cloud User Guide.

    Args:
        name(Text): A name, ID to identify the resource.
        cidr_block_association_set(List, optional): Information about the IPv4 CIDR blocks associated with the VPC.
            Defaults to None.
            * CidrBlock (string) -- An IPv4 CIDR block to associate with the VPC.
            * Ipv4IpamPoolId (string) -- Associate a CIDR allocated from an IPv4 IPAM pool to a VPC.
            * Ipv4NetmaskLength (integer) -- The netmask length of the IPv4 CIDR you would like to associate from an Amazon VPC IP Address Manager (IPAM) pool.
        ipv6_cidr_block_association_set(List, optional): Information about the IPv6 CIDR blocks associated with the VPC.
            Defaults to None.
            * Ipv6CidrBlock (string) -- An IPv6 CIDR block from the IPv6 address pool. You must also specify Ipv6Pool in the request.
            * Ipv6IpamPoolId (string) -- Associates a CIDR allocated from an IPv6 IPAM pool to a VPC.
            * Ipv6NetmaskLength (integer) -- The netmask length of the IPv6 CIDR you would like to associate from an
             Amazon VPC IP Address Manager (IPAM) pool.
            * Ipv6CidrBlockNetworkBorderGroup (string) -- The name of the location from which we advertise the IPV6 CIDR
             block. Use this parameter to limit the CIDR block to this location. You must set AmazonProvidedIpv6CidrBlock
              to true to use this parameter. You can have one IPv6 CIDR block association per network border group.
            * AmazonProvidedIpv6CidrBlock (boolean) -- Requests an Amazon-provided IPv6 CIDR block with a /56 prefix
             length for the VPC. You cannot specify the range of IPv6 addresses, or the size of the CIDR block.
        instance_tenancy(Text, optional): The tenancy options for instances launched into the VPC. For default, instances are launched
            with shared tenancy by default. You can launch instances with any tenancy into a shared tenancy
            VPC. For dedicated, instances are launched as dedicated tenancy instances by default. You can
            only launch instances with a tenancy of dedicated or host into a dedicated tenancy VPC.
            Important: The host value cannot be used with this parameter. Use the default or dedicated
            values only. Default: default. Defaults to None.
        tags(List, optional): The tags to assign to the VPC. Defaults to None.
            * Key (string) -- The key of the tag. Tag keys are case-sensitive and accept a maximum of 127 Unicode characters. May not begin with aws: .
            * Value (string) -- The value of the tag. Tag values are case-sensitive and accept a maximum of 255 Unicode characters.

    Request Syntax:
        [vpc-resource-id]:
          aws.ec2.vpc.present:
          - cidr_block_association_set:
            - CidrBlock: 'string'
              Ipv4IpamPoolId: 'string'
              Ipv4NetmaskLength: 'integer'
          - ipv6_cidr_block_association_set:
            - Ipv6CidrBlock: 'string'
              Ipv6IpamPoolId: 'string'
              Ipv6NetmaskLength: 'integer'
              Ipv6CidrBlockNetworkBorderGroup: 'string'
              AmazonProvidedIpv6CidrBlock: True|False
          - instance_tenancy: 'default'|'dedicated'|'host'
          - tags:
            - Key: 'string'
              Value: 'string'

    Returns:
        Dict[str, Any]

    Examples:

        .. code-block:: sls

            vpc-01234672f3336db8:
              aws.ec2.vpc.present:
              - cidr_block_association_set:
                - CidrBlock: 10.1.150.0/28
              - instance_tenancy: default
              - tags:
                - Key: Name
                  Value: vpc-name
                - Key: vpc-tag-key-2
                  Value: vpc-tag-value-2
    """
    result = dict(comment="", old_state=None, new_state=None, name=name, result=True)
    resource = hub.tool.boto3.resource.create(ctx, "ec2", "Vpc", name)
    before = await hub.tool.boto3.resource.describe(resource)
    if ctx.get("test", False):
        if before:
            result["comment"] = f"Would update aws.ec2.instance {name}"
            result["result"] = True
        else:
            result["comment"] = f"Would create aws.ec2.instance {name}"
            result["result"] = True
        return result

    if before:
        try:
            result["old_state"] = before
            # Update cidr blocks
            update_ret = await hub.exec.aws.ec2.vpc.update_cidr_blocks(
                ctx=ctx,
                vpc_id=before.get("VpcId"),
                old_ipv4_cidr_blocks=before.get("CidrBlockAssociationSet", list()),
                old_ipv6_cidr_blocks=before.get("Ipv6CidrBlockAssociationSet", list()),
                new_ipv4_cidr_blocks=cidr_block_association_set,
                new_ipv6_cidr_blocks=ipv6_cidr_block_association_set,
            )
            if not update_ret["result"]:
                result["comment"] = update_ret["comment"]
                result["result"] = False
            if result["result"] and (tags is not None):
                # Update tags
                update_ret = await hub.exec.aws.ec2.tag.update_tags(
                    ctx=ctx,
                    resource_id=before.get("VpcId"),
                    old_tags=before.get("Tags"),
                    new_tags=tags,
                )
                if not update_ret["result"]:
                    result["comment"] = update_ret["comment"]
                    result["result"] = False
        except hub.tool.boto3.exception.ClientError as e:
            result["comment"] = f"{e.__class__.__name__}: {e}"
            result["result"] = False
        if result["result"]:
            result["comment"] = f"'Update {name}'"
    else:
        cidr_request_payload = dict()
        # Since boto3 only allows one cidr association when creating a vpc, we use the first cidr associations
        # during vpc creation, associate the rest after creation.
        if cidr_block_association_set:
            cidr_request_payload = (
                hub.tool.aws.network_utils.generate_cidr_request_payload_for_vpc(
                    cidr_block_association_set[0], "ipv4"
                )
            )
            cidr_block_association_set.pop(0)
        elif ipv6_cidr_block_association_set:
            cidr_request_payload = (
                hub.tool.aws.network_utils.generate_cidr_request_payload_for_vpc(
                    ipv6_cidr_block_association_set[0], "ipv6"
                )
            )
            ipv6_cidr_block_association_set.pop(0)
        try:
            ret = await hub.exec.boto3.client.ec2.create_vpc(
                ctx,
                **cidr_request_payload,
                **{
                    "InstanceTenancy": instance_tenancy,
                    "TagSpecifications": [{"ResourceType": "vpc", "Tags": tags}],
                },
            )
            result["result"] = ret["result"]
            if not result["result"]:
                result["comment"] = ret["comment"]
                return result
            result["comment"] = f"Created '{name}'"
            resource._id = ret["ret"]["Vpc"]["VpcId"]
            # Associate the rest cidr associations
            update_ret = await hub.exec.aws.ec2.vpc.update_cidr_blocks(
                ctx=ctx,
                vpc_id=resource._id,
                old_ipv4_cidr_blocks=list(),
                old_ipv6_cidr_blocks=list(),
                new_ipv4_cidr_blocks=cidr_block_association_set,
                new_ipv6_cidr_blocks=ipv6_cidr_block_association_set,
            )
            if not update_ret["result"]:
                result["comment"] = result["comment"]
                result["result"] = False

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

    try:
        after = await hub.tool.boto3.resource.describe(resource)
        result["new_state"] = after
    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**

    Deletes the specified VPC. You must detach or delete all gateways and resources that are associated with the VPC
    before you can delete it. For example, you must terminate all instances running in the VPC, delete all security
    groups associated with the VPC (except the default one), delete all route tables associated with the VPC (except
    the default one), and so on.

    Args:
        name(Text): The ID of the VPC.

    Returns:
        Dict[str, Any]

    Examples:

        .. code-block:: sls

            vpc-01234672f3336db8:
              aws.ec2.vpc.absent:
                - name: value
    """

    result = dict(comment="", old_state=None, new_state=None, name=name, result=True)
    resource = hub.tool.boto3.resource.create(ctx, "ec2", "Vpc", name)
    before = await hub.tool.boto3.resource.describe(resource)

    if not before:
        result["comment"] = f"'{name}' already absent"
    elif ctx.get("test", False):
        result["comment"] = f"Would delete aws.ec2.vpc {name}"
        return result
    else:
        result["old_state"] = before
        try:
            ret = await hub.exec.boto3.client.ec2.delete_vpc(ctx, **{"VpcId": name})
            result["result"] = ret["result"]
            if not result["result"]:
                result["comment"] = ret["comment"]
                result["result"] = False
                return result
            result["comment"] = f"Deleted '{name}'"
        except hub.tool.boto3.exception.ClientError as e:
            result["comment"] = f"{e.__class__.__name__}: {e}"

    return result


async def describe(hub, ctx) -> Dict[str, Dict[str, Any]]:
    result = {}
    ret = await hub.exec.boto3.client.ec2.describe_vpcs(ctx)

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

    for resource in ret["ret"]["Vpcs"]:
        resource_id = resource.get("VpcId")
        describe_parameters = OrderedDict(
            {"InstanceTenancy": "instance_tenancy", "Tags": "tags"}
        )
        resource_translated = []
        for parameter_old_key, parameter_new_key in describe_parameters.items():
            if resource.get(parameter_old_key) is not None:
                resource_translated.append(
                    {parameter_new_key: resource.get(parameter_old_key)}
                )
        # The following code block is to make sure the describe function only outputs the associated cidr blocks and
        # dis-regard the disassociated cidr blocks.
        if resource.get("CidrBlockAssociationSet"):
            ipv4_cidr_block_association_set = list()
            for cidr_block in resource.get("CidrBlockAssociationSet"):
                if cidr_block.get("CidrBlockState").get("State") == "associated":
                    ipv4_cidr_block_association_set.append(cidr_block)
            resource_translated.append(
                {"cidr_block_association_set": ipv4_cidr_block_association_set}
            )
        if resource.get("Ipv6CidrBlockAssociationSet"):
            ipv6_cidr_block_association_set = list()
            for cidr_block in resource.get("Ipv6CidrBlockAssociationSet"):
                if cidr_block.get("Ipv6CidrBlockState").get("State") == "associated":
                    if "NetworkBorderGroup" in cidr_block:
                        # Translate describe output to the correct present input format
                        ipv6_cidr_block_network_border_group = cidr_block.pop(
                            "NetworkBorderGroup"
                        )
                        cidr_block[
                            "Ipv6CidrBlockNetworkBorderGroup"
                        ] = ipv6_cidr_block_network_border_group
                    ipv6_cidr_block_association_set.append(cidr_block)
            resource_translated.append(
                {"ipv6_cidr_block_association_set": ipv6_cidr_block_association_set}
            )

        result[resource_id] = {"aws.ec2.vpc.present": resource_translated}
    return result
