import os
import pytest
from calculate.templates.template_engine import TemplateEngine, Variables,\
                                                ConditionFailed, DIR, FILE,\
                                                TemplateSyntaxError
from calculate.templates.template_processor import TemplateExecutor


CHROOT_PATH = os.path.join(os.getcwd(), 'tests/templates/testfiles/test_template_engine_root')

APPENDS_SET = TemplateExecutor(cl_config_path=os.path.join(
                                          CHROOT_PATH,
                                          'test_root/var/lib/calculate/config')
                               ).available_appends


@pytest.mark.template_engine
class TestTemplateEngine():
    def test_if_an_input_template_contains_calculate_tag_with_some_parameters__the_template_engine_object_will_collect_its_parameters(self):
        input_template = '''{% calculate name = 'filename', path = '/etc/path', force %}'''
        template_engine = TemplateEngine(appends_set=APPENDS_SET)
        template_engine.process_template_from_string(input_template, DIR)

        output_parameters = template_engine.parameters
        assert (output_parameters.name == 'filename' and
                output_parameters.path == '/etc/path' and
                output_parameters.force)

    def test_if_an_input_template_contains_more_than_one_calculate_tag__the_template_engine_raises_the_TemplateSyntaxError_exception(self):
        input_template = '''{% calculate name = 'filename' %}
                            {% calculate path = '/etc/path'%}
                            {% calculate force %}'''
        template_engine = TemplateEngine(appends_set=APPENDS_SET)
        with pytest.raises(TemplateSyntaxError):
            template_engine.process_template_from_string(input_template, DIR)

    def test_if_an_input_template_binded_with_datavars_module__variables_available_in_a_template(self):
        input_template = '''{% calculate name = vars.var_1, path = vars.var_2, autoupdate %}'''
        datavars_module = Variables({'vars':
                                     Variables({'var_1': 'filename',
                                                'var_2': '/etc/path'})})
        template_engine = TemplateEngine(appends_set=APPENDS_SET,
                                         datavars_module=datavars_module)
        template_engine.process_template_from_string(input_template, FILE)

        output_parameters = template_engine.parameters
        assert (output_parameters.name == 'filename' and
                output_parameters.path == '/etc/path' and
                output_parameters.autoupdate)

    def test_if_an_input_template_contains_env_parameter_in_which_module_name_is_assigned__the_variables_from_this_module_can_be_used_in_template_without_determining_of_their_module(self):
        input_template = '''{% calculate name = vars.var_1,
                               env = 'other_vars', path = var_3 %}'''
        datavars_module = Variables({'vars':
                                     Variables({'var_1': 'filename',
                                                'var_2': '/etc/path'}),
                                     'other_vars':
                                     Variables({'var_3': '/etc/other_path'})})
        template_engine = TemplateEngine(appends_set=APPENDS_SET,
                                         datavars_module=datavars_module)
        template_engine.process_template_from_string(input_template, DIR)

        output_parameters = template_engine.parameters
        assert (output_parameters.name == 'filename' and
                output_parameters.path == '/etc/other_path' and
                output_parameters.env == {datavars_module['other_vars']})

    def test_if_an_input_template_contains_condition_and_it_is_True__the_template_engine_object_will_be_initialized_without_any_exceptions(self):
        input_template = '''{% calculate vars.var_1 < vars.var_2 or (not (var_3 == 'required status') and vars.var_4), env = 'vars' %}'''
        datavars_module = Variables({'vars':
                                     Variables({'var_1': 12,
                                                'var_2': 1.2,
                                                'var_3': 'unrequired status',
                                                'var_4': True})})
        template_engine = TemplateEngine(appends_set=APPENDS_SET,
                                         datavars_module=datavars_module)

        try:
            template_engine.process_template_from_string(input_template, FILE)
        except ConditionFailed:
            pytest.fail('Unexpected ConditionFailed exception.')

    def test_if_an_input_template_contains_several_conditions_and_it_is_False__the_template_engine_raises_ConditionFailed_exception(self):
        input_template = '''{% calculate name = vars.var_1, env = 'other_vars',
                            var_4 < var_5, path = var_3, var_6 == 'value' %}'''

        datavars_module = Variables({'vars':
                                     Variables({'var_1': 'filename',
                                                'var_2': '/etc/path'}),
                                     'other_vars':
                                     Variables({'var_3': '/etc/other_path',
                                                'var_4': 12, 'var_5': 1.2,
                                                'var_6': 'value'})})
        template_engine = TemplateEngine(appends_set=APPENDS_SET,
                                         datavars_module=datavars_module)

        with pytest.raises(ConditionFailed):
            template_engine.process_template_from_string(input_template, DIR)

    def test_if_an_input_template_contains_several_calculate_tags__the_template_engine_will_parse_them_all_and_will_contain_all_parameters_and_result_of_all_conditions(self):
        input_template = '''{% calculate name = vars.var_1,
                            env = 'other_vars, vars', var_4 > var_5,
                            path = var_3, var_6 == 'value' %}'''
        datavars_module = Variables({'vars':
                                     Variables({'var_1': 'filename',
                                                'var_2': '/etc/path'}),
                                     'other_vars':
                                     Variables({'var_3': '/etc/other_path',
                                                'var_4': 12, 'var_5': 1.2,
                                                'var_6': 'value'})})
        template_engine = TemplateEngine(appends_set=APPENDS_SET,
                                         datavars_module=datavars_module)
        template_engine.process_template_from_string(input_template, FILE)

        output_parameters = template_engine.parameters
        assert (output_parameters.name == 'filename' and
                output_parameters.path == '/etc/other_path' and
                output_parameters.env == {datavars_module['other_vars'],
                                          datavars_module['vars']})

    def test_if_an_input_template_contains_variables_in_its_text__the_rendered_text_will_contain_values_of_this_variables(self):
        input_template = '''{% calculate name = 'filename', force,
        env = 'vars.vars_1' -%}
        parameter_1 = {{ vars_2.var_3 }}
        parameter_2 = {{ vars_2.var_4 }}
        parameter_3 = {{ var_1 }}'''
        output_text = '''parameter_1 = value_1
        parameter_2 = value_2
        parameter_3 = 12'''
        datavars_module = Variables({'vars_2':
                                     Variables({'var_3': 'value_1',
                                                'var_4': 'value_2'}),
                                     'vars':
                                     Variables({'vars_1':
                                                Variables({'var_1': 12,
                                                           'var_2': 1.2})})})
        template_engine = TemplateEngine(appends_set=APPENDS_SET,
                                         datavars_module=datavars_module)
        template_engine.process_template_from_string(input_template, DIR)

        text = template_engine.template_text
        assert text == output_text

    def test_if_an_input_template_contains_pkg_function_with_existing_package_as_its_argument__it_works_correctly_and_pkg_function_returns_version_value(self):
        input_template = '''{% calculate name = 'filename', force -%}
        {% if pkg('test-category/test-package') < 2.3 -%}
        pkg() works correctly.
        {%- else -%}
        pkg() does not work correctly.
        {%- endif -%}'''
        output_text = 'pkg() works correctly.'
        template_engine = TemplateEngine(appends_set=APPENDS_SET,
                                         chroot_path=CHROOT_PATH)
        template_engine.process_template_from_string(input_template, FILE)

        text = template_engine.template_text
        assert text == output_text

    def test_if_an_input_template_contains_pkg_function_with_package_that_does_not_exist_in_its_argument__it_works_correctly_and_pkg_function_returns_empty_version_value(self):
        input_template = '''{% calculate name = 'filename', force -%}
        {% if pkg() == Version() -%}
        pkg() works correctly.
        {%- else -%}
        pkg() does not work correctly.
        {%- endif -%}'''
        output_text = 'pkg() works correctly.'
        template_engine = TemplateEngine(appends_set=APPENDS_SET,
                                         chroot_path=CHROOT_PATH)
        template_engine.process_template_from_string(input_template, FILE)

        text = template_engine.template_text
        assert text == output_text

    def test_if_an_input_template_contains_pkg_function_without_any_arguments_but_with_package_parameter_in_calculate_tag__the_pkg_function_returns_version_value_of_the_package_from_package_parameter(self):
        input_template = '''{% calculate name = 'filename',
        package='test-category/test-package', force -%}
        {% if pkg() < 2.7 -%}
        pkg() works correctly.
        {%- else -%}
        pkg() does not work correctly.
        {%- endif -%}'''
        output_text = 'pkg() works correctly.'
        template_engine = TemplateEngine(appends_set=APPENDS_SET,
                                         chroot_path=CHROOT_PATH)
        template_engine.process_template_from_string(input_template, FILE)

        text = template_engine.template_text
        assert text == output_text

    def test_if_an_input_template_contains_pkg_function_without_any_arguments_and_without_package_parameter_in_calculate_tag__the_pkg_function_returns_empty_version_value(self):
        input_template = '''{% calculate name = 'filename', force -%}
        {% if pkg() == Version() -%}
        pkg() works correctly.
        {%- else -%}
        pkg() does not work correctly.
        {%- endif -%}'''
        output_text = 'pkg() works correctly.'
        template_engine = TemplateEngine(appends_set=APPENDS_SET,
                                         chroot_path=CHROOT_PATH)
        template_engine.process_template_from_string(input_template, FILE)

        text = template_engine.template_text
        assert text == output_text

    def test_if_an_input_template_calculate_tag_contains_pkg_function_with_an_existing_package_in_its_argument__the_pkg_function_returns_version_value_of_the_package_from_package_parameter_without_any_exceptions(self):
        input_template = '''{% calculate name = 'filename',
        package='test-category/test-package', force, pkg() < 2.7 -%}'''

        try:
            template_engine = TemplateEngine(appends_set=APPENDS_SET,
                                             chroot_path=CHROOT_PATH)
            template_engine.process_template_from_string(input_template, FILE)
        except ConditionFailed:
            pytest.fail('Unexpected ConditionFailed exception.')

    def test_two_template_engines(self):
        input_template_1 = '''{% calculate name = 'filename', force -%}
parameter_1 = {{ vars_1.var_1 }}'''
        input_template_2 = '''{% calculate name = 'filename', force -%}
parameter_2 = {{ vars_1.var_1 }}'''

        output_text_1 = '''parameter_1 = first'''
        output_text_2 = '''parameter_2 = second'''

        datavars_module_1 = Variables({'vars_1':
                                       Variables({'var_1': 'first'})})
        datavars_module_2 = Variables({'vars_1':
                                       Variables({'var_1': 'second'})})

        template_engine_1 = TemplateEngine(appends_set=APPENDS_SET,
                                           datavars_module=datavars_module_1,
                                           chroot_path=CHROOT_PATH)
        template_engine_1.process_template_from_string(input_template_1, DIR)

        template_engine_2 = TemplateEngine(appends_set=APPENDS_SET,
                                           datavars_module=datavars_module_2,
                                           chroot_path=CHROOT_PATH)
        template_engine_2.process_template_from_string(input_template_2, DIR)

        text_1 = template_engine_1.template_text
        text_2 = template_engine_2.template_text

        assert text_1 == output_text_1
        assert text_2 == output_text_2

    def test_pkg_with_vars(self):
        datavars_module = Variables({'vars_1':
                                     Variables({'var_1':
                                                'test-category/test-package',
                                                'var_2': 1.2})})
        input_template = '''{% calculate name = 'filename', force -%}
        {% if pkg(vars_1.var_1) < 4.1 -%}
        pkg() works correctly.
        {%- else -%}
        pkg() does not work correctly.
        {%- endif -%}'''
        output_text = 'pkg() works correctly.'
        template_engine = TemplateEngine(appends_set=APPENDS_SET,
                                         datavars_module=datavars_module,
                                         chroot_path=CHROOT_PATH)
        template_engine.process_template_from_string(input_template, FILE)

        text = template_engine.template_text
        assert text == output_text

    @pytest.mark.parametrize('case',
        [
            {
                "name": "pattern not found",
                "grep": "'/test_grep', 'QWERTY'",
                "result": ""
            },
            {
                "name": "file not found",
                "grep": "'/test_grep_not_found', 'QWERTY'",
                "result": ""
            },
            {
                "name": "pattern found",
                "grep": "'/test_grep', 'NOSTATUSLINE=true'",
                "result": "NOSTATUSLINE=true"
            },
            {
                "name": "regexp found",
                "grep": "'/test_grep', 'NOSTATUS.*'",
                "result": "NOSTATUSLINE=true"
            },
            {
                "name": "group regexp found",
                "grep": "'/test_grep', 'NOSTATUSLINE=(.*)'",
                "result": "true"
            },
    ], 
    ids=lambda x:x["name"])
    def test_grep(self, case):
        datavars_module = Variables({'vars_1':
                                     Variables({'var_1':
                                                'test-category/test-package',
                                                'var_2': 1.2})})
        input_template = '''{% calculate name = 'filename', force -%}
        {{ grep(''' + case['grep'] + ''') }}'''
        output_text = case["result"]
        template_engine = TemplateEngine(appends_set=APPENDS_SET,
                                         datavars_module=datavars_module,
                                         chroot_path=CHROOT_PATH)
        template_engine.process_template_from_string(input_template, FILE)

        text = template_engine.template_text
        assert text == output_text
