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

hub.exec.boto3.client.ec2.create_subnet
hub.exec.boto3.client.ec2.delete_subnet
hub.exec.boto3.client.ec2.describe_subnets
resource = hub.tool.boto3.resource.create(ctx, "ec2", "Subnet", name)
hub.tool.boto3.resource.exec(resource, create_instances, *args, **kwargs)
hub.tool.boto3.resource.exec(resource, create_network_interface, *args, **kwargs)
hub.tool.boto3.resource.exec(resource, create_tags, *args, **kwargs)
hub.tool.boto3.resource.exec(resource, delete, *args, **kwargs)
"""
import copy
from collections import OrderedDict
from typing import Any
from typing import Dict
from typing import List

__contracts__ = ["resource"]

TREQ = {
    "absent": {
        "require": [
            "aws.ec2.nat_gateway.absent",
            "aws.ec2.elastic_ip.absent",
            "aws.ec2.route_table.absent",
            "aws.eks.cluster.absent",
        ],
    },
    "present": {
        "require": [
            "aws.ec2.vpc.present",
        ],
    },
}


async def present(
    hub,
    ctx,
    name: str,
    vpc_id: str,
    cidr_block: str,
    resource_id: str = None,
    tags: List = None,
    availability_zone: str = None,
    availability_zone_id: str = None,
    ipv6_cidr_block: str = None,
    outpost_arn: str = None,
) -> Dict[str, Any]:
    r"""
    **Autogenerated function**

    Creates a subnet in a specified VPC. You must specify an IPv4 CIDR block for the subnet. After you create a
    subnet, you can't change its CIDR block. The allowed block size is between a /16 netmask (65,536 IP addresses)
    and /28 netmask (16 IP addresses). The CIDR block must not overlap with the CIDR block of an existing subnet in
    the VPC. If you've associated an IPv6 CIDR block with your VPC, you can create a subnet with an IPv6 CIDR block
    that uses a /64 prefix length.   Amazon Web Services reserves both the first four and the last IPv4 address in
    each subnet's CIDR block. They're not available for use.  If you add more than one subnet to a VPC, they're set
    up in a star topology with a logical router in the middle. When you stop an instance in a subnet, it retains its
    private IPv4 address. It's therefore possible to have a subnet with no running instances (they're all stopped),
    but no remaining IP addresses available. For more information about subnets, see Your VPC and subnets in the
    Amazon Virtual Private Cloud User Guide.

    Args:
        name(Text): An Idem name of the resource.
        vpc_id(Text): ID of the VPC.
        cidr_block(Text): The IPv4 network range for the subnet, in CIDR notation. For example, 10.0.0.0/24. We modify the
            specified CIDR block to its canonical form; for example, if you specify 100.68.0.18/18, we
            modify it to 100.68.0.0/18.
        resource_id(Text, optional): AWS Subnet ID
        tags(List, optional): The tags to assign to the subnet. 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.
        availability_zone(Text, optional): The Availability Zone or Local Zone for the subnet. Default: Amazon Web Services selects one for
            you. If you create more than one subnet in your VPC, we do not necessarily select a different
            zone for each subnet. To create a subnet in a Local Zone, set this value to the Local Zone ID,
            for example us-west-2-lax-1a. For information about the Regions that support Local Zones, see
            Available Regions in the Amazon Elastic Compute Cloud User Guide. To create a subnet in an
            Outpost, set this value to the Availability Zone for the Outpost and specify the Outpost ARN. Defaults to None.
        availability_zone_id(Text, optional): The AZ ID or the Local Zone ID of the subnet. Defaults to None.
        ipv6_cidr_block(Text, optional): The IPv6 network range for the subnet, in CIDR notation. The subnet size must use a /64 prefix
            length. Defaults to None.
        outpost_arn(Text, optional): The Amazon Resource Name (ARN) of the Outpost. If you specify an Outpost ARN, you must also
            specify the Availability Zone of the Outpost subnet. Defaults to None.

    Request Syntax:
        [subnet-resource-name]:
          aws.ec2.subnet.present:
          - resource_id: 'string'
          - cidr_block: 'string'
          - ipv6_cidr_block: 'string'
          - vpc_id: 'string'
          - availability_zone: 'string'
          - availability_zone_id: 'string'
          - outpost_arn: 'string'
          - tags:
            - Key: 'string'
              Value: 'string'

    Returns:
        Dict[str, Any]

    Examples:

        .. code-block:: sls

            my-subnet:
              aws.ec2.subnet.present:
                - vpc_id: vpc-07123af5a5zwqcc0
                - cidr_block: 10.10.10.0/28
                - availability_zone: eu-west-2c
                - tags:
                  - Key: Name
                    Value: Idem-test-subnet
                - ipv6_cidr_block: 2a05:d01c:74f:7200::/64
    """
    result = dict(comment=(), old_state=None, new_state=None, name=name, result=True)
    before = None
    resource_updated = False
    if resource_id:
        resource = hub.tool.boto3.resource.create(ctx, "ec2", "Subnet", resource_id)
        before = await hub.tool.boto3.resource.describe(resource)
    if before:
        try:
            result[
                "old_state"
            ] = hub.tool.aws.ec2.conversion_utils.convert_raw_subnet_to_present(
                raw_resource=before, idem_resource_name=name
            )
            plan_state = copy.deepcopy(result["old_state"])
            # Update ipv6 cidr block
            ipv6_cidr_block_association_set = (
                hub.tool.aws.network_utils.get_associated_ipv6_cidr_blocks(
                    before.get("Ipv6CidrBlockAssociationSet")
                )
            )
            if ipv6_cidr_block != (
                None
                if not ipv6_cidr_block_association_set
                else ipv6_cidr_block_association_set[0]
            ):
                update_ret = await hub.exec.aws.ec2.subnet.update_ipv6_cidr_blocks(
                    ctx=ctx,
                    subnet_id=before.get("SubnetId"),
                    old_ipv6_cidr_block=None
                    if not ipv6_cidr_block_association_set
                    else ipv6_cidr_block_association_set[0],
                    new_ipv6_cidr_block={"Ipv6CidrBlock": ipv6_cidr_block}
                    if ipv6_cidr_block
                    else None,
                )
                result["result"] = result["result"] and update_ret["result"]
                result["comment"] = update_ret["comment"]
                resource_updated = True
                if ctx.get("test", False) and update_ret["ret"] is not None:
                    plan_state["ipv6_cidr_block"] = update_ret["ret"].get(
                        "ipv6_cidr_block"
                    )
            if tags is not None:
                # Update tags
                update_ret = await hub.exec.aws.ec2.tag.update_tags(
                    ctx=ctx,
                    resource_id=before.get("SubnetId"),
                    old_tags=before.get("Tags"),
                    new_tags=tags,
                )
                result["comment"] = result["comment"] + update_ret["comment"]
                result["result"] = result["result"] and update_ret["result"]
                resource_updated = resource_updated or bool(update_ret["ret"])
                if ctx.get("test", False) and update_ret["ret"] is not None:
                    plan_state["tags"] = update_ret["ret"].get("tags")
            if resource_updated:
                if ctx.get("test", False):
                    result["comment"] = result["comment"] + (
                        f"'Would update aws.ec2.subnet '{name}'",
                    )
                else:
                    result["comment"] = result["comment"] + (f"Updated '{name}'",)
        except hub.tool.boto3.exception.ClientError as e:
            result["comment"] = result["comment"] + (f"{e.__class__.__name__}: {e}",)
            result["result"] = False
    else:
        if ctx.get("test", False):
            result["new_state"] = hub.tool.aws.test_state_utils.generate_test_state(
                enforced_state={},
                desired_state={
                    "name": name,
                    "availability_zone": availability_zone,
                    "availability_zone_id": availability_zone_id,
                    "cidr_block": cidr_block,
                    "ipv6_cidr_block": ipv6_cidr_block,
                    "outpost_arn": outpost_arn,
                    "vpc_id": vpc_id,
                    "tags": tags,
                },
            )
            result["comment"] = (f"Would create aws.ec2.subnet '{name}'",)
            return result
        try:
            ret = await hub.exec.boto3.client.ec2.create_subnet(
                ctx,
                **{
                    "TagSpecifications": [{"ResourceType": "subnet", "Tags": tags}]
                    if tags
                    else None,
                    "AvailabilityZone": availability_zone,
                    "AvailabilityZoneId": availability_zone_id,
                    "Ipv6CidrBlock": ipv6_cidr_block,
                    "OutpostArn": outpost_arn,
                    "VpcId": vpc_id,
                    "CidrBlock": cidr_block,
                },
            )
            result["result"] = ret["result"]
            if not result["result"]:
                result["comment"] = ret["comment"]
                return result
            result["comment"] = (f"Created '{name}'",)
            resource_id = ret["ret"]["Subnet"]["SubnetId"]
        except hub.tool.boto3.exception.ClientError as e:
            result["comment"] = result["comment"] + (f"{e.__class__.__name__}: {e}",)
            result["result"] = False

    try:
        if ctx.get("test", False):
            result["new_state"] = plan_state
        elif (not before) or resource_updated:
            resource = hub.tool.boto3.resource.create(ctx, "ec2", "Subnet", resource_id)
            after = await hub.tool.boto3.resource.describe(resource)
            result[
                "new_state"
            ] = hub.tool.aws.ec2.conversion_utils.convert_raw_subnet_to_present(
                raw_resource=after, idem_resource_name=name
            )
        else:
            result["new_state"] = copy.deepcopy(result["old_state"])
    except Exception as e:
        result["comment"] = result["comment"] + (str(e),)
        result["result"] = False
    return result


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

    Deletes the specified subnet. You must terminate all running instances in the subnet before you can delete the
    subnet.

    Args:
        name(Text): The Idem name of the subnet.
        resource_id(Text, optional): AWS Subnet ID. Idem automatically considers this resource being absent if
         this field is not specified.

    Returns:
        Dict[str, Any]

    Examples:

        .. code-block:: sls

            resource_is_absent:
              aws.ec2.subnet.absent:
                - name: value
                - resource_id: value
    """

    result = dict(comment="", old_state=None, new_state=None, name=name, result=True)
    if not resource_id:
        result["comment"] = (f"aws.ec2.subnet '{name}' already absent",)
        return result
    resource = hub.tool.boto3.resource.create(ctx, "ec2", "Subnet", resource_id)
    before = await hub.tool.boto3.resource.describe(resource)

    if not before:
        result["comment"] = (f"aws.ec2.subnet '{name}' already absent",)
    elif ctx.get("test", False):
        result[
            "old_state"
        ] = hub.tool.aws.ec2.conversion_utils.convert_raw_subnet_to_present(
            raw_resource=before, idem_resource_name=name
        )
        result["comment"] = (f"Would delete aws.ec2.subnet '{name}'",)
        return result
    else:
        result[
            "old_state"
        ] = hub.tool.aws.ec2.conversion_utils.convert_raw_subnet_to_present(
            raw_resource=before, idem_resource_name=name
        )
        try:
            ret = await hub.exec.boto3.client.ec2.delete_subnet(
                ctx, SubnetId=resource_id
            )
            result["result"] = ret["result"]
            if not result["result"]:
                result["comment"] = ret["comment"]
                return result
            result["comment"] = (f"Deleted aws.ec2.subnet '{name}'",)
        except hub.tool.boto3.exception.ClientError as e:
            result["comment"] = 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_subnets(ctx)

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

    for subnet in ret["ret"]["Subnets"]:
        subnet_id = subnet.get("SubnetId")
        resource_translated = (
            hub.tool.aws.ec2.conversion_utils.convert_raw_subnet_to_present(
                raw_resource=subnet, idem_resource_name=subnet_id
            )
        )
        result[subnet_id] = {
            "aws.ec2.subnet.present": [
                {parameter_key: parameter_value}
                for parameter_key, parameter_value in resource_translated.items()
            ]
        }
    return result


async def search(
    hub,
    ctx,
    name,
    resource_id: str = None,
    availability_zone: str = None,
    availability_zone_id: str = None,
    cidr_block: str = None,
    default_for_az: str = None,
    filters: List = None,
    ipv6_cidr_block: str = None,
    status: str = None,
    vpc_id: str = None,
    tags: List = None,
):
    """
    Use an un-managed subnet as a data-source. Supply one of the inputs as the filter.

    Args:
        name(string): The name of the Idem state.
        resource_id(string, optional): AWS subnet id to identify the resource.
        availability_zone(string, optional): The Availability Zone for the subnet.
        availability_zone_id(string, optional): The ID of the Availability Zone for the subnet.
        cidr_block(string, optional): The IPv4 CIDR block of the subnet. The CIDR block you specify must exactly match
         the subnet's CIDR block for information to be returned for the subnet.
        default_for_az(string, optional): Indicate whether the subnet is the default subnet in the Availability Zone.
        filters(List, optional): One or more filters: for example, tag :<key>, tag-key. A complete list of filters can be found at
         https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ec2.html#EC2.Client.describe_subnets
        ipv6_cidr_block(string, optional): An IPv6 CIDR block associated with the subnet.
        status(string, optional): The state of the subnet (pending | available ).
        vpc_id(string, optional): The ID of the VPC for the subnet.
        tags(Lict, optional): The list of tags to filter by. For example, to find all resources that have a tag with the key
            "Owner" and the value "TeamA" , specify "tag:Owner" for the Dict key and "TeamA" for the Dict value.


    Request Syntax:
        [Idem-state-name]:
          aws.ec2.subnet.search:
          - resource_id: 'string'
          - availability_zone: 'string'
          - availability_zone_id: 'string'
          - cidr_block: 'string'
          - default_for_az: 'string'
          - filters:
            - name: 'string'
              values: 'list'
            - name: 'string'
              values: 'list'
          - ipv6_cidr_block: 'string'
          - status: 'string'
          - vpc_id: 'string'
          - tags: 'List'

        Examples:

            my-unmanaged-subnet:
              aws.ec2.subnet.search:
                - resource_id: value
                - availability_zone: eu-central-1c
                - cidr_block: 10.0.0.0/28
                - default_for_az: "true"|"false"
                - status: "pending"|"available"
                - tags:
                  - Key: tag:Name
                    Value: idem-test-subnet

    """
    result = dict(comment=(), old_state=None, new_state=None, name=name, result=True)
    syntax_validation = hub.tool.aws.search_utils.search_filter_syntax_validation(
        filters=filters
    )
    if not syntax_validation["result"]:
        result["comment"] = syntax_validation["comment"]
        return result
    boto3_filter = hub.tool.aws.search_utils.convert_search_filter_to_boto3(
        filters=filters
    )
    filter_params = OrderedDict(
        {
            "availability-zone": availability_zone,
            "availability-zone-id": availability_zone_id,
            "cidr-block": cidr_block,
            "default-for-az": default_for_az,
            "ipv6-cidr-block-association.ipv6-cidr-block": ipv6_cidr_block,
            "state": status,
            "vpc-id": vpc_id,
        }
    )
    for filter_param, value in filter_params.items():
        if value is not None:
            boto3_filter.append({"Name": filter_param, "Values": [value]})
    if tags is not None:
        for tag in tags:
            boto3_filter.append({"Name": tag["Key"], "Values": [tag["Value"]]})

    ret = await hub.exec.boto3.client.ec2.describe_subnets(
        ctx,
        Filters=boto3_filter,
        SubnetIds=[resource_id] if resource_id else None,
    )
    if not ret["result"]:
        result["result"] = False
        result["comment"] = ret["comment"]
        return result
    if not ret["ret"]["Subnets"]:
        result["comment"] = (
            f"Unable to find aws.ec2.subnet resource with filters {filters} and subnet id {resource_id}",
        )
        return result
    resource = ret["ret"]["Subnets"][0]
    if len(ret["ret"]["Subnets"]) > 1:
        result["comment"] = (
            f"More than one aws.ec2.subnet resource was found. Use resource {resource.get('SubnetId')}",
        )
    result[
        "old_state"
    ] = hub.tool.aws.ec2.conversion_utils.convert_raw_subnet_to_present(
        raw_resource=resource, idem_resource_name=name
    )
    # Populate both "old_state" and "new_state" with the same data
    result["new_state"] = copy.deepcopy(result["old_state"])

    return result
