package com.njcn.rdms.module.project.service.product;

import com.njcn.rdms.framework.common.exception.ServiceException;
import com.njcn.rdms.framework.security.core.util.SecurityFrameworkUtils;
import com.njcn.rdms.framework.test.core.ut.BaseMockitoUnitTest;
import com.njcn.rdms.module.project.controller.admin.product.vo.requirement.*;
import com.njcn.rdms.module.project.dal.dataobject.audit.BizAuditLogDO;
import com.njcn.rdms.module.project.dal.dataobject.product.ProductRequirementDO;
import com.njcn.rdms.module.project.dal.dataobject.product.ProductRequirementModuleDO;
import com.njcn.rdms.module.project.dal.dataobject.product.ProductRequirementStatusLogDO;
import com.njcn.rdms.module.project.dal.dataobject.status.ObjectStatusTransitionDO;
import com.njcn.rdms.module.project.dal.mysql.audit.BizAuditLogMapper;
import com.njcn.rdms.module.project.dal.mysql.product.ProductRequirementMapper;
import com.njcn.rdms.module.project.dal.mysql.product.ProductRequirementModuleMapper;
import com.njcn.rdms.module.project.dal.mysql.product.ProductRequirementStatusLogMapper;
import com.njcn.rdms.module.project.dal.mysql.status.ObjectStatusModelMapper;
import com.njcn.rdms.module.project.dal.mysql.status.ObjectStatusTransitionMapper;
import com.njcn.rdms.module.project.enums.ErrorCodeConstants;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockedStatic;

import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;

/**
 * 产品需求 Service 单元测试
 */
class ProductRequirementServiceImplTest extends BaseMockitoUnitTest {

    @InjectMocks
    private ProductRequirementServiceImpl requirementService;
    @Mock
    private ProductRequirementMapper requirementMapper;
    @Mock
    private ProductRequirementModuleMapper moduleMapper;
    @Mock
    private ProductRequirementStatusLogMapper statusLogMapper;
    @Mock
    private BizAuditLogMapper bizAuditLogMapper;
    @Mock
    private ObjectStatusTransitionMapper statusTransitionMapper;
    @Mock
    private ObjectStatusModelMapper statusModelMapper;

    // ========== 创建需求测试 ==========

    @Test
    void createRequirement_withoutReview_shouldCreateWithPendingDispatchStatus() {
        Long loginUserId = 1001L;
        Long defaultModuleId = 50L;
        ProductRequirementSaveReqVO reqVO = new ProductRequirementSaveReqVO();
        reqVO.setProductId(100L);
        reqVO.setTitle("测试需求");
        reqVO.setDescription("测试描述");
        reqVO.setCategory("function");
        reqVO.setReviewRequired(0); // 不需要评审
        reqVO.setPriority(1);
        reqVO.setProposerId(2001L);
        reqVO.setCurrentHandlerUserId(2002L);
        reqVO.setCompletionDate(LocalDateTime.now());

        // 模拟"全部需求"模块存在（parentId = 0L 的根模块）
        ProductRequirementModuleDO defaultModule = createDefaultModule(defaultModuleId, 100L);
        when(moduleMapper.selectByProductIdAndParentId(100L, 0L)).thenReturn(defaultModule);

        try (MockedStatic<SecurityFrameworkUtils> mockedStatic = mockLoginUser(loginUserId, "测试人")) {
            requirementService.createRequirement(reqVO);
        }

        ArgumentCaptor<ProductRequirementDO> captor = ArgumentCaptor.forClass(ProductRequirementDO.class);
        verify(requirementMapper, times(1)).insert(captor.capture());
        ProductRequirementDO created = captor.getValue();
        assertEquals("pending_dispatch", created.getStatusCode()); // 不需要评审时初始状态为待分流
        assertEquals("manual", created.getSourceType());
        assertEquals(0L, created.getParentId());
        assertEquals("测试需求", created.getTitle());
        assertEquals(defaultModuleId, created.getModuleId()); // 未选择模块时自动归属到"全部需求"模块
        assertEquals(100L, created.getProductId());
    }

    @Test
    void createRequirement_withReview_shouldCreateWithPendingReviewStatus() {
        Long loginUserId = 1001L;
        Long defaultModuleId = 50L;
        ProductRequirementSaveReqVO reqVO = new ProductRequirementSaveReqVO();
        reqVO.setProductId(100L);
        reqVO.setTitle("需评审的需求");
        reqVO.setCategory("function");
        reqVO.setReviewRequired(1); // 需要评审
        reqVO.setPriority(2);
        reqVO.setProposerId(2001L);
        reqVO.setCompletionDate(LocalDateTime.now());

        // 模拟"全部需求"模块存在（parentId = 0L 的根模块）
        ProductRequirementModuleDO defaultModule = createDefaultModule(defaultModuleId, 100L);
        when(moduleMapper.selectByProductIdAndParentId(100L, 0L)).thenReturn(defaultModule);

        try (MockedStatic<SecurityFrameworkUtils> mockedStatic = mockLoginUser(loginUserId, "测试人")) {
            requirementService.createRequirement(reqVO);
        }

        ArgumentCaptor<ProductRequirementDO> captor = ArgumentCaptor.forClass(ProductRequirementDO.class);
        verify(requirementMapper, times(1)).insert(captor.capture());
        ProductRequirementDO created = captor.getValue();
        assertEquals("pending_review", created.getStatusCode()); // 需要评审时初始状态为待评审
        assertEquals(defaultModuleId, created.getModuleId()); // 未选择模块时自动归属到"全部需求"模块
    }

    // ========== 更新需求测试 ==========

    @Test
    void updateRequirement_whenTerminalStatus_shouldThrowException() {
        Long requirementId = 1001L;
        ProductRequirementDO requirement = createRequirement(requirementId, 100L, "已关闭需求",
                "closed", 0L, 0);
        ProductRequirementUpdateReqVO reqVO = new ProductRequirementUpdateReqVO();
        reqVO.setId(requirementId);
        reqVO.setProductId(100L);
        reqVO.setTitle("修改标题");
        reqVO.setCategory("function");
        reqVO.setReviewRequired(0);
        reqVO.setPriority(1);
        reqVO.setProposerId(2001L);
        reqVO.setCompletionDate(LocalDateTime.now());

        when(requirementMapper.selectById(requirementId)).thenReturn(requirement);

        ServiceException ex = assertThrows(ServiceException.class,
                () -> requirementService.updateRequirement(reqVO));
        assertEquals(ErrorCodeConstants.REQUIREMENT_STATUS_NOT_ALLOW_EDIT.getCode(), ex.getCode());
        verify(requirementMapper, never()).updateById(any(ProductRequirementDO.class));
    }

    @Test
    void updateRequirement_whenNormalStatus_shouldUpdateSuccessfully() {
        Long requirementId = 1002L;
        Long loginUserId = 1001L;
        Long defaultModuleId = 50L;
        ProductRequirementDO requirement = createRequirement(requirementId, 100L, "待分流需求",
                "pending_dispatch", 0L, 0);
        ProductRequirementUpdateReqVO reqVO = new ProductRequirementUpdateReqVO();
        reqVO.setId(requirementId);
        reqVO.setProductId(100L);
        reqVO.setTitle("修改后的标题");
        reqVO.setCategory("security");
        reqVO.setReviewRequired(0);
        reqVO.setPriority(2);
        reqVO.setProposerId(2001L);
        reqVO.setCompletionDate(LocalDateTime.now());

        // 模拟"全部需求"模块存在（未选择模块时自动归属，parentId = 0L 的根模块）
        ProductRequirementModuleDO defaultModule = createDefaultModule(defaultModuleId, 100L);
        when(moduleMapper.selectByProductIdAndParentId(100L, 0L)).thenReturn(defaultModule);
        when(requirementMapper.selectById(requirementId)).thenReturn(requirement);

        try (MockedStatic<SecurityFrameworkUtils> mockedStatic = mockLoginUser(loginUserId, "测试人")) {
            requirementService.updateRequirement(reqVO);
        }

        ArgumentCaptor<ProductRequirementDO> captor = ArgumentCaptor.forClass(ProductRequirementDO.class);
        verify(requirementMapper, times(1)).updateById(captor.capture());
        assertEquals(defaultModuleId, captor.getValue().getModuleId()); // 未选择模块时自动归属到"全部需求"模块
        verify(bizAuditLogMapper, times(1)).insert(any(BizAuditLogDO.class));
    }

    // ========== 状态变更测试 ==========

    @Test
    void changeRequirementStatus_whenActionAllowed_shouldUpdateStatusAndWriteLogs() {
        Long requirementId = 1003L;
        Long loginUserId = 1001L;
        ProductRequirementDO requirement = createRequirement(requirementId, 100L, "待分流需求",
                "pending_dispatch", 0L, 0);
        ProductRequirementStatusActionReqVO reqVO = new ProductRequirementStatusActionReqVO();
        reqVO.setId(requirementId);
        reqVO.setActionCode("dispatch");
        reqVO.setReason(null);

        when(requirementMapper.selectById(requirementId)).thenReturn(requirement);
        when(statusTransitionMapper.selectByObjectTypeAndFromStatusAndAction(
                "product_requirement", "pending_dispatch", "dispatch"))
                .thenReturn(createTransition("dispatch", "implementing", false));
        when(requirementMapper.updateStatusByIdAndStatus(requirementId, "pending_dispatch", "implementing", null))
                .thenReturn(1);

        try (MockedStatic<SecurityFrameworkUtils> mockedStatic = mockLoginUser(loginUserId, "测试人")) {
            requirementService.changeRequirementStatus(reqVO);
        }

        verify(requirementMapper, times(1))
                .updateStatusByIdAndStatus(requirementId, "pending_dispatch", "implementing", null);
        verify(statusLogMapper, times(1)).insert(any(ProductRequirementStatusLogDO.class));
        verify(bizAuditLogMapper, times(1)).insert(any(BizAuditLogDO.class));
    }

    @Test
    void changeRequirementStatus_whenActionNotAllowed_shouldThrowException() {
        Long requirementId = 1004L;
        ProductRequirementDO requirement = createRequirement(requirementId, 100L, "已关闭需求",
                "closed", 0L, 0);
        ProductRequirementStatusActionReqVO reqVO = new ProductRequirementStatusActionReqVO();
        reqVO.setId(requirementId);
        reqVO.setActionCode("dispatch");

        when(requirementMapper.selectById(requirementId)).thenReturn(requirement);
        when(statusTransitionMapper.selectByObjectTypeAndFromStatusAndAction(
                "product_requirement", "closed", "dispatch"))
                .thenReturn(null);

        ServiceException ex = assertThrows(ServiceException.class,
                () -> requirementService.changeRequirementStatus(reqVO));
        assertEquals(ErrorCodeConstants.REQUIREMENT_STATUS_ACTION_NOT_ALLOWED.getCode(), ex.getCode());
    }

    @Test
    void changeRequirementStatus_whenReasonRequiredButMissing_shouldThrowException() {
        Long requirementId = 1005L;
        ProductRequirementDO requirement = createRequirement(requirementId, 100L, "待分流需求",
                "pending_dispatch", 0L, 0);
        ProductRequirementStatusActionReqVO reqVO = new ProductRequirementStatusActionReqVO();
        reqVO.setId(requirementId);
        reqVO.setActionCode("cancel");
        reqVO.setReason("   "); // 空原因

        when(requirementMapper.selectById(requirementId)).thenReturn(requirement);
        when(statusTransitionMapper.selectByObjectTypeAndFromStatusAndAction(
                "product_requirement", "pending_dispatch", "cancel"))
                .thenReturn(createTransition("cancel", "cancelled", true));

        ServiceException ex = assertThrows(ServiceException.class,
                () -> requirementService.changeRequirementStatus(reqVO));
        assertEquals(ErrorCodeConstants.REQUIREMENT_STATUS_ACTION_REASON_REQUIRED.getCode(), ex.getCode());
    }

    @Test
    void changeRequirementStatus_whenConcurrentModified_shouldThrowException() {
        Long requirementId = 1006L;
        Long loginUserId = 1001L;
        ProductRequirementDO requirement = createRequirement(requirementId, 100L, "待分流需求",
                "pending_dispatch", 0L, 0);
        ProductRequirementStatusActionReqVO reqVO = new ProductRequirementStatusActionReqVO();
        reqVO.setId(requirementId);
        reqVO.setActionCode("dispatch");

        when(requirementMapper.selectById(requirementId)).thenReturn(requirement);
        when(statusTransitionMapper.selectByObjectTypeAndFromStatusAndAction(
                "product_requirement", "pending_dispatch", "dispatch"))
                .thenReturn(createTransition("dispatch", "implementing", false));
        when(requirementMapper.updateStatusByIdAndStatus(requirementId, "pending_dispatch", "implementing", null))
                .thenReturn(0); // 并发修改，更新失败

        try (MockedStatic<SecurityFrameworkUtils> mockedStatic = mockLoginUser(loginUserId, "测试人")) {
            ServiceException ex = assertThrows(ServiceException.class,
                    () -> requirementService.changeRequirementStatus(reqVO));
            assertEquals(ErrorCodeConstants.REQUIREMENT_STATUS_CONCURRENT_MODIFIED.getCode(), ex.getCode());
        }
        verify(statusLogMapper, never()).insert(any(ProductRequirementStatusLogDO.class));
        verify(bizAuditLogMapper, never()).insert(any(BizAuditLogDO.class));
    }

    // ========== 删除需求测试 ==========

    @Test
    void deleteRequirement_shouldDeleteByConditionAndWriteLogs() {
        Long requirementId = 1007L;
        Long loginUserId = 1001L;
        ProductRequirementDO requirement = createRequirement(requirementId, 100L, "待分流需求",
                "pending_dispatch", 0L, 0);

        when(requirementMapper.selectById(requirementId)).thenReturn(requirement);
        when(requirementMapper.deleteByIdAndStatus(requirementId, "pending_dispatch")).thenReturn(1);

        try (MockedStatic<SecurityFrameworkUtils> mockedStatic = mockLoginUser(loginUserId, "测试人")) {
            requirementService.deleteRequirement(requirementId, 100L);
        }

        verify(requirementMapper, times(1)).deleteByIdAndStatus(requirementId, "pending_dispatch");
        verify(bizAuditLogMapper, times(1)).insert(any(BizAuditLogDO.class));
    }

    // ========== 拆分需求测试 ==========

    @Test
    void splitRequirement_whenParentPendingDispatch_shouldChangeParentToImplementing() {
        Long parentId = 1008L;
        Long loginUserId = 1001L;
        ProductRequirementDO parent = createRequirement(parentId, 100L, "大需求",
                "pending_dispatch", 0L, 0);
        parent.setModuleId(50L);
        parent.setProposerId(2001L);
        ProductRequirementSplitReqVO reqVO = new ProductRequirementSplitReqVO();
        reqVO.setParentId(parentId);
        reqVO.setProductId(100L);
        reqVO.setTitle("子需求");
        reqVO.setCategory("function");
        reqVO.setPriority(1);
        reqVO.setCompletionDate(LocalDateTime.now());

        when(requirementMapper.selectById(parentId)).thenReturn(parent);
        when(requirementMapper.updateStatusByIdAndStatus(parentId, "pending_dispatch", "implementing", null))
                .thenReturn(1);

        try (MockedStatic<SecurityFrameworkUtils> mockedStatic = mockLoginUser(loginUserId, "测试人")) {
            requirementService.splitRequirement(reqVO);
        }

        // 验证子需求被创建
        ArgumentCaptor<ProductRequirementDO> childCaptor = ArgumentCaptor.forClass(ProductRequirementDO.class);
        verify(requirementMapper, times(1)).insert(childCaptor.capture());
        ProductRequirementDO child = childCaptor.getValue();
        assertEquals(parentId, child.getParentId());
        assertEquals("pending_dispatch", child.getStatusCode());
        assertEquals(parent.getModuleId(), child.getModuleId());
        assertEquals(parent.getProposerId(), child.getProposerId());

        // 验证父需求状态变为实施中
        verify(requirementMapper, times(1))
                .updateStatusByIdAndStatus(parentId, "pending_dispatch", "implementing", null);
        verify(statusLogMapper, times(1)).insert(any(ProductRequirementStatusLogDO.class));
    }

    @Test
    void splitRequirement_whenParentImplementing_shouldNotChangeParentStatus() {
        Long parentId = 1009L;
        Long loginUserId = 1001L;
        ProductRequirementDO parent = createRequirement(parentId, 100L, "实施中的大需求",
                "implementing", 0L, 0);
        parent.setModuleId(50L);
        parent.setProposerId(2001L);
        ProductRequirementSplitReqVO reqVO = new ProductRequirementSplitReqVO();
        reqVO.setParentId(parentId);
        reqVO.setProductId(100L);
        reqVO.setTitle("子需求2");
        reqVO.setCategory("function");
        reqVO.setPriority(1);
        reqVO.setCompletionDate(LocalDateTime.now());

        when(requirementMapper.selectById(parentId)).thenReturn(parent);

        try (MockedStatic<SecurityFrameworkUtils> mockedStatic = mockLoginUser(loginUserId, "测试人")) {
            requirementService.splitRequirement(reqVO);
        }

        // 父需求已经是实施中，不需要再更新状态
        verify(requirementMapper, never())
                .updateStatusByIdAndStatus(parentId, "implementing", "implementing", null);
    }

    @Test
    void splitRequirement_whenParentNotAllowSplit_shouldThrowException() {
        Long parentId = 1010L;
        ProductRequirementDO parent = createRequirement(parentId, 100L, "已验收的大需求",
                "accepted", 0L, 0);
        ProductRequirementSplitReqVO reqVO = new ProductRequirementSplitReqVO();
        reqVO.setParentId(parentId);
        reqVO.setProductId(100L);
        reqVO.setTitle("子需求");
        reqVO.setCategory("function");
        reqVO.setPriority(1);
        reqVO.setCompletionDate(LocalDateTime.now());

        when(requirementMapper.selectById(parentId)).thenReturn(parent);

        ServiceException ex = assertThrows(ServiceException.class,
                () -> requirementService.splitRequirement(reqVO));
        assertEquals(ErrorCodeConstants.REQUIREMENT_PARENT_NOT_ALLOW_SPLIT.getCode(), ex.getCode());
        verify(requirementMapper, never()).insert(any(ProductRequirementDO.class));
    }

    // ========== 关闭需求测试 ==========

    @Test
    void closeRequirement_whenAccepted_shouldCloseSuccessfully() {
        Long requirementId = 1011L;
        Long loginUserId = 1001L;
        ProductRequirementDO requirement = createRequirement(requirementId, 100L, "已验收需求",
                "accepted", 0L, 0);
        ProductRequirementCloseReqVO reqVO = new ProductRequirementCloseReqVO();
        reqVO.setId(requirementId);
        reqVO.setProductId(100L);
        reqVO.setReason("需求已完成");

        when(requirementMapper.selectById(requirementId)).thenReturn(requirement);
        when(requirementMapper.selectListByParentId(requirementId)).thenReturn(Collections.emptyList());
        when(requirementMapper.updateStatusByIdAndStatus(requirementId, "accepted", "closed", "需求已完成"))
                .thenReturn(1);

        try (MockedStatic<SecurityFrameworkUtils> mockedStatic = mockLoginUser(loginUserId, "测试人")) {
            requirementService.closeRequirement(reqVO);
        }

        verify(requirementMapper, times(1))
                .updateStatusByIdAndStatus(requirementId, "accepted", "closed", "需求已完成");
        verify(statusLogMapper, times(1)).insert(any(ProductRequirementStatusLogDO.class));
        verify(bizAuditLogMapper, times(1)).insert(any(BizAuditLogDO.class));
    }

    @Test
    void closeRequirement_whenNotAccepted_shouldThrowException() {
        Long requirementId = 1012L;
        ProductRequirementDO requirement = createRequirement(requirementId, 100L, "实施中需求",
                "implementing", 0L, 0);
        ProductRequirementCloseReqVO reqVO = new ProductRequirementCloseReqVO();
        reqVO.setId(requirementId);
        reqVO.setProductId(100L);
        reqVO.setReason("需求已完成");

        when(requirementMapper.selectById(requirementId)).thenReturn(requirement);

        ServiceException ex = assertThrows(ServiceException.class,
                () -> requirementService.closeRequirement(reqVO));
        assertEquals(ErrorCodeConstants.REQUIREMENT_STATUS_NOT_ALLOW_CLOSE.getCode(), ex.getCode());
    }

    @Test
    void closeRequirement_withChildren_shouldCascadeCloseAcceptedChildren() {
        Long parentId = 1013L;
        Long childId = 1014L;
        Long loginUserId = 1001L;
        ProductRequirementDO parent = createRequirement(parentId, 100L, "父需求",
                "accepted", 0L, 0);
        ProductRequirementDO child = createRequirement(childId, 100L, "子需求",
                "accepted", parentId, 0);

        ProductRequirementCloseReqVO reqVO = new ProductRequirementCloseReqVO();
        reqVO.setId(parentId);
        reqVO.setProductId(100L);
        reqVO.setReason("全部验收完成");

        when(requirementMapper.selectById(parentId)).thenReturn(parent);
        when(requirementMapper.selectListByParentId(parentId)).thenReturn(List.of(child));
        when(requirementMapper.updateStatusByIdAndStatus(childId, "accepted", "closed", "全部验收完成"))
                .thenReturn(1);
        when(requirementMapper.updateStatusByIdAndStatus(parentId, "accepted", "closed", "全部验收完成"))
                .thenReturn(1);

        try (MockedStatic<SecurityFrameworkUtils> mockedStatic = mockLoginUser(loginUserId, "测试人")) {
            requirementService.closeRequirement(reqVO);
        }

        // 子需求被关闭
        verify(requirementMapper, times(1))
                .updateStatusByIdAndStatus(childId, "accepted", "closed", "全部验收完成");
        // 父需求被关闭
        verify(requirementMapper, times(1))
                .updateStatusByIdAndStatus(parentId, "accepted", "closed", "全部验收完成");
    }

    @Test
    void closeRequirement_withChildNotAllowClose_shouldThrowException() {
        Long parentId = 1015L;
        ProductRequirementDO parent = createRequirement(parentId, 100L, "父需求",
                "accepted", 0L, 0);
        ProductRequirementDO child = createRequirement(1016L, 100L, "实施中子需求",
                "implementing", parentId, 0);

        ProductRequirementCloseReqVO reqVO = new ProductRequirementCloseReqVO();
        reqVO.setId(parentId);
        reqVO.setProductId(100L);
        reqVO.setReason("全部验收完成");

        when(requirementMapper.selectById(parentId)).thenReturn(parent);
        when(requirementMapper.selectListByParentId(parentId)).thenReturn(List.of(child));

        ServiceException ex = assertThrows(ServiceException.class,
                () -> requirementService.closeRequirement(reqVO));
        assertEquals(ErrorCodeConstants.REQUIREMENT_CHILD_NOT_ALLOW_CLOSE.getCode(), ex.getCode());
    }

    // ========== 模块管理测试 ==========

    @Test
    void createRequirementModule_whenNameDuplicate_shouldThrowException() {
        Long productId = 100L;
        ProductRequirementModuleDO existModule = new ProductRequirementModuleDO();
        existModule.setId(50L);
        existModule.setProductId(productId);
        existModule.setModuleName("核心功能");

        ProductRequirementModuleReqVO reqVO = new ProductRequirementModuleReqVO();
        reqVO.setProductId(productId);
        reqVO.setModuleName("核心功能");

        when(moduleMapper.selectByProductIdAndModuleName(productId, "核心功能")).thenReturn(existModule);

        ServiceException ex = assertThrows(ServiceException.class,
                () -> requirementService.createRequirementModule(reqVO));
        assertEquals(ErrorCodeConstants.REQUIREMENT_MODULE_NAME_DUPLICATE.getCode(), ex.getCode());
    }

    @Test
    void deleteRequirementModule_whenHasNonTerminalRequirements_shouldThrowException() {
        Long moduleId = 50L;
        Long productId = 100L;
        ProductRequirementModuleDO module = new ProductRequirementModuleDO();
        module.setId(moduleId);
        module.setProductId(productId);
        module.setModuleName("核心功能");

        ProductRequirementDO nonTerminalReq = createRequirement(2001L, productId, "实施中需求",
                "implementing", 0L, 0);
        nonTerminalReq.setModuleId(moduleId);

        when(moduleMapper.selectById(moduleId)).thenReturn(module);
        when(requirementMapper.selectListByModuleId(moduleId)).thenReturn(List.of(nonTerminalReq));

        ServiceException ex = assertThrows(ServiceException.class,
                () -> requirementService.deleteRequirementModule(moduleId, productId));
        assertEquals(ErrorCodeConstants.REQUIREMENT_MODULE_HAS_NON_TERMINAL_REQUIREMENTS.getCode(), ex.getCode());
        verify(moduleMapper, never()).deleteById(moduleId);
    }

    @Test
    void deleteRequirementModule_whenAllTerminal_shouldDeleteSuccessfully() {
        Long moduleId = 50L;
        Long productId = 100L;
        Long loginUserId = 1001L;
        ProductRequirementModuleDO module = new ProductRequirementModuleDO();
        module.setId(moduleId);
        module.setProductId(productId);
        module.setModuleName("核心功能");

        ProductRequirementDO closedReq = createRequirement(2001L, productId, "已关闭需求",
                "closed", 0L, 0);
        closedReq.setModuleId(moduleId);

        when(moduleMapper.selectById(moduleId)).thenReturn(module);
        when(requirementMapper.selectListByModuleId(moduleId)).thenReturn(List.of(closedReq));

        try (MockedStatic<SecurityFrameworkUtils> mockedStatic = mockLoginUser(loginUserId, "测试人")) {
            requirementService.deleteRequirementModule(moduleId, productId);
        }

        verify(requirementMapper, times(1)).deleteById(2001L);
        verify(moduleMapper, times(1)).deleteById(moduleId);
    }

    // ========== resolveModuleId 测试 ==========

    @Test
    void resolveModuleId_whenModuleIdIsNull_shouldReturnDefaultModuleId() {
        Long defaultModuleId = 50L;
        Long productId = 100L;
        ProductRequirementModuleDO defaultModule = createDefaultModule(defaultModuleId, productId);

        when(moduleMapper.selectByProductIdAndParentId(productId, 0L)).thenReturn(defaultModule);

        Long result = requirementService.resolveModuleId(null, productId);

        assertEquals(defaultModuleId, result);
    }

    @Test
    void resolveModuleId_whenModuleIdProvided_shouldReturnSameId() {
        Long moduleId = 60L;
        Long productId = 100L;

        // 传入有效moduleId时直接返回，不查询数据库
        Long result = requirementService.resolveModuleId(moduleId, productId);

        assertEquals(moduleId, result);
        verify(moduleMapper, never()).selectByProductIdAndParentId(any(), any());
    }

    // ========== isAllRequirementsModule 测试 ==========

    @Test
    void isAllRequirementsModule_whenRootModule_shouldReturnTrue() {
        Long moduleId = 50L;
        ProductRequirementModuleDO rootModule = createDefaultModule(moduleId, 100L);

        when(moduleMapper.selectById(moduleId)).thenReturn(rootModule);

        boolean result = requirementService.isAllRequirementsModule(moduleId);

        assertEquals(true, result);
    }

    @Test
    void isAllRequirementsModule_whenChildModule_shouldReturnFalse() {
        Long moduleId = 60L;
        ProductRequirementModuleDO childModule = new ProductRequirementModuleDO();
        childModule.setId(moduleId);
        childModule.setProductId(100L);
        childModule.setParentId(50L); // parentId != 0
        childModule.setModuleName("子模块");

        when(moduleMapper.selectById(moduleId)).thenReturn(childModule);

        boolean result = requirementService.isAllRequirementsModule(moduleId);

        assertEquals(false, result);
    }

    @Test
    void isAllRequirementsModule_whenModuleNotExists_shouldReturnFalse() {
        Long moduleId = 70L;

        when(moduleMapper.selectById(moduleId)).thenReturn(null);

        boolean result = requirementService.isAllRequirementsModule(moduleId);

        assertEquals(false, result);
    }

    @Test
    void resolveModuleId_whenDefaultModuleNotExists_shouldThrowException() {
        Long productId = 100L;

        when(moduleMapper.selectByProductIdAndParentId(productId, 0L)).thenReturn(null);

        ServiceException ex = assertThrows(ServiceException.class,
                () -> requirementService.resolveModuleId(null, productId));
        assertEquals(ErrorCodeConstants.REQUIREMENT_MODULE_NOT_EXISTS.getCode(), ex.getCode());
    }

    // ========== 辅助方法 ==========

    private ProductRequirementDO createRequirement(Long id, Long productId, String title,
                                                    String statusCode, Long parentId, Integer reviewRequired) {
        ProductRequirementDO requirement = new ProductRequirementDO();
        requirement.setId(id);
        requirement.setProductId(productId);
        requirement.setTitle(title);
        requirement.setStatusCode(statusCode);
        requirement.setParentId(parentId);
        requirement.setReviewRequired(reviewRequired);
        requirement.setCategory("function");
        requirement.setSourceType("manual");
        requirement.setPriority(1);
        requirement.setProposerId(2001L);
        requirement.setSort(0);
        return requirement;
    }

    private ProductRequirementModuleDO createDefaultModule(Long id, Long productId) {
        ProductRequirementModuleDO module = new ProductRequirementModuleDO();
        module.setId(id);
        module.setProductId(productId);
        module.setParentId(0L);
        module.setModuleName("全部需求");
        module.setRemark("自动创建的模块");
        module.setSort(0);
        return module;
    }

    private ObjectStatusTransitionDO createTransition(String actionCode, String toStatus, boolean needReason) {
        ObjectStatusTransitionDO transition = new ObjectStatusTransitionDO();
        transition.setActionCode(actionCode);
        transition.setToStatusCode(toStatus);
        transition.setNeedReason(needReason);
        return transition;
    }

    private MockedStatic<SecurityFrameworkUtils> mockLoginUser(Long loginUserId, String nickname) {
        MockedStatic<SecurityFrameworkUtils> mockedStatic = mockStatic(SecurityFrameworkUtils.class);
        mockedStatic.when(SecurityFrameworkUtils::getLoginUserId).thenReturn(loginUserId);
        mockedStatic.when(SecurityFrameworkUtils::getLoginUserNickname).thenReturn(nickname);
        return mockedStatic;
    }

}
