"""
Stateflow 服务层单元测试
测试新增的服务函数以及 current_state 语义修正
"""
from django.test import TestCase
from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType
from stateflow import models, services

User = get_user_model()


class StateFlowServicesTestCase(TestCase):
    """测试订单状态流转逻辑和新增服务函数"""

    def setUp(self):
        """设置测试数据"""
        # 创建测试用户
        self.user = User.objects.create_user(username='testuser', password='testpass')
        
        # 创建状态节点
        self.state1 = models.State.objects.create(name='状态1', description='第一个状态')
        self.state2 = models.State.objects.create(name='状态2', description='第二个状态')
        self.state3 = models.State.objects.create(name='状态3', description='第三个状态')
        
        # 创建流程
        self.process = models.Process.objects.create(name='测试流程', description='用于测试的流程')
        
        # 添加流程节点
        models.ProcessNode.objects.create(process=self.process, state=self.state1, order=0)
        models.ProcessNode.objects.create(process=self.process, state=self.state2, order=1)
        models.ProcessNode.objects.create(process=self.process, state=self.state3, order=2)
        
        # 创建业务对象
        ct = ContentType.objects.get_for_model(models.Process)
        self.business_object = models.BusinessObject.objects.create(
            name='测试订单',
            process=self.process,
            description='测试订单描述',
            content_type=ct,
            object_id=self.process.id
        )
        self.business_object_pending = models.BusinessObject.objects.create(
            name='待开始订单',
            process=self.process,
            description='尚未推进的订单',
            content_type=ct,
            object_id=self.process.id
        )

        self.other_process = models.Process.objects.create(name='其他流程', description='另一个流程用于过滤测试')
        models.ProcessNode.objects.create(process=self.other_process, state=self.state1, order=0)
        self.other_business_object = models.BusinessObject.objects.create(
            name='其他流程订单',
            process=self.other_process,
            description='不同流程的订单',
            content_type=ct,
            object_id=self.other_process.id
        )

    def test_initial_state(self):
        """测试初始状态 - current_state 是下一个待执行节点（第一个节点）"""
        current_state = self.business_object.get_current_state()
        # current_state 返回"下一个待执行的节点"，未开始时是第一个节点
        self.assertEqual(current_state.name, self.state1.name)
        self.assertEqual(self.business_object.get_progress_percentage(), 0.0)
        
        # 整体状态应该是 in_progress（废除了 not_started）
        status = services.get_overall_status(self.business_object)
        self.assertEqual(status, 'in_progress')

    def test_advance_to_next_state(self):
        """测试推进到下一个状态"""
        # 第一次推进应该完成第一个状态
        success, message, _ = services.advance_to_next_state(self.business_object, self.user)
        self.assertTrue(success)
        
        # current_state 返回"下一个待执行的节点"，完成state1后是state2
        current_state = self.business_object.get_current_state()
        self.assertEqual(current_state.name, self.state2.name)
        self.assertAlmostEqual(self.business_object.get_progress_percentage(), 33.33, places=1)
        
        # 推进到状态2（完成state2）
        success, message, _ = services.advance_to_next_state(self.business_object, self.user)
        self.assertTrue(success)
        
        # current_state 应该是 state3（下一个待执行的）
        current_state = self.business_object.get_current_state()
        self.assertEqual(current_state.name, self.state3.name)
        self.assertAlmostEqual(self.business_object.get_progress_percentage(), 66.67, places=1)
        
        # 完成最后一个状态（state3）
        success, message, _ = services.advance_to_next_state(self.business_object, self.user)
        self.assertTrue(success)
        self.assertEqual(self.business_object.get_progress_percentage(), 100.0)
        
        # current_state 应该是 None（没有待执行的节点了）
        current_state = self.business_object.get_current_state()
        self.assertIsNone(current_state)
        
        # 整体状态应该是 completed
        status = services.get_overall_status(self.business_object)
        self.assertEqual(status, 'completed')

    def test_state_status(self):
        """测试状态的状态"""
        # 初始状态，所有状态都应该是 not_started
        self.assertEqual(services.get_business_object_state_status(self.business_object, self.state1), 'not_started')
        self.assertEqual(services.get_business_object_state_status(self.business_object, self.state2), 'not_started')
        
        # 完成第一个状态
        services.advance_to_next_state(self.business_object, self.user)
        self.assertEqual(services.get_business_object_state_status(self.business_object, self.state1), 'completed')
        self.assertEqual(services.get_business_object_state_status(self.business_object, self.state2), 'not_started')
        self.assertEqual(services.get_business_object_state_status(self.business_object, self.state3), 'not_started')
        
        # 完成第二个状态
        services.advance_to_next_state(self.business_object, self.user)
        self.assertEqual(services.get_business_object_state_status(self.business_object, self.state1), 'completed')
        self.assertEqual(services.get_business_object_state_status(self.business_object, self.state2), 'completed')
        self.assertEqual(services.get_business_object_state_status(self.business_object, self.state3), 'not_started')

    def test_timeline(self):
        """测试时间线"""
        timeline = self.business_object.get_timeline()
        self.assertEqual(len(timeline), 3)
        
        # 所有状态都应该是 not_started
        for item in timeline:
            self.assertEqual(item['status'], 'not_started')
        
        # 完成第一个状态
        services.advance_to_next_state(self.business_object, self.user)
        
        timeline = self.business_object.get_timeline()
        self.assertEqual(timeline[0]['status'], 'completed')
        # 第二个状态是 current_state（下一个待执行的节点），还未开始，所以是 not_started
        self.assertEqual(timeline[1]['status'], 'not_started')
        self.assertEqual(timeline[2]['status'], 'not_started')
        self.assertIsNotNone(timeline[0]['completed_by'])
        self.assertEqual(timeline[0]['completed_by'].id, self.user.id)
        self.assertFalse(timeline[0]['is_cancelled'])

    def test_reset_progress(self):
        """测试重置进度 - 不删除记录，而是标记为已撤销"""
        # 完成两个状态
        services.advance_to_next_state(self.business_object, self.user)
        services.advance_to_next_state(self.business_object, self.user)
        
        # 确认有2条日志记录
        self.assertEqual(self.business_object.state_logs.count(), 2)
        
        # 重置进度
        services.reset_business_object_progress(self.business_object)
        
        # 日志记录应该还在（不删除）
        self.assertEqual(self.business_object.state_logs.count(), 2)
        
        # 但所有记录都应该标记为已撤销
        cancelled_count = self.business_object.state_logs.filter(is_cancelled=True).count()
        self.assertEqual(cancelled_count, 2)
        
        # current_state 返回下一个待执行的节点（第一个节点）
        current_state = self.business_object.get_current_state()
        self.assertEqual(current_state.name, self.state1.name)
        
        # 所有撤销的记录都应该有撤销时间
        for log in self.business_object.state_logs.all():
            self.assertTrue(log.is_cancelled)
            self.assertIsNotNone(log.cancelled_at)
    
    def test_timeline_with_cancelled(self):
        """测试包含撤销记录的时间线"""
        # 推进并完成所有状态
        services.advance_to_next_state(self.business_object, self.user)
        services.advance_to_next_state(self.business_object, self.user)
        services.advance_to_next_state(self.business_object, self.user)
        
        # 重置进度
        services.reset_business_object_progress(self.business_object)
        
        timeline = self.business_object.get_timeline()
        
        # 所有状态都应该显示为 cancelled
        for item in timeline:
            self.assertEqual(item['status'], 'cancelled')
            self.assertTrue(item['is_cancelled'])
            self.assertIsNotNone(item['cancelled_at'])
    
    def test_get_next_pending_state(self):
        """测试获取下一个待执行节点（新增功能）"""
        # 创建参数
        param1 = models.StateParameter.objects.create(
            key='param1', value='value1', is_required=True
        )
        param2 = models.StateParameter.objects.create(
            key='param2', value='value2', is_required=False
        )
        self.state1.parameters.add(param1, param2)
        
        # 初始状态 - 应该返回第一个节点
        next_state = services.get_next_pending_state(self.business_object)
        self.assertIsNotNone(next_state)
        self.assertEqual(next_state['state'].name, self.state1.name)
        self.assertEqual(next_state['order'], 0)
        self.assertIn('parameters', next_state)
        self.assertEqual(len(next_state['parameters']), 2)
        
        # 测试不包含参数
        next_state_no_params = services.get_next_pending_state(
            self.business_object, include_parameters=False
        )
        self.assertIsNotNone(next_state_no_params)
        self.assertNotIn('parameters', next_state_no_params)
        
        # 完成第一个状态（提供必填参数）
        success, msg, _ = services.advance_to_next_state(
            self.business_object, 
            self.user, 
            param1='value1'  # 提供必填参数
        )
        self.assertTrue(success)
        
        # 应该返回第二个节点
        next_state = services.get_next_pending_state(self.business_object)
        self.assertIsNotNone(next_state)
        self.assertEqual(next_state['state'].name, self.state2.name)
        self.assertEqual(next_state['order'], 1)
        
        # 完成所有状态
        services.advance_to_next_state(self.business_object, self.user)
        services.advance_to_next_state(self.business_object, self.user)
        
        # 应该返回 None（已完成）
        next_state = services.get_next_pending_state(self.business_object)
        self.assertIsNone(next_state)
    
    def test_get_all_pending_states(self):
        """测试获取所有待执行节点列表（新增功能）"""
        # 初始状态 - 所有节点都待执行
        pending_states = services.get_all_pending_states(self.business_object)
        self.assertEqual(len(pending_states), 3)
        self.assertEqual(pending_states[0]['state'].name, self.state1.name)
        self.assertEqual(pending_states[1]['state'].name, self.state2.name)
        self.assertEqual(pending_states[2]['state'].name, self.state3.name)
        
        # 验证不包含 parameters 字段
        self.assertNotIn('parameters', pending_states[0])
        
        # 完成第一个状态
        services.advance_to_next_state(self.business_object, self.user)
        
        # 应该只剩下两个待执行节点
        pending_states = services.get_all_pending_states(self.business_object)
        self.assertEqual(len(pending_states), 2)
        self.assertEqual(pending_states[0]['state'].name, self.state2.name)
        self.assertEqual(pending_states[1]['state'].name, self.state3.name)
        
        # 完成第二个状态
        services.advance_to_next_state(self.business_object, self.user)
        
        # 应该只剩下一个待执行节点
        pending_states = services.get_all_pending_states(self.business_object)
        self.assertEqual(len(pending_states), 1)
        self.assertEqual(pending_states[0]['state'].name, self.state3.name)
        
        # 完成所有状态
        services.advance_to_next_state(self.business_object, self.user)
        
        # 应该没有待执行节点
        pending_states = services.get_all_pending_states(self.business_object)
        self.assertEqual(len(pending_states), 0)
    
    def test_get_state_parameters(self):
        """测试获取节点参数列表（新增功能）"""
        # 创建必填和可选参数
        required_param = models.StateParameter.objects.create(
            key='required_param', value='required_value', is_required=True
        )
        optional_param1 = models.StateParameter.objects.create(
            key='optional_param1', value='optional_value1', is_required=False
        )
        optional_param2 = models.StateParameter.objects.create(
            key='optional_param2', value='optional_value2', is_required=False
        )
        
        # 关联参数到状态
        self.state1.parameters.add(required_param, optional_param1, optional_param2)
        
        # 获取所有参数
        all_params = services.get_state_parameters(self.state1)
        self.assertEqual(len(all_params), 3)
        
        # 只获取必填参数
        required_params = services.get_state_parameters(self.state1, required_only=True)
        self.assertEqual(len(required_params), 1)
        self.assertEqual(required_params[0].key, 'required_param')
        self.assertTrue(required_params[0].is_required)
        
        # 测试没有参数的状态
        no_params = services.get_state_parameters(self.state2)
        self.assertEqual(len(no_params), 0)
        
        # 测试只有必填参数的状态
        self.state3.parameters.add(required_param)
        params = services.get_state_parameters(self.state3, required_only=True)
        self.assertEqual(len(params), 1)
    
    def test_get_process_nodes(self):
        """测试获取业务对象所属流程的所有状态节点"""
        # 获取流程节点
        nodes = services.get_process_nodes(self.business_object)
        
        # 验证节点数量
        self.assertEqual(len(nodes), 3)
        
        # 验证节点顺序和内容
        self.assertEqual(nodes[0]['state_id'], self.state1.id)
        self.assertEqual(nodes[0]['state_name'], self.state1.name)
        self.assertEqual(nodes[0]['order'], 0)
        
        self.assertEqual(nodes[1]['state_id'], self.state2.id)
        self.assertEqual(nodes[1]['state_name'], self.state2.name)
        self.assertEqual(nodes[1]['order'], 1)
        
        self.assertEqual(nodes[2]['state_id'], self.state3.id)
        self.assertEqual(nodes[2]['state_name'], self.state3.name)
        self.assertEqual(nodes[2]['order'], 2)
        
        # 验证每个节点都有必要的字段
        for node in nodes:
            self.assertIn('id', node)
            self.assertIn('state', node)
            self.assertIn('state_id', node)
            self.assertIn('state_name', node)
            self.assertIn('order', node)
            self.assertIsInstance(node['state'], models.State)

    def test_query_business_objects_by_state_status_filters_by_completion(self):
        """验证按节点状态过滤业务对象的服务函数"""
        # 初始状态：两个业务对象都未开始 state1
        qs = services.query_business_objects_by_state_status(
            state_ids=[self.state1.id],
            status='not_started',
            process_id=self.process.id,
        )
        ids = set(qs.values_list('id', flat=True))
        self.assertIn(self.business_object.id, ids)
        self.assertIn(self.business_object_pending.id, ids)
        self.assertNotIn(self.other_business_object.id, ids)

        # 推进第一个业务对象，变为已完成 state1
        services.advance_to_next_state(self.business_object, self.user)

        qs = services.query_business_objects_by_state_status(
            state_ids=[self.state1.id],
            status='not_started',
            process_id=self.process.id,
        )
        ids = set(qs.values_list('id', flat=True))
        self.assertNotIn(self.business_object.id, ids)
        self.assertIn(self.business_object_pending.id, ids)

        qs_completed = services.query_business_objects_by_state_status(
            state_ids=[self.state1.id],
            status='completed',
        )
        completed_ids = set(qs_completed.values_list('id', flat=True))
        self.assertIn(self.business_object.id, completed_ids)
        self.assertNotIn(self.business_object_pending.id, completed_ids)

    def test_query_business_objects_by_state_status_cancelled(self):
        """撤销记录应该匹配 cancelled 状态"""
        services.advance_to_next_state(self.business_object_pending, self.user)
        services.reset_business_object_progress(self.business_object_pending)
        qs = services.query_business_objects_by_state_status(
            state_ids=[self.state1.id],
            status='cancelled',
        )
        ids = set(qs.values_list('id', flat=True))
        self.assertIn(self.business_object_pending.id, ids)

    def test_query_business_objects_by_state_status_invalid_status(self):
        """非法状态值应抛出异常"""
        with self.assertRaises(ValueError):
            services.query_business_objects_by_state_status(status='unknown')
