from unittest import TestCase

from parameterized import parameterized
from samtranslator.policy_template_processor.processor import PolicyTemplatesProcessor


class TestTemplates:
    """
    Write your test cases here as different variables that store the entire template file. Start the variable with
    "succeed" if you want to test success case, "fail" if you want to test failure cases. The test runner will know
    to test for the right expectations

    Just trying out a BDDish test runner
    """

    succeed_with_no_template = {"Version": "1.2.3", "Templates": {}}

    succeed_with_single_statement = {
        "Version": "1.0.0",
        "Templates": {
            "ManagedPolicy1Policy": {
                "Description": "Very first managed policy",
                "Parameters": {"Param1": {"Description": "some desc"}, "Param2": {"Description": "some desc"}},
                "Definition": {"Statement": [{"key": "value"}]},
            }
        },
    }

    succeed_with_multiple_templates = {
        "Version": "1.0.0",
        "Templates": {
            "ManagedPolicy1Policy": {
                "Description": "Very first managed policy",
                "Parameters": {"Param1": {"Description": "some desc"}, "Param2": {"Description": "some desc"}},
                "Definition": {"Statement": [{"key": "value"}, {"otherkey": "othervalue"}]},
            },
            "ManagedPolicy2Policy": {
                "Description": "Second managed policy",
                "Parameters": {"1stParam": {"Description": "some desc"}},
                "Definition": {"Statement": [{"key": "value"}]},
            },
        },
    }

    fail_for_template_with_no_version = {"Templates": {}}

    fail_without_template = {"Version": "1.2.3"}

    fail_with_non_semantic_version = {"Version": "version1", "Templates": {}}

    fail_without_all_three_parts_of_semver = {
        # Yes! you need all three parts
        "Version": "1.0",
        "Templates": {},
    }

    fail_with_additional_properties = {"Version": "1.2.3", "Templates": {}, "Something": "value"}

    fail_for_bad_template_name = {
        "Version": "1.0.0",
        "Templates": {
            # Names must have the suffix "Policy"
            "ThisNameDoesNotHaveTheSuffix": {"Parameters": {}, "Definition": {"Statement": [{"key": "value"}]}}
        },
    }

    fail_for_template_without_parameters = {
        "Version": "1.0.0",
        "Templates": {"MyTemplate": {"Definition": {"Statement": [{"key": "value"}]}}},
    }

    fail_for_template_without_definition = {"Version": "1.0.0", "Templates": {"MyTemplate": {"Parameters": {}}}}

    fail_for_template_with_empty_definition = {
        "Version": "1.0.0",
        "Templates": {"MyTemplate": {"Parameters": {}, "Definition": {}}},
    }

    fail_for_parameter_with_no_description = {
        "Version": "1.0.0",
        "Templates": {"MyTemplate": {"Parameters": {"Param1": {}}, "Definition": {"Statement": [{"key": "value"}]}}},
    }

    fail_for_parameter_name_with_underscores = {
        "Version": "1.0.0",
        "Templates": {
            "MyTemplate": {
                "Parameters": {
                    # Underscores are not allowed. Following CFN naming convention here
                    "invalid_name": {"Description": "value"}
                },
                "Definition": {"Statement": [{"key": "value"}]},
            }
        },
    }

    fail_for_definition_is_an_array = {
        "Version": "1.0.0",
        "Templates": {
            "MyTemplate": {
                "Parameters": {"Param1": {"Description": "value"}},
                # Definition must be a direct statement object. This is not allowed
                "Definition": [{"Statement": [{"key": "value"}]}],
            }
        },
    }


class TestPolicyTemplateSchema(TestCase):
    """
    Some basic test cases to validate that the JSON Schema representing policy templates actually work as intended
    """

    # Grab all variables of the class TestTemplates
    @parameterized.expand([d for d in dir(TestTemplates) if not d.startswith("__")])
    def test_schema(self, case):
        failure_case = case.startswith("fail")
        template = getattr(TestTemplates, case)

        if failure_case:
            with self.assertRaises(ValueError):
                PolicyTemplatesProcessor._is_valid_templates_dict(template)

        else:
            # Success case
            self.assertTrue(PolicyTemplatesProcessor._is_valid_templates_dict(template))
