import pytest
from collections import OrderedDict
from calculate.templates.format.bind_format import BINDFormat


@pytest.mark.bind
class TestParsingMethods:
    def test_if_input_document_contains_just_few_parameter_lines__the_initialised_object_contains_correct_dictionary(self):
        document_text = '''directory "/var/bind";
pid-file "/run/named/named.pid";
disable-empty-zone "10.in-addr.arpa";
        '''

        result = OrderedDict({
                                  ('', 'directory'):
                                  ['"/var/bind"'],
                                  ('', 'pid-file'):
                                  ['"/run/named/named.pid"'],
                                  ('', 'disable-empty-zone'):
                                  ['"10.in-addr.arpa"']
                             })

        bind_object = BINDFormat(document_text, '/path/to/template')
        assert bind_object._document_dictionary == result

    def test_if_input_document_contains_some_block_of_parameters__the_initialised_object_contains_correct_dictionary(self):
        document_text = '''
acl "dns_servers" {
    127.0.0.1;
    10.0.1.3;
    10.1.0.3;
};

options {
    response-policy {
            zone "rpz.zone";
    };
    recursion yes;
};

zone "localhost" IN {
    type master;
    file "pri/localhost.zone";
    notify no;
}
        '''

        acl_section = OrderedDict({('', '127.0.0.1'): [''],
                                   ('', '10.0.1.3'): [''],
                                   ('', '10.1.0.3'): ['']})
        response_section = OrderedDict({('', 'zone'): ['"rpz.zone"']})
        options_section = OrderedDict({('', 'response-policy'): response_section,
                                       ('', 'recursion'): ['yes']})
        zone_section = OrderedDict({('', 'type'): ['master'],
                                    ('', 'file'): ['"pri/localhost.zone"'],
                                    ('', 'notify'): ['no']})
        result = OrderedDict({('', 'acl', '"dns_servers"'): acl_section,
                              ('', 'options'): options_section,
                              ('', 'zone', '"localhost"', 'IN'): zone_section})

        bind_object = BINDFormat(document_text, '/path/to/template')
        assert bind_object._document_dictionary == result

    def test_if_input_document_contains_some_blocks_with_similar_names__the_blocks_join_recursively(self):
        document_text = '''
acl "dns_servers" {
    127.0.0.1;
    10.0.1.3;
    10.1.0.3;
};

options {
    response-policy {
            mood "almost.blue";
    };
    todo "drink.beer"
};

acl "dns_servers" {
    10.3.0.3;
    10.4.0.3;
};

options {
    response-policy {
            zone "rpz.zone";
    };
}
'''
        acl_section = OrderedDict({('', '127.0.0.1'): [''],
                                   ('', '10.0.1.3'): [''],
                                   ('', '10.1.0.3'): [''],
                                   ('', '10.3.0.3'): [''],
                                   ('', '10.4.0.3'): ['']})
        response_section = OrderedDict({('', 'mood'): ['"almost.blue"'],
                                        ('', 'zone'): ['"rpz.zone"']})
        options_section = OrderedDict({('', 'response-policy'):
                                       response_section,
                                       ('', 'todo'): ['"drink.beer"']})

        result = OrderedDict({('', 'acl', '"dns_servers"'): acl_section,
                              ('', 'options'): options_section})

        bind_object = BINDFormat(document_text, '/path/to/template')
        assert bind_object._document_dictionary == result

    def test_if_input_document_contains_blocks_and_parameters_with_action_marks__the_key_tuples_of_object_s_dictionary_have_it_as_its_first_element(self):
        document_text = '''
!pid-file "/run/named/named.pid";
-disable-empty-zone "10.in-addr.arpa";

acl "dns_servers" {
    !127.0.0.1;
    10.0.1.3;
    10.1.0.3;
};

-options {
    !response-policy {
            zone "rpz.zone";
    };
    !recursion yes;
}
'''
        acl_section = OrderedDict({('!', '127.0.0.1'): [''],
                                   ('', '10.0.1.3'): [''],
                                   ('', '10.1.0.3'): ['']})
        response_section = OrderedDict({('', 'zone'): ['"rpz.zone"']})
        options_section = OrderedDict({('!', 'response-policy'):
                                      response_section,
                                      ('!', 'recursion'): ['yes']})

        result = OrderedDict({('!', 'pid-file'):
                              ['"/run/named/named.pid"'],
                              ('-', 'disable-empty-zone'):
                              ['"10.in-addr.arpa"'],
                              ('', 'acl', '"dns_servers"'): acl_section,
                              ('-', 'options'): options_section})

        bind_object = BINDFormat(document_text, '/path/to/template')
        assert bind_object._document_dictionary == result

    def test_if_parameters_and_blocks_in_input_document_has_some_comments__the_comments_will_be_collected_in_the_list_of_parameter_value_or_with_special_key_in_block_dictionary(self):
        document_text = '''
// Comment 1
!pid-file "/run/named/named.pid";
/*
* A very big comment.
* Still here...
* The pure giant of the comment kind.
*/
-disable-empty-zone "10.in-addr.arpa";

# Comment 2
// Comment 3
acl "dns_servers" {
    !127.0.0.1;
    // Comment 4
    10.0.1.3;
    10.1.0.3;
};

-options {
    !response-policy {
            /*
            * This comment is very important.
            * And I have no idea, why this
            * comment is so important.
            */
            zone "rpz.zone";
    };
    !recursion yes;
}
        '''

        acl_section = OrderedDict({'#': ['# Comment 2', '// Comment 3'],
                                   ('!', '127.0.0.1'): [''],
                                   ('', '10.0.1.3'): ['// Comment 4', ''],
                                   ('', '10.1.0.3'): ['']})
        response_section = OrderedDict({('', 'zone'):
                                        ['/*',
                                         '* This comment is very important.',
                                         '* And I have no idea, why this',
                                         '* comment is so important.',
                                         '*/',
                                         '"rpz.zone"']})
        options_section = OrderedDict({('!', 'response-policy'):
                                       response_section,
                                       ('!', 'recursion'): ['yes']})

        result = OrderedDict({('!', 'pid-file'):
                              ['// Comment 1',
                               '"/run/named/named.pid"'],
                              ('-', 'disable-empty-zone'):
                              ['/*',
                               '* A very big comment.',
                               '* Still here...',
                               '* The pure giant of the comment kind.',
                               '*/',
                               '"10.in-addr.arpa"'],
                              ('', 'acl', '"dns_servers"'): acl_section,
                              ('-', 'options'): options_section})

        bind_object = BINDFormat(document_text, '/path/to/template')
        assert bind_object._document_dictionary == result

    def test_if_the_IgnoreComments_flag_is_set__the_parser_ignores_all_comments(self):
        document_text = '''
// Comment 1
!pid-file "/run/named/named.pid";
/*
* A very big comment.
* Still here...
* The pure giant of comment kind.
*/
-disable-empty-zone "10.in-addr.arpa";

# Comment 2
// Comment 3
acl "dns_servers" {
    !127.0.0.1;
    // Comment 4
    10.0.1.3;
    10.1.0.3;
};

-options {
    !response-policy {
            /*
            * This comment is very important.
            * And I have no idea, why this
            * comment is so important.
            */
            zone "rpz.zone";
    };
    !recursion yes;
}
        '''

        acl_section = OrderedDict({('!', '127.0.0.1'): [''],
                                   ('', '10.0.1.3'): [''],
                                   ('', '10.1.0.3'): ['']})
        response_section = OrderedDict({('', 'zone'): ['"rpz.zone"']})
        options_section = OrderedDict({('!', 'response-policy'):
                                       response_section,
                                       ('!', 'recursion'): ['yes']})

        result = OrderedDict({('!', 'pid-file'):
                              ['"/run/named/named.pid"'],
                              ('-', 'disable-empty-zone'):
                              ['"10.in-addr.arpa"'],
                              ('', 'acl', '"dns_servers"'): acl_section,
                              ('-', 'options'): options_section})

        bind_object = BINDFormat(document_text, '/path/to/template',
                                 ignore_comments=True)
        assert bind_object._document_dictionary == result

    def test_join_before(self):
        original_text = '''// Comment1
section-name-2 {
    parameter-name /home/divanov/Home;
    // Comment2
    // Comment3
    other-parameter yes;
};

section-name-3 {
    // Comment
    another-parameter 1;
}'''

        template_text = '''
section-name-1 {
    parameter-name /homeless/poorness;
    one-more-parameter no;
};

section-name-3 {
// Comment
unspoken-parameter 1;
};'''

        result = OrderedDict({('', 'section-name-1'):
                              OrderedDict({('', 'parameter-name'):
                                           ['/homeless/poorness'],
                                           ('', 'one-more-parameter'):
                                           ['no']}),
                              ('', 'section-name-2'):
                              OrderedDict({'#': ['// Comment1'],
                                           ('', 'parameter-name'):
                                           ['/home/divanov/Home'],
                                           ('', 'other-parameter'):
                                           ['// Comment2',
                                            '// Comment3', 'yes']}),
                              ('', 'section-name-3'):
                              OrderedDict({('', 'another-parameter'):
                                           ['// Comment', '1'],
                                           ('', 'unspoken-parameter'):
                                           ['1']})})

        bind_original_object = BINDFormat(original_text, '/path/to/template',
                                          join_before=True)
        bind_template_object = BINDFormat(template_text, '/path/to/template',
                                          ignore_comments=True)
        bind_original_object.join_template(bind_template_object)
        assert bind_original_object._document_dictionary == result
        print('result text:\n{}\n'.format(bind_original_object.document_text))

    def test_if_input_document_contains_parameters_to_delete_without_values_or_with_empty_block__the_document_object_contains_dictionary_with_item_to_delete(self):
        document_text = '''
!pid-file;

!acl "dns_servers" {};

options {
    !response-policy {};
    !recursion;
}
        '''

        options_section = OrderedDict({('!', 'response-policy'):
                                       OrderedDict(),
                                       ('!', 'recursion'): ['']})

        result = OrderedDict({('!', 'pid-file'):
                              [''],
                              ('!', 'acl', '"dns_servers"'): OrderedDict(),
                              ('', 'options'): options_section})

        bind_object = BINDFormat(document_text, '/path/to/template')
        assert bind_object._document_dictionary == result

    def test_joining_documents_1(self):
        with open('./tests/templates/format/testfiles/bind/original.conf',
                  'r') as original_file:
            original_text = original_file.read()
            print(original_text)
            bind_original_object = BINDFormat(original_text,
                                              '/path/to/template')

        with open('./tests/templates/format/testfiles/bind/'
                  'template.conf', 'r') as template_file:
            template_text = template_file.read()
            bind_template_object = BINDFormat(template_text,
                                              '/path/to/template',
                                              ignore_comments=True)

        bind_original_object.join_template(bind_template_object)

        with open('./tests/templates/format/testfiles/bind/'
                  'result.conf', 'r') as result_file:
            result_text = result_file.read()

        assert bind_original_object.document_text == result_text

    def test_make_template(self):
        document_1 = '''
pid-file "/run/named/named.pid";
disable-empty-zone "12.out-addr.arpa";

acl "dns_servers" {
    127.0.0.1;
    10.0.1.3;
    10.1.0.3;
    10.2.0.3;
    10.3.0.3;
};

options {
    response-policy {
            zone "any.zone";
    };
    recursion no;
}
'''
        document_2 = '''pid-file "/run/named/named.pid";
disable-empty-zone "10.in-addr.arpa";

acl "dns_servers" {
    127.0.0.1;
    10.3.0.3;
    10.1.1.3;
    10.0.0.3;
};

options {
    response-policy {
        zone "rpz.zone";
    };
    recursion yes;
};
'''
        document_1_object = BINDFormat(document_1, '/path/to/template')
        document_2_object = BINDFormat(document_2, '/path/to/template')
        template = document_1_object.make_template(document_2_object)
        document_1_object.join_template(template)
        assert document_1_object.document_text == document_2

    def test_if_input_documents_are_an_original_document_without_calculate_header_and_a_template_and_the_add_header_flag_is_set__the_format_object_joins_an_original_document_and_a_template_and_adds_the_calculate_header(self):
        original_text = '''// Comment1
section-name-2 {
    parameter-name /home/divanov/Home;
    // Comment2
    // Comment3
    other-parameter yes;
};

section-name-3 {
    // Comment
    another-parameter 1;
}'''

        template_text = '''section-name-1 {
    parameter-name /homeless/poorness;
    one-more-parameter no;
};

section-name-3 {
// Comment
unspoken-parameter 1;
};'''

        join_result = '''#-------------------------------------------------------------------------------
# Modified by Calculate Utilities 4.0
# Processed template files:
# /path/to/template
#-------------------------------------------------------------------------------
// Comment1
section-name-2 {
    parameter-name /home/divanov/Home;
    // Comment2
    // Comment3
    other-parameter yes;
};

section-name-3 {
    // Comment
    another-parameter 1;
    // Comment
    unspoken-parameter 1;
};

section-name-1 {
    parameter-name /homeless/poorness;
    one-more-parameter no;
};
'''

        original_object = BINDFormat(original_text, '/path/to/template',
                                     add_header=True)
        template_object = BINDFormat(template_text, '/path/to/template')
        original_object.join_template(template_object)
        assert original_object.document_text == join_result

    def test_if_input_documents_are_an_original_document_with_calculate_header_and_a_template_the_add_header_flag_is_set_and_the_already_changed_flag_is_not_set__the_format_object_removes_calculate_header_from_original_document__joins_an_original_document_and_a_template_and_adds_the_calculate_header(self):
        original_text = '''#-------------------------------------------------------------------------------
# Modified by Calculate Utilities 4.0
# Processed template files:
# /path/to/ancient/template
#-------------------------------------------------------------------------------
// Comment1
section-name-2 {
    parameter-name /home/divanov/Home;
    // Comment2
    // Comment3
    other-parameter yes;
};

section-name-3 {
    // Comment
    another-parameter 1;
}'''

        template_text = '''section-name-1 {
    parameter-name /homeless/poorness;
    one-more-parameter no;
};

section-name-3 {
// Comment
unspoken-parameter 1;
};'''

        join_result = '''#-------------------------------------------------------------------------------
# Modified by Calculate Utilities 4.0
# Processed template files:
# /path/to/template
#-------------------------------------------------------------------------------
// Comment1
section-name-2 {
    parameter-name /home/divanov/Home;
    // Comment2
    // Comment3
    other-parameter yes;
};

section-name-3 {
    // Comment
    another-parameter 1;
    // Comment
    unspoken-parameter 1;
};

section-name-1 {
    parameter-name /homeless/poorness;
    one-more-parameter no;
};
'''

        original_object = BINDFormat(original_text, '/path/to/template',
                                     add_header=True)
        template_object = BINDFormat(template_text, '/path/to/template')
        original_object.join_template(template_object)
        assert original_object.document_text == join_result

    def test_if_input_documents_are_an_original_document_with_calculate_header_and_a_template_the_add_header_flag_is_set_and_the_already_changed_flag_is_set__the_format_object_joins_an_original_document_and_a_template_and_adds_the_calculate_header_with_a_template_paths_from_the_old_header_and_paths_to_a_current_template(self):
        original_text = '''#-------------------------------------------------------------------------------
# Modified by Calculate Utilities 4.0
# Processed template files:
# /path/to/ancient/template
#-------------------------------------------------------------------------------
// Comment1
section-name-2 {
    parameter-name /home/divanov/Home;
    // Comment2
    // Comment3
    other-parameter yes;
};

section-name-3 {
    // Comment
    another-parameter 1;
}'''

        template_text = '''section-name-1 {
    parameter-name /homeless/poorness;
    one-more-parameter no;
};

section-name-3 {
// Comment
unspoken-parameter 1;
};'''

        join_result = '''#-------------------------------------------------------------------------------
# Modified by Calculate Utilities 4.0
# Processed template files:
# /path/to/ancient/template
# /path/to/template
#-------------------------------------------------------------------------------
// Comment1
section-name-2 {
    parameter-name /home/divanov/Home;
    // Comment2
    // Comment3
    other-parameter yes;
};

section-name-3 {
    // Comment
    another-parameter 1;
    // Comment
    unspoken-parameter 1;
};

section-name-1 {
    parameter-name /homeless/poorness;
    one-more-parameter no;
};
'''

        original_object = BINDFormat(original_text, '/path/to/template',
                                     add_header=True, already_changed=True)
        template_object = BINDFormat(template_text, '/path/to/template')
        original_object.join_template(template_object)
        assert original_object.document_text == join_result
