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.StockOutboundCreateDTO;
import com.ruoyi.web.modules.stock.dto.StockOutboundCreateItemDTO;
import com.ruoyi.web.modules.stock.dto.StockOutboundQuery;
import com.ruoyi.web.modules.stock.mapper.BizStockOutboundItemMapper;
import com.ruoyi.web.modules.stock.mapper.BizStockOutboundMapper;
import com.ruoyi.web.modules.stock.service.impl.StockOutboundServiceImpl;
import com.ruoyi.web.modules.stock.support.StockSupport;
import com.ruoyi.web.modules.stock.vo.StockOutboundDetailVO;
import com.ruoyi.web.modules.stock.vo.StockOutboundListRowVO;

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

    private static final Long OUTBOUND_ID = 900L;

    @Mock
    private BizStockOutboundMapper outboundMapper;

    @Mock
    private BizStockOutboundItemMapper outboundItemMapper;

    @Mock
    private BizGoodsMapper goodsMapper;

    @Mock
    private IStockLogService stockLogService;

    @Mock
    private StockSupport stockSupport;

    @InjectMocks
    private StockOutboundServiceImpl stockOutboundService;

    @Test
    void selectList_enrichesTypeText()
    {
        StockOutboundListRowVO row = new StockOutboundListRowVO();
        row.setOutboundType(StockConstants.OUTBOUND_TYPE_MANUAL_ORDER);
        when(outboundMapper.selectList(eq(SHOP_ID), any(StockOutboundQuery.class)))
                .thenReturn(Collections.singletonList(row));
        when(stockSupport.outboundTypeText(StockConstants.OUTBOUND_TYPE_MANUAL_ORDER)).thenReturn("订单出库");

        StockOutboundListRowVO result = stockOutboundService.selectList(SHOP_ID, new StockOutboundQuery()).get(0);

        assertEquals("订单出库", result.getOutboundTypeText());
    }

    @Test
    void getDetail_notFound_throws()
    {
        when(outboundMapper.selectDetail(SHOP_ID, OUTBOUND_ID)).thenReturn(null);

        ServiceException ex = assertThrows(ServiceException.class,
                () -> stockOutboundService.getDetail(SHOP_ID, OUTBOUND_ID));
        assertEquals(StockConstants.MSG_OUTBOUND_NOT_FOUND, ex.getMessage());
    }

    @Test
    void getDetail_success()
    {
        StockOutboundDetailVO detail = new StockOutboundDetailVO();
        detail.setOutboundId(OUTBOUND_ID);
        detail.setOutboundType(StockConstants.OUTBOUND_TYPE_EXPIRED);
        when(outboundMapper.selectDetail(SHOP_ID, OUTBOUND_ID)).thenReturn(detail);
        when(stockSupport.outboundTypeText(StockConstants.OUTBOUND_TYPE_EXPIRED)).thenReturn("过期出库");
        when(outboundItemMapper.selectByOutboundId(OUTBOUND_ID)).thenReturn(Collections.emptyList());

        StockOutboundDetailVO result = stockOutboundService.getDetail(SHOP_ID, OUTBOUND_ID);

        assertEquals("过期出库", result.getOutboundTypeText());
    }

    @Test
    void createManual_invalidType_throws()
    {
        StockOutboundCreateDTO dto = new StockOutboundCreateDTO();
        dto.setOutboundType(StockConstants.OUTBOUND_TYPE_ORDER_DEDUCT);
        dto.setItems(Collections.singletonList(validItem()));

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

    @Test
    void createManual_goodsNotFound_throws()
    {
        StockOutboundCreateDTO dto = validCreateDto();
        when(stockSupport.nextOutboundNo()).thenReturn("OUT20260526000001");
        when(stockSupport.changeReasonForOutboundType(StockConstants.OUTBOUND_TYPE_MANUAL_ORDER))
                .thenReturn(StockConstants.REASON_MANUAL_ORDER_OUT);
        when(goodsMapper.selectByIdAndShopForUpdate(1001L, SHOP_ID)).thenReturn(null);

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

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

        when(stockSupport.nextOutboundNo()).thenReturn("OUT20260526000001");
        when(stockSupport.changeReasonForOutboundType(StockConstants.OUTBOUND_TYPE_MANUAL_ORDER))
                .thenReturn(StockConstants.REASON_MANUAL_ORDER_OUT);
        when(goodsMapper.selectByIdAndShopForUpdate(1001L, SHOP_ID)).thenReturn(goods);
        when(goodsMapper.decrementStockForOutbound(1001L, SHOP_ID, 5)).thenReturn(1);
        when(stockSupport.buildSpecText(1001L)).thenReturn(null);
        when(stockSupport.normalizeSpecText(null)).thenReturn("—");

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

        assertEquals("OUT20260526000001", result.get("outboundNo"));
        verify(outboundMapper).insert(any());
        verify(outboundItemMapper).batchInsert(any());
        verify(stockLogService).insertLog(any());
    }

    private StockOutboundCreateDTO validCreateDto()
    {
        StockOutboundCreateDTO dto = new StockOutboundCreateDTO();
        dto.setOutboundType(StockConstants.OUTBOUND_TYPE_MANUAL_ORDER);
        dto.setItems(Collections.singletonList(validItem()));
        return dto;
    }

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