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.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.StockInboundCreateDTO;
import com.ruoyi.web.modules.stock.dto.StockInboundCreateItemDTO;
import com.ruoyi.web.modules.stock.dto.StockInboundQuery;
import com.ruoyi.web.modules.stock.mapper.BizStockInboundItemMapper;
import com.ruoyi.web.modules.stock.mapper.BizStockInboundMapper;
import com.ruoyi.web.modules.stock.service.impl.StockInboundServiceImpl;
import com.ruoyi.web.modules.stock.support.StockSupport;
import com.ruoyi.web.modules.stock.vo.StockInboundDetailVO;
import com.ruoyi.web.modules.stock.vo.StockInboundListRowVO;

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

    private static final Long INBOUND_ID = 900L;

    @Mock
    private BizStockInboundMapper inboundMapper;

    @Mock
    private BizStockInboundItemMapper inboundItemMapper;

    @Mock
    private BizGoodsMapper goodsMapper;

    @Mock
    private IStockLogService stockLogService;

    @Mock
    private StockSupport stockSupport;

    @InjectMocks
    private StockInboundServiceImpl stockInboundService;

    @Test
    void selectList_enrichesTypeText()
    {
        StockInboundListRowVO row = new StockInboundListRowVO();
        row.setInboundType(StockConstants.INBOUND_TYPE_PURCHASE);
        when(inboundMapper.selectList(eq(SHOP_ID), any(StockInboundQuery.class)))
                .thenReturn(Collections.singletonList(row));
        when(stockSupport.inboundTypeText(StockConstants.INBOUND_TYPE_PURCHASE)).thenReturn("采购入库");

        StockInboundListRowVO result = stockInboundService.selectList(SHOP_ID, new StockInboundQuery()).get(0);

        assertEquals("采购入库", result.getInboundTypeText());
    }

    @Test
    void getDetail_notFound_throws()
    {
        when(inboundMapper.selectDetail(SHOP_ID, INBOUND_ID)).thenReturn(null);

        ServiceException ex = assertThrows(ServiceException.class,
                () -> stockInboundService.getDetail(SHOP_ID, INBOUND_ID));
        assertEquals(StockConstants.MSG_INBOUND_NOT_FOUND, ex.getMessage());
    }

    @Test
    void getDetail_success()
    {
        StockInboundDetailVO detail = new StockInboundDetailVO();
        detail.setInboundId(INBOUND_ID);
        detail.setInboundType(StockConstants.INBOUND_TYPE_RETURN);
        when(inboundMapper.selectDetail(SHOP_ID, INBOUND_ID)).thenReturn(detail);
        when(stockSupport.inboundTypeText(StockConstants.INBOUND_TYPE_RETURN)).thenReturn("退货入库");
        when(inboundItemMapper.selectByInboundId(INBOUND_ID)).thenReturn(Collections.emptyList());

        StockInboundDetailVO result = stockInboundService.getDetail(SHOP_ID, INBOUND_ID);

        assertEquals("退货入库", result.getInboundTypeText());
    }

    @Test
    void createManual_invalidType_throws()
    {
        StockInboundCreateDTO dto = new StockInboundCreateDTO();
        dto.setInboundType(StockConstants.INBOUND_TYPE_NEW_SPEC);
        dto.setItems(Collections.singletonList(validItem()));

        ServiceException ex = assertThrows(ServiceException.class,
                () -> stockInboundService.createManual(SHOP_ID, dto, 1L, "seller"));
        assertEquals(StockConstants.MSG_INBOUND_TYPE_INVALID, ex.getMessage());
        verify(inboundMapper, never()).insert(any());
    }

    @Test
    void createManual_goodsNotFound_throws()
    {
        StockInboundCreateDTO dto = validCreateDto();
        when(stockSupport.nextInboundNo()).thenReturn("IN20260526000001");
        when(stockSupport.changeReasonForInboundType(StockConstants.INBOUND_TYPE_PURCHASE))
                .thenReturn(StockConstants.REASON_PURCHASE_IN);
        when(goodsMapper.selectByIdAndShopForUpdate(1001L, SHOP_ID)).thenReturn(null);

        ServiceException ex = assertThrows(ServiceException.class,
                () -> stockInboundService.createManual(SHOP_ID, dto, 1L, "seller"));
        assertEquals(StockConstants.MSG_INBOUND_GOODS_INVALID, ex.getMessage());
    }

    @Test
    void createManual_success()
    {
        StockInboundCreateDTO dto = validCreateDto();
        BizGoods goods = new BizGoods();
        goods.setGoodsId(1001L);
        goods.setGoodsName("测试商品");
        goods.setMainPic("pic.jpg");
        goods.setStock(10);

        when(stockSupport.nextInboundNo()).thenReturn("IN20260526000001");
        when(stockSupport.changeReasonForInboundType(StockConstants.INBOUND_TYPE_PURCHASE))
                .thenReturn(StockConstants.REASON_PURCHASE_IN);
        when(goodsMapper.selectByIdAndShopForUpdate(1001L, SHOP_ID)).thenReturn(goods);
        when(goodsMapper.incrementStock(1001L, SHOP_ID, 5)).thenReturn(1);
        when(stockSupport.buildSpecText(1001L)).thenReturn(null);
        when(stockSupport.normalizeSpecText(null)).thenReturn("—");

        Map<String, Object> result = stockInboundService.createManual(SHOP_ID, dto, 1L, "seller");

        assertEquals("IN20260526000001", result.get("inboundNo"));
        verify(inboundMapper).insert(any());
        verify(inboundItemMapper).batchInsert(any());
        verify(stockLogService).insertLog(any());
    }

    private StockInboundCreateDTO validCreateDto()
    {
        StockInboundCreateDTO dto = new StockInboundCreateDTO();
        dto.setInboundType(StockConstants.INBOUND_TYPE_PURCHASE);
        dto.setItems(Collections.singletonList(validItem()));
        return dto;
    }

    private StockInboundCreateItemDTO validItem()
    {
        StockInboundCreateItemDTO item = new StockInboundCreateItemDTO();
        item.setGoodsId(1001L);
        item.setQuantity(5);
        return item;
    }
}
