# Copyright (c) Huawei Technologies Co., Ltd. 2026-2026. All rights reserved.
# OpenOLC is licensed under Mulan PSL v2.
# You can use this software according to the terms and conditions of the Mulan PSL v2.
# You may obtain a copy of Mulan PSL v2 at:
#         `http://license.coscl.org.cn/MulanPSL2`
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
# See the Mulan PSL v2 for more details.

import os
import tempfile
import unittest
from unittest.mock import Mock, patch
from olc.config.properties_loader import OlcConfigManager, OlcConfig
from olc.utils.path_utils import PathUtils


class TestOlcConfigManager(unittest.TestCase):
    def setUp(self):
        # 重置单例实例,确保每个测试都是独立的
        OlcConfigManager._instance = None
    
    def test_singleton_pattern(self):
        """测试OlcConfigManager的单例模式"""
        # 获取第一个实例
        instance1 = OlcConfigManager.get_instance()
        # 获取第二个实例
        instance2 = OlcConfigManager.get_instance()
        # 验证两个实例是同一个对象
        self.assertIs(instance1, instance2)
    
    def test_initialization(self):
        """测试OlcConfigManager的初始化过程"""
        # 创建临时配置文件
        with tempfile.NamedTemporaryFile(mode='w', suffix='.properties', delete=False) as f:
            f.write('olc.sdk.domain=test.example.com\n')
            f.write('olc.sdk.switch=true\n')
            temp_file = f.name
        
        try:
            # 创建OlcConfigManager实例
            config_manager = OlcConfigManager(temp_file)
            
            # 验证实例创建成功
            self.assertIsNotNone(config_manager)
            
            # 验证配置加载成功
            config = config_manager.get_config()
            self.assertIsInstance(config, OlcConfig)
            self.assertEqual(config.sdk_config.domain, 'test.example.com')
            self.assertEqual(config.sdk_config.switch, True)
        finally:
            # 清理临时文件
            os.unlink(temp_file)
    
    def test_get_config_methods(self):
        """测试配置获取方法"""
        # 创建临时配置文件
        with tempfile.NamedTemporaryFile(mode='w', suffix='.properties', delete=False) as f:
            f.write('olc.sdk.domain=test.example.com\n')
            f.write('olc.sdk.switch=true\n')
            f.write('olc.redis.mode=cluster\n')
            f.write('olc.redis.timeout=3000\n')
            temp_file = f.name
        
        try:
            # 创建OlcConfigManager实例
            config_manager = OlcConfigManager(temp_file)
            
            # 测试get_config方法
            config = config_manager.get_config()
            self.assertIsInstance(config, OlcConfig)
            
            # 测试get_sdk_config方法
            sdk_config = config_manager.get_sdk_config()
            self.assertEqual(sdk_config.domain, 'test.example.com')
            self.assertEqual(sdk_config.switch, True)
            
            # 测试get_redis_config方法
            redis_config = config_manager.get_redis_config()
            self.assertEqual(redis_config.mode, 'cluster')
            self.assertEqual(redis_config.timeout, 3000)
        finally:
            # 清理临时文件
            os.unlink(temp_file)
    
    def test_nonexistent_config_file(self):
        """测试配置文件不存在的场景"""
        # 使用不存在的文件路径
        nonexistent_file = 'nonexistent_config.properties'
        
        try:
            # 创建OlcConfigManager实例,应该能够正常初始化(使用默认值)
            config_manager = OlcConfigManager(nonexistent_file)
            
            # 验证实例创建成功
            self.assertIsNotNone(config_manager)
            
            # 验证返回默认配置
            config = config_manager.get_config()
            self.assertIsInstance(config, OlcConfig)
            # 验证默认值
            self.assertEqual(config.sdk_config.domain, '')
            self.assertEqual(config.sdk_config.switch, True)
            self.assertEqual(config.redis_config.mode, 'alone')
        finally:
            # 清理(如果文件被创建)
            if os.path.exists(nonexistent_file):
                os.unlink(nonexistent_file)
    
    @patch('olc.config.properties_loader.PathUtils.get_config_path')
    def test_default_config_path(self, mock_get_config_path):
        """测试默认配置路径"""
        # 创建临时目录
        temp_dir = tempfile.mkdtemp()
        
        try:
            # 模拟PathUtils.get_config_path的返回值
            mock_get_config_path.return_value = temp_dir + '\\'
            
            # 创建临时文件,路径与模拟的默认路径匹配
            default_file_name = 'overload-config.properties'
            test_file_path = os.path.join(temp_dir, default_file_name)
            
            # 创建临时文件
            with open(test_file_path, 'w') as f:
                f.write('olc.sdk.domain=default.example.com\n')
            
            # 获取OlcConfigManager实例(使用默认路径)
            config_manager = OlcConfigManager.get_instance()
            
            # 验证配置加载成功
            config = config_manager.get_config()
            self.assertEqual(config.sdk_config.domain, 'default.example.com')
        finally:
            # 清理临时文件和目录
            if os.path.exists(test_file_path):
                os.unlink(test_file_path)
            if os.path.exists(temp_dir):
                os.rmdir(temp_dir)
    
    def test_config_update_callback(self):
        """测试配置变更的监听器功能"""
        # 创建临时配置文件
        with tempfile.NamedTemporaryFile(mode='w', suffix='.properties', delete=False) as f:
            f.write('olc.sdk.domain=initial.example.com\n')
            temp_file = f.name
        
        try:
            # 创建回调函数的模拟
            callback_mock = Mock()
            
            # 导入on_config_update函数,以便我们可以临时替换它
            from olc.config.properties_loader import on_config_update
            original_callback = on_config_update
            
            try:
                # 替换on_config_update为我们的模拟
                import olc.config.properties_loader
                olc.config.properties_loader.on_config_update = callback_mock
                
                # 创建OlcConfigManager实例
                config_manager = OlcConfigManager(temp_file)
                
                # 手动调用_load_config方法来模拟配置变更
                config_manager._config._load_config()
                
                # 验证回调是否被调用
                callback_mock.assert_called_once()
            finally:
                # 恢复原始的on_config_update函数
                import olc.config.properties_loader
                olc.config.properties_loader.on_config_update = original_callback
        finally:
            # 清理临时文件
            os.unlink(temp_file)
    
    def test_invalid_config_values(self):
        """测试无效配置值的处理"""
        # 创建临时配置文件,包含类型错误的配置值
        with tempfile.NamedTemporaryFile(mode='w', suffix='.properties', delete=False) as f:
            f.write('olc.sdk.switch=not_a_boolean\n')  # 类型错误
            f.write('olc.sdk.collector_capacity=not_a_number\n')  # 类型错误
            temp_file = f.name
        
        try:
            # 创建OlcConfigManager实例
            config_manager = OlcConfigManager(temp_file)
            
            # 获取配置,应该使用默认值而不是崩溃
            config = config_manager.get_config()
            
            # 验证配置对象创建成功
            self.assertIsInstance(config, OlcConfig)
        finally:
            # 清理临时文件
            os.unlink(temp_file)


    def test_redis_config_repr(self):
        """测试RedisConfig的__repr__方法(密码脱敏)"""
        # 创建临时配置文件
        with tempfile.NamedTemporaryFile(mode='w', suffix='.properties', delete=False) as f:
            f.write('olc.redis.password=test_password\n')
            temp_file = f.name
        
        try:
            # 创建OlcConfigManager实例
            config_manager = OlcConfigManager(temp_file)
            
            # 获取Redis配置
            redis_config = config_manager.get_redis_config()
            
            # 测试__repr__方法
            repr_str = repr(redis_config)
            self.assertIn('password=******', repr_str)
            self.assertNotIn('test_password', repr_str)
        finally:
            # 清理临时文件
            os.unlink(temp_file)
    
    def test_stop_method(self):
        """测试停止热加载的方法"""
        # 创建临时配置文件
        with tempfile.NamedTemporaryFile(mode='w', suffix='.properties', delete=False) as f:
            f.write('olc.sdk.domain=test.example.com\n')
            temp_file = f.name
        
        try:
            # 创建OlcConfigManager实例
            config_manager = OlcConfigManager(temp_file)
            
            # 调用stop方法
            config_manager._config.stop()
            
            # 验证没有抛出异常
            self.assertTrue(True)
        finally:
            # 清理临时文件
            os.unlink(temp_file)
    
    def test_encryption_error_handling(self):
        """测试加密配置的异常处理"""
        # 创建临时配置文件,包含加密的配置值
        with tempfile.NamedTemporaryFile(mode='w', suffix='.properties', delete=False) as f:
            f.write('olc.redis.password=ENC(encrypted_password)\n')
            temp_file = f.name
        
        try:
            # 创建模拟的Encryptor,故意抛出异常
            mock_encryptor = Mock()
            mock_encryptor.decrypt.side_effect = Exception("Decryption error")
            
            # 导入PropertiesManager
            from olc.config.properties_loader import PropertiesManager
            
            # 创建PropertiesManager实例,传入模拟的Encryptor
            properties_manager = PropertiesManager(temp_file, encryptor=mock_encryptor)
            
            # 获取配置,应该使用默认值而不是崩溃
            config = properties_manager.get()
            
            # 验证配置对象创建成功
            self.assertIsInstance(config, OlcConfig)
        finally:
            # 清理临时文件
            os.unlink(temp_file)

    def test_new_statistic_config_default_values(self):
        """测试新增统计配置项的默认值"""
        with tempfile.NamedTemporaryFile(mode='w', suffix='.properties', delete=False) as f:
            temp_file = f.name
        
        try:
            config_manager = OlcConfigManager(temp_file)
            sdk_config = config_manager.get_sdk_config()
            
            self.assertEqual(sdk_config.statistic_expire, 300)
            self.assertEqual(sdk_config.statistic_max_size, 5000000)
            self.assertEqual(sdk_config.sub_group_cache_size, 100000)
        finally:
            os.unlink(temp_file)

    def test_new_statistic_config_loading(self):
        """测试新增统计配置项从文件加载"""
        with tempfile.NamedTemporaryFile(mode='w', suffix='.properties', delete=False) as f:
            f.write('olc.sdk.statistic.expire=120\n')
            f.write('olc.sdk.statistic.maxsize=2000\n')
            f.write('olc.sdk.subgroup.maxsize=50000\n')
            temp_file = f.name
        
        try:
            config_manager = OlcConfigManager(temp_file)
            sdk_config = config_manager.get_sdk_config()
            
            self.assertEqual(sdk_config.statistic_expire, 120)
            self.assertEqual(sdk_config.statistic_max_size, 2000)
            self.assertEqual(sdk_config.sub_group_cache_size, 50000)
        finally:
            os.unlink(temp_file)


if __name__ == '__main__':
    print("Running OlcConfigManager tests...")
    unittest.main(verbosity=2)