###############################################################################
#
# (C) Copyright 2023 EVERYSK TECHNOLOGIES
#
# This is an unpublished work containing confidential and proprietary
# information of EVERYSK TECHNOLOGIES. Disclosure, use, or reproduction
# without authorization of EVERYSK TECHNOLOGIES is prohibited.
#
###############################################################################
from unittest import TestCase, mock
from everysk.core.compress import compress, decompress, zip_directory_to_str
from everysk.core.datetime import DateTime, Date
from everysk.core import serialize

class CompressTestCase(TestCase):
    @mock.patch.object(serialize, 'dumps')
    def test_compress_serialize_none(self, dumps: mock.MagicMock):
        result = compress('a', serialize=None)
        self.assertEqual(result, b'x\x9cK\x04\x00\x00b\x00b')
        dumps.assert_not_called()

    @mock.patch.object(serialize, 'loads')
    def test_decompress_serialize_none(self, loads: mock.MagicMock):
        result = decompress(b'x\x9cK\x04\x00\x00b\x00b', serialize=None)
        self.assertEqual(result, b'a')
        loads.assert_not_called()

    def test_compress_protocol_invalid(self):
        with self.assertRaisesRegex(ValueError, "Unsupported compression protocol 'invalid'. Use 'zlib' or 'gzip'."):
            compress('string', protocol='invalid', serialize='json')

    def test_decompress_protocol_invalid(self):
        with self.assertRaisesRegex(ValueError, "Unsupported decompression protocol 'invalid'. Use 'zlib' or 'gzip'."):
            decompress('string', protocol='invalid', serialize='json')


class CompressZlibJsonTestCase(TestCase):

    def test_compress(self):
        string = 'aa aa aa aa aa aa aa aa'
        self.assertEqual(compress(string, protocol='zlib', serialize='json'), b'x\x9cSJLT\xc0\x86\x94\x00]\xbd\x075')

    def test_decompress(self):
        string = b'x\x9cSJLT\xc0\x86\x94\x00]\xbd\x075'
        self.assertEqual(decompress(string, protocol='zlib', serialize='json'), 'aa aa aa aa aa aa aa aa')

    def test_undefined(self):
        obj_compressed = compress(Undefined, protocol='zlib', serialize='json')
        self.assertEqual(decompress(obj_compressed, protocol='zlib', serialize='json'), Undefined)

    def test_complex_obj(self):
        obj = [
            '2024-01-01',
            {'datetime': DateTime.now()},
            {'a': 1, 'b': True},
            'Test',
            1.1,
            ['1', '2', '3', Date.today()],
            Undefined,
            {'undefined': Undefined}
        ]
        obj_compressed = compress(obj, protocol='zlib', serialize='json')
        self.assertListEqual(decompress(obj_compressed, protocol='zlib', serialize='json'), obj)

    def test_date_format(self):
        obj = Date.today()
        with mock.patch.dict('os.environ', {'SERIALIZE_DATE_FORMAT':'%Y%m%d'}):
            obj_compressed = compress(obj, protocol='zlib', serialize='json')
            self.assertEqual(decompress(obj_compressed, protocol='zlib', serialize='json'), obj.strftime('%Y%m%d'))

    def test_datetime_format(self):
        obj = DateTime.now()
        with mock.patch.dict('os.environ', {'SERIALIZE_DATETIME_FORMAT':'%Y%m%d %HH:%M:%S'}):
            obj_compressed = compress(obj, protocol='zlib', serialize='json')
            self.assertEqual(decompress(obj_compressed, protocol='zlib', serialize='json'), obj.strftime('%Y%m%d %HH:%M:%S'))


class CompressZlibPickleTestCase(TestCase):

    def setUp(self) -> None:
        self.str_repr = b'x\x9ck`\x99*\xcd\x00\x01=\xe2\x89\x89\n\xd8\xd0\x14=\x00\x9f\xef\t\x8a'

    def test_compress(self):
        string = 'aa aa aa aa aa aa aa aa'
        self.assertEqual(compress(string, protocol='zlib', serialize='pickle'), self.str_repr)

    def test_decompress(self):
        self.assertEqual(decompress(self.str_repr, protocol='zlib', serialize='pickle'), 'aa aa aa aa aa aa aa aa')

    def test_undefined(self):
        obj_compressed = compress(Undefined, protocol='zlib', serialize='pickle')
        self.assertEqual(decompress(obj_compressed, protocol='zlib', serialize='pickle'), Undefined)

    def test_complex_obj(self):
        obj = [
            '2024-01-01',
            {'datetime': DateTime.now()},
            {'a': 1, 'b': True},
            'Test',
            1.1,
            ['1', '2', '3', Date.today()],
            Undefined,
            {'undefined': Undefined}
        ]
        obj_compressed = compress(obj, protocol='zlib', serialize='pickle')
        self.assertListEqual(decompress(obj_compressed, protocol='zlib', serialize='pickle'), obj)
###############################################################################
#   File Handling Test Case Implementation
###############################################################################
class FileHandlingTestCase(TestCase):

    @mock.patch('os.walk')
    @mock.patch('zipfile.ZipFile.write')
    def test_zip_directory_to_str_returns_expected_data(self, mock_walk, mock_write):
        expected_base64 = 'UEsFBgAAAAAAAAAAAAAAAAAAAAAAAA=='
        mock_walk.return_value = [
            ('/engine', ('portfolio'), ('tests')),
            ('/entities/file', (), ('base', 'settings'))
        ]
        mock_write.return_value = ['123', '456']
        result = zip_directory_to_str('engine/portfolio/tests', 'zip_root_folder')

        self.assertTrue(result.isascii())
        self.assertTrue(mock_write.called)
        self.assertTrue(mock_walk.called)
        self.assertIsInstance(result, str)
        self.assertEqual(result, expected_base64)

    @mock.patch('os.walk')
    def test_zip_directory_to_str_with_invalid_paths(self, mock_walk):
        with self.assertRaises(FileNotFoundError) as context:
            mock_walk.return_value = [
            ('/engine', ('portfolio'), ('tests')),
            ('/entities/file', (), ('base', 'settings'))
        ]
            zip_directory_to_str('invalid/path', 'invalid_root_folder')

        self.assertEqual(str(context.exception), "[Errno 2] No such file or directory: '/engine/t'")

    def test_date_format(self):
        obj = Date.today()
        with mock.patch.dict('os.environ', {'SERIALIZE_DATE_FORMAT':'%Y%m%d'}):
            obj_compressed = compress(obj, protocol='zlib', serialize='pickle')
            self.assertEqual(decompress(obj_compressed, protocol='zlib', serialize='pickle'), obj)

    def test_datetime_format(self):
        obj = DateTime.now()
        with mock.patch.dict('os.environ', {'SERIALIZE_DATETIME_FORMAT':'%Y%m%d %HH:%M:%S'}):
            obj_compressed = compress(obj, protocol='zlib', serialize='pickle')
            self.assertEqual(decompress(obj_compressed, protocol='zlib', serialize='pickle'), obj)


class CompressGzipJsonTestCase(TestCase):

    def test_undefined(self):
        obj_compressed = compress(Undefined, protocol='gzip', serialize='json')
        self.assertEqual(decompress(obj_compressed, protocol='gzip', serialize='json'), Undefined)

    def test_complex_obj(self):
        obj = [
            '2024-01-01',
            {'datetime': DateTime.now()},
            {'a': 1, 'b': True},
            'Test',
            1.1,
            ['1', '2', '3', Date.today()],
            Undefined,
            {'undefined': Undefined}
        ]
        obj_compressed = compress(obj, protocol='gzip', serialize='json')
        self.assertListEqual(decompress(obj_compressed, protocol='gzip', serialize='json'), obj)

    def test_date_format(self):
        obj = Date.today()
        with mock.patch.dict('os.environ', {'SERIALIZE_DATE_FORMAT':'%Y%m%d'}):
            obj_compressed = compress(obj, protocol='gzip', serialize='json')
            self.assertEqual(decompress(obj_compressed, protocol='gzip', serialize='json'), obj.strftime('%Y%m%d'))

    def test_datetime_format(self):
        obj = DateTime.now()
        with mock.patch.dict('os.environ', {'SERIALIZE_DATETIME_FORMAT':'%Y%m%d %HH:%M:%S'}):
            obj_compressed = compress(obj, protocol='gzip', serialize='json')
            self.assertEqual(decompress(obj_compressed, protocol='gzip', serialize='json'), obj.strftime('%Y%m%d %HH:%M:%S'))


class CompressGzipPickleTestCase(TestCase):

    def test_undefined(self):
        obj_compressed = compress(Undefined, protocol='gzip', serialize='pickle')
        self.assertEqual(decompress(obj_compressed, protocol='gzip', serialize='pickle'), Undefined)

    def test_complex_obj(self):
        obj = [
            '2024-01-01',
            {'datetime': DateTime.now()},
            {'a': 1, 'b': True},
            'Test',
            1.1,
            ['1', '2', '3', Date.today()],
            Undefined,
            {'undefined': Undefined}
        ]
        obj_compressed = compress(obj, protocol='gzip', serialize='pickle')
        self.assertListEqual(decompress(obj_compressed, protocol='gzip', serialize='pickle'), obj)

    def test_date_format(self):
        obj = Date.today()
        with mock.patch.dict('os.environ', {'SERIALIZE_DATE_FORMAT':'%Y%m%d'}):
            obj_compressed = compress(obj, protocol='gzip', serialize='pickle')
            self.assertEqual(decompress(obj_compressed, protocol='gzip', serialize='pickle'), obj)

    def test_datetime_format(self):
        obj = DateTime.now()
        with mock.patch.dict('os.environ', {'SERIALIZE_DATETIME_FORMAT':'%Y%m%d %HH:%M:%S'}):
            obj_compressed = compress(obj, protocol='gzip', serialize='pickle')
            self.assertEqual(decompress(obj_compressed, protocol='gzip', serialize='pickle'), obj)
