#  -*- coding: utf-8 -*-
# -------------------------------------------------------------------------
# This file is part of the MindStudio project.
# Copyright (c) 2025-2026 Huawei Technologies Co.,Ltd.
#
# MindStudio 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.
# -------------------------------------------------------------------------

"""
msmodelslim.utils.logging 模块的单元测试
"""

import logging
from unittest import TestCase

from msmodelslim.utils.logging import (
    logger_setter, get_logger, set_logger_level
)


class TestLoggerSetter(TestCase):
    """测试 logger_setter 装饰器和上下文管理器"""

    def test_decorator_when_decorate_function_with_prefix_then_use_specified_logger(self):
        """测试装饰器功能:当装饰函数并指定前缀时,函数内部应使用指定的logger名称"""

        @logger_setter(prefix="test.module")
        def test_function():
            logger = get_logger()
            # 验证logger名称
            self.assertEqual(logger.name, "test.module")

        test_function()

    def test_decorator_when_decorate_function_without_prefix_then_use_module_name(self):
        """测试装饰器功能:当装饰函数但不指定前缀时,应使用函数所在模块名作为logger名称"""

        @logger_setter()
        def test_function():
            logger = get_logger()
            # 验证logger名称使用当前模块名
            self.assertIn("test_logging", logger.name)

        test_function()

    def test_decorator_when_decorate_class_with_prefix_and_subfix_then_all_methods_use_specified_logger(self):
        """测试装饰器功能:当装饰类并指定前缀和后缀时,类的所有方法都应使用指定的logger名称"""

        @logger_setter(prefix="test.class", subfix="methods")
        class TestClass:
            def method1(self):
                logger = get_logger()
                # 返回logger名称供外部验证
                return logger.name

            @staticmethod
            def static_method():
                logger = get_logger()
                # 返回logger名称供外部验证
                return logger.name

            @classmethod
            def class_method(cls):
                logger = get_logger()
                # 返回logger名称供外部验证
                return logger.name

        # 创建实例并测试方法
        obj = TestClass()
        self.assertEqual(obj.method1(), "test.class.methods")
        self.assertEqual(TestClass.static_method(), "test.class.methods")
        self.assertEqual(TestClass.class_method(), "test.class.methods")

    def test_context_manager_when_use_with_prefix_then_use_specified_logger_and_restore_after_exit(self):
        """测试上下文管理器功能:使用前缀时应在内部使用指定logger,退出后恢复原始logger"""
        # 在上下文管理器外部
        original_logger = get_logger()
        original_name = original_logger.name

        # 在上下文管理器内部
        with logger_setter(prefix="test.context"):
            logger = get_logger()
            # 验证logger名称
            self.assertEqual(logger.name, "test.context")

        # 验证恢复原始logger
        restored_logger = get_logger()
        self.assertEqual(restored_logger.name, original_name)

    def test_context_manager_when_use_with_prefix_and_subfix_then_use_combined_logger_name(self):
        """测试上下文管理器功能:使用前缀和后缀时logger名称应为prefix.subfix格式"""
        # 在上下文管理器外部
        original_logger = get_logger()
        original_name = original_logger.name

        # 在上下文管理器内部,使用前缀和后缀
        with logger_setter(prefix="test.app", subfix="database"):
            logger = get_logger()
            # 验证logger名称
            self.assertEqual(logger.name, "test.app.database")

        # 验证恢复原始logger
        restored_logger = get_logger()
        self.assertEqual(restored_logger.name, original_name)

    def test_context_manager_when_nested_use_then_each_level_has_own_logger_and_restore_correctly(self):
        """测试嵌套上下文管理器:每层应使用自己的logger,退出时正确恢复到上一层的logger"""
        original_logger = get_logger()
        original_name = original_logger.name

        with logger_setter(prefix="outer"):
            outer_logger = get_logger()
            self.assertEqual(outer_logger.name, "outer")

            with logger_setter(prefix="inner", subfix="nested"):
                inner_logger = get_logger()
                self.assertEqual(inner_logger.name, "inner.nested")

            # 验证回到外层
            current_logger = get_logger()
            self.assertEqual(current_logger.name, "outer")

        # 验证回到最外层
        final_logger = get_logger()
        self.assertEqual(final_logger.name, original_name)

    def test_context_manager_when_no_prefix_specified_then_use_calling_module_name(self):
        """测试上下文管理器功能:不指定前缀时应使用调用模块名作为logger名称"""
        original_logger = get_logger()
        original_name = original_logger.name

        # 在上下文管理器内部,不指定 prefix
        with logger_setter():
            logger = get_logger()
            # 验证logger名称使用当前模块名
            self.assertIn("test_logging", logger.name)

        # 验证恢复原始logger
        restored_logger = get_logger()
        self.assertEqual(restored_logger.name, original_name)

    def test_context_manager_when_only_subfix_specified_then_use_calling_module_with_subfix(self):
        """测试上下文管理器功能:只指定后缀时logger名称应为模块名.subfix格式"""
        original_logger = get_logger()
        original_name = original_logger.name

        # 在上下文管理器内部,只指定 subfix
        with logger_setter(subfix="test"):
            logger = get_logger()
            # 验证logger名称使用当前模块名.test
            self.assertTrue(logger.name.endswith(".test"))

        # 验证恢复原始logger
        restored_logger = get_logger()
        self.assertEqual(restored_logger.name, original_name)

    def test_decorator_when_specify_prefix_then_use_specified_prefix(self):
        """测试装饰器功能:指定前缀时应使用指定的前缀作为logger名称"""

        @logger_setter(prefix="msmodelslim.utils.logging")
        def test_function_with_prefix():
            logger = get_logger()
            # 验证logger名称
            self.assertEqual(logger.name, "msmodelslim.utils.logging")

        test_function_with_prefix()

    def test_decorator_when_specify_subfix_then_use_function_module_with_subfix(self):
        """测试装饰器功能:指定后缀时logger名称应为函数模块名.subfix格式"""

        @logger_setter(subfix="custom")
        def test_function_with_subfix():
            logger = get_logger()
            # 验证logger名称使用当前模块名.custom
            self.assertTrue(logger.name.endswith(".custom"))

        test_function_with_subfix()


class TestLoggingFunctions(TestCase):
    """测试其他日志相关函数"""

    def test_get_logger_when_no_name_specified_then_return_default_logger(self):
        """测试get_logger函数:不指定名称时应返回默认logger,指定名称时应返回对应名称的logger"""
        # 测试获取默认 logger
        logger = get_logger()
        self.assertIsInstance(logger, logging.Logger)

        # 测试获取指定名称的 logger
        named_logger = get_logger("test.named")
        self.assertIsInstance(named_logger, logging.Logger)
        self.assertEqual(named_logger.name, "test.named")

    def test_set_logger_level_when_valid_level_then_set_successfully(self):
        """测试set_logger_level函数:设置有效级别时应成功,设置无效级别时应抛出异常"""
        # 测试设置有效的日志级别
        set_logger_level("debug")
        logger = get_logger()
        self.assertEqual(logger.level, logging.DEBUG)

        set_logger_level("info")
        logger = get_logger()
        self.assertEqual(logger.level, logging.INFO)

        set_logger_level("warning")
        logger = get_logger()
        self.assertEqual(logger.level, logging.WARNING)

        set_logger_level("error")
        logger = get_logger()
        self.assertEqual(logger.level, logging.ERROR)

        set_logger_level("critical")
        logger = get_logger()
        self.assertEqual(logger.level, logging.CRITICAL)

        # 测试设置无效的日志级别
        with self.assertRaises(Exception):
            set_logger_level(123)  # 非字符串类型


if __name__ == "__main__":
    import unittest

    unittest.main()