import json

import boto3

from moto import mock_aws
from moto.core import DEFAULT_ACCOUNT_ID as ACCOUNT_ID


@mock_aws
def test_redirect_action_listener_rule_cloudformation():
    cnf_conn = boto3.client("cloudformation", region_name="us-east-1")
    elbv2_client = boto3.client("elbv2", region_name="us-east-1")

    template = {
        "AWSTemplateFormatVersion": "2010-09-09",
        "Description": "ECS Cluster Test CloudFormation",
        "Resources": {
            "testVPC": {
                "Type": "AWS::EC2::VPC",
                "Properties": {"CidrBlock": "10.0.0.0/16"},
            },
            "subnet1": {
                "Type": "AWS::EC2::Subnet",
                "Properties": {
                    "CidrBlock": "10.0.0.0/24",
                    "VpcId": {"Ref": "testVPC"},
                    "AvalabilityZone": "us-east-1b",
                },
            },
            "subnet2": {
                "Type": "AWS::EC2::Subnet",
                "Properties": {
                    "CidrBlock": "10.0.1.0/24",
                    "VpcId": {"Ref": "testVPC"},
                    "AvalabilityZone": "us-east-1b",
                },
            },
            "testLb": {
                "Type": "AWS::ElasticLoadBalancingV2::LoadBalancer",
                "Properties": {
                    "Name": "my-lb",
                    "Subnets": [{"Ref": "subnet1"}, {"Ref": "subnet2"}],
                    "Type": "application",
                    "SecurityGroups": [],
                },
            },
            "testListener": {
                "Type": "AWS::ElasticLoadBalancingV2::Listener",
                "Properties": {
                    "LoadBalancerArn": {"Ref": "testLb"},
                    "Port": 80,
                    "Protocol": "HTTP",
                    "DefaultActions": [
                        {
                            "Type": "redirect",
                            "RedirectConfig": {
                                "Port": "443",
                                "Protocol": "HTTPS",
                                "StatusCode": "HTTP_301",
                            },
                        }
                    ],
                },
            },
        },
    }
    template_json = json.dumps(template)
    cnf_conn.create_stack(StackName="test-stack", TemplateBody=template_json)

    resp = elbv2_client.describe_load_balancers(Names=["my-lb"])
    assert len(resp["LoadBalancers"]) == 1
    load_balancer_arn = resp["LoadBalancers"][0]["LoadBalancerArn"]

    listeners = elbv2_client.describe_listeners(LoadBalancerArn=load_balancer_arn)

    assert len(listeners["Listeners"]) == 1
    assert listeners["Listeners"][0]["DefaultActions"] == [
        {
            "Type": "redirect",
            "RedirectConfig": {
                "Port": "443",
                "Protocol": "HTTPS",
                "StatusCode": "HTTP_301",
            },
        }
    ]


@mock_aws
def test_cognito_action_listener_rule_cloudformation():
    cnf_conn = boto3.client("cloudformation", region_name="us-east-1")
    elbv2_client = boto3.client("elbv2", region_name="us-east-1")

    template = {
        "AWSTemplateFormatVersion": "2010-09-09",
        "Description": "ECS Cluster Test CloudFormation",
        "Resources": {
            "testVPC": {
                "Type": "AWS::EC2::VPC",
                "Properties": {"CidrBlock": "10.0.0.0/16"},
            },
            "subnet1": {
                "Type": "AWS::EC2::Subnet",
                "Properties": {
                    "CidrBlock": "10.0.0.0/24",
                    "VpcId": {"Ref": "testVPC"},
                    "AvalabilityZone": "us-east-1b",
                },
            },
            "subnet2": {
                "Type": "AWS::EC2::Subnet",
                "Properties": {
                    "CidrBlock": "10.0.1.0/24",
                    "VpcId": {"Ref": "testVPC"},
                    "AvalabilityZone": "us-east-1b",
                },
            },
            "testLb": {
                "Type": "AWS::ElasticLoadBalancingV2::LoadBalancer",
                "Properties": {
                    "Name": "my-lb",
                    "Subnets": [{"Ref": "subnet1"}, {"Ref": "subnet2"}],
                    "Type": "application",
                    "SecurityGroups": [],
                },
            },
            "testListener": {
                "Type": "AWS::ElasticLoadBalancingV2::Listener",
                "Properties": {
                    "LoadBalancerArn": {"Ref": "testLb"},
                    "Port": 80,
                    "Protocol": "HTTP",
                    "DefaultActions": [
                        {
                            "Type": "authenticate-cognito",
                            "AuthenticateCognitoConfig": {
                                "UserPoolArn": f"arn:aws:cognito-idp:us-east-1:{ACCOUNT_ID}:userpool/us-east-1_ABCD1234",
                                "UserPoolClientId": "abcd1234abcd",
                                "UserPoolDomain": "testpool",
                            },
                        }
                    ],
                },
            },
        },
    }
    template_json = json.dumps(template)
    cnf_conn.create_stack(StackName="test-stack", TemplateBody=template_json)

    describe_load_balancers_response = elbv2_client.describe_load_balancers(
        Names=["my-lb"]
    )
    load_balancer_arn = describe_load_balancers_response["LoadBalancers"][0][
        "LoadBalancerArn"
    ]
    describe_listeners_response = elbv2_client.describe_listeners(
        LoadBalancerArn=load_balancer_arn
    )

    assert len(describe_listeners_response["Listeners"]) == 1
    assert describe_listeners_response["Listeners"][0]["DefaultActions"] == [
        {
            "Type": "authenticate-cognito",
            "AuthenticateCognitoConfig": {
                "UserPoolArn": f"arn:aws:cognito-idp:us-east-1:{ACCOUNT_ID}:userpool/us-east-1_ABCD1234",
                "UserPoolClientId": "abcd1234abcd",
                "UserPoolDomain": "testpool",
            },
        }
    ]


@mock_aws
def test_create_target_groups_through_cloudformation():
    cfn_conn = boto3.client("cloudformation", region_name="us-east-1")
    elbv2_client = boto3.client("elbv2", region_name="us-east-1")

    # test that setting a name manually as well as letting cloudformation create a name both work
    # this is a special case because test groups have a name length limit of 22 characters, and must be unique
    # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-elasticloadbalancingv2-targetgroup.html#cfn-elasticloadbalancingv2-targetgroup-name
    template = {
        "AWSTemplateFormatVersion": "2010-09-09",
        "Description": "ECS Cluster Test CloudFormation",
        "Resources": {
            "testVPC": {
                "Type": "AWS::EC2::VPC",
                "Properties": {"CidrBlock": "10.0.0.0/16"},
            },
            "testGroup1": {
                "Type": "AWS::ElasticLoadBalancingV2::TargetGroup",
                "Properties": {
                    "Port": 80,
                    "Protocol": "HTTP",
                    "VpcId": {"Ref": "testVPC"},
                },
            },
            "testGroup2": {
                "Type": "AWS::ElasticLoadBalancingV2::TargetGroup",
                "Properties": {
                    "Port": 90,
                    "Protocol": "HTTP",
                    "VpcId": {"Ref": "testVPC"},
                },
            },
            "testGroup3": {
                "Type": "AWS::ElasticLoadBalancingV2::TargetGroup",
                "Properties": {
                    "Name": "MyTargetGroup",
                    "Port": 70,
                    "Protocol": "HTTPS",
                    "VpcId": {"Ref": "testVPC"},
                },
            },
        },
    }
    template_json = json.dumps(template)
    cfn_conn.create_stack(StackName="test-stack", TemplateBody=template_json)

    describe_target_groups_response = elbv2_client.describe_target_groups()
    target_group_dicts = describe_target_groups_response["TargetGroups"]
    assert len(target_group_dicts) == 3

    # there should be 2 target groups with the same prefix of 10 characters (since the random suffix is 12)
    # and one named MyTargetGroup
    assert (
        len(
            [
                tg
                for tg in target_group_dicts
                if tg["TargetGroupName"] == "MyTargetGroup"
            ]
        )
        == 1
    )
    assert (
        len(
            [
                tg
                for tg in target_group_dicts
                if tg["TargetGroupName"].startswith("test-stack")
            ]
        )
        == 2
    )


@mock_aws
def test_fixed_response_action_listener_rule_cloudformation():
    cnf_conn = boto3.client("cloudformation", region_name="us-east-1")
    elbv2_client = boto3.client("elbv2", region_name="us-east-1")

    template = {
        "AWSTemplateFormatVersion": "2010-09-09",
        "Description": "ECS Cluster Test CloudFormation",
        "Resources": {
            "testVPC": {
                "Type": "AWS::EC2::VPC",
                "Properties": {"CidrBlock": "10.0.0.0/16"},
            },
            "subnet1": {
                "Type": "AWS::EC2::Subnet",
                "Properties": {
                    "CidrBlock": "10.0.0.0/24",
                    "VpcId": {"Ref": "testVPC"},
                    "AvalabilityZone": "us-east-1b",
                },
            },
            "subnet2": {
                "Type": "AWS::EC2::Subnet",
                "Properties": {
                    "CidrBlock": "10.0.1.0/24",
                    "VpcId": {"Ref": "testVPC"},
                    "AvalabilityZone": "us-east-1b",
                },
            },
            "testLb": {
                "Type": "AWS::ElasticLoadBalancingV2::LoadBalancer",
                "Properties": {
                    "Name": "my-lb",
                    "Subnets": [{"Ref": "subnet1"}, {"Ref": "subnet2"}],
                    "Type": "application",
                    "SecurityGroups": [],
                },
            },
            "testListener": {
                "Type": "AWS::ElasticLoadBalancingV2::Listener",
                "Properties": {
                    "LoadBalancerArn": {"Ref": "testLb"},
                    "Port": 80,
                    "Protocol": "HTTP",
                    "DefaultActions": [
                        {
                            "Type": "fixed-response",
                            "FixedResponseConfig": {
                                "ContentType": "text/plain",
                                "MessageBody": "This page does not exist",
                                "StatusCode": "404",
                            },
                        }
                    ],
                },
            },
        },
    }
    template_json = json.dumps(template)
    cnf_conn.create_stack(StackName="test-stack", TemplateBody=template_json)

    describe_load_balancers_response = elbv2_client.describe_load_balancers(
        Names=["my-lb"]
    )
    load_balancer_arn = describe_load_balancers_response["LoadBalancers"][0][
        "LoadBalancerArn"
    ]
    describe_listeners_response = elbv2_client.describe_listeners(
        LoadBalancerArn=load_balancer_arn
    )

    assert len(describe_listeners_response["Listeners"]) == 1
    assert describe_listeners_response["Listeners"][0]["DefaultActions"] == [
        {
            "Type": "fixed-response",
            "FixedResponseConfig": {
                "ContentType": "text/plain",
                "MessageBody": "This page does not exist",
                "StatusCode": "404",
            },
        }
    ]


@mock_aws
def test_https_listener_rule_with_certificate_cloudformation():
    cnf_conn = boto3.client("cloudformation", region_name="us-east-1")
    elbv2_client = boto3.client("elbv2", region_name="us-east-1")
    acm_client = boto3.client("acm", region_name="us-east-1")

    acm_request_response = acm_client.request_certificate(
        DomainName="fake.domain.com",
        DomainValidationOptions=[
            {"DomainName": "fake.domain.com", "ValidationDomain": "domain.com"},
        ],
    )
    certificate_arn = acm_request_response["CertificateArn"]

    template = {
        "AWSTemplateFormatVersion": "2010-09-09",
        "Description": "ECS Cluster Test CloudFormation",
        "Resources": {
            "testVPC": {
                "Type": "AWS::EC2::VPC",
                "Properties": {"CidrBlock": "10.0.0.0/16"},
            },
            "subnet1": {
                "Type": "AWS::EC2::Subnet",
                "Properties": {
                    "CidrBlock": "10.0.0.0/24",
                    "VpcId": {"Ref": "testVPC"},
                    "AvalabilityZone": "us-east-1b",
                },
            },
            "subnet2": {
                "Type": "AWS::EC2::Subnet",
                "Properties": {
                    "CidrBlock": "10.0.1.0/24",
                    "VpcId": {"Ref": "testVPC"},
                    "AvalabilityZone": "us-east-1b",
                },
            },
            "testLb": {
                "Type": "AWS::ElasticLoadBalancingV2::LoadBalancer",
                "Properties": {
                    "Name": "my-lb",
                    "Subnets": [{"Ref": "subnet1"}, {"Ref": "subnet2"}],
                    "Type": "application",
                    "SecurityGroups": [],
                },
            },
            "testListener": {
                "Type": "AWS::ElasticLoadBalancingV2::Listener",
                "Properties": {
                    "LoadBalancerArn": {"Ref": "testLb"},
                    "Certificates": [{"CertificateArn": certificate_arn}],
                    "Port": 443,
                    "Protocol": "HTTPS",
                    "DefaultActions": [
                        {
                            "Type": "fixed-response",
                            "FixedResponseConfig": {
                                "ContentType": "text/plain",
                                "MessageBody": "This page does not exist",
                                "StatusCode": "404",
                            },
                        }
                    ],
                },
            },
        },
    }
    template_json = json.dumps(template)
    cnf_conn.create_stack(StackName="test-stack", TemplateBody=template_json)

    resp = elbv2_client.describe_load_balancers(Names=["my-lb"])
    assert len(resp["LoadBalancers"]) == 1
    load_balancer_arn = resp["LoadBalancers"][0]["LoadBalancerArn"]

    listeners = elbv2_client.describe_listeners(LoadBalancerArn=load_balancer_arn)

    assert len(listeners["Listeners"]) == 1
    assert listeners["Listeners"][0]["DefaultActions"] == [
        {
            "Type": "fixed-response",
            "FixedResponseConfig": {
                "ContentType": "text/plain",
                "MessageBody": "This page does not exist",
                "StatusCode": "404",
            },
        }
    ]
    assert listeners["Listeners"][0]["Certificates"] == [
        {"CertificateArn": certificate_arn}
    ]
