"""

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

hub.exec.boto3.client.ec2.create_security_group
hub.exec.boto3.client.ec2.delete_security_group
hub.exec.boto3.client.ec2.describe_security_groups
hub.tool.boto3.resource.exec(resource, authorize_egress, *args, **kwargs)
hub.tool.boto3.resource.exec(resource, authorize_ingress, *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, revoke_egress, *args, **kwargs)
hub.tool.boto3.resource.exec(resource, revoke_ingress, *args, **kwargs)
"""
import copy
from typing import Any
from typing import Dict

__contracts__ = ["resource"]

TREQ = {
    "present": {
        "require": [
            "aws.ec2.security_group.present",
        ],
    },
}


async def present(
    hub,
    ctx,
    name: str,
    group_id: str,
    ip_protocol: str,
    from_port: int,
    to_port: int,
    is_egress: bool,
    resource_id: str = None,
    cidr_ipv4: str = None,
    cidr_ipv6: str = None,
    prefix_list_id: str = None,
    referenced_group_info: Dict = None,
    tags: list = None,
    description: str = None,
    cidr_ipv4_desc: str = None,
    cidr_ipv6_desc: str = None,
    prefix_list_id_desc: str = None,
) -> Dict[str, Any]:
    r"""
    **Autogenerated function**

    Creates a security group rule.
    authorize_security_group_ingress: Adds the specified inbound (ingress) rules to a security group. An inbound rule
    permits instances to receive traffic from the specified IPv4 or IPv6 CIDR address range, or from the instances that
    are associated with the specified destination security groups.You specify a protocol for each rule (for example, TCP).
    For TCP and UDP, you must also specify the destination port or port range. For ICMP/ICMPv6, you must also specify the
    ICMP/ICMPv6 type and code. You can use -1 to mean all types or all codes. Rule changes are propagated to instances within
    the security group as quickly as possible. However, a small delay might occur.
    authorize_security_group_egress: [VPC only] Adds the specified outbound (egress) rules to a security group for use with a VPC.
    An outbound rule permits instances to send traffic to the specified IPv4 or IPv6 CIDR address ranges, or to the instances that
    are associated with the specified source security groups. You specify a protocol for each rule (for example, TCP). For the TCP
    and UDP protocols, you must also specify the destination port or port range. For the ICMP protocol, you must also specify the
    ICMP type and code. You can use -1 for the type or code to mean all types or all codes. Rule changes are propagated to affected
    instances as quickly as possible. However, a small delay might occur.

    Args:
        hub:
        ctx:
        name(Text): An Idem name of the resource.
        group_id(str): The ID of the security group
        is_egress(bool): To find the type of rule, whether it is a ingress or egress.
        ip_protocol(str): The IP protocol name (tcp , udp , icmp , icmpv6 ) or number (see Protocol Numbers ). [VPC only] Use -1 to specify all protocols.
                          When authorizing security group rules, specifying -1 or a protocol number other than tcp , udp , icmp , or icmpv6 allows traffic
                          on all ports, regardless of any port range you specify. For tcp , udp , and icmp , you must specify a port range. For icmpv6 ,
                          the port range is optional; if you omit the port range, traffic for all types and codes is allowed.
        from_port(int): The start of port range for the TCP and UDP protocols, or an ICMP/ICMPv6 type number. A value of -1 indicates all ICMP/ICMPv6 types.
                        If you specify all ICMP/ICMPv6 types, you must specify all codes.
        to_port(int): The end of port range for the TCP and UDP protocols, or an ICMP/ICMPv6 code. A value of -1 indicates all ICMP/ICMPv6 codes.
                      If you specify all ICMP/ICMPv6 types, you must specify all codes.
        cidr_ipv4(str, optional): The IPv4 CIDR range. You can either specify a CIDR range or a source security group, not both.
                        To specify a single IPv4 address, use the /32 prefix length.
        resource_id(Text, optional): AWS Security group rule ID
        cidr_ipv6(str, optional): The IPv6 CIDR range. You can either specify a CIDR range or a source security group, not both.
                                  To specify a single IPv6 address, use the /128 prefix length.
        prefix_list_id(str, optional): The ID of the prefix.
        referenced_group_info(Dict, optional): The security group and Amazon Web Services account ID pairs.
        description(str, optional): The description of the security group rule.
        cidr_ipv4_desc(str, optional): A description for the security group rule that references this IPv4 address range.
                                       Constraints: Up to 255 characters in length. Allowed characters are a-z, A-Z, 0-9, spaces, and ._-:/()#,@[]+=&;{}!$*
        cidr_ipv6_desc(str, optional): A description for the security group rule that references this IPv6 address range.
                                    Constraints: Up to 255 characters in length. Allowed characters are a-z, A-Z, 0-9, spaces, and ._-:/()#,@[]+=&;{}!$*
        prefix_list_id_desc(str, optional): A description for the security group rule that references this prefix list ID.
                                        Constraints: Up to 255 characters in length. Allowed characters are a-z, A-Z, 0-9, spaces, and ._-:/()#,@[]+=;{}!$*
        tags(Dict, Optional): List of Name, Value pair to tag security group

    Request Syntax:
        [security_group_rule_name]:
          aws.ec2.security_group_rule.present:
            - group_id: 'string'
            - is_egress: 'bool'
            - ip_protocol: 'string'
            - from_port: 'int'
            - to_port: 'int'
            - cidr_ipv4: 'string'
            - cidr_ipv6: 'string'
            - prefix_list_id: 'string'
            - referenced_group_info: 'dict'
            - tags:
                - Key: 'string'
                  Value: 'string'
            - description: 'string'
            - cidr_ipv4_desc: 'string'
            - cidr_ipv6_desc: 'string'
            - prefix_list_id_desc: 'string'

    Returns:
        Dict[str, Any]

    Examples:

        .. code-block:: sls

            my-sg-rule:
              aws.ec2.security_group_rule.present:
                - group_id: sg-0dd442ba9f89c5d59
                - is_egress: false
                - ip_protocol: tcp
                - from_port: 60
                - to_port: 60
                - cidr_ipv4: 0.0.0.0/0
                - tags:
                    - Key: test_name5
                      Value: test-rule5
                - description: 'Security group desc'
                - cidr_ipv4_desc: 'This is new cidr_ipv4_desc'
                - cidr_ipv6_desc: 'This is new cidr_ipv6_desc'
                - prefix_list_id_desc: 'This is new prefix_list_id_desc'
    """

    result = dict(comment="", old_state=None, new_state=None, name=name, result=True)
    before = None
    resource_updated = False
    if resource_id:
        before = await hub.exec.boto3.client.ec2.describe_security_group_rules(
            ctx, SecurityGroupRuleIds=[resource_id]
        )
    if before and before["result"] and before["ret"]["SecurityGroupRules"]:
        result[
            "old_state"
        ] = hub.tool.aws.ec2.conversion_utils.convert_raw_sg_rule_to_present(
            raw_resource=before["ret"]["SecurityGroupRules"][0], idem_resource_name=name
        )
        plan_state = copy.deepcopy(result["old_state"])
        result["comment"] = f"'{name}' already present."
        security_group_rules = []
        updated_rule = {}
        new_modified_rule = dict(SecurityGroupRuleId=None, SecurityGroupRule=None)
        new_modified_rule["SecurityGroupRuleId"] = name
        if ip_protocol:
            updated_rule["IpProtocol"] = ip_protocol
        if from_port:
            updated_rule["FromPort"] = from_port
        if to_port:
            updated_rule["ToPort"] = to_port
        if cidr_ipv4:
            updated_rule["CidrIpv4"] = cidr_ipv4
        if cidr_ipv6:
            updated_rule["CidrIpv6"] = cidr_ipv6
        if prefix_list_id:
            updated_rule["PrefixListId"] = prefix_list_id
        if referenced_group_info:
            updated_rule["ReferencedGroupId"] = referenced_group_info
        if description:
            updated_rule["Description"] = description
        new_modified_rule["SecurityGroupRule"] = updated_rule
        security_group_rules.append(new_modified_rule)
        if ctx.get("test", False):
            if security_group_rules is not None:
                plan_state["security_group_rules"] = security_group_rules
            if group_id is not None:
                plan_state["group_id"] = group_id
        if not ctx.get("test", False):
            update_ret = await hub.exec.boto3.client.ec2.modify_security_group_rules(
                ctx,
                GroupId=group_id,
                SecurityGroupRules=security_group_rules,
            )
            result["comment"] = update_ret["comment"]
            result["result"] = update_ret["result"]
            resource_updated = resource_updated or bool(update_ret["ret"])
    else:
        if ctx.get("test", False):
            result["new_state"] = hub.tool.aws.test_state_utils.generate_test_state(
                enforced_state={},
                desired_state={
                    "name": name,
                    "group_id": group_id,
                    "ip_protocol": ip_protocol,
                    "from_port": from_port,
                    "to_port": to_port,
                    "is_egress": is_egress,
                    "cidr_ipv4": cidr_ipv4,
                    "tags": tags,
                    "description": description,
                    "cidr_ipv4_desc": cidr_ipv4_desc,
                    "cidr_ipv6_desc": cidr_ipv6_desc,
                    "prefix_list_id_desc": prefix_list_id_desc,
                },
            )
            result["comment"] = f"Would create aws.ec2.security_group_rule {name}"
            return result
        try:
            ip_permissions = []
            ip_perm = {}
            if ip_protocol:
                ip_perm["IpProtocol"] = ip_protocol
            if from_port:
                ip_perm["FromPort"] = from_port
            if to_port:
                ip_perm["ToPort"] = to_port
            if cidr_ipv4:
                IpRanges = {}
                if cidr_ipv4_desc:
                    IpRanges["Description"] = cidr_ipv4_desc
                if cidr_ipv4:
                    IpRanges["CidrIp"] = cidr_ipv4
                ip_perm["IpRanges"] = [IpRanges]
            if cidr_ipv6:
                Ipv6Ranges = {}
                if cidr_ipv6_desc:
                    Ipv6Ranges["Description"] = cidr_ipv6_desc
                if cidr_ipv6:
                    Ipv6Ranges["CidrIpv6"] = cidr_ipv6
                ip_perm["Ipv6Ranges"] = [Ipv6Ranges]
            if prefix_list_id:
                prefix_list = {}
                if prefix_list_id_desc:
                    prefix_list["Description"] = prefix_list_id_desc
                if prefix_list_id:
                    prefix_list["PrefixListId"] = prefix_list_id
                ip_perm["PrefixListIds"] = [prefix_list]
            if referenced_group_info:
                ip_perm["UserIdGroupPairs"] = [referenced_group_info]
            ip_permissions.append(ip_perm)
            if not is_egress:
                ret = await hub.exec.boto3.client.ec2.authorize_security_group_ingress(
                    ctx,
                    GroupId=group_id,
                    IpPermissions=ip_permissions,
                    TagSpecifications=[
                        {"ResourceType": "security-group-rule", "Tags": tags}
                    ]
                    if tags is not None
                    else None,
                )
                result["result"] = ret["result"]
                if not ret["result"]:
                    result["comment"] = ret["comment"]
                    return result
                result["comment"] = f"Created '{name}'"
            else:
                ret = await hub.exec.boto3.client.ec2.authorize_security_group_egress(
                    ctx,
                    GroupId=group_id,
                    IpPermissions=ip_permissions,
                    TagSpecifications=[
                        {"ResourceType": "security-group-rule", "Tags": tags}
                    ]
                    if tags is not None
                    else None,
                )
                result["result"] = ret["result"]
                if not ret["result"]:
                    result["comment"] = ret["comment"]
                    return result
                result["comment"] = f"Created '{name}'"
            resource_id = ret["ret"]["SecurityGroupRules"][0]["SecurityGroupRuleId"]
        except hub.tool.boto3.exception.ClientError as e:
            result["comment"] = f"{e.__class__.__name__}: {e}"
    try:
        if ctx.get("test", False):
            result["new_state"] = plan_state
        elif (not before) or resource_updated:
            after = await hub.exec.boto3.client.ec2.describe_security_group_rules(
                ctx,
                SecurityGroupRuleIds=[resource_id],
            )
            result[
                "new_state"
            ] = hub.tool.aws.ec2.conversion_utils.convert_raw_sg_rule_to_present(
                raw_resource=after["ret"]["SecurityGroupRules"][0],
                idem_resource_name=name,
            )
        else:
            result["new_state"] = copy.deepcopy(result["old_state"])
    except Exception as e:
        result["comment"] = str(e)
        result["result"] = False
    return result


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

    Deletes a security group rule.
    revoke_ingress: Removes the specified inbound (ingress) rules from a security group. You can specify rules using either
    rule IDs or security group rule properties. If you use rule properties, the values that you specify (for example, ports)
    must match the existing rule's values exactly. Each rule has a protocol, from and to ports, and source (CIDR range,
    security group, or prefix list). For the TCP and UDP protocols, you must also specify the destination port or range of ports.
    For the ICMP protocol, you must also specify the ICMP type and code. If the security group rule has a description, you do
    not need to specify the description to revoke the rule. [EC2-Classic, default VPC] If the values you specify do not match
    the existing rule's values, no error is returned, and the output describes the security group rules that were not revoked.
    Amazon Web Services recommends that you describe the security group to verify that the rules were removed.
    Rule changes are propagated to instances within the security group as quickly as possible. However, a small delay might occur.
    revoke_egress: [VPC only] Removes the specified outbound (egress) rules from a security group for EC2-VPC. This action does not
    apply to security groups for use in EC2-Classic. You can specify rules using either rule IDs or security group rule properties.
    If you use rule properties, the values that you specify (for example, ports) must match the existing rule's values exactly.
    Each rule has a protocol, from and to ports, and destination (CIDR range, security group, or prefix list). For the TCP and UDP
    protocols, you must also specify the destination port or range of ports. For the ICMP protocol, you must also specify the ICMP
    type and code. If the security group rule has a description, you do not need to specify the description to revoke the rule.
    [Default VPC] If the values you specify do not match the existing rule's values, no error is returned, and the output describes
    the security group rules that were not revoked. Amazon Web Services recommends that you describe the security group to verify
    that the rules were removed. Rule changes are propagated to instances within the security group as quickly as possible. However,
    a small delay might occur.

    Args:
        hub:
        ctx:
        name(Text): A name, ID to identify the resource.
        group_id(str): The ID of the Security group
        resource_id(Text): The AWS ID of the Security group rule

    Returns:
        Dict[str, Any]

    Examples:

        .. code-block:: sls

            resource_is_absent:
              aws.ec2.security_group_rule.absent:
                - name: value
                - group_id: "sg-0008bd25b7867b5cf"
    """
    result = dict(comment="", old_state=None, new_state=None, name=name, result=True)
    resource = hub.tool.boto3.resource.create(ctx, "ec2", "SecurityGroup", group_id)
    before = await hub.exec.boto3.client.ec2.describe_security_group_rules(
        ctx, SecurityGroupRuleIds=[resource_id]
    )
    if not before and not before["result"]:
        result["comment"] = f"'{name}' is already in deleted state."
        return result
    else:
        result[
            "old_state"
        ] = hub.tool.aws.ec2.conversion_utils.convert_raw_sg_rule_to_present(
            raw_resource=before["ret"]["SecurityGroupRules"][0], idem_resource_name=name
        )
        if ctx.get("test", False):
            result["comment"] = f"Would delete aws.ec2.security_group_rule {name}"
            return result
        try:
            # checks for egress or ingress using before's output and revoke function needed sg resource
            if not before["ret"]["SecurityGroupRules"][0]["IsEgress"]:
                ret = resource.revoke_ingress(SecurityGroupRuleIds=[resource_id])
                result["result"] = ret["Return"]
                if not result["result"]:
                    result["comment"] = f"'{name}' is already in deleted state."
                    return result
                result["comment"] = f"Deleted '{name}'"
            else:
                ret = resource.revoke_egress(SecurityGroupRuleIds=[resource_id])
                result["result"] = ret["result"]
                if not result["result"]:
                    result["comment"] = f"'{name}' is already in deleted state."
                    return result
                result["comment"] = f"Deleted '{name}'"
        except hub.tool.boto3.exception.ClientError as e:
            result["result"] = False
            result["comment"] = f"{e.__class__.__name__}: {e}"

    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 one or more of security group rules. Allows to use filter based on resource type i.e.
    group-id, security-group-rule-id and tags.


    Returns:
        Dict[str, Any]

    Examples:

        .. code-block:: bash

            $ idem describe aws.ec2.security_group_rule
    """
    result = {}

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

    for resource in ret["ret"]["SecurityGroupRules"]:
        resource_id = resource.get("SecurityGroupRuleId")
        resource_translated = (
            hub.tool.aws.ec2.conversion_utils.convert_raw_sg_rule_to_present(
                raw_resource=resource, idem_resource_name=resource_id
            )
        )
        result[resource_id] = {
            "aws.ec2.security_group_rule.present": [
                {parameter_key: parameter_value}
                for parameter_key, parameter_value in resource_translated.items()
            ]
        }
    return result
