import json

import boto3
import botocore
import pytest

from moto import mock_aws
from tests import EXAMPLE_AMI_ID, aws_verified

json_template = {
    "AWSTemplateFormatVersion": "2010-09-09",
    "Description": "Stack 1",
    "Resources": {
        "EC2Instance1": {
            "Type": "AWS::EC2::Instance",
            "Properties": {
                "ImageId": EXAMPLE_AMI_ID,
                "KeyName": "dummy",
                "InstanceType": "t2.micro",
                "Tags": [
                    {"Key": "Description", "Value": "Test tag"},
                    {"Key": "Name", "Value": "Name tag for tests"},
                ],
            },
        }
    },
}

json_valid_template_with_tabs = """
{
\t"AWSTemplateFormatVersion": "2010-09-09",
\t"Description": "Stack 2",
\t"Resources": {
\t\t"Queue": {"Type": "AWS::SQS::Queue", "Properties": {"VisibilityTimeout": 60}}
\t}
}
"""

# One resource is required
json_bad_template = {"AWSTemplateFormatVersion": "2010-09-09", "Description": "Stack 1"}

dummy_template_json = json.dumps(json_template)
dummy_bad_template_json = json.dumps(json_bad_template)


@mock_aws
def test_json_validate_successful():
    cf_conn = boto3.client("cloudformation", region_name="us-east-1")
    response = cf_conn.validate_template(TemplateBody=dummy_template_json)
    assert response["Description"] == "Stack 1"
    assert response["Parameters"] == []
    assert response["ResponseMetadata"]["HTTPStatusCode"] == 200


@mock_aws
def test_json_with_tabs_validate_successful():
    cf_conn = boto3.client("cloudformation", region_name="us-east-1")
    response = cf_conn.validate_template(TemplateBody=json_valid_template_with_tabs)
    assert response["Description"] == "Stack 2"
    assert response["Parameters"] == []
    assert response["ResponseMetadata"]["HTTPStatusCode"] == 200


@mock_aws
def test_json_invalid_missing_resource():
    cf_conn = boto3.client("cloudformation", region_name="us-east-1")
    with pytest.raises(botocore.exceptions.ClientError) as exc:
        cf_conn.validate_template(TemplateBody=dummy_bad_template_json)
    err = exc.value.response["Error"]
    assert (
        err["Message"]
        in (
            "Stack with id Missing top level template section Resources does not exist",  # cfn-lint 0.x
            "Stack with id 'Resources' is a required property does not exist",  # cfn-lint 1.x
        )
    )


yaml_template = """
    AWSTemplateFormatVersion: '2010-09-09'
    Description: Simple CloudFormation Test Template
    Resources:
      S3Bucket:
        Type: AWS::S3::Bucket
        Properties:
          BucketName: cf-test-bucket-1
"""

yaml_bad_template = """
    AWSTemplateFormatVersion: '2010-09-09'
    Description: Simple CloudFormation Test Template
"""


@mock_aws
def test_yaml_validate_successful():
    cf_conn = boto3.client("cloudformation", region_name="us-east-1")
    response = cf_conn.validate_template(TemplateBody=yaml_template)
    assert response["Description"] == "Simple CloudFormation Test Template"
    assert response["Parameters"] == []
    assert response["ResponseMetadata"]["HTTPStatusCode"] == 200


@mock_aws
def test_yaml_validate_template_url_successful():
    s3 = boto3.client("s3", region_name="us-east-1")
    s3_conn = boto3.resource("s3", region_name="us-east-1")
    s3_conn.create_bucket(Bucket="foobar")

    s3_conn.Object("foobar", "template-key").put(Body=yaml_template)
    key_url = s3.generate_presigned_url(
        ClientMethod="get_object", Params={"Bucket": "foobar", "Key": "template-key"}
    )

    cf_conn = boto3.client("cloudformation", region_name="us-east-1")
    response = cf_conn.validate_template(TemplateURL=key_url)
    assert response["Description"] == "Simple CloudFormation Test Template"
    assert response["Parameters"] == []
    assert response["ResponseMetadata"]["HTTPStatusCode"] == 200


@mock_aws
def test_yaml_invalid_missing_resource():
    cf_conn = boto3.client("cloudformation", region_name="us-east-1")
    with pytest.raises(botocore.exceptions.ClientError) as exc:
        cf_conn.validate_template(TemplateBody=yaml_bad_template)
    err = exc.value.response["Error"]
    assert (
        err["Message"]
        in (
            "Stack with id Missing top level template section Resources does not exist",  # cfn-lint 0.x
            "Stack with id 'Resources' is a required property does not exist",  # cfn-lint 1.x
        )
    )


@aws_verified
@pytest.mark.aws_verified
def test_validate_yaml_using_short_func_refs():
    """Template using !Sub as a key, which is not valid YAML"""
    cf_client = boto3.client("cloudformation", region_name="us-east-1")

    template = """AWSTemplateFormatVersion: '2010-09-09'
Description: Test template
Parameters:
  BucketName:
    Type: String
Resources:
  TestBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName:
        !Sub "${AWS::StackName}-${BucketName}"
Outputs:
  BucketName:
    Value:
      !Sub "${TestBucket}"
"""

    response = cf_client.validate_template(TemplateBody=template)
    assert response["ResponseMetadata"]["HTTPStatusCode"] == 200
