package com.ruoyi.web.modules.stock.service;

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.ArgumentMatchers.eq;
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 java.util.Map;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.web.modules.goods.domain.BizGoods;
import com.ruoyi.web.modules.goods.mapper.BizGoodsMapper;
import com.ruoyi.web.modules.stock.constant.StockConstants;
import com.ruoyi.web.modules.stock.dto.StockAdjustCreateDTO;
import com.ruoyi.web.modules.stock.dto.StockAdjustQuery;
import com.ruoyi.web.modules.stock.dto.StockAdjustRecordQuery;
import com.ruoyi.web.modules.stock.mapper.BizStockAdjustMapper;
import com.ruoyi.web.modules.stock.service.impl.StockAdjustServiceImpl;
import com.ruoyi.web.modules.stock.support.StockSupport;
import com.ruoyi.web.modules.stock.vo.StockAdjustRecordRowVO;
import com.ruoyi.web.modules.stock.vo.StockQueryListRowVO;

@ExtendWith(MockitoExtension.class)
class StockAdjustServiceImplTest
{
    private static final Long SHOP_ID = 101L;

    private static final Long GOODS_ID = 1001L;

    @Mock
    private BizGoodsMapper goodsMapper;

    @Mock
    private BizStockAdjustMapper adjustMapper;

    @Mock
    private IStockLogService stockLogService;

    @Mock
    private StockSupport stockSupport;

    @InjectMocks
    private StockAdjustServiceImpl stockAdjustService;

    @Test
    void selectList_enrichesSpecText()
    {
        StockQueryListRowVO row = new StockQueryListRowVO();
        row.setGoodsId(GOODS_ID);
        row.setGoodsName("测试商品");
        when(goodsMapper.selectStockQueryList(SHOP_ID, null)).thenReturn(Collections.singletonList(row));
        when(stockSupport.buildSpecText(GOODS_ID)).thenReturn("颜色:蓝色");
        when(stockSupport.normalizeSpecText("颜色:蓝色")).thenReturn("颜色:蓝色");

        assertEquals("颜色:蓝色", stockAdjustService.selectList(SHOP_ID, new StockAdjustQuery()).get(0).getSpecText());
    }

    @Test
    void confirmAdjust_invalidMode_throws()
    {
        StockAdjustCreateDTO dto = new StockAdjustCreateDTO();
        dto.setAdjustMode("9");
        dto.setAdjustQty(1);

        ServiceException ex = assertThrows(ServiceException.class,
                () -> stockAdjustService.confirmAdjust(SHOP_ID, GOODS_ID, dto, 1L, "seller"));
        assertEquals(StockConstants.MSG_ADJUST_MODE_INVALID, ex.getMessage());
        verify(adjustMapper, never()).insert(any());
    }

    @Test
    void confirmAdjust_incrementZero_throws()
    {
        StockAdjustCreateDTO dto = new StockAdjustCreateDTO();
        dto.setAdjustMode(StockConstants.ADJUST_MODE_INCREMENT);
        dto.setAdjustQty(0);

        BizGoods goods = new BizGoods();
        goods.setGoodsId(GOODS_ID);
        goods.setGoodsName("测试商品");
        goods.setStock(10);
        when(goodsMapper.selectByIdAndShopForUpdate(GOODS_ID, SHOP_ID)).thenReturn(goods);

        ServiceException ex = assertThrows(ServiceException.class,
                () -> stockAdjustService.confirmAdjust(SHOP_ID, GOODS_ID, dto, 1L, "seller"));
        assertEquals(StockConstants.MSG_ADJUST_QTY_ZERO, ex.getMessage());
    }

    @Test
    void confirmAdjust_negativeResult_throws()
    {
        StockAdjustCreateDTO dto = new StockAdjustCreateDTO();
        dto.setAdjustMode(StockConstants.ADJUST_MODE_INCREMENT);
        dto.setAdjustQty(-20);

        BizGoods goods = new BizGoods();
        goods.setGoodsId(GOODS_ID);
        goods.setGoodsName("测试商品");
        goods.setStock(10);
        when(goodsMapper.selectByIdAndShopForUpdate(GOODS_ID, SHOP_ID)).thenReturn(goods);

        ServiceException ex = assertThrows(ServiceException.class,
                () -> stockAdjustService.confirmAdjust(SHOP_ID, GOODS_ID, dto, 1L, "seller"));
        assertEquals(StockConstants.MSG_ADJUST_STOCK_NEGATIVE, ex.getMessage());
    }

    @Test
    void confirmAdjust_incrementSuccess()
    {
        StockAdjustCreateDTO dto = new StockAdjustCreateDTO();
        dto.setAdjustMode(StockConstants.ADJUST_MODE_INCREMENT);
        dto.setAdjustQty(5);

        BizGoods goods = new BizGoods();
        goods.setGoodsId(GOODS_ID);
        goods.setGoodsName("测试商品");
        goods.setStock(10);
        when(goodsMapper.selectByIdAndShopForUpdate(GOODS_ID, SHOP_ID)).thenReturn(goods);
        when(goodsMapper.setStockForAdjust(GOODS_ID, SHOP_ID, 15)).thenReturn(1);
        when(stockSupport.buildSpecText(GOODS_ID)).thenReturn(null);
        when(stockSupport.normalizeSpecText(null)).thenReturn("—");
        when(stockSupport.adjustTypeText(StockConstants.ADJUST_TYPE_INCREASE)).thenReturn("增加库存");

        Map<String, Object> result = stockAdjustService.confirmAdjust(SHOP_ID, GOODS_ID, dto, 1L, "seller");

        assertEquals(15, result.get("stockAfter"));
        assertEquals("增加库存", result.get("adjustTypeText"));
        verify(adjustMapper).insert(any());
        verify(stockLogService).insertLog(any());
    }

    @Test
    void confirmAdjust_overrideSuccess()
    {
        StockAdjustCreateDTO dto = new StockAdjustCreateDTO();
        dto.setAdjustMode(StockConstants.ADJUST_MODE_OVERRIDE);
        dto.setAdjustQty(20);

        BizGoods goods = new BizGoods();
        goods.setGoodsId(GOODS_ID);
        goods.setGoodsName("测试商品");
        goods.setStock(10);
        when(goodsMapper.selectByIdAndShopForUpdate(GOODS_ID, SHOP_ID)).thenReturn(goods);
        when(goodsMapper.setStockForAdjust(GOODS_ID, SHOP_ID, 20)).thenReturn(1);
        when(stockSupport.buildSpecText(GOODS_ID)).thenReturn(null);
        when(stockSupport.normalizeSpecText(null)).thenReturn("—");
        when(stockSupport.adjustTypeText(StockConstants.ADJUST_TYPE_OVERRIDE)).thenReturn("覆盖库存");

        Map<String, Object> result = stockAdjustService.confirmAdjust(SHOP_ID, GOODS_ID, dto, 1L, "seller");

        assertEquals(20, result.get("stockAfter"));
        assertEquals(StockConstants.ADJUST_TYPE_OVERRIDE, result.get("adjustType"));
        verify(stockLogService).insertLog(any());
    }

    @Test
    void listRecords_enrichesTypeText()
    {
        BizGoods goods = new BizGoods();
        goods.setGoodsId(GOODS_ID);
        when(goodsMapper.selectByIdAndShop(GOODS_ID, SHOP_ID)).thenReturn(goods);

        StockAdjustRecordRowVO row = new StockAdjustRecordRowVO();
        row.setAdjustType(StockConstants.ADJUST_TYPE_DECREASE);
        when(adjustMapper.selectByGoodsId(SHOP_ID, GOODS_ID, null)).thenReturn(Collections.singletonList(row));
        when(stockSupport.adjustTypeText(StockConstants.ADJUST_TYPE_DECREASE)).thenReturn("减少库存");

        List<StockAdjustRecordRowVO> rows = stockAdjustService.listRecords(SHOP_ID, GOODS_ID,
                new StockAdjustRecordQuery());

        assertEquals("减少库存", rows.get(0).getAdjustTypeText());
    }

    @Test
    void listRecords_goodsNotFound_throws()
    {
        when(goodsMapper.selectByIdAndShop(GOODS_ID, SHOP_ID)).thenReturn(null);

        ServiceException ex = assertThrows(ServiceException.class,
                () -> stockAdjustService.listRecords(SHOP_ID, GOODS_ID, new StockAdjustRecordQuery()));
        assertEquals(StockConstants.MSG_ADJUST_GOODS_INVALID, ex.getMessage());
    }
}
