#!/usr/bin/python
# -*- coding: utf-8 -*-

# Copyright: Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

DOCUMENTATION = r"""
---
module: ec2_vpc_nat_gateway
version_added: 1.0.0
short_description: Manage AWS VPC NAT Gateways
description:
  - Ensure the state of AWS VPC NAT Gateways based on their id, allocation and subnet ids.
options:
  state:
    description:
      - Ensure NAT Gateway is present or absent.
    default: "present"
    choices: ["present", "absent"]
    type: str
  nat_gateway_id:
    description:
      - The id AWS dynamically allocates to the NAT Gateway on creation.
        This is required when the absent option is present.
    type: str
  subnet_id:
    description:
      - The id of the subnet to create the NAT Gateway in. This is required
        with the present option.
    type: str
  allocation_id:
    description:
      - The id of the elastic IP allocation. If this is not passed and the
        eip_address is not passed. An EIP is generated for this NAT Gateway.
    type: str
  connectivity_type:
    description:
      - Indicates whether the NAT gateway supports public or private connectivity.
    choices: ["public", "private"]
    default: "public"
    type: str
    version_added: 5.5.0
  eip_address:
    description:
      - The elastic IP address of the EIP you want attached to this NAT Gateway.
        If this is not passed and the allocation_id is not passed,
        an EIP is generated for this NAT Gateway.
    type: str
  if_exist_do_not_create:
    description:
      - if a NAT Gateway exists already in the subnet_id, then do not create a new one.
    required: false
    default: false
    type: bool
  release_eip:
    description:
      - Deallocate the EIP from the VPC.
      - Option is only valid with the absent state.
      - You should use this with the wait option. Since you can not release an address while a delete operation is happening.
    default: false
    type: bool
  wait:
    description:
      - Wait for operation to complete before returning.
    default: false
    type: bool
  wait_timeout:
    description:
      - How many seconds to wait for an operation to complete before timing out.
    default: 320
    type: int
  client_token:
    description:
      - Optional unique token to be used during create to ensure idempotency.
        When specifying this option, ensure you specify the eip_address parameter
        as well otherwise any subsequent runs will fail.
    type: str
  default_create:
    description:
      - When I(default_create=True) and I(eip_address) has been set, but not yet
        allocated, the NAT gateway is created and a new EIP is automatically allocated.
      - When I(default_create=False) and I(eip_address) has been set, but not yet
        allocated, the module will fail.
      - If I(eip_address) has not been set, this parameter has no effect.
    default: false
    type: bool
    version_added: 6.2.0
author:
  - Allen Sanabria (@linuxdynasty)
  - Jon Hadfield (@jonhadfield)
  - Karen Cheng (@Etherdaemon)
  - Alina Buzachis (@alinabuzachis)
notes:
  - Support for I(tags) and I(purge_tags) was added in release 1.4.0.
extends_documentation_fragment:
  - amazon.aws.common.modules
  - amazon.aws.region.modules
  - amazon.aws.tags
  - amazon.aws.boto3
"""

EXAMPLES = r"""
# Note: These examples do not set authentication details, see the AWS Guide for details.

- name: Create new nat gateway with client token.
  amazon.aws.ec2_vpc_nat_gateway:
    state: present
    subnet_id: subnet-12345678
    eip_address: 52.1.1.1
    region: ap-southeast-2
    client_token: abcd-12345678
  register: new_nat_gateway

- name: Create new nat gateway using an allocation-id and connectivity type.
  amazon.aws.ec2_vpc_nat_gateway:
    state: present
    subnet_id: subnet-12345678
    allocation_id: eipalloc-12345678
    connectivity_type: "private"
    region: ap-southeast-2
  register: new_nat_gateway

- name: Create new nat gateway, using an EIP address  and wait for available status.
  amazon.aws.ec2_vpc_nat_gateway:
    state: present
    subnet_id: subnet-12345678
    eip_address: 52.1.1.1
    wait: true
    region: ap-southeast-2
  register: new_nat_gateway

- name: Create new nat gateway and allocate new EIP.
  amazon.aws.ec2_vpc_nat_gateway:
    state: present
    subnet_id: subnet-12345678
    wait: true
    region: ap-southeast-2
  register: new_nat_gateway

- name: Create new nat gateway and allocate new EIP if a nat gateway does not yet exist in the subnet.
  amazon.aws.ec2_vpc_nat_gateway:
    state: present
    subnet_id: subnet-12345678
    wait: true
    region: ap-southeast-2
    if_exist_do_not_create: true
  register: new_nat_gateway

- name: Delete nat gateway using discovered nat gateways from facts module.
  amazon.aws.ec2_vpc_nat_gateway:
    state: absent
    region: ap-southeast-2
    wait: true
    nat_gateway_id: "{{ item.NatGatewayId }}"
    release_eip: true
  register: delete_nat_gateway_result
  loop: "{{ gateways_to_remove.result }}"

- name: Delete nat gateway and wait for deleted status.
  amazon.aws.ec2_vpc_nat_gateway:
    state: absent
    nat_gateway_id: nat-12345678
    wait: true
    wait_timeout: 500
    region: ap-southeast-2

- name: Delete nat gateway and release EIP.
  amazon.aws.ec2_vpc_nat_gateway:
    state: absent
    nat_gateway_id: nat-12345678
    release_eip: true
    wait: true
    wait_timeout: 300
    region: ap-southeast-2

- name: Create new nat gateway using allocation-id and tags.
  amazon.aws.ec2_vpc_nat_gateway:
    state: present
    subnet_id: subnet-12345678
    allocation_id: eipalloc-12345678
    region: ap-southeast-2
    tags:
      Tag1: tag1
      Tag2: tag2
  register: new_nat_gateway

- name: Update tags without purge
  amazon.aws.ec2_vpc_nat_gateway:
    subnet_id: subnet-12345678
    allocation_id: eipalloc-12345678
    region: ap-southeast-2
    purge_tags: false
    tags:
      Tag3: tag3
    wait: true
  register: update_tags_nat_gateway
"""

RETURN = r"""
create_time:
  description: The ISO 8601 date time format in UTC.
  returned: In all cases.
  type: str
  sample: "2016-03-05T05:19:20.282000+00:00'"
nat_gateway_id:
  description: id of the VPC NAT Gateway
  returned: In all cases.
  type: str
  sample: "nat-0d1e3a878585988f8"
subnet_id:
  description: id of the Subnet
  returned: In all cases.
  type: str
  sample: "subnet-12345"
state:
  description: The current state of the NAT Gateway.
  returned: In all cases.
  type: str
  sample: "available"
tags:
  description: The tags associated the VPC NAT Gateway.
  type: dict
  returned: When tags are present.
  sample:
    tags:
        "Ansible": "Test"
vpc_id:
  description: id of the VPC.
  returned: In all cases.
  type: str
  sample: "vpc-12345"
nat_gateway_addresses:
  description: List of dictionaries containing the public_ip, network_interface_id, private_ip, and allocation_id.
  returned: In all cases.
  type: str
  sample: [
      {
        'public_ip': '52.52.52.52',
        'network_interface_id': 'eni-12345',
        'private_ip': '10.0.0.100',
        'allocation_id': 'eipalloc-12345'
      }
  ]
"""

import datetime

try:
    import botocore
except ImportError:
    pass  # Handled by AnsibleAWSModule

from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict

from ansible_collections.amazon.aws.plugins.module_utils.botocore import is_boto3_error_code
from ansible_collections.amazon.aws.plugins.module_utils.ec2 import describe_ec2_tags
from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ensure_ec2_tags
from ansible_collections.amazon.aws.plugins.module_utils.modules import AnsibleAWSModule
from ansible_collections.amazon.aws.plugins.module_utils.retries import AWSRetry
from ansible_collections.amazon.aws.plugins.module_utils.tagging import boto3_tag_specifications
from ansible_collections.amazon.aws.plugins.module_utils.waiters import get_waiter


@AWSRetry.jittered_backoff(retries=10)
def _describe_nat_gateways(client, **params):
    try:
        paginator = client.get_paginator("describe_nat_gateways")
        return paginator.paginate(**params).build_full_result()["NatGateways"]
    except is_boto3_error_code("InvalidNatGatewayID.NotFound"):
        return None


def wait_for_status(client, module, waiter_name, nat_gateway_id):
    wait_timeout = module.params.get("wait_timeout")
    try:
        waiter = get_waiter(client, waiter_name)
        attempts = 1 + int(wait_timeout / waiter.config.delay)
        waiter.wait(
            NatGatewayIds=[nat_gateway_id],
            WaiterConfig={"MaxAttempts": attempts},
        )
    except botocore.exceptions.WaiterError as e:
        module.fail_json_aws(e, msg="NAT gateway failed to reach expected state.")
    except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
        module.fail_json_aws(e, msg="Unable to wait for NAT gateway state to update.")


def get_nat_gateways(client, module, subnet_id=None, nat_gateway_id=None, states=None):
    """Retrieve a list of NAT Gateways
    Args:
        client (botocore.client.EC2): Boto3 client
        module: AnsibleAWSModule class instance

    Kwargs:
        subnet_id (str): The subnet_id the nat resides in.
        nat_gateway_id (str): The Amazon NAT id.
        states (list): States available (pending, failed, available, deleting, and deleted)
            default=None

    Basic Usage:
        >>> client = boto3.client('ec2')
        >>> module = AnsibleAWSModule(...)
        >>> subnet_id = 'subnet-12345678'
        >>> get_nat_gateways(client, module, subnet_id)
        [
            {
                "create_time": "2016-03-05T00:33:21.209000+00:00",
                "delete_time": "2016-03-05T00:36:37.329000+00:00",
                "nat_gateway_addresses": [
                    {
                        "public_ip": "55.55.55.55",
                        "network_interface_id": "eni-1234567",
                        "private_ip": "10.0.0.102",
                        "allocation_id": "eipalloc-1234567"
                    }
                ],
                "nat_gateway_id": "nat-123456789",
                "state": "deleted",
                "subnet_id": "subnet-123456789",
                "tags": {},
                "vpc_id": "vpc-12345678"
            }
        ]

    Returns:
        list
    """

    params = dict()
    existing_gateways = list()

    if not states:
        states = ["available", "pending"]
    if nat_gateway_id:
        params["NatGatewayIds"] = [nat_gateway_id]
    else:
        params["Filter"] = [
            {"Name": "subnet-id", "Values": [subnet_id]},
            {"Name": "state", "Values": states},
        ]

    try:
        gateways = _describe_nat_gateways(client, **params)
        if gateways:
            for gw in gateways:
                existing_gateways.append(camel_dict_to_snake_dict(gw))
    except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
        module.fail_json_aws(e)

    return existing_gateways


def gateway_in_subnet_exists(client, module, subnet_id, allocation_id=None):
    """Retrieve all NAT Gateways for a subnet.
    Args:
        client (botocore.client.EC2): Boto3 client
        module: AnsibleAWSModule class instance
        subnet_id (str): The subnet_id the nat resides in.

    Kwargs:
        allocation_id (str): The EIP Amazon identifier.
            default = None

    Basic Usage:
        >>> client = boto3.client('ec2')
        >>> module = AnsibleAWSModule(...)
        >>> subnet_id = 'subnet-1234567'
        >>> allocation_id = 'eipalloc-1234567'
        >>> gateway_in_subnet_exists(client, module, subnet_id, allocation_id)
        (
            [
                {
                    "create_time": "2016-03-05T00:33:21.209000+00:00",
                    "delete_time": "2016-03-05T00:36:37.329000+00:00",
                    "nat_gateway_addresses": [
                        {
                            "public_ip": "55.55.55.55",
                            "network_interface_id": "eni-1234567",
                            "private_ip": "10.0.0.102",
                            "allocation_id": "eipalloc-1234567"
                        }
                    ],
                    "nat_gateway_id": "nat-123456789",
                    "state": "deleted",
                    "subnet_id": "subnet-123456789",
                    "tags": {},
                    "vpc_id": "vpc-1234567"
                }
            ],
            False
        )

    Returns:
        Tuple (list, bool)
    """

    allocation_id_exists = False
    gateways = []
    states = ["available", "pending"]

    gws_retrieved = get_nat_gateways(client, module, subnet_id, states=states)

    if gws_retrieved:
        for gw in gws_retrieved:
            for address in gw["nat_gateway_addresses"]:
                if allocation_id:
                    if address.get("allocation_id") == allocation_id:
                        allocation_id_exists = True
                        gateways.append(gw)
                else:
                    gateways.append(gw)

    return gateways, allocation_id_exists


def get_eip_allocation_id_by_address(client, module, eip_address):
    """Release an EIP from your EIP Pool
    Args:
        client (botocore.client.EC2): Boto3 client
        module: AnsibleAWSModule class instance
        eip_address (str): The Elastic IP Address of the EIP.

    Basic Usage:
        >>> client = boto3.client('ec2')
        >>> module = AnsibleAWSModule(...)
        >>> eip_address = '52.87.29.36'
        >>> get_eip_allocation_id_by_address(client, module, eip_address)
        (
            'eipalloc-36014da3', ''
        )

    Returns:
        Tuple (str, str)
    """

    params = {
        "PublicIps": [eip_address],
    }
    allocation_id = None
    msg = ""

    try:
        allocations = client.describe_addresses(aws_retry=True, **params)["Addresses"]

        if len(allocations) == 1:
            allocation = allocations[0]
        else:
            allocation = None

        if allocation:
            if allocation.get("Domain") != "vpc":
                msg = f"EIP {eip_address} is a non-VPC EIP, please allocate a VPC scoped EIP"
            else:
                allocation_id = allocation.get("AllocationId")

    except is_boto3_error_code("InvalidAddress.Malformed"):
        module.fail_json(msg=f"EIP address {eip_address} is invalid.")
    except is_boto3_error_code("InvalidAddress.NotFound"):  # pylint: disable=duplicate-except
        msg = f"EIP {eip_address} does not exist"
        allocation_id = None
    except (
        botocore.exceptions.ClientError,
        botocore.exceptions.BotoCoreError,
    ) as e:  # pylint: disable=duplicate-except
        module.fail_json_aws(e, msg="Unable to describe EIP")

    return allocation_id, msg


def allocate_eip_address(client, module):
    """Release an EIP from your EIP Pool
    Args:
        client (botocore.client.EC2): Boto3 client
        module: AnsibleAWSModule class instance

    Basic Usage:
        >>> client = boto3.client('ec2')
        >>> module = AnsibleAWSModule(...)
        >>> allocate_eip_address(client, module)
        (
            True, '', ''
        )

    Returns:
        Tuple (bool, str, str)
    """

    new_eip = None
    msg = ""
    params = {
        "Domain": "vpc",
    }

    if module.check_mode:
        ip_allocated = True
        new_eip = None
        return ip_allocated, msg, new_eip

    try:
        new_eip = client.allocate_address(aws_retry=True, **params)["AllocationId"]
        ip_allocated = True
        msg = f"eipalloc id {new_eip} created"
    except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
        module.fail_json_aws(e)

    return ip_allocated, msg, new_eip


def release_address(client, module, allocation_id):
    """Release an EIP from your EIP Pool
    Args:
        client (botocore.client.EC2): Boto3 client
        module: AnsibleAWSModule class instance
        allocation_id (str): The eip Amazon identifier.

    Basic Usage:
        >>> client = boto3.client('ec2')
        >>> module = AnsibleAWSModule(...)
        >>> allocation_id = "eipalloc-123456"
        >>> release_address(client, module, allocation_id)
        (
            True, ''
        )

    Returns:
        Tuple (bool, str)
    """

    msg = ""

    if module.check_mode:
        return True, ""

    ip_released = False

    try:
        client.describe_addresses(aws_retry=True, AllocationIds=[allocation_id])
    except is_boto3_error_code("InvalidAllocationID.NotFound") as e:
        # IP address likely already released
        # Happens with gateway in 'deleted' state that
        # still lists associations
        return True, e
    except (
        botocore.exceptions.ClientError,
        botocore.exceptions.BotoCoreError,
    ) as e:  # pylint: disable=duplicate-except
        module.fail_json_aws(e)

    try:
        client.release_address(aws_retry=True, AllocationId=allocation_id)
        ip_released = True
    except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
        module.fail_json_aws(e)

    return ip_released, msg


def create(client, module, subnet_id, allocation_id, tags, client_token=None, wait=False, connectivity_type="public"):
    """Create an Amazon NAT Gateway.
    Args:
        client (botocore.client.EC2): Boto3 client
        module: AnsibleAWSModule class instance
        subnet_id (str): The subnet_id the nat resides in
        allocation_id (str): The eip Amazon identifier
        connectivity_type (str): public or private connectivity support
        tags (dict): Tags to associate to the NAT gateway
        purge_tags (bool): If true, remove tags not listed in I(tags)
            type: bool

    Kwargs:
        wait (bool): Wait for the nat to be in the deleted state before returning.
            default = False
        client_token (str):
            default = None

    Basic Usage:
        >>> client = boto3.client('ec2')
        >>> module = AnsibleAWSModule(...)
        >>> subnet_id = 'subnet-1234567'
        >>> allocation_id = 'eipalloc-1234567'
        >>> create(client, module, subnet_id, allocation_id, wait=True, connectivity_type='public')
        [
            true,
            {
                "create_time": "2016-03-05T00:33:21.209000+00:00",
                "delete_time": "2016-03-05T00:36:37.329000+00:00",
                "nat_gateway_addresses": [
                    {
                        "public_ip": "55.55.55.55",
                        "network_interface_id": "eni-1234567",
                        "private_ip": "10.0.0.102",
                        "allocation_id": "eipalloc-1234567"
                    }
                ],
                "nat_gateway_id": "nat-123456789",
                "state": "deleted",
                "subnet_id": "subnet-1234567",
                "tags": {},
                "vpc_id": "vpc-1234567"
            },
            ""
        ]

    Returns:
        Tuple (bool, str, list)
    """

    params = {"SubnetId": subnet_id, "ConnectivityType": connectivity_type}

    if connectivity_type == "public":
        params.update({"AllocationId": allocation_id})

    request_time = datetime.datetime.utcnow()
    changed = False
    token_provided = False
    result = {}
    msg = ""

    if client_token:
        token_provided = True
        params["ClientToken"] = client_token

    if tags:
        params["TagSpecifications"] = boto3_tag_specifications(tags, ["natgateway"])

    if module.check_mode:
        changed = True
        return changed, result, msg

    try:
        result = camel_dict_to_snake_dict(client.create_nat_gateway(aws_retry=True, **params)["NatGateway"])
        changed = True

        create_time = result["create_time"].replace(tzinfo=None)

        if token_provided and (request_time > create_time):
            changed = False

        elif wait and result.get("state") != "available":
            wait_for_status(client, module, "nat_gateway_available", result["nat_gateway_id"])

            # Get new result
            result = camel_dict_to_snake_dict(
                _describe_nat_gateways(client, NatGatewayIds=[result["nat_gateway_id"]])[0]
            )

    except is_boto3_error_code("IdempotentParameterMismatch") as e:
        msg = "NAT Gateway does not support update and token has already been provided:" + e
        changed = False
        result = None
    except (
        botocore.exceptions.BotoCoreError,
        botocore.exceptions.ClientError,
    ) as e:  # pylint: disable=duplicate-except
        module.fail_json_aws(e)

    result["tags"] = describe_ec2_tags(client, module, result["nat_gateway_id"], resource_type="natgateway")

    return changed, result, msg


def pre_create(
    client,
    module,
    subnet_id,
    tags,
    purge_tags,
    allocation_id=None,
    eip_address=None,
    if_exist_do_not_create=False,
    wait=False,
    client_token=None,
    connectivity_type="public",
    default_create=False,
):
    """Create an Amazon NAT Gateway.
    Args:
        client (botocore.client.EC2): Boto3 client
        module: AnsibleAWSModule class instance
        subnet_id (str): The subnet_id the nat resides in
        tags (dict): Tags to associate to the NAT gateway
        purge_tags (bool): If true, remove tags not listed in I(tags)

    Kwargs:
        allocation_id (str): The EIP Amazon identifier.
            default = None
        eip_address (str): The Elastic IP Address of the EIP.
            default = None
        if_exist_do_not_create (bool): if a nat gateway already exists in this
            subnet, than do not create another one.
            default = False
        wait (bool): Wait for the nat to be in the deleted state before returning.
            default = False
        client_token (str):
            default = None
        default_create (bool): create a NAT gateway even if EIP address is not found.
            default = False

    Basic Usage:
        >>> client = boto3.client('ec2')
        >>> module = AnsibleAWSModule(...)
        >>> subnet_id = 'subnet-w4t12897'
        >>> allocation_id = 'eipalloc-36014da3'
        >>> pre_create(client, module, subnet_id, allocation_id, if_exist_do_not_create=True, wait=True, connectivity_type=public)
        [
            true,
            "",
            {
                "create_time": "2016-03-05T00:33:21.209000+00:00",
                "delete_time": "2016-03-05T00:36:37.329000+00:00",
                "nat_gateway_addresses": [
                    {
                        "public_ip": "52.87.29.36",
                        "network_interface_id": "eni-5579742d",
                        "private_ip": "10.0.0.102",
                        "allocation_id": "eipalloc-36014da3"
                    }
                ],
                "nat_gateway_id": "nat-03835afb6e31df79b",
                "state": "deleted",
                "subnet_id": "subnet-w4t12897",
                "tags": {},
                "vpc_id": "vpc-w68571b5"
            }
        ]

    Returns:
        Tuple (bool, str, list)
    """

    changed = False
    msg = ""
    results = {}

    if not allocation_id and not eip_address:
        existing_gateways, allocation_id_exists = gateway_in_subnet_exists(client, module, subnet_id)

        if len(existing_gateways) > 0 and if_exist_do_not_create:
            results = existing_gateways[0]
            changed |= ensure_ec2_tags(
                client, module, results["nat_gateway_id"], resource_type="natgateway", tags=tags, purge_tags=purge_tags
            )

            results["tags"] = describe_ec2_tags(client, module, results["nat_gateway_id"], resource_type="natgateway")

            if changed:
                return changed, msg, results

            changed = False
            msg = f"NAT Gateway {existing_gateways[0]['nat_gateway_id']} already exists in subnet_id {subnet_id}"
            return changed, msg, results
        else:
            if connectivity_type == "public":
                changed, msg, allocation_id = allocate_eip_address(client, module)

                if not changed:
                    return changed, msg, dict()

    elif eip_address or allocation_id:
        if eip_address and not allocation_id:
            allocation_id, msg = get_eip_allocation_id_by_address(client, module, eip_address)
            if not allocation_id and not default_create:
                changed = False
                module.fail_json(msg=msg)
            elif not allocation_id and default_create:
                eip_address = None
                return pre_create(
                    client,
                    module,
                    subnet_id,
                    tags,
                    purge_tags,
                    allocation_id,
                    eip_address,
                    if_exist_do_not_create,
                    wait,
                    client_token,
                    connectivity_type,
                    default_create,
                )

        existing_gateways, allocation_id_exists = gateway_in_subnet_exists(client, module, subnet_id, allocation_id)

        if len(existing_gateways) > 0 and (allocation_id_exists or if_exist_do_not_create):
            results = existing_gateways[0]
            changed |= ensure_ec2_tags(
                client, module, results["nat_gateway_id"], resource_type="natgateway", tags=tags, purge_tags=purge_tags
            )

            results["tags"] = describe_ec2_tags(client, module, results["nat_gateway_id"], resource_type="natgateway")

            if changed:
                return changed, msg, results

            changed = False
            msg = f"NAT Gateway {existing_gateways[0]['nat_gateway_id']} already exists in subnet_id {subnet_id}"
            return changed, msg, results

    changed, results, msg = create(
        client, module, subnet_id, allocation_id, tags, client_token, wait, connectivity_type
    )

    return changed, msg, results


def remove(client, module, nat_gateway_id, wait=False, release_eip=False, connectivity_type="public"):
    """Delete an Amazon NAT Gateway.
    Args:
        client (botocore.client.EC2): Boto3 client
        module: AnsibleAWSModule class instance
        nat_gateway_id (str): The Amazon nat id

    Kwargs:
        wait (bool): Wait for the nat to be in the deleted state before returning.
        release_eip (bool): Once the nat has been deleted, you can deallocate the eip from the vpc.
        connectivity_type (str): private/public connection type

    Basic Usage:
        >>> client = boto3.client('ec2')
        >>> module = AnsibleAWSModule(...)
        >>> nat_gw_id = 'nat-03835afb6e31df79b'
        >>> remove(client, module, nat_gw_id, wait=True, release_eip=True, connectivity_type='public')
        [
            true,
            "",
            {
                "create_time": "2016-03-05T00:33:21.209000+00:00",
                "delete_time": "2016-03-05T00:36:37.329000+00:00",
                "nat_gateway_addresses": [
                    {
                        "public_ip": "52.87.29.36",
                        "network_interface_id": "eni-5579742d",
                        "private_ip": "10.0.0.102",
                        "allocation_id": "eipalloc-36014da3"
                    }
                ],
                "nat_gateway_id": "nat-03835afb6e31df79b",
                "state": "deleted",
                "subnet_id": "subnet-w4t12897",
                "tags": {},
                "vpc_id": "vpc-w68571b5"
            }
        ]

    Returns:
        Tuple (bool, str, list)
    """

    allocation_id = None
    params = {"NatGatewayId": nat_gateway_id}
    changed = False
    results = {}
    states = ["pending", "available"]
    msg = ""

    if module.check_mode:
        changed = True
        return changed, msg, results

    try:
        gw_list = get_nat_gateways(client, module, nat_gateway_id=nat_gateway_id, states=states)

        if len(gw_list) == 1:
            results = gw_list[0]
            client.delete_nat_gateway(aws_retry=True, **params)
            if connectivity_type == "public":
                allocation_id = results["nat_gateway_addresses"][0]["allocation_id"]
            changed = True
            msg = f"NAT gateway {nat_gateway_id} is in a deleting state. Delete was successful"

            if wait and results.get("state") != "deleted":
                wait_for_status(client, module, "nat_gateway_deleted", nat_gateway_id)

                # Get new results
                results = camel_dict_to_snake_dict(_describe_nat_gateways(client, NatGatewayIds=[nat_gateway_id])[0])
                results["tags"] = describe_ec2_tags(client, module, nat_gateway_id, resource_type="natgateway")
    except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
        module.fail_json_aws(e)

    if release_eip and allocation_id:
        eip_released, msg = release_address(client, module, allocation_id)
        if not eip_released:
            module.fail_json(msg=f"Failed to release EIP {allocation_id}: {msg}")

    return changed, msg, results


def main():
    argument_spec = dict(
        subnet_id=dict(type="str"),
        eip_address=dict(type="str"),
        allocation_id=dict(type="str"),
        connectivity_type=dict(type="str", default="public", choices=["private", "public"]),
        if_exist_do_not_create=dict(type="bool", default=False),
        state=dict(default="present", choices=["present", "absent"]),
        wait=dict(type="bool", default=False),
        wait_timeout=dict(type="int", default=320, required=False),
        release_eip=dict(type="bool", default=False),
        nat_gateway_id=dict(type="str"),
        client_token=dict(type="str", no_log=False),
        tags=dict(required=False, type="dict", aliases=["resource_tags"]),
        purge_tags=dict(default=True, type="bool"),
        default_create=dict(type="bool", default=False),
    )

    module = AnsibleAWSModule(
        argument_spec=argument_spec,
        supports_check_mode=True,
        mutually_exclusive=[["allocation_id", "eip_address"]],
        required_if=[["state", "absent", ["nat_gateway_id"]], ["state", "present", ["subnet_id"]]],
    )

    state = module.params.get("state").lower()
    subnet_id = module.params.get("subnet_id")
    allocation_id = module.params.get("allocation_id")
    connectivity_type = module.params.get("connectivity_type")
    eip_address = module.params.get("eip_address")
    nat_gateway_id = module.params.get("nat_gateway_id")
    wait = module.params.get("wait")
    release_eip = module.params.get("release_eip")
    client_token = module.params.get("client_token")
    if_exist_do_not_create = module.params.get("if_exist_do_not_create")
    tags = module.params.get("tags")
    purge_tags = module.params.get("purge_tags")
    default_create = module.params.get("default_create")

    try:
        client = module.client("ec2", retry_decorator=AWSRetry.jittered_backoff())
    except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
        module.fail_json_aws(e, msg="Failed to connect to AWS.")

    changed = False
    msg = ""

    if state == "present":
        changed, msg, results = pre_create(
            client,
            module,
            subnet_id,
            tags,
            purge_tags,
            allocation_id,
            eip_address,
            if_exist_do_not_create,
            wait,
            client_token,
            connectivity_type,
            default_create,
        )
    else:
        changed, msg, results = remove(client, module, nat_gateway_id, wait, release_eip, connectivity_type)

    module.exit_json(msg=msg, changed=changed, **results)


if __name__ == "__main__":
    main()
