package com.ruoyi.asset.service.impl;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.util.Collections;
import java.util.List;
import com.ruoyi.asset.constant.AssetStatus;
import com.ruoyi.asset.constant.InventoryIdentifyMethod;
import com.ruoyi.asset.constant.InventoryResult;
import com.ruoyi.asset.constant.InventoryScopeType;
import com.ruoyi.asset.constant.InventorySurplusStatus;
import com.ruoyi.asset.constant.InventorySurplusType;
import com.ruoyi.asset.constant.InventoryTaskStatus;
import com.ruoyi.asset.constant.RfidBindStatus;
import com.ruoyi.asset.domain.AmsAsset;
import com.ruoyi.asset.domain.AmsAssetCategory;
import com.ruoyi.asset.domain.AmsAssetLocation;
import com.ruoyi.asset.domain.AmsInventoryTask;
import com.ruoyi.asset.domain.AmsInventoryTaskItem;
import com.ruoyi.asset.domain.AmsInventoryTaskSurplus;
import com.ruoyi.asset.domain.AmsRfidTag;
import com.ruoyi.asset.domain.AmsWarehouse;
import com.ruoyi.asset.mapper.AmsAssetMapper;
import com.ruoyi.asset.mapper.AmsInventoryTaskMapper;
import com.ruoyi.asset.mapper.AmsRfidTagMapper;
import com.ruoyi.asset.service.IAmsAssetCategoryService;
import com.ruoyi.asset.service.IAmsAssetLocationService;
import com.ruoyi.asset.service.IAmsWarehouseService;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.system.service.ISysCodeRuleService;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

@ExtendWith(MockitoExtension.class)
class AmsInventoryTaskServiceImplTest
{
    @Mock
    private AmsInventoryTaskMapper amsInventoryTaskMapper;

    @Mock
    private AmsAssetMapper amsAssetMapper;

    @Mock
    private AmsRfidTagMapper amsRfidTagMapper;

    @Mock
    private ISysCodeRuleService sysCodeRuleService;

    @Mock
    private IAmsWarehouseService amsWarehouseService;

    @Mock
    private IAmsAssetLocationService amsAssetLocationService;

    @Mock
    private IAmsAssetCategoryService amsAssetCategoryService;

    @InjectMocks
    private AmsInventoryTaskServiceImpl service;

    /** 新增任务时，任务号、负责人、状态和范围快照必须由服务端统一生成。 */
    @Test
    void insertShouldGenerateCodeOwnerAndScopeSnapshots()
    {
        AmsInventoryTask task = buildLocationCategoryTask();
        when(sysCodeRuleService.nextCode("INVENTORY_TASK")).thenReturn("PD202606180001");
        stubWarehouse();
        stubLocation();
        stubCategory();
        doAnswer(invocation -> {
            AmsInventoryTask inserted = invocation.getArgument(0);
            inserted.setTaskId(100L);
            return 1;
        }).when(amsInventoryTaskMapper).insertAmsInventoryTask(any(AmsInventoryTask.class));

        assertEquals(1, service.insertAmsInventoryTask(task, 9L, "管理员"));

        assertEquals(100L, task.getTaskId());
        assertEquals("PD202606180001", task.getTaskNo());
        assertEquals(9L, task.getOwnerUserId());
        assertEquals("管理员", task.getOwnerUserName());
        assertEquals(InventoryTaskStatus.DRAFT, task.getTaskStatus());
        assertEquals("WH-001", task.getWarehouseCode());
        assertEquals("LOC-001", task.getLocationCode());
        assertEquals("CAT-001", task.getCategoryCode());
        assertNull(task.getIssueTime());
    }

    /** 下发任务时，应从资产台账生成快照明细，并把任务推进到已下发。 */
    @Test
    @SuppressWarnings("unchecked")
    void issueShouldGenerateSnapshotItemsAndMoveToIssued()
    {
        AmsInventoryTask task = buildPersistedTask(InventoryTaskStatus.DRAFT);
        task.setScopeType(InventoryScopeType.WAREHOUSE);
        task.setWarehouseId(1L);
        when(amsInventoryTaskMapper.selectAmsInventoryTaskByTaskIdForUpdate(100L)).thenReturn(task);
        stubWarehouse();
        when(amsInventoryTaskMapper.updateAmsInventoryTask(any(AmsInventoryTask.class))).thenReturn(1);
        when(amsInventoryTaskMapper.selectInventoryAssetSnapshotList(eq(task), any()))
                .thenReturn(Collections.singletonList(buildSnapshotItem()));
        when(amsInventoryTaskMapper.batchAmsInventoryTaskItem(anyList())).thenReturn(1);
        when(amsInventoryTaskMapper.updateAmsInventoryTaskStatus(any(AmsInventoryTask.class))).thenReturn(1);

        assertEquals(1, service.issueTask(100L, "admin"));

        ArgumentCaptor<List<AmsInventoryTaskItem>> itemCaptor = ArgumentCaptor.forClass(List.class);
        verify(amsInventoryTaskMapper).batchAmsInventoryTaskItem(itemCaptor.capture());
        AmsInventoryTaskItem item = itemCaptor.getValue().get(0);
        assertEquals(100L, item.getTaskId());
        assertEquals("PD202606180001", item.getTaskNo());
        assertEquals("admin", item.getCreateBy());
        assertEquals(InventoryTaskStatus.ISSUED, task.getTaskStatus());
        assertNotNull(task.getIssueTime());
    }

    /** 提交结果时，正常结果未填写盘点位置则默认使用账面仓位，不信任前端盘点人。 */
    @Test
    void submitResultShouldUpdateItemsAndMoveToPendingConfirm()
    {
        AmsInventoryTask current = buildPersistedTask(InventoryTaskStatus.ISSUED);
        current.setAmsInventoryTaskItemList(Collections.singletonList(buildPersistedItem()));
        AmsInventoryTask submit = new AmsInventoryTask();
        submit.setTaskId(100L);
        AmsInventoryTaskItem submitItem = new AmsInventoryTaskItem();
        submitItem.setItemId(500L);
        submitItem.setInventoryResult(InventoryResult.NORMAL);
        submit.setAmsInventoryTaskItemList(Collections.singletonList(submitItem));
        when(amsInventoryTaskMapper.selectAmsInventoryTaskByTaskIdForUpdate(100L)).thenReturn(current);
        when(amsInventoryTaskMapper.updateAmsInventoryTaskItemResult(any(AmsInventoryTaskItem.class))).thenReturn(1);
        when(amsInventoryTaskMapper.updateAmsInventoryTaskStatus(any(AmsInventoryTask.class))).thenReturn(1);

        assertEquals(1, service.submitResult(submit, 9L, "管理员", "admin"));

        ArgumentCaptor<AmsInventoryTaskItem> itemCaptor = ArgumentCaptor.forClass(AmsInventoryTaskItem.class);
        verify(amsInventoryTaskMapper).updateAmsInventoryTaskItemResult(itemCaptor.capture());
        AmsInventoryTaskItem savedItem = itemCaptor.getValue();
        assertEquals(InventoryResult.NORMAL, savedItem.getInventoryResult());
        assertEquals(1L, savedItem.getInventoryWarehouseId());
        assertEquals(10L, savedItem.getInventoryLocationId());
        assertEquals(9L, savedItem.getInventoryUserId());
        assertEquals("管理员", savedItem.getInventoryUserName());
        assertEquals(InventoryTaskStatus.PENDING_RESULT_CONFIRM, current.getTaskStatus());
    }

    /** 应盘快照行不允许提交盘盈，真实盘盈必须进入盘盈明细。 */
    @Test
    void submitResultShouldRejectSurplusOnExpectedItem()
    {
        AmsInventoryTask current = buildPersistedTask(InventoryTaskStatus.ISSUED);
        current.setAmsInventoryTaskItemList(Collections.singletonList(buildPersistedItem()));
        AmsInventoryTask submit = new AmsInventoryTask();
        submit.setTaskId(100L);
        AmsInventoryTaskItem submitItem = new AmsInventoryTaskItem();
        submitItem.setItemId(500L);
        submitItem.setInventoryResult(InventoryResult.SURPLUS);
        submit.setAmsInventoryTaskItemList(Collections.singletonList(submitItem));
        when(amsInventoryTaskMapper.selectAmsInventoryTaskByTaskIdForUpdate(100L)).thenReturn(current);

        ServiceException exception = assertThrows(ServiceException.class,
                () -> service.submitResult(submit, 9L, "管理员", "admin"));

        assertTrue(exception.getMessage().contains("不合法"));
        verify(amsInventoryTaskMapper, never()).updateAmsInventoryTaskItemResult(any(AmsInventoryTaskItem.class));
    }

    /** EPC 识别到已绑定账内资产时，生成账内范围外盘盈并推进到盘点中。 */
    @Test
    void addSurplusByBoundEpcShouldCreateKnownOutOfScopeItem()
    {
        AmsInventoryTask current = buildPersistedTask(InventoryTaskStatus.ISSUED);
        AmsInventoryTaskSurplus request = buildSurplusRequest(InventoryIdentifyMethod.EPC);
        request.setEpcCode("EPC-002");
        when(amsInventoryTaskMapper.selectAmsInventoryTaskByTaskIdForUpdate(100L)).thenReturn(current);
        stubWarehouse();
        stubLocation();
        when(amsRfidTagMapper.selectAmsRfidTagByEpcForUpdate("EPC-002")).thenReturn(buildBoundTag());
        when(amsAssetMapper.selectAmsAssetByAssetIdForUpdate(2L)).thenReturn(buildOutOfScopeAsset());
        when(amsInventoryTaskMapper.countInventoryTaskItemByTaskIdAndAssetId(100L, 2L)).thenReturn(0);
        when(amsInventoryTaskMapper.countInventoryTaskSurplusByAssetId(100L, 2L)).thenReturn(0);
        when(amsInventoryTaskMapper.countInventoryTaskSurplusByEpcCode(100L, "EPC-002")).thenReturn(0);
        doAnswer(invocation -> {
            AmsInventoryTaskSurplus surplus = invocation.getArgument(0);
            surplus.setSurplusId(700L);
            return 1;
        }).when(amsInventoryTaskMapper).insertAmsInventoryTaskSurplus(any(AmsInventoryTaskSurplus.class));
        when(amsInventoryTaskMapper.updateAmsInventoryTaskStatus(any(AmsInventoryTask.class))).thenReturn(1);

        assertEquals(1, service.addSurplusItem(request, 9L, "管理员", "admin"));

        ArgumentCaptor<AmsInventoryTaskSurplus> surplusCaptor =
                ArgumentCaptor.forClass(AmsInventoryTaskSurplus.class);
        verify(amsInventoryTaskMapper).insertAmsInventoryTaskSurplus(surplusCaptor.capture());
        AmsInventoryTaskSurplus saved = surplusCaptor.getValue();
        assertEquals(InventorySurplusType.KNOWN_OUT_OF_SCOPE, saved.getSurplusType());
        assertEquals(InventoryIdentifyMethod.EPC, saved.getIdentifyMethod());
        assertEquals(2L, saved.getAssetId());
        assertEquals("EPC-002", saved.getEpcCode());
        assertEquals(InventorySurplusStatus.RECORDED, saved.getConfirmStatus());
        assertEquals(InventoryTaskStatus.INVENTORYING, current.getTaskStatus());
    }

    /** EPC 未建档或未绑定资产时，生成未知实物盘盈。 */
    @Test
    void addSurplusByUnknownEpcShouldCreateUnknownItem()
    {
        AmsInventoryTask current = buildPersistedTask(InventoryTaskStatus.INVENTORYING);
        AmsInventoryTaskSurplus request = buildSurplusRequest(InventoryIdentifyMethod.EPC);
        request.setEpcCode("EPC-UNKNOWN");
        request.setAssetName("未知设备");
        when(amsInventoryTaskMapper.selectAmsInventoryTaskByTaskIdForUpdate(100L)).thenReturn(current);
        stubWarehouse();
        stubLocation();
        when(amsRfidTagMapper.selectAmsRfidTagByEpcForUpdate("EPC-UNKNOWN")).thenReturn(null);
        when(amsInventoryTaskMapper.countInventoryTaskSurplusByEpcCode(100L, "EPC-UNKNOWN")).thenReturn(0);
        doAnswer(invocation -> {
            AmsInventoryTaskSurplus surplus = invocation.getArgument(0);
            surplus.setSurplusId(701L);
            return 1;
        }).when(amsInventoryTaskMapper).insertAmsInventoryTaskSurplus(any(AmsInventoryTaskSurplus.class));

        assertEquals(1, service.addSurplusItem(request, 9L, "管理员", "admin"));

        ArgumentCaptor<AmsInventoryTaskSurplus> surplusCaptor =
                ArgumentCaptor.forClass(AmsInventoryTaskSurplus.class);
        verify(amsInventoryTaskMapper).insertAmsInventoryTaskSurplus(surplusCaptor.capture());
        AmsInventoryTaskSurplus saved = surplusCaptor.getValue();
        assertEquals(InventorySurplusType.UNKNOWN_OBJECT, saved.getSurplusType());
        assertEquals("未知设备", saved.getAssetName());
        assertNull(saved.getAssetId());
    }

    /** 资产编码识别必须匹配到账内资产，且该资产不能已经在本任务应盘明细中。 */
    @Test
    void addSurplusByAssetCodeShouldCreateKnownOutOfScopeItem()
    {
        AmsInventoryTask current = buildPersistedTask(InventoryTaskStatus.INVENTORYING);
        AmsInventoryTaskSurplus request = buildSurplusRequest(InventoryIdentifyMethod.ASSET_CODE);
        request.setAssetCode("A002");
        when(amsInventoryTaskMapper.selectAmsInventoryTaskByTaskIdForUpdate(100L)).thenReturn(current);
        stubWarehouse();
        stubLocation();
        when(amsAssetMapper.selectAmsAssetByAssetCodeForUpdate("A002")).thenReturn(buildOutOfScopeAsset());
        when(amsInventoryTaskMapper.countInventoryTaskItemByTaskIdAndAssetId(100L, 2L)).thenReturn(0);
        when(amsInventoryTaskMapper.countInventoryTaskSurplusByAssetId(100L, 2L)).thenReturn(0);
        when(amsInventoryTaskMapper.countInventoryTaskSurplusByEpcCode(100L, "EPC-002")).thenReturn(0);
        doAnswer(invocation -> {
            AmsInventoryTaskSurplus surplus = invocation.getArgument(0);
            surplus.setSurplusId(702L);
            return 1;
        }).when(amsInventoryTaskMapper).insertAmsInventoryTaskSurplus(any(AmsInventoryTaskSurplus.class));

        assertEquals(1, service.addSurplusItem(request, 9L, "管理员", "admin"));

        ArgumentCaptor<AmsInventoryTaskSurplus> surplusCaptor =
                ArgumentCaptor.forClass(AmsInventoryTaskSurplus.class);
        verify(amsInventoryTaskMapper).insertAmsInventoryTaskSurplus(surplusCaptor.capture());
        AmsInventoryTaskSurplus saved = surplusCaptor.getValue();
        assertEquals(InventorySurplusType.KNOWN_OUT_OF_SCOPE, saved.getSurplusType());
        assertEquals("A002", saved.getAssetCode());
    }

    /** 手工盘盈用于记录没有可靠资产编码或 EPC 的现场实物线索。 */
    @Test
    void addManualSurplusShouldCreateUnknownItem()
    {
        AmsInventoryTask current = buildPersistedTask(InventoryTaskStatus.INVENTORYING);
        AmsInventoryTaskSurplus request = buildSurplusRequest(InventoryIdentifyMethod.MANUAL);
        request.setAssetName("无标签显示器");
        request.setSpecModel("24寸");
        when(amsInventoryTaskMapper.selectAmsInventoryTaskByTaskIdForUpdate(100L)).thenReturn(current);
        stubWarehouse();
        stubLocation();
        doAnswer(invocation -> {
            AmsInventoryTaskSurplus surplus = invocation.getArgument(0);
            surplus.setSurplusId(703L);
            return 1;
        }).when(amsInventoryTaskMapper).insertAmsInventoryTaskSurplus(any(AmsInventoryTaskSurplus.class));

        assertEquals(1, service.addSurplusItem(request, 9L, "管理员", "admin"));

        ArgumentCaptor<AmsInventoryTaskSurplus> surplusCaptor =
                ArgumentCaptor.forClass(AmsInventoryTaskSurplus.class);
        verify(amsInventoryTaskMapper).insertAmsInventoryTaskSurplus(surplusCaptor.capture());
        AmsInventoryTaskSurplus saved = surplusCaptor.getValue();
        assertEquals(InventoryIdentifyMethod.MANUAL, saved.getIdentifyMethod());
        assertEquals(InventorySurplusType.UNKNOWN_OBJECT, saved.getSurplusType());
        assertEquals("无标签显示器", saved.getAssetName());
    }

    /** 同一 EPC 在同一任务中不能重复登记盘盈。 */
    @Test
    void addSurplusShouldRejectDuplicateEpc()
    {
        AmsInventoryTask current = buildPersistedTask(InventoryTaskStatus.INVENTORYING);
        AmsInventoryTaskSurplus request = buildSurplusRequest(InventoryIdentifyMethod.EPC);
        request.setEpcCode("EPC-UNKNOWN");
        when(amsInventoryTaskMapper.selectAmsInventoryTaskByTaskIdForUpdate(100L)).thenReturn(current);
        stubWarehouse();
        stubLocation();
        when(amsRfidTagMapper.selectAmsRfidTagByEpcForUpdate("EPC-UNKNOWN")).thenReturn(null);
        when(amsInventoryTaskMapper.countInventoryTaskSurplusByEpcCode(100L, "EPC-UNKNOWN")).thenReturn(1);

        ServiceException exception = assertThrows(ServiceException.class,
                () -> service.addSurplusItem(request, 9L, "管理员", "admin"));

        assertTrue(exception.getMessage().contains("已登记为盘盈"));
        verify(amsInventoryTaskMapper, never()).insertAmsInventoryTaskSurplus(any(AmsInventoryTaskSurplus.class));
    }

    /** 确认结果只完成任务，不调用资产台账状态更新，也不写资产履历。 */
    @Test
    void confirmResultShouldCompleteWithoutUpdatingAssetLedger()
    {
        AmsInventoryTask current = buildPersistedTask(InventoryTaskStatus.PENDING_RESULT_CONFIRM);
        current.setAmsInventoryTaskItemList(Collections.singletonList(buildPersistedItemWithResult()));
        when(amsInventoryTaskMapper.selectAmsInventoryTaskByTaskIdForUpdate(100L)).thenReturn(current);
        when(amsInventoryTaskMapper.updateAmsInventoryTaskStatus(any(AmsInventoryTask.class))).thenReturn(1);

        assertEquals(1, service.confirmResult(100L, 9L, "管理员", "admin"));

        verify(amsAssetMapper, never()).selectAmsAssetByAssetIdForUpdate(1L);
        verify(amsAssetMapper, never()).updateAssetForTransition(any(AmsAsset.class));
        assertEquals(InventoryTaskStatus.INVENTORY_DONE, current.getTaskStatus());
        assertNotNull(current.getFinishTime());
    }

    /** 确认结果时，只把盘盈明细置为已确认，不写资产履历。 */
    @Test
    void confirmResultShouldConfirmKnownSurplusWithoutLifecycle()
    {
        AmsInventoryTask current = buildPersistedTask(InventoryTaskStatus.PENDING_RESULT_CONFIRM);
        current.setAmsInventoryTaskItemList(Collections.singletonList(buildPersistedItemWithResult()));
        current.setAmsInventoryTaskSurplusList(Collections.singletonList(buildPersistedKnownSurplus()));
        when(amsInventoryTaskMapper.selectAmsInventoryTaskByTaskIdForUpdate(100L)).thenReturn(current);
        when(amsInventoryTaskMapper.updateAmsInventoryTaskStatus(any(AmsInventoryTask.class))).thenReturn(1);
        when(amsInventoryTaskMapper.confirmAmsInventoryTaskSurplusByTaskId(100L,
                InventorySurplusStatus.CONFIRMED, "admin")).thenReturn(1);

        assertEquals(1, service.confirmResult(100L, 9L, "管理员", "admin"));

        verify(amsAssetMapper, never()).selectAmsAssetByAssetIdForUpdate(1L);
        verify(amsAssetMapper, never()).selectAmsAssetByAssetIdForUpdate(2L);
        verify(amsAssetMapper, never()).updateAssetForTransition(any(AmsAsset.class));
        verify(amsInventoryTaskMapper).confirmAmsInventoryTaskSurplusByTaskId(100L,
                InventorySurplusStatus.CONFIRMED, "admin");
    }

    /** 非草稿任务不允许删除。 */
    @Test
    void deleteShouldRejectNonDraftTask()
    {
        AmsInventoryTask current = buildPersistedTask(InventoryTaskStatus.ISSUED);
        when(amsInventoryTaskMapper.selectAmsInventoryTaskByTaskIdForUpdate(100L)).thenReturn(current);

        ServiceException exception = assertThrows(ServiceException.class,
                () -> service.deleteAmsInventoryTaskByTaskId(100L));

        assertTrue(exception.getMessage().contains("仅草稿"));
        verify(amsInventoryTaskMapper, never()).deleteAmsInventoryTaskByTaskId(100L);
    }

    /** 已完成任务不允许关闭。 */
    @Test
    void closeShouldRejectDoneTask()
    {
        AmsInventoryTask current = buildPersistedTask(InventoryTaskStatus.INVENTORY_DONE);
        when(amsInventoryTaskMapper.selectAmsInventoryTaskByTaskIdForUpdate(100L)).thenReturn(current);

        ServiceException exception = assertThrows(ServiceException.class,
                () -> service.closeTask(100L, "admin"));

        assertTrue(exception.getMessage().contains("不能关闭"));
        verify(amsInventoryTaskMapper, never()).updateAmsInventoryTaskStatus(any(AmsInventoryTask.class));
    }

    private AmsInventoryTask buildLocationCategoryTask()
    {
        AmsInventoryTask task = new AmsInventoryTask();
        task.setScopeType(InventoryScopeType.LOCATION_CATEGORY);
        task.setWarehouseId(1L);
        task.setLocationId(10L);
        task.setCategoryId(2L);
        task.setCreateBy("admin");
        return task;
    }

    private AmsInventoryTask buildPersistedTask(String status)
    {
        AmsInventoryTask task = new AmsInventoryTask();
        task.setTaskId(100L);
        task.setTaskNo("PD202606180001");
        task.setScopeType(InventoryScopeType.ALL);
        task.setTaskStatus(status);
        task.setOwnerUserId(9L);
        task.setOwnerUserName("管理员");
        return task;
    }

    private AmsInventoryTaskItem buildSnapshotItem()
    {
        AmsInventoryTaskItem item = new AmsInventoryTaskItem();
        item.setAssetId(1L);
        item.setAssetCode("A001");
        item.setAssetName("资产A");
        item.setBookWarehouseId(1L);
        item.setBookWarehouseName("一号仓");
        item.setBookLocationId(10L);
        item.setBookLocationName("一号仓A区");
        return item;
    }

    private AmsInventoryTaskItem buildPersistedItem()
    {
        AmsInventoryTaskItem item = buildSnapshotItem();
        item.setItemId(500L);
        item.setTaskId(100L);
        item.setTaskNo("PD202606180001");
        return item;
    }

    private AmsInventoryTaskItem buildPersistedItemWithResult()
    {
        AmsInventoryTaskItem item = buildPersistedItem();
        item.setInventoryResult(InventoryResult.NORMAL);
        item.setInventoryWarehouseId(1L);
        item.setInventoryWarehouseName("一号仓");
        item.setInventoryLocationId(10L);
        item.setInventoryLocationName("一号仓A区");
        return item;
    }

    private AmsAsset buildOutOfScopeAsset()
    {
        AmsAsset asset = new AmsAsset();
        asset.setAssetId(2L);
        asset.setAssetCode("A002");
        asset.setAssetName("资产B");
        asset.setAssetStatus(AssetStatus.IN_STOCK);
        asset.setWarehouseId(2L);
        asset.setWarehouseCode("WH-002");
        asset.setWarehouseName("二号仓");
        asset.setLocationId(20L);
        asset.setLocationCode("LOC-002");
        asset.setLocationName("二号仓A区");
        asset.setTagId(20L);
        asset.setTagCode("TAG-002");
        asset.setEpcCode("EPC-002");
        return asset;
    }

    private AmsRfidTag buildBoundTag()
    {
        AmsRfidTag tag = new AmsRfidTag();
        tag.setTagId(20L);
        tag.setTagCode("TAG-002");
        tag.setEpcCode("EPC-002");
        tag.setBindStatus(RfidBindStatus.BOUND);
        tag.setAssetId(2L);
        tag.setAssetCode("A002");
        tag.setAssetName("资产B");
        return tag;
    }

    private AmsInventoryTaskSurplus buildSurplusRequest(String identifyMethod)
    {
        AmsInventoryTaskSurplus surplus = new AmsInventoryTaskSurplus();
        surplus.setTaskId(100L);
        surplus.setIdentifyMethod(identifyMethod);
        surplus.setInventoryWarehouseId(1L);
        surplus.setInventoryLocationId(10L);
        return surplus;
    }

    private AmsInventoryTaskSurplus buildPersistedKnownSurplus()
    {
        AmsInventoryTaskSurplus surplus = new AmsInventoryTaskSurplus();
        surplus.setSurplusId(700L);
        surplus.setTaskId(100L);
        surplus.setTaskNo("PD202606180001");
        surplus.setSurplusType(InventorySurplusType.KNOWN_OUT_OF_SCOPE);
        surplus.setIdentifyMethod(InventoryIdentifyMethod.EPC);
        surplus.setAssetId(2L);
        surplus.setAssetCode("A002");
        surplus.setAssetName("资产B");
        surplus.setEpcCode("EPC-002");
        surplus.setBookWarehouseName("二号仓");
        surplus.setBookLocationName("二号仓A区");
        surplus.setInventoryWarehouseId(1L);
        surplus.setInventoryWarehouseName("一号仓");
        surplus.setInventoryLocationId(10L);
        surplus.setInventoryLocationName("一号仓A区");
        surplus.setConfirmStatus(InventorySurplusStatus.RECORDED);
        return surplus;
    }

    private void stubWarehouse()
    {
        AmsWarehouse warehouse = new AmsWarehouse();
        warehouse.setWarehouseId(1L);
        warehouse.setWarehouseCode("WH-001");
        warehouse.setWarehouseName("一号仓");
        warehouse.setEnabled("Y");
        when(amsWarehouseService.selectAmsWarehouseByWarehouseId(1L)).thenReturn(warehouse);
    }

    private void stubLocation()
    {
        AmsAssetLocation location = new AmsAssetLocation();
        location.setLocationId(10L);
        location.setLocationCode("LOC-001");
        location.setLocationName("一号仓A区");
        location.setWarehouseId(1L);
        location.setEnabled("Y");
        when(amsAssetLocationService.selectAmsAssetLocationByLocationId(10L)).thenReturn(location);
    }

    private void stubCategory()
    {
        AmsAssetCategory category = new AmsAssetCategory();
        category.setCategoryId(2L);
        category.setCategoryCode("CAT-001");
        category.setCategoryName("测试类别");
        category.setEnabled("Y");
        when(amsAssetCategoryService.selectAmsAssetCategoryByCategoryId(2L)).thenReturn(category);
    }
}
