"""
tests for the pre merge validation logic
"""

import copy
from datetime import datetime, timedelta, timezone
import logging
import unittest

import pre_merge_validation


class TestPrePublishValidations(unittest.TestCase):
    """Tests for all the pre merge validations"""
    def setUp(self):
        logging.getLogger().setLevel(logging.ERROR)

    def test_are_changes_valid(self):
        """test are_changes_valid function"""
        current_state = {'invariant_ping': {
                            'Columns':
                                {'origin': {
                                    'InboundName': 'origin',
                                    'OutboundName': 'origin',
                                    'Transformer': 'varchar',
                                    'ColumnCreationOptions': '(8)',
                                    'SensitivityType': None
                                },
                                'ip': {
                                    'InboundName': 'ip',
                                    'OutboundName': 'ip',
                                    'Transformer': 'varchar',
                                    'ColumnCreationOptions': '(15)',
                                    'SensitivityType': 'ip'
                                }
                            },
                            'Metadata': {
                                'UserName': "not-ssr"
                            }}
                        }
        desired_state = {'invariant_ping': {
                            'Columns': {
                                'origin': {
                                    'inboundName': 'origin',
                                    'outboundName': 'origin',
                                    'transformer': 'varchar',
                                    'columnCreationOptions': '(8)',
                                    'sensitivityType': None
                                },
                                'ip': {
                                    'inboundName': 'ip',
                                    'outboundName': 'ip',
                                    'transformer': 'varchar',
                                    'columnCreationOptions': '(15)',
                                    'sensitivityType': 'ip'
                                }
                            }
                        },
                            'test-event1': {
                                'Columns': {}
                            }
                        }
        valid, reasons = pre_merge_validation.are_changes_valid(current_state, desired_state)
        self.assertTrue(valid)
        self.assertEqual(reasons, [])

        desired_state['invariant_ping']['Columns']['origin']['inboundName'] = 'not_origin'
        valid, reasons = pre_merge_validation.are_changes_valid(current_state, desired_state)
        # inboundName cannot be changed
        self.assertFalse(valid)
        self.assertEqual(['event invariant_ping column origin inbound '+
                            'name change from origin to not_origin not allowed'], reasons)

        # undo previous change
        desired_state['invariant_ping']['Columns']['origin']['inboundName'] = 'origin'
        valid, reasons = pre_merge_validation.are_changes_valid(current_state, desired_state)
        self.assertTrue(valid)
        self.assertEqual(reasons, [])

        desired_state['invariant_ping']['Columns']['ip']['sensitivityType'] = 'userid'
        valid, reasons = pre_merge_validation.are_changes_valid(current_state, desired_state)
        # sensitivityType cannot be changed
        self.assertFalse(valid)
        self.assertEqual(['event invariant_ping column ip sensitivity '
                        +'change from ip to userid not allowed'], reasons)

        desired_state['invariant_ping']['Columns']['ip']['sensitivityType'] = 'ip'
        valid, reasons = pre_merge_validation.are_changes_valid(current_state, desired_state)
        self.assertTrue(valid)
        self.assertEqual([], reasons)

        desired_state['invariant_ping']['Columns']['origin']['transformer'] = 'bigint'
        desired_state['invariant_ping']['Columns']['origin']['columnCreationOptions'] = ''
        # changes are not valid because origin's type is changed from varchar
        # to bigint in the desired state
        valid, reasons = pre_merge_validation.are_changes_valid(current_state, desired_state)
        self.assertFalse(valid)
        self.assertEqual(['event invariant_ping column origin type change '+
                            'from varchar to bigint not allowed'], reasons)

        del desired_state['invariant_ping']['Columns']['origin']
        valid, reasons = pre_merge_validation.are_changes_valid(current_state, desired_state)
        self.assertFalse(valid)
        self.assertEqual(['You cannot drop columns the first time you import an event.'+
                     ' You are trying to drop columns {\'origin\'}.'], reasons)

        # undo previous change
        desired_state['invariant_ping']['Columns']['origin'] = {
                                    'inboundName': 'origin',
                                    'outboundName': 'origin',
                                    'transformer': 'varchar',
                                    'columnCreationOptions': '(8)',
                                    'sensitivityType': None
                                }

        desired_state['invariant_ping']['Columns']['origin2'] = {
                                    'inboundName': 'origin2',
                                    'outboundName': 'origin2',
                                    'transformer': 'varchar',
                                    'columnCreationOptions': '(8)',
                                    'sensitivityType': None
                                }
        # adding columns is allowed at import time
        valid, reasons = pre_merge_validation.are_changes_valid(current_state, desired_state)
        self.assertTrue(valid)
        self.assertEqual([], reasons)

        # for events owned by Spade Schema Registry, drops should be allowed
        current_state['invariant_ping']['Metadata']['UserName'] = 'Spade Schema Registry'
        del desired_state['invariant_ping']['Columns']['origin']
        valid, reasons = pre_merge_validation.are_changes_valid(current_state, desired_state)
        self.assertTrue(valid)
        self.assertEqual([], reasons)


    def test_get_events_to_change(self):
        """tests for the get_events_to_change function"""
        current_state = {'invariant_ping': {
                            'Columns':
                                {
                                'origin': {
                                    'InboundName': 'origin',
                                    'OutboundName': 'origin',
                                    'Transformer': 'varchar',
                                    'ColumnCreationOptions': '(8)',
                                    'SensitivityType': None
                                },
                                'ip': {
                                    'InboundName': 'ip',
                                    'OutboundName': 'ip',
                                    'Transformer': 'varchar',
                                    'ColumnCreationOptions': '(15)',
                                    'SensitivityType': 'ip'
                                }
                            },
                            'Metadata': {
                                'CreatedTS': '2019-02-11T22:50:53.152607Z'
                            }},
                            'test_event1': {
                                    'Columns': {
                                        'time': {
                                            'InboundName': 'time',
                                            'OutboundName': 'time',
                                            'Transformer': 'f@timestamp@unix',
                                            'ColumnCreationOptions': '',
                                            'SensitivityType': None
                                        }
                                    },
                                    'Metadata': {
                                        'CreatedTS': '2019-02-11T22:50:53.152607Z',
                                    }
                            },
                            'test_event2': {
                                    'Columns': {
                                        'time': {
                                            'InboundName': 'time',
                                            'OutboundName': 'time',
                                            'Transformer': 'f@timestamp@unix',
                                            'ColumnCreationOptions': '',
                                            'SensitivityType': None
                                        },
                                        'is_live': {
                                            'InboundName': 'is_live',
                                            'OutboundName': 'is_live',
                                            'Transformer': 'boolean',
                                            'ColumnCreationOptions': '',
                                            'SensitivityType': None
                                        },
                                    },
                                    'Metadata': {
                                        'CreatedTS': '2019-02-11T22:50:53.152607Z',
                                    }
                            }
                        }
        desired_state = {'invariant_ping':
                            {'Columns':
                            {  'origin': {
                                    'inboundName': 'origin',
                                    'outboundName': 'origin',
                                    'transformer': 'varchar',
                                    'columnCreationOptions': '(8)',
                                    'sensitivityType': None
                                },
                                'ip': {
                                    'inboundName': 'ip',
                                    'outboundName': 'ip',
                                    'transformer': 'varchar',
                                    'columnCreationOptions': '(15)',
                                    'sensitivityType': 'ip'
                                }
                            }},
                            'test_event1': {
                                'Columns': {}
                            },
                            'test_event2': {
                                'Columns': {
                                    'time': {
                                        'InboundName': 'time',
                                        'OutboundName': 'time',
                                        'Transformer': 'f@timestamp@unix',
                                        'ColumnCreationOptions': '',
                                        'SensitivityType': None
                                    },
                                    'is_live': {
                                        'InboundName': 'is_live',
                                        'OutboundName': 'is_live',
                                        'Transformer': 'boolean',
                                        'ColumnCreationOptions': '',
                                        'SensitivityType': None
                                    },
                                    # bitrate field is being added to test that
                                    # field addition is picked up as a change
                                    'bitrate': {
                                        'InboundName': 'bitrate',
                                        'OutboundName': 'bitrate',
                                        'Transformer': 'bigint',
                                        'ColumnCreationOptions': '',
                                        'SensitivityType': None
                                    }
                                }
                            }
                        }

        events_being_changed = pre_merge_validation.get_old_event_changes_proposed(
                                                    current_state, desired_state)
        # test_event1 is being request dropped and bitrate field is being added to test_event2
        # so number of changes should be 2
        self.assertEqual(2, events_being_changed)

        current_state2 = copy.deepcopy(current_state)
        desired_state2 = copy.deepcopy(desired_state)

        # change invariant_ping as well by dropping a column
        del desired_state['invariant_ping']['Columns']['ip']
        events_being_changed = pre_merge_validation.get_old_event_changes_proposed(
                                                    current_state, desired_state)
        self.assertEqual(3, events_being_changed)

        # make current state and desired state identical
        del current_state2['test_event1']
        del current_state2['test_event2']
        del desired_state2['test_event1']
        del desired_state2['test_event2']
        events_being_changed = pre_merge_validation.get_old_event_changes_proposed(
                                                    current_state2, desired_state2)
        self.assertEqual(0, events_being_changed)

        desired_state2['invariant_ping']['Columns']['origin']['columnCreationOptions'] = '(16)'
        events_being_changed = pre_merge_validation.get_old_event_changes_proposed(
                                                    current_state2, desired_state2)
        self.assertEqual(1, events_being_changed)

        # # change CreateTS to make invariant_ping a new event
        current_state2['invariant_ping']['Metadata']['CreatedTS'] = \
                        datetime.now().strftime('%Y-%m-%dT%H:%M:%S.%fZ')
        events_being_changed = pre_merge_validation.get_old_event_changes_proposed(
                                                    current_state2, desired_state2)
        self.assertEqual(0, events_being_changed)

    def test_event_change_window_checking(self):
        """tests for the event_change_window_checking function"""
        now = datetime.now(timezone.utc)
        block_period_start = now.replace(hour = 4, minute = 0, second = 0, microsecond = 0)
        block_period_end = now.replace(hour = 8, minute = 0, second = 0, microsecond = 0)

        change_allowed = pre_merge_validation.check_time_range_valid(
                            block_period_start-timedelta(hours=1),
                            block_period_start,
                            block_period_end
                        )
        self.assertTrue(change_allowed)

        change_allowed = pre_merge_validation.check_time_range_valid(
                            block_period_start+timedelta(hours=1),
                            block_period_start,
                            block_period_end
                        )
        self.assertFalse(change_allowed)

    def test_transform_bp_state(self):
        """tests for the transform_bp_state function"""
        s3_format_schema_config = [{
            'EventName': 'multiview_banner_action',
            'Columns': [
                {
                    'InboundName': 'time',
                    'OutboundName': 'time',
                    'Transformer': 'f@timestamp@unix',
                    'ColumnCreationOptions': ' sortkey',
                    'SupportingColumns': '',
                    'OriginalName': 'time',
                    'SensitivityType': '',
                    'Version': 0
                },
                {
                    'InboundName': 'time',
                    'OutboundName': 'time_utc',
                    'Transformer': 'f@timestamp@unix-utc',
                    'ColumnCreationOptions': '',
                    'SupportingColumns': '',
                    'OriginalName': 'time_utc',
                    'SensitivityType': '',
                    'Version': 0
                }
            ],
            'Version': 0,
            'CreatedTS': '2019-02-11T22:50:53.152607Z',
            'TS': '2019-02-11T22:50:53.152607Z',
            'UserName': 'tyoung',
            'Dropped': False,
            'DropRequested': False,
            'Reason': ''
        }]

        curr_bp_state = pre_merge_validation.transform_bp_state(s3_format_schema_config)
        expected_bp_state = {
            'multiview_banner_action': {
                'Columns': {
                    'time': {
                        "InboundName": "time",
                        "OutboundName": "time",
                        "Transformer": "f@timestamp@unix",
                        "ColumnCreationOptions": " sortkey",
                        "SupportingColumns": "",
                        "OriginalName": "time",
                        "SensitivityType": "",
                        "Version": 0
                    },
                    'time_utc': {
                        "InboundName": "time",
                        "OutboundName": "time_utc",
                        "Transformer": "f@timestamp@unix-utc",
                        "ColumnCreationOptions": "",
                        "SupportingColumns": "",
                        "OriginalName": "time_utc",
                        "SensitivityType": "",
                        "Version": 0
                    }
                },
                'Metadata': {
                    'CreatedTS': '2019-02-11T22:50:53.152607Z',
                    'TS': '2019-02-11T22:50:53.152607Z',
                    'UserName': 'tyoung'
                }
            }
        }

        self.assertEqual(expected_bp_state, curr_bp_state)

    def test_get_num_recently_changed_events(self):
        """tests for the get_num_recently_changed_events"""
        current_bp_state = {
            'multiview_banner_action': {
                'Columns': {
                    'time': {
                        "InboundName": "time",
                        "OutboundName": "time",
                        "Transformer": "f@timestamp@unix",
                        "ColumnCreationOptions": " sortkey",
                        "SupportingColumns": "",
                        "OriginalName": "time",
                        "SensitivityType": "",
                        "Version": 0
                    }
                },
                'Metadata': {
                    'CreatedTS': '2019-02-11T22:50:53.152607Z',
                    'TS': '2019-02-11T22:50:53.152607Z'
                }
            }
        }
        num_recently_changed_events = pre_merge_validation.\
                                        get_num_recently_changed_events(current_bp_state)
        self.assertEqual(0, num_recently_changed_events)

        last_modified_time = (datetime.now(timezone.utc)-\
                                timedelta(minutes=5)).strftime('%Y-%m-%dT%H:%M:%S.%fZ')
        current_bp_state['multiview_banner_action']['Metadata']['TS'] = last_modified_time
        num_recently_changed_events = pre_merge_validation.\
                                        get_num_recently_changed_events(current_bp_state)
        self.assertEqual(1, num_recently_changed_events)
