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.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.util.List;
import com.ruoyi.asset.constant.AssetLifecycleBusinessType;
import com.ruoyi.asset.domain.AmsAsset;
import com.ruoyi.asset.domain.AmsAssetLifecycleLog;
import com.ruoyi.asset.mapper.AmsAssetLifecycleLogMapper;
import com.ruoyi.common.core.domain.entity.SysDictData;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.system.service.ISysDictDataService;
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 AssetLifecycleServiceImplTest
{
    @Mock
    private AmsAssetLifecycleLogMapper lifecycleLogMapper;

    @Mock
    private ISysDictDataService sysDictDataService;

    @InjectMocks
    private AssetLifecycleServiceImpl service;

    @Test
    void recordLifecycleShouldFillSnapshotsAndDefaults()
    {
        AmsAsset beforeAsset = buildAsset(10L, "IN_STOCK", 1L, "一号仓", 11L, "A区", null, null, null, null);
        AmsAsset afterAsset = buildAsset(10L, "IN_USE", 2L, "二号仓", 22L, "B区", 3L, "张三", 4L, "生产部");
        AmsAssetLifecycleLog request = new AmsAssetLifecycleLog();
        request.setBusinessType(" " + AssetLifecycleBusinessType.RECEIVE + " ");
        request.setOperateUserId(100L);
        request.setOperateUserName(" 管理员 ");
        request.setCreateBy(" admin ");
        request.setChangeSummary(" 领用确认 ");

        SysDictData businessType = new SysDictData();
        businessType.setDictValue(AssetLifecycleBusinessType.RECEIVE);
        when(sysDictDataService.selectDictDataList(any(SysDictData.class))).thenReturn(List.of(businessType));
        when(lifecycleLogMapper.insertAmsAssetLifecycleLog(any(AmsAssetLifecycleLog.class))).thenReturn(1);

        int rows = service.recordLifecycle(beforeAsset, afterAsset, request);

        ArgumentCaptor<AmsAssetLifecycleLog> captor = ArgumentCaptor.forClass(AmsAssetLifecycleLog.class);
        verify(lifecycleLogMapper).insertAmsAssetLifecycleLog(captor.capture());
        AmsAssetLifecycleLog actual = captor.getValue();
        assertEquals(1, rows);
        assertEquals(10L, actual.getAssetId());
        assertEquals("ASSET-001", actual.getAssetCode());
        assertEquals(AssetLifecycleBusinessType.RECEIVE, actual.getBusinessType());
        assertEquals("IN_STOCK", actual.getBeforeStatus());
        assertEquals("IN_USE", actual.getAfterStatus());
        assertEquals("一号仓", actual.getBeforeWarehouseName());
        assertEquals("二号仓", actual.getAfterWarehouseName());
        assertEquals("管理员", actual.getOperateUserName());
        assertEquals("领用确认", actual.getChangeSummary());
        assertEquals("admin", actual.getCreateBy());
        assertEquals("0", actual.getDelFlag());
        assertNotNull(actual.getOperateTime());
        assertNotNull(actual.getCreateTime());
    }

    @Test
    void recordLifecycleShouldRejectDifferentAssets()
    {
        AmsAsset beforeAsset = buildAsset(10L, "IN_STOCK", null, null, null, null, null, null, null, null);
        AmsAsset afterAsset = buildAsset(11L, "IN_USE", null, null, null, null, null, null, null, null);
        AmsAssetLifecycleLog request = new AmsAssetLifecycleLog();
        request.setBusinessType(AssetLifecycleBusinessType.RECEIVE);

        assertThrows(ServiceException.class, () -> service.recordLifecycle(beforeAsset, afterAsset, request));

        verify(sysDictDataService, never()).selectDictDataList(any(SysDictData.class));
        verify(lifecycleLogMapper, never()).insertAmsAssetLifecycleLog(any(AmsAssetLifecycleLog.class));
    }

    @Test
    void recordLifecycleShouldRejectUnknownBusinessType()
    {
        AmsAsset asset = buildAsset(10L, "IN_STOCK", null, null, null, null, null, null, null, null);
        AmsAssetLifecycleLog request = new AmsAssetLifecycleLog();
        request.setBusinessType("UNKNOWN");
        SysDictData otherBusinessType = new SysDictData();
        otherBusinessType.setDictValue(AssetLifecycleBusinessType.INBOUND);
        when(sysDictDataService.selectDictDataList(any(SysDictData.class)))
                .thenReturn(List.of(otherBusinessType));

        assertThrows(ServiceException.class, () -> service.recordLifecycle(asset, asset, request));

        verify(lifecycleLogMapper, never()).insertAmsAssetLifecycleLog(any(AmsAssetLifecycleLog.class));
    }

    @Test
    void hasRfidLifecycleShouldQueryBindAndUnbindTypes()
    {
        when(lifecycleLogMapper.countRfidLifecycleByTagId(any(), any())).thenReturn(1);

        assertTrue(service.hasRfidLifecycle(10L));

        ArgumentCaptor<String[]> typesCaptor = ArgumentCaptor.forClass(String[].class);
        verify(lifecycleLogMapper).countRfidLifecycleByTagId(org.mockito.ArgumentMatchers.eq(10L),
                typesCaptor.capture());
        assertEquals(AssetLifecycleBusinessType.RFID_BIND, typesCaptor.getValue()[0]);
        assertEquals(AssetLifecycleBusinessType.RFID_UNBIND, typesCaptor.getValue()[1]);
    }

    private AmsAsset buildAsset(Long assetId, String assetStatus,
            Long warehouseId, String warehouseName, Long locationId, String locationName,
            Long userId, String userName, Long deptId, String deptName)
    {
        AmsAsset asset = new AmsAsset();
        asset.setAssetId(assetId);
        asset.setAssetCode("ASSET-001");
        asset.setAssetName("测试资产");
        asset.setAssetStatus(assetStatus);
        asset.setWarehouseId(warehouseId);
        asset.setWarehouseName(warehouseName);
        asset.setLocationId(locationId);
        asset.setLocationName(locationName);
        asset.setUseUserId(userId);
        asset.setUseUserName(userName);
        asset.setUseDeptId(deptId);
        asset.setUseDeptName(deptName);
        return asset;
    }
}
