# 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 unittest
from typing import List
from olc.control.handler_chain.box_handler import AbsBoxHandler
from olc.control.handler_chain.box_handler_chain_builder import OlcHandlerChain, DefaultBoxHandlerChain
from olc.control.context.context import OlcContext
from olc.bean.tag_group import TagGroup
from olc.bean.olc_control_request import OlcControlRequest


class MockHandler(AbsBoxHandler):
    """覆盖 do_in_coming / do_out_coming(业务),不再管链路推进"""

    def __init__(self, name, next_handler=None, private_handler=None):
        super().__init__(next_handler, private_handler)
        self.name = name
        self.in_coming_called = False
        self.out_coming_called = False
        self.in_coming_context = None
        self.in_coming_groups = None
        self.out_coming_context = None

    def do_in_coming(self, context: OlcContext, groups: List[TagGroup]):
        self.in_coming_called = True
        self.in_coming_context = context
        self.in_coming_groups = groups
        return None

    def do_out_coming(self, context: OlcContext):
        self.out_coming_called = True
        self.out_coming_context = context


class ExceptionHandler(AbsBoxHandler):
    def do_in_coming(self, context: OlcContext, groups: List[TagGroup]):
        raise Exception("Test exception in in_coming")

    def do_out_coming(self, context: OlcContext):
        raise Exception("Test exception in out_coming")


class TestHandlerChain(unittest.TestCase):
    def setUp(self):
        # 重置单例
        OlcHandlerChain.chain = DefaultBoxHandlerChain()

    def test_add_handler(self):
        """测试添加处理器到调用链"""
        handler1 = MockHandler("handler1")
        handler2 = MockHandler("handler2")
        handler3 = MockHandler("handler3")

        OlcHandlerChain.add_last(handler1)
        OlcHandlerChain.add_last(handler2)
        OlcHandlerChain.add_last(handler3)

        chain = OlcHandlerChain.get_chain()
        self.assertIsNotNone(chain.first_box)

    def test_in_coming_execution_order(self):
        handler1 = MockHandler("handler1")
        handler2 = MockHandler("handler2")
        handler3 = MockHandler("handler3")

        OlcHandlerChain.add_last(handler1)
        OlcHandlerChain.add_last(handler2)
        OlcHandlerChain.add_last(handler3)

        context = OlcContext(OlcControlRequest())
        groups = [TagGroup(domain="test", name="group1")]

        chain = OlcHandlerChain.get_chain()
        chain.in_coming(context, groups)

        self.assertTrue(handler1.in_coming_called)
        self.assertTrue(handler2.in_coming_called)
        self.assertTrue(handler3.in_coming_called)
        self.assertEqual(handler1.in_coming_context, context)
        self.assertEqual(handler2.in_coming_context, context)
        self.assertEqual(handler3.in_coming_context, context)
        self.assertEqual(handler1.in_coming_groups, groups)
        self.assertEqual(handler2.in_coming_groups, groups)
        self.assertEqual(handler3.in_coming_groups, groups)

    def test_out_coming_execution_order(self):
        handler1 = MockHandler("handler1")
        handler2 = MockHandler("handler2")
        handler3 = MockHandler("handler3")

        OlcHandlerChain.add_last(handler1)
        OlcHandlerChain.add_last(handler2)
        OlcHandlerChain.add_last(handler3)

        context = OlcContext(OlcControlRequest())

        chain = OlcHandlerChain.get_chain()
        chain.out_coming(context)

        self.assertTrue(handler1.out_coming_called)
        self.assertTrue(handler2.out_coming_called)
        self.assertTrue(handler3.out_coming_called)
        self.assertEqual(handler1.out_coming_context, context)
        self.assertEqual(handler2.out_coming_context, context)
        self.assertEqual(handler3.out_coming_context, context)

    def test_empty_chain(self):
        """测试空调用链"""
        context = OlcContext(OlcControlRequest())
        groups = [TagGroup(domain="test", name="group1")]

        chain = OlcHandlerChain.get_chain()
        # 空链应该不会抛出异常
        chain.in_coming(context, groups)
        chain.out_coming(context)

    def test_exception_handling_in_coming(self):
        """测试 do_in_coming 方法的异常会向上传播"""
        exception_handler = ExceptionHandler()
        OlcHandlerChain.add_last(exception_handler)

        context = OlcContext(OlcControlRequest())
        groups = [TagGroup(domain="test", name="group1")]

        chain = OlcHandlerChain.get_chain()
        # 异常应该被传播
        with self.assertRaises(Exception):
            chain.in_coming(context, groups)

    def test_exception_handling_out_coming(self):
        """测试 do_out_coming 方法的异常会向上传播"""
        exception_handler = ExceptionHandler()
        OlcHandlerChain.add_last(exception_handler)

        context = OlcContext(OlcControlRequest())

        chain = OlcHandlerChain.get_chain()
        # 异常应该被传播
        with self.assertRaises(Exception):
            chain.out_coming(context)

    def test_stop_flag(self):
        """v3 stop 语义:
        - 默认 stop_aware=True:context.stop=True 时跳过下游 do_in_coming(业务)
        - 但链路推进仍然继续:handler 节点本身仍被访问
        - 如果下游 handler 设置 stop_aware=False,则即便 stop=True 也执行业务
        """
        class StopHandler(AbsBoxHandler):
            def do_in_coming(self, context: OlcContext, groups: List[TagGroup]):
                context.stop = True
                return None

            def do_out_coming(self, context: OlcContext):
                pass

        stop_handler = StopHandler()
        handler_after_stop = MockHandler("handler_after_stop")  # 默认 stop_aware=True

        OlcHandlerChain.add_last(stop_handler)
        OlcHandlerChain.add_last(handler_after_stop)

        context = OlcContext(OlcControlRequest())
        groups = [TagGroup(domain="test", name="group1")]

        chain = OlcHandlerChain.get_chain()
        chain.in_coming(context, groups)

        # v3 行为:stop=True 后,stop_aware=True 的下游业务被跳过
        self.assertFalse(handler_after_stop.in_coming_called)

    def test_stop_flag_with_stop_unaware_handler(self):
        """stop_aware=False 的 handler 即便 stop=True 也照常执行业务(如 StatisticHandler)"""
        class StopHandler(AbsBoxHandler):
            def do_in_coming(self, context: OlcContext, groups: List[TagGroup]):
                context.stop = True
                return None

            def do_out_coming(self, context: OlcContext):
                pass

        class StopUnawareHandler(MockHandler):
            stop_aware = False

        stop_handler = StopHandler()
        stop_unaware = StopUnawareHandler("unaware")

        OlcHandlerChain.add_last(stop_handler)
        OlcHandlerChain.add_last(stop_unaware)

        context = OlcContext(OlcControlRequest())
        groups = [TagGroup(domain="test", name="group1")]

        chain = OlcHandlerChain.get_chain()
        chain.in_coming(context, groups)

        # stop_aware=False → 业务照常执行
        self.assertTrue(stop_unaware.in_coming_called)


if __name__ == '__main__':
    unittest.main()