"""
Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: MIT-0
"""
from test.testlib.testcase import BaseTestCase
import cfnlint.decode.node  # pylint: disable=E0401


class TestNode(BaseTestCase):
    """Test Node Objects """

    def test_success_init(self):
        """Test Dict Object"""
        template = cfnlint.decode.node.dict_node({
            "test": "string"
        }, (0, 1), (2, 3))

        self.assertEqual(template, {"test": "string"})
        self.assertEqual(template.start_mark[0], 0)
        self.assertEqual(template.start_mark[1], 1)
        self.assertEqual(template.end_mark[0], 2)
        self.assertEqual(template.end_mark[1], 3)

    def test_success_fnif_object(self):
        """Test Dict Object"""
        template = cfnlint.decode.node.dict_node({
            "Fn::If": [
                "string",
                cfnlint.decode.node.dict_node({"Test": True}, (0, 1), (2, 3)),
                cfnlint.decode.node.dict_node({"Test": False}, (0, 1), (2, 3))]
        }, (0, 1), (2, 3))

        results = []
        for items, p in template.items_safe():
            results.append((items, p))

        self.assertEqual(results, [
            ({'Test': True}, ['Fn::If', 1]),
            ({'Test': False}, ['Fn::If', 2])
        ])

    def test_success_fnif_string(self):
        """Test Dict Object"""
        template = cfnlint.decode.node.dict_node({
            "Fn::If": [
                "string",
                "True",
                "False"]
        }, (0, 1), (2, 3))

        results = []
        for items, p in template.items_safe():
            results.append((items, p))

        self.assertEqual(results, [
            ('True', ['Fn::If', 1]),
            ('False', ['Fn::If', 2])
        ])

    def test_success_fnif_list(self):
        """Test List Object"""
        template = cfnlint.decode.node.dict_node({
            "Fn::If": [
                "string",
                ["1", "2", "3"],
                ["a", "b", "c"]]
        }, (0, 1), (2, 3))

        results = []
        for items, p in template.items_safe():
            results.append((items, p))

        self.assertEqual(results, [
            (['1', '2', '3'], ['Fn::If', 1]),
            (['a', 'b', 'c'], ['Fn::If', 2])
        ])

    def test_success_fnif_ref_novalue(self):
        """Test List Object"""
        template = cfnlint.decode.node.dict_node({
            "Fn::If": [
                "string",
                True,
                cfnlint.decode.node.dict_node({
                    "Ref": "AWS::NoValue"
                }, (0, 1), (2, 3))
            ]
        }, (0, 1), (2, 3))

        results = []
        for items, p in template.items_safe():
            results.append((items, p))

        self.assertEqual(results, [(True, ['Fn::If', 1])])

    def test_success_fnif_nested(self):
        """Test List Object"""
        template = cfnlint.decode.node.dict_node({
            "Fn::If": [
                "string",
                cfnlint.decode.node.dict_node({"Test": "True"}, (0, 1), (2, 3)),
                cfnlint.decode.node.dict_node({
                    "Fn::If": [
                        "nested",
                        cfnlint.decode.node.dict_node({"NestedTest": "False,True"}, (0, 1), (2, 3)),
                        False
                    ]
                }, (0, 1), (2, 3))
            ]
        }, (0, 1), (2, 3))

        results = []
        for items, p in template.items_safe(path=['Start']):
            results.append((items, p))

        # Adding testing of Paths to make sure nested paths are good
        self.assertEqual(results, [
            ({'Test': 'True'}, ['Start', 'Fn::If', 1]),
            ({'NestedTest': 'False,True'}, ['Start', 'Fn::If', 2, 'Fn::If', 1]),
            (False, ['Start', 'Fn::If', 2, 'Fn::If', 2])
        ])

        # Testing the filters based on type
        results = []
        for items, p in template.items_safe(type_t=(dict)):
            results.append((items, p))

        self.assertEqual(results, [
            ({'Test': 'True'}, ['Fn::If', 1]),
            ({'NestedTest': 'False,True'}, ['Fn::If', 2, 'Fn::If', 1])
        ])

    def test_success_fnif_get(self):
        """Test Dict Object"""
        template = cfnlint.decode.node.dict_node({
            "Test":
                cfnlint.decode.node.dict_node({
                    "Fn::If": [
                        "string",
                        "True",
                        "False"]
                }, (0, 1), (2, 3))
        }, (0, 1), (2, 3))

        obj = template.get_safe("Test")
        self.assertEqual(obj, [('True', ['Test', 'Fn::If', 1]), ('False', ['Test', 'Fn::If', 2])])

    def test_success_fnif_get_nested(self):
        """Test Dict Object"""
        template = cfnlint.decode.node.dict_node({
            "Test":
                cfnlint.decode.node.dict_node({
                    "Fn::If": [
                        "string",
                        cfnlint.decode.node.dict_node({
                            "Fn::If": [
                                "string",
                                "True,True",
                                "True,False"]
                        }, (0, 1), (2, 3)),
                        "False"]
                }, (0, 1), (2, 3))
        }, (0, 1), (2, 3))

        obj = template.get_safe("Test")
        self.assertEqual(obj, [
            ('True,True', ['Test', 'Fn::If', 1, 'Fn::If', 1]),
            ('True,False', ['Test', 'Fn::If', 1, 'Fn::If', 2]),
            ('False', ['Test', 'Fn::If', 2])
        ])

    def test_success_fnif_list_strings(self):
        """Test List Object"""
        template = cfnlint.decode.node.list_node([
            '1', '2', '3'
        ], (0, 1), (2, 3))

        results = []
        for v, p in template.items_safe():
            results.append((v, p))

        self.assertEqual(results, [('1', [0]), ('2', [1]), ('3', [2])])

    def test_success_fnif_list_conditions(self):
        """Test List Object"""
        template = cfnlint.decode.node.list_node([
            '1',
            '2',
            cfnlint.decode.node.dict_node({
                'Fn::If': [
                    'string',
                    '4',
                    '5']
            }, (0, 1), (2, 3)),
        ], (0, 1), (2, 3))

        results = []
        for v, p in template.items_safe():
            results.append((v, p))

        self.assertEqual(results, [('1', [0]), ('2', [1]),
                                   ('4', [2, 'Fn::If', 1]), ('5', [2, 'Fn::If', 2])])

    def test_success_fnif_list_conditions_no_value(self):
        """Test List Object"""
        template = cfnlint.decode.node.list_node([
            '1',
            '2',
            cfnlint.decode.node.dict_node({
                'Fn::If': [
                    'string',
                    '4',
                    cfnlint.decode.node.dict_node({
                        "Ref": "AWS::NoValue"
                    }, (0, 1), (2, 3))
                ]
            }, (0, 1), (2, 3)),
        ], (0, 1), (2, 3))

        results = []
        for v, p in template.items_safe():
            results.append((v, p))

        self.assertEqual(results, [('1', [0]), ('2', [1]), ('4', [2, 'Fn::If', 1])])

    def test_success_dict_select(self):
        """ Test if select could return an object """

        template = cfnlint.decode.node.dict_node({
            'Fn::Select': cfnlint.decode.node.list_node([
                0,
                cfnlint.decode.node.dict_node({
                    'Fn::FindInMap': ['mapping', 'key', 'value']
                }, (0, 1), (2, 3))
            ], (0, 1), (2, 3))
        }, (0, 1), (2, 3))

        self.assertTrue(template.is_function_returning_object())

    def test_failure_dict_select(self):
        """ Test if select could return an object """

        template = cfnlint.decode.node.dict_node({
            'Key': 'Value',
            'Fn::Select': cfnlint.decode.node.list_node([
                0,
                cfnlint.decode.node.dict_node({
                    'Fn::FindInMap': ['mapping', 'key', 'value']
                }, (0, 1), (2, 3))
            ], (0, 1), (2, 3))
        }, (0, 1), (2, 3))

        self.assertFalse(template.is_function_returning_object())

        template = cfnlint.decode.node.dict_node({
            'Key': 'Value',
            'Fn::Select': cfnlint.decode.node.list_node([
                0,
                cfnlint.decode.node.list_node([0, 1, 2], (0, 1), (2, 3))
            ], (0, 1), (2, 3))
        }, (0, 1), (2, 3))

        self.assertFalse(template.is_function_returning_object())
