# Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
#     http://aws.amazon.com/apache2.0e
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
from botocore.compat import OrderedDict

from awscli.customizations.cloudformation.deployer import Deployer
from awscli.customizations.cloudformation.yamlhelper import (
    yaml_dump,
    yaml_parse,
)
from tests.unit.customizations.cloudformation import BaseYAMLTest


class TestYaml(BaseYAMLTest):
    yaml_with_tags = """
    Resource:
        Key1: !Ref Something
        Key2: !GetAtt Another.Arn
        Key3: !FooBar [!Baz YetAnother, "hello"]
        Key4: !SomeTag {"a": "1"}
        Key5: !GetAtt OneMore.Outputs.Arn
        Key6: !Condition OtherCondition
    """

    parsed_yaml_dict = {
        "Resource": {
            "Key1": {"Ref": "Something"},
            "Key2": {"Fn::GetAtt": ["Another", "Arn"]},
            "Key3": {"Fn::FooBar": [{"Fn::Baz": "YetAnother"}, "hello"]},
            "Key4": {"Fn::SomeTag": {"a": "1"}},
            "Key5": {"Fn::GetAtt": ["OneMore", "Outputs.Arn"]},
            "Key6": {"Condition": "OtherCondition"},
        }
    }

    def test_yaml_with_tags(self):
        output = yaml_parse(self.yaml_with_tags)
        self.assertEqual(self.parsed_yaml_dict, output)

        # Make sure formatter and parser work well with each other
        formatted_str = yaml_dump(output)
        output_again = yaml_parse(formatted_str)
        self.assertEqual(output, output_again)

    def test_yaml_getatt(self):
        # This is an invalid syntax for !GetAtt. But make sure the code does
        # not crash when we encounter this syntax. Let CloudFormation
        # interpret this value at runtime
        yaml_input = """
        Resource:
            Key: !GetAtt ["a", "b"]
        """

        output = {"Resource": {"Key": {"Fn::GetAtt": ["a", "b"]}}}

        actual_output = yaml_parse(yaml_input)
        self.assertEqual(actual_output, output)

    def test_parse_json_with_tabs(self):
        template = '{\n\t"foo": "bar"\n}'
        output = yaml_parse(template)
        self.assertEqual(output, {'foo': 'bar'})

    def test_parse_json_preserve_elements_order(self):
        input_template = """
        {
            "B_Resource": {
                "Key2": {
                    "Name": "name2"
                },
                "Key1": {
                    "Name": "name1"
                }
            },
            "A_Resource": {
                "Key2": {
                    "Name": "name2"
                },
                "Key1": {
                    "Name": "name1"
                }
            }
        }
        """
        expected_dict = OrderedDict(
            [
                (
                    'B_Resource',
                    OrderedDict(
                        [
                            ('Key2', {'Name': 'name2'}),
                            ('Key1', {'Name': 'name1'}),
                        ]
                    ),
                ),
                (
                    'A_Resource',
                    OrderedDict(
                        [
                            ('Key2', {'Name': 'name2'}),
                            ('Key1', {'Name': 'name1'}),
                        ]
                    ),
                ),
            ]
        )
        output_dict = yaml_parse(input_template)
        self.assertEqual(expected_dict, output_dict)

    def test_parse_yaml_preserve_elements_order(self):
        input_template = (
            'B_Resource:\n'
            '  Key2:\n'
            '    Name: name2\n'
            '  Key1:\n'
            '    Name: name1\n'
            'A_Resource:\n'
            '  Key2:\n'
            '    Name: name2\n'
            '  Key1:\n'
            '    Name: name1\n'
        )
        output_dict = yaml_parse(input_template)
        expected_dict = OrderedDict(
            [
                (
                    'B_Resource',
                    OrderedDict(
                        [
                            ('Key2', {'Name': 'name2'}),
                            ('Key1', {'Name': 'name1'}),
                        ]
                    ),
                ),
                (
                    'A_Resource',
                    OrderedDict(
                        [
                            ('Key2', {'Name': 'name2'}),
                            ('Key1', {'Name': 'name1'}),
                        ]
                    ),
                ),
            ]
        )
        self.assertEqual(expected_dict, output_dict)

        output_template = yaml_dump(output_dict)
        self.assertEqual(input_template, output_template)

    def test_yaml_merge_tag(self):
        test_yaml = """
        base: &base
            property: value
        test:
            <<: *base
        """
        output = yaml_parse(test_yaml)
        self.assertTrue(isinstance(output, OrderedDict))
        self.assertEqual(output.get('test').get('property'), 'value')

    def test_unroll_yaml_anchors(self):
        properties = {
            "Foo": "bar",
            "Spam": "eggs",
        }
        template = {
            "Resources": {
                "Resource1": {"Properties": properties},
                "Resource2": {"Properties": properties},
            }
        }

        expected = (
            'Resources:\n'
            '  Resource1:\n'
            '    Properties:\n'
            '      Foo: bar\n'
            '      Spam: eggs\n'
            '  Resource2:\n'
            '    Properties:\n'
            '      Foo: bar\n'
            '      Spam: eggs\n'
        )
        actual = yaml_dump(template)
        self.assertEqual(actual, expected)

    def test_yaml_dump_quotes_boolean_strings(self):
        bools_as_strings = [
            'yes',
            'Yes',
            'YES',
            'no',
            'No',
            'NO',
            'true',
            'True',
            'TRUE',
            'false',
            'False',
            'FALSE',
            'on',
            'On',
            'ON',
            'off',
            'Off',
            'OFF',
        ]
        for bool_as_string in bools_as_strings:
            self.assertEqual(
                yaml_dump(bool_as_string), "'%s'\n" % bool_as_string
            )
