"""Tests for runway.cfngin.blueprints.raw."""
import json
import unittest

from mock import MagicMock

from runway.cfngin.blueprints.raw import (
    RawTemplateBlueprint,
    get_template_params,
    get_template_path,
)
from runway.variables import Variable

from ..factories import mock_context

RAW_JSON_TEMPLATE_PATH = "tests/unit/cfngin/fixtures/cfn_template.json"
RAW_YAML_TEMPLATE_PATH = "tests/unit/cfngin/fixtures/cfn_template.yaml"
RAW_J2_TEMPLATE_PATH = "tests/unit/cfngin/fixtures/cfn_template.json.j2"


def test_get_template_path_local_file(tmpdir):
    """Verify get_template_path finding a file relative to CWD."""
    template_path = tmpdir.join("cfn_template.json")
    template_path.ensure()

    with tmpdir.as_cwd():
        result = get_template_path("cfn_template.json")
        assert template_path.samefile(result)


def test_get_template_path_invalid_file(tmpdir):
    """Verify get_template_path with an invalid filename."""
    with tmpdir.as_cwd():
        assert get_template_path("cfn_template.json") is None


def test_get_template_path_file_in_syspath(tmpdir, monkeypatch):
    """Verify get_template_path with a file in sys.path.

    This ensures templates are able to be retrieved from remote packages.

    """
    template_path = tmpdir.join("cfn_template.json")
    template_path.ensure()

    monkeypatch.syspath_prepend(tmpdir)
    result = get_template_path(template_path.basename)
    assert template_path.samefile(result)


def test_get_template_params():
    """Verify get_template_params function operation."""
    template_dict = {
        "AWSTemplateFormatVersion": "2010-09-09",
        "Description": "TestTemplate",
        "Parameters": {
            "Param1": {"Type": "String"},
            "Param2": {"Default": "default", "Type": "CommaDelimitedList"},
        },
        "Resources": {},
    }
    template_params = {
        "Param1": {"Type": "String"},
        "Param2": {"Default": "default", "Type": "CommaDelimitedList"},
    }

    assert get_template_params(template_dict) == template_params


class TestBlueprintRendering(unittest.TestCase):
    """Test class for blueprint rendering."""

    def test_to_json(self):
        """Verify to_json method operation."""
        expected_json = json.dumps(
            {
                "AWSTemplateFormatVersion": "2010-09-09",
                "Description": "TestTemplate",
                "Parameters": {
                    "Param1": {"Type": "String"},
                    "Param2": {"Default": "default", "Type": "CommaDelimitedList"},
                },
                "Resources": {
                    "Dummy": {"Type": "AWS::CloudFormation::WaitConditionHandle"}
                },
                "Outputs": {"DummyId": {"Value": "dummy-1234"}},
            },
            sort_keys=True,
            indent=4,
        )
        self.assertEqual(
            RawTemplateBlueprint(
                name="test",
                context=mock_context(),
                raw_template_path=RAW_JSON_TEMPLATE_PATH,
            ).to_json(),
            expected_json,
        )

    def test_j2_to_json(self):
        """Verify jinja2 template parsing."""
        expected_json = json.dumps(
            {
                "AWSTemplateFormatVersion": "2010-09-09",
                "Description": "TestTemplate",
                "Parameters": {
                    "Param1": {"Type": "String"},
                    "Param2": {"Default": "default", "Type": "CommaDelimitedList"},
                },
                "Resources": {
                    "Dummy": {"Type": "AWS::CloudFormation::WaitConditionHandle"}
                },
                "Outputs": {"DummyId": {"Value": "dummy-bar-param1val-foo-1234"}},
            },
            sort_keys=True,
            indent=4,
        )
        blueprint = RawTemplateBlueprint(
            name="stack1",
            context=mock_context(
                extra_config_args={
                    "stacks": [
                        {
                            "name": "stack1",
                            "template_path": "unused",
                            "variables": {"Param1": "param1val", "bar": "foo"},
                        }
                    ]
                },
                environment={"foo": "bar"},
            ),
            raw_template_path=RAW_J2_TEMPLATE_PATH,
        )
        blueprint.resolve_variables(
            [
                Variable("Param1", "param1val", "cfngin"),
                Variable("bar", "foo", "cfngin"),
            ]
        )
        self.assertEqual(expected_json, blueprint.to_json())


class TestVariables(unittest.TestCase):
    """Test class for blueprint variable methods."""

    def test_get_parameter_definitions_json(self):
        """Verify get_parameter_definitions method with json raw template."""
        blueprint = RawTemplateBlueprint(
            name="test", context=MagicMock(), raw_template_path=RAW_JSON_TEMPLATE_PATH
        )
        parameters = blueprint.get_parameter_definitions()
        self.assertEqual(
            parameters,
            {
                "Param1": {"Type": "String"},
                "Param2": {"Default": "default", "Type": "CommaDelimitedList"},
            },
        )

    def test_get_parameter_definitions_yaml(self):
        """Verify get_parameter_definitions method with yaml raw template."""
        blueprint = RawTemplateBlueprint(
            name="test", context=MagicMock(), raw_template_path=RAW_YAML_TEMPLATE_PATH
        )
        parameters = blueprint.get_parameter_definitions()
        self.assertEqual(
            parameters,
            {
                "Param1": {"Type": "String"},
                "Param2": {"Default": "default", "Type": "CommaDelimitedList"},
            },
        )

    def test_get_required_parameter_definitions_json(self,):
        """Verify get_required_param... method with json raw template."""
        blueprint = RawTemplateBlueprint(
            name="test", context=MagicMock(), raw_template_path=RAW_JSON_TEMPLATE_PATH
        )
        self.assertEqual(
            blueprint.get_required_parameter_definitions(),
            {"Param1": {"Type": "String"}},
        )

    def test_get_required_parameter_definitions_yaml(self,):
        """Verify get_required_param... method with yaml raw template."""
        blueprint = RawTemplateBlueprint(
            name="test", context=MagicMock(), raw_template_path=RAW_YAML_TEMPLATE_PATH
        )
        self.assertEqual(
            blueprint.get_required_parameter_definitions(),
            {"Param1": {"Type": "String"}},
        )
