import boto3
import sure  # noqa # pylint: disable=unused-import
import json

from moto import mock_autoscaling, mock_cloudformation, mock_ec2, mock_elb

from .utils import setup_networking
from tests import EXAMPLE_AMI_ID


@mock_autoscaling
@mock_cloudformation
def test_launch_configuration():
    cf_client = boto3.client("cloudformation", region_name="us-east-1")
    client = boto3.client("autoscaling", region_name="us-east-1")

    stack_name = "test-launch-configuration"

    cf_template = """
Resources:
    LaunchConfiguration:
        Type: AWS::AutoScaling::LaunchConfiguration
        Properties:
            ImageId: {0}
            InstanceType: t2.micro
            LaunchConfigurationName: test_launch_configuration
Outputs:
    LaunchConfigurationName:
        Value: !Ref LaunchConfiguration
""".strip().format(
        EXAMPLE_AMI_ID
    )

    cf_client.create_stack(
        StackName=stack_name, TemplateBody=cf_template,
    )
    stack = cf_client.describe_stacks(StackName=stack_name)["Stacks"][0]
    stack["Outputs"][0]["OutputValue"].should.be.equal("test_launch_configuration")

    lc = client.describe_launch_configurations()["LaunchConfigurations"][0]
    lc["LaunchConfigurationName"].should.be.equal("test_launch_configuration")
    lc["ImageId"].should.be.equal(EXAMPLE_AMI_ID)
    lc["InstanceType"].should.be.equal("t2.micro")

    cf_template = """
Resources:
    LaunchConfiguration:
        Type: AWS::AutoScaling::LaunchConfiguration
        Properties:
            ImageId: {0}
            InstanceType: m5.large
            LaunchConfigurationName: test_launch_configuration
Outputs:
    LaunchConfigurationName:
        Value: !Ref LaunchConfiguration
""".strip().format(
        EXAMPLE_AMI_ID
    )

    cf_client.update_stack(
        StackName=stack_name, TemplateBody=cf_template,
    )
    stack = cf_client.describe_stacks(StackName=stack_name)["Stacks"][0]
    stack["Outputs"][0]["OutputValue"].should.be.equal("test_launch_configuration")

    lc = client.describe_launch_configurations()["LaunchConfigurations"][0]
    lc["LaunchConfigurationName"].should.be.equal("test_launch_configuration")
    lc["ImageId"].should.be.equal(EXAMPLE_AMI_ID)
    lc["InstanceType"].should.be.equal("m5.large")


@mock_autoscaling
@mock_cloudformation
def test_autoscaling_group_from_launch_config():
    subnet_id = setup_networking()["subnet1"]

    cf_client = boto3.client("cloudformation", region_name="us-east-1")
    client = boto3.client("autoscaling", region_name="us-east-1")

    client.create_launch_configuration(
        LaunchConfigurationName="test_launch_configuration",
        InstanceType="t2.micro",
        ImageId=EXAMPLE_AMI_ID,
    )
    stack_name = "test-auto-scaling-group"

    cf_template = """
Parameters:
    SubnetId:
        Type: AWS::EC2::Subnet::Id
Resources:
    AutoScalingGroup:
        Type: AWS::AutoScaling::AutoScalingGroup
        Properties:
            AutoScalingGroupName: test_auto_scaling_group
            AvailabilityZones:
                - us-east-1a
            LaunchConfigurationName: test_launch_configuration
            MaxSize: "5"
            MinSize: "1"
            VPCZoneIdentifier:
                - !Ref SubnetId
Outputs:
    AutoScalingGroupName:
        Value: !Ref AutoScalingGroup
""".strip()

    cf_client.create_stack(
        StackName=stack_name,
        TemplateBody=cf_template,
        Parameters=[{"ParameterKey": "SubnetId", "ParameterValue": subnet_id}],
    )
    stack = cf_client.describe_stacks(StackName=stack_name)["Stacks"][0]
    stack["Outputs"][0]["OutputValue"].should.be.equal("test_auto_scaling_group")

    asg = client.describe_auto_scaling_groups()["AutoScalingGroups"][0]
    asg["AutoScalingGroupName"].should.be.equal("test_auto_scaling_group")
    asg["MinSize"].should.be.equal(1)
    asg["MaxSize"].should.be.equal(5)
    asg["LaunchConfigurationName"].should.be.equal("test_launch_configuration")

    client.create_launch_configuration(
        LaunchConfigurationName="test_launch_configuration_new",
        InstanceType="t2.micro",
        ImageId=EXAMPLE_AMI_ID,
    )

    cf_template = """
Parameters:
    SubnetId:
        Type: AWS::EC2::Subnet::Id
Resources:
    AutoScalingGroup:
        Type: AWS::AutoScaling::AutoScalingGroup
        Properties:
            AutoScalingGroupName: test_auto_scaling_group
            AvailabilityZones:
                - us-east-1a
            LaunchConfigurationName: test_launch_configuration_new
            MaxSize: "6"
            MinSize: "2"
            VPCZoneIdentifier:
                - !Ref SubnetId
Outputs:
    AutoScalingGroupName:
        Value: !Ref AutoScalingGroup
""".strip()

    cf_client.update_stack(
        StackName=stack_name,
        TemplateBody=cf_template,
        Parameters=[{"ParameterKey": "SubnetId", "ParameterValue": subnet_id}],
    )
    stack = cf_client.describe_stacks(StackName=stack_name)["Stacks"][0]
    stack["Outputs"][0]["OutputValue"].should.be.equal("test_auto_scaling_group")

    asg = client.describe_auto_scaling_groups()["AutoScalingGroups"][0]
    asg["AutoScalingGroupName"].should.be.equal("test_auto_scaling_group")
    asg["MinSize"].should.be.equal(2)
    asg["MaxSize"].should.be.equal(6)
    asg["LaunchConfigurationName"].should.be.equal("test_launch_configuration_new")


@mock_autoscaling
@mock_cloudformation
@mock_ec2
def test_autoscaling_group_from_launch_template():
    subnet_id = setup_networking()["subnet1"]

    cf_client = boto3.client("cloudformation", region_name="us-east-1")
    ec2_client = boto3.client("ec2", region_name="us-east-1")
    client = boto3.client("autoscaling", region_name="us-east-1")

    template_response = ec2_client.create_launch_template(
        LaunchTemplateName="test_launch_template",
        LaunchTemplateData={"ImageId": EXAMPLE_AMI_ID, "InstanceType": "t2.micro",},
    )
    launch_template_id = template_response["LaunchTemplate"]["LaunchTemplateId"]
    stack_name = "test-auto-scaling-group"

    cf_template = """
Parameters:
    SubnetId:
        Type: AWS::EC2::Subnet::Id
    LaunchTemplateId:
        Type: String
Resources:
    AutoScalingGroup:
        Type: AWS::AutoScaling::AutoScalingGroup
        Properties:
            AutoScalingGroupName: test_auto_scaling_group
            AvailabilityZones:
                - us-east-1a
            LaunchTemplate:
                LaunchTemplateId: !Ref LaunchTemplateId
                Version: "1"
            MaxSize: "5"
            MinSize: "1"
            VPCZoneIdentifier:
                - !Ref SubnetId
Outputs:
    AutoScalingGroupName:
        Value: !Ref AutoScalingGroup
""".strip()

    cf_client.create_stack(
        StackName=stack_name,
        TemplateBody=cf_template,
        Parameters=[
            {"ParameterKey": "SubnetId", "ParameterValue": subnet_id},
            {"ParameterKey": "LaunchTemplateId", "ParameterValue": launch_template_id},
        ],
    )
    stack = cf_client.describe_stacks(StackName=stack_name)["Stacks"][0]
    stack["Outputs"][0]["OutputValue"].should.be.equal("test_auto_scaling_group")

    asg = client.describe_auto_scaling_groups()["AutoScalingGroups"][0]
    asg["AutoScalingGroupName"].should.be.equal("test_auto_scaling_group")
    asg["MinSize"].should.be.equal(1)
    asg["MaxSize"].should.be.equal(5)
    lt = asg["LaunchTemplate"]
    lt["LaunchTemplateId"].should.be.equal(launch_template_id)
    lt["LaunchTemplateName"].should.be.equal("test_launch_template")
    lt["Version"].should.be.equal("1")

    template_response = ec2_client.create_launch_template(
        LaunchTemplateName="test_launch_template_new",
        LaunchTemplateData={"ImageId": EXAMPLE_AMI_ID, "InstanceType": "m5.large",},
    )
    launch_template_id = template_response["LaunchTemplate"]["LaunchTemplateId"]

    cf_template = """
Parameters:
    SubnetId:
        Type: AWS::EC2::Subnet::Id
    LaunchTemplateId:
        Type: String
Resources:
    AutoScalingGroup:
        Type: AWS::AutoScaling::AutoScalingGroup
        Properties:
            AutoScalingGroupName: test_auto_scaling_group
            AvailabilityZones:
                - us-east-1a
            LaunchTemplate:
                LaunchTemplateId: !Ref LaunchTemplateId
                Version: "1"
            MaxSize: "6"
            MinSize: "2"
            VPCZoneIdentifier:
                - !Ref SubnetId
Outputs:
    AutoScalingGroupName:
        Value: !Ref AutoScalingGroup
""".strip()

    cf_client.update_stack(
        StackName=stack_name,
        TemplateBody=cf_template,
        Parameters=[
            {"ParameterKey": "SubnetId", "ParameterValue": subnet_id},
            {"ParameterKey": "LaunchTemplateId", "ParameterValue": launch_template_id},
        ],
    )
    stack = cf_client.describe_stacks(StackName=stack_name)["Stacks"][0]
    stack["Outputs"][0]["OutputValue"].should.be.equal("test_auto_scaling_group")

    asg = client.describe_auto_scaling_groups()["AutoScalingGroups"][0]
    asg["AutoScalingGroupName"].should.be.equal("test_auto_scaling_group")
    asg["MinSize"].should.be.equal(2)
    asg["MaxSize"].should.be.equal(6)
    lt = asg["LaunchTemplate"]
    lt["LaunchTemplateId"].should.be.equal(launch_template_id)
    lt["LaunchTemplateName"].should.be.equal("test_launch_template_new")
    lt["Version"].should.be.equal("1")


@mock_autoscaling
@mock_elb
@mock_cloudformation
@mock_ec2
def test_autoscaling_group_with_elb():
    web_setup_template = {
        "AWSTemplateFormatVersion": "2010-09-09",
        "Resources": {
            "my-as-group": {
                "Type": "AWS::AutoScaling::AutoScalingGroup",
                "Properties": {
                    "AvailabilityZones": ["us-east-1a"],
                    "LaunchConfigurationName": {"Ref": "my-launch-config"},
                    "MinSize": "2",
                    "MaxSize": "2",
                    "DesiredCapacity": "2",
                    "LoadBalancerNames": [{"Ref": "my-elb"}],
                    "Tags": [
                        {
                            "Key": "propagated-test-tag",
                            "Value": "propagated-test-tag-value",
                            "PropagateAtLaunch": True,
                        },
                        {
                            "Key": "not-propagated-test-tag",
                            "Value": "not-propagated-test-tag-value",
                            "PropagateAtLaunch": False,
                        },
                    ],
                },
            },
            "my-launch-config": {
                "Type": "AWS::AutoScaling::LaunchConfiguration",
                "Properties": {
                    "ImageId": EXAMPLE_AMI_ID,
                    "InstanceType": "t2.medium",
                    "UserData": "some user data",
                },
            },
            "my-elb": {
                "Type": "AWS::ElasticLoadBalancing::LoadBalancer",
                "Properties": {
                    "AvailabilityZones": ["us-east-1a"],
                    "Listeners": [
                        {
                            "LoadBalancerPort": "80",
                            "InstancePort": "80",
                            "Protocol": "HTTP",
                        }
                    ],
                    "LoadBalancerName": "my-elb",
                    "HealthCheck": {
                        "Target": "HTTP:80",
                        "HealthyThreshold": "3",
                        "UnhealthyThreshold": "5",
                        "Interval": "30",
                        "Timeout": "5",
                    },
                },
            },
        },
    }

    web_setup_template_json = json.dumps(web_setup_template)

    cf = boto3.client("cloudformation", region_name="us-east-1")
    ec2 = boto3.client("ec2", region_name="us-east-1")
    elb = boto3.client("elb", region_name="us-east-1")
    client = boto3.client("autoscaling", region_name="us-east-1")

    cf.create_stack(StackName="web_stack", TemplateBody=web_setup_template_json)

    autoscale_group = client.describe_auto_scaling_groups()["AutoScalingGroups"][0]
    autoscale_group["LaunchConfigurationName"].should.contain("my-launch-config")
    autoscale_group["LoadBalancerNames"].should.equal(["my-elb"])

    # Confirm the Launch config was actually created
    client.describe_launch_configurations()[
        "LaunchConfigurations"
    ].should.have.length_of(1)

    # Confirm the ELB was actually created
    elb.describe_load_balancers()["LoadBalancerDescriptions"].should.have.length_of(1)

    resources = cf.list_stack_resources(StackName="web_stack")["StackResourceSummaries"]
    as_group_resource = [
        resource
        for resource in resources
        if resource["ResourceType"] == "AWS::AutoScaling::AutoScalingGroup"
    ][0]
    as_group_resource["PhysicalResourceId"].should.contain("my-as-group")

    launch_config_resource = [
        resource
        for resource in resources
        if resource["ResourceType"] == "AWS::AutoScaling::LaunchConfiguration"
    ][0]
    launch_config_resource["PhysicalResourceId"].should.contain("my-launch-config")

    elb_resource = [
        resource
        for resource in resources
        if resource["ResourceType"] == "AWS::ElasticLoadBalancing::LoadBalancer"
    ][0]
    elb_resource["PhysicalResourceId"].should.contain("my-elb")

    # confirm the instances were created with the right tags
    reservations = ec2.describe_instances()["Reservations"]

    reservations.should.have.length_of(1)
    reservations[0]["Instances"].should.have.length_of(2)
    for instance in reservations[0]["Instances"]:
        tag_keys = [t["Key"] for t in instance["Tags"]]
        tag_keys.should.contain("propagated-test-tag")
        tag_keys.should_not.contain("not-propagated-test-tag")


@mock_autoscaling
@mock_cloudformation
@mock_ec2
def test_autoscaling_group_update():
    asg_template = {
        "AWSTemplateFormatVersion": "2010-09-09",
        "Resources": {
            "my-as-group": {
                "Type": "AWS::AutoScaling::AutoScalingGroup",
                "Properties": {
                    "AvailabilityZones": ["us-west-1a"],
                    "LaunchConfigurationName": {"Ref": "my-launch-config"},
                    "MinSize": "2",
                    "MaxSize": "2",
                    "DesiredCapacity": "2",
                },
            },
            "my-launch-config": {
                "Type": "AWS::AutoScaling::LaunchConfiguration",
                "Properties": {
                    "ImageId": EXAMPLE_AMI_ID,
                    "InstanceType": "t2.medium",
                    "UserData": "some user data",
                },
            },
        },
    }
    asg_template_json = json.dumps(asg_template)

    cf = boto3.client("cloudformation", region_name="us-west-1")
    ec2 = boto3.client("ec2", region_name="us-west-1")
    client = boto3.client("autoscaling", region_name="us-west-1")
    cf.create_stack(StackName="asg_stack", TemplateBody=asg_template_json)

    asg = client.describe_auto_scaling_groups()["AutoScalingGroups"][0]
    asg["MinSize"].should.equal(2)
    asg["MaxSize"].should.equal(2)
    asg["DesiredCapacity"].should.equal(2)

    asg_template["Resources"]["my-as-group"]["Properties"]["MaxSize"] = 3
    asg_template["Resources"]["my-as-group"]["Properties"]["Tags"] = [
        {
            "Key": "propagated-test-tag",
            "Value": "propagated-test-tag-value",
            "PropagateAtLaunch": True,
        },
        {
            "Key": "not-propagated-test-tag",
            "Value": "not-propagated-test-tag-value",
            "PropagateAtLaunch": False,
        },
    ]
    asg_template_json = json.dumps(asg_template)
    cf.update_stack(StackName="asg_stack", TemplateBody=asg_template_json)
    asg = client.describe_auto_scaling_groups()["AutoScalingGroups"][0]
    asg["MinSize"].should.equal(2)
    asg["MaxSize"].should.equal(3)
    asg["DesiredCapacity"].should.equal(2)

    # confirm the instances were created with the right tags
    reservations = ec2.describe_instances()["Reservations"]
    running_instance_count = 0
    for res in reservations:
        for instance in res["Instances"]:
            if instance["State"]["Name"] == "running":
                running_instance_count += 1
                instance["Tags"].should.contain(
                    {"Key": "propagated-test-tag", "Value": "propagated-test-tag-value"}
                )
                tag_keys = [t["Key"] for t in instance["Tags"]]
                tag_keys.should_not.contain("not-propagated-test-tag")
    running_instance_count.should.equal(2)