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

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.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collections;
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.system.service.ISysConfigService;
import com.ruoyi.web.modules.account.constant.MemberConstants;
import com.ruoyi.web.modules.account.domain.BizMember;
import com.ruoyi.web.modules.account.domain.BizMemberAddress;
import com.ruoyi.web.modules.account.facade.IMemberFacade;
import com.ruoyi.web.modules.account.mapper.BizMemberAddressMapper;
import com.ruoyi.web.modules.cart.constant.CartConstants;
import com.ruoyi.web.modules.cart.dto.CartItemRowDTO;
import com.ruoyi.web.modules.cart.mapper.CartAppMapper;
import com.ruoyi.web.modules.freight.constant.FreightTemplateConstants;
import com.ruoyi.web.modules.goods.domain.BizGoodsFreight;
import com.ruoyi.web.modules.goods.facade.IGoodsPurchaseFacade;
import com.ruoyi.web.modules.goods.mapper.BizGoodsAttrMapper;
import com.ruoyi.web.modules.goods.mapper.BizGoodsFreightMapper;
import com.ruoyi.web.modules.goods.mapper.BizGoodsMapper;
import com.ruoyi.web.modules.goods.mapper.BizGoodsServiceSnapshotMapper;
import com.ruoyi.web.modules.goods.vo.GoodsPurchaseVO;
import com.ruoyi.web.modules.order.constant.CheckoutConstants;
import com.ruoyi.web.modules.order.constant.OrderConstants;
import com.ruoyi.web.modules.order.domain.BizOrder;
import com.ruoyi.web.modules.order.dto.CheckoutLineDTO;
import com.ruoyi.web.modules.order.dto.CheckoutPreviewDTO;
import com.ruoyi.web.modules.order.dto.CheckoutSubmitDTO;
import com.ruoyi.web.modules.order.mapper.BizOrderItemMapper;
import com.ruoyi.web.modules.order.mapper.BizOrderMapper;
import com.ruoyi.web.modules.order.service.impl.CheckoutAppServiceImpl;
import com.ruoyi.web.modules.order.support.CheckoutSupport;
import com.ruoyi.web.modules.order.vo.CheckoutPreviewVO;
import com.ruoyi.web.modules.order.vo.CheckoutSubmitResultVO;
import com.ruoyi.web.modules.store.domain.BizShop;
import com.ruoyi.web.modules.store.mapper.BizShopMapper;

@ExtendWith(MockitoExtension.class)
class CheckoutAppServiceImplTest
{
    private static final Long MEMBER_ID = 200L;

    @Mock
    private IMemberFacade memberFacade;

    @Mock
    private IGoodsPurchaseFacade purchaseFacade;

    @Mock
    private CartAppMapper cartAppMapper;

    @Mock
    private BizGoodsMapper goodsMapper;

    @Mock
    private BizGoodsAttrMapper goodsAttrMapper;

    @Mock
    private BizGoodsFreightMapper goodsFreightMapper;

    @Mock
    private BizGoodsServiceSnapshotMapper snapshotMapper;

    @Mock
    private BizMemberAddressMapper addressMapper;

    @Mock
    private BizShopMapper shopMapper;

    @Mock
    private BizOrderMapper orderMapper;

    @Mock
    private BizOrderItemMapper orderItemMapper;

    @Mock
    private CheckoutSupport checkoutSupport;

    @Mock
    private ISysConfigService configService;

    @InjectMocks
    private CheckoutAppServiceImpl checkoutAppService;

    @Test
    void preview_cartSuccess()
    {
        when(memberFacade.isMemberEnabled(MEMBER_ID)).thenReturn(true);
        CartItemRowDTO row = cartRow(10L, 1L, 101L);
        when(cartAppMapper.selectItemRowsByIdsAndMember(MEMBER_ID, Collections.singletonList(10L)))
                .thenReturn(Collections.singletonList(row));
        GoodsPurchaseVO purchase = allowed();
        when(purchaseFacade.canPurchase(1L)).thenReturn(purchase);
        BizMemberAddress address = address(1L);
        when(addressMapper.selectByMemberId(MEMBER_ID)).thenReturn(Collections.singletonList(address));
        BizShop shop = shop(101L);
        when(shopMapper.selectById(101L)).thenReturn(shop);
        mockFreight(1L);
        when(checkoutSupport.sumGoodsAmount(anyList())).thenReturn(new BigDecimal("20.00"));
        when(checkoutSupport.resolveProvinceId(address)).thenReturn(11);
        when(checkoutSupport.calcFreight(eq(101L), anyList(), eq(11), any())).thenReturn(new BigDecimal("5.00"));
        when(checkoutSupport.buildFreightDesc(eq(101L), anyList(), any(), any())).thenReturn("运费 ¥5.00");
        when(checkoutSupport.buildServiceDesc(any())).thenReturn("7天无理由");
        when(checkoutSupport.buildLineFreightDesc(any(), eq(101L))).thenReturn("运费 ¥5.00");
        when(snapshotMapper.selectVisibleByGoodsId(1L)).thenReturn(Collections.emptyList());

        CheckoutPreviewDTO dto = new CheckoutPreviewDTO();
        dto.setSource(CheckoutConstants.SOURCE_CART);
        dto.setCartItemIds(Collections.singletonList(10L));
        CheckoutPreviewVO vo = checkoutAppService.preview(MEMBER_ID, dto);

        assertEquals(1, vo.getItems().size());
        assertEquals(0, vo.getPayAmount().compareTo(new BigDecimal("25.00")));
    }

    @Test
    void preview_crossShopRejected()
    {
        when(memberFacade.isMemberEnabled(MEMBER_ID)).thenReturn(true);
        when(cartAppMapper.selectItemRowsByIdsAndMember(eq(MEMBER_ID), anyList()))
                .thenReturn(Arrays.asList(cartRow(10L, 1L, 101L), cartRow(11L, 2L, 102L)));
        doThrow(new ServiceException(CheckoutConstants.MSG_CROSS_SHOP)).when(checkoutSupport)
                .assertSameShop(anyList());

        CheckoutPreviewDTO dto = new CheckoutPreviewDTO();
        dto.setSource(CheckoutConstants.SOURCE_CART);
        dto.setCartItemIds(Arrays.asList(10L, 11L));

        ServiceException ex = assertThrows(ServiceException.class, () -> checkoutAppService.preview(MEMBER_ID, dto));
        assertEquals(CheckoutConstants.MSG_CROSS_SHOP, ex.getMessage());
    }

    @Test
    void submit_createsPendingPayOrder()
    {
        when(memberFacade.isMemberEnabled(MEMBER_ID)).thenReturn(true);
        when(memberFacade.getMember(MEMBER_ID)).thenReturn(member());
        CartItemRowDTO row = cartRow(10L, 1L, 101L);
        when(cartAppMapper.selectItemRowsByIdsAndMember(MEMBER_ID, Collections.singletonList(10L)))
                .thenReturn(Collections.singletonList(row));
        when(purchaseFacade.canPurchase(1L)).thenReturn(allowed());
        BizMemberAddress address = address(5L);
        when(addressMapper.selectById(5L, MEMBER_ID)).thenReturn(address);
        BizShop shop = shop(101L);
        when(shopMapper.selectById(101L)).thenReturn(shop);
        mockFreight(1L);
        when(checkoutSupport.sumGoodsAmount(anyList())).thenReturn(new BigDecimal("20.00"));
        when(checkoutSupport.resolveProvinceId(address)).thenReturn(11);
        when(checkoutSupport.calcFreight(eq(101L), anyList(), eq(11), any())).thenReturn(new BigDecimal("5.00"));
        when(checkoutSupport.buildFreightDesc(any(), anyList(), any(), any())).thenReturn("运费 ¥5.00");
        when(checkoutSupport.buildServiceDesc(any())).thenReturn("");
        when(checkoutSupport.buildLineFreightDesc(any(), eq(101L))).thenReturn("运费 ¥5.00");
        when(snapshotMapper.selectVisibleByGoodsId(1L)).thenReturn(Collections.emptyList());
        when(checkoutSupport.nextOrderNo(any())).thenReturn("O20260526000001");
        when(configService.selectConfigByKey(OrderConstants.CONFIG_PAY_TIMEOUT_MINUTES)).thenReturn("1440");
        when(orderMapper.insert(any(BizOrder.class))).thenAnswer(invocation -> {
            BizOrder order = invocation.getArgument(0);
            order.setOrderId(900L);
            return 1;
        });

        CheckoutSubmitDTO dto = new CheckoutSubmitDTO();
        dto.setSource(CheckoutConstants.SOURCE_CART);
        dto.setAddressId(5L);
        dto.setPayType(OrderConstants.PAY_TYPE_WECHAT);
        dto.setCartItemIds(Collections.singletonList(10L));
        CheckoutSubmitResultVO result = checkoutAppService.submit(MEMBER_ID, dto);

        assertEquals(Long.valueOf(900L), result.getOrderId());
        assertEquals("O20260526000001", result.getOrderNo());
        verify(orderItemMapper).batchInsert(anyList());
    }

    private void mockFreight(Long goodsId)
    {
        BizGoodsFreight freight = new BizGoodsFreight();
        freight.setFreightType(FreightTemplateConstants.FREIGHT_TYPE_FIXED);
        freight.setFixedAmount(new BigDecimal("5.00"));
        when(goodsFreightMapper.selectByGoodsId(goodsId)).thenReturn(freight);
    }

    private GoodsPurchaseVO allowed()
    {
        GoodsPurchaseVO vo = new GoodsPurchaseVO();
        vo.setAllowed(true);
        return vo;
    }

    private CartItemRowDTO cartRow(Long cartItemId, Long goodsId, Long shopId)
    {
        CartItemRowDTO row = new CartItemRowDTO();
        row.setCartItemId(cartItemId);
        row.setGoodsId(goodsId);
        row.setShopId(shopId);
        row.setQuantity(2);
        row.setSpecText("默认");
        row.setGoodsName("商品A");
        row.setMainPic("/pic.jpg");
        row.setSalePrice(new BigDecimal("10.00"));
        row.setStock(99);
        return row;
    }

    private BizMemberAddress address(Long id)
    {
        BizMemberAddress address = new BizMemberAddress();
        address.setAddressId(id);
        address.setConsigneeName("张三");
        address.setMobile("13800000000");
        address.setRegionName("广东省深圳市南山区");
        address.setDetailAddress("科技园");
        address.setRegionCode("440305");
        address.setIsDefault(MemberConstants.DEFAULT_YES);
        return address;
    }

    private BizShop shop(Long shopId)
    {
        BizShop shop = new BizShop();
        shop.setShopId(shopId);
        shop.setMerchantId(50L);
        shop.setShopName("测试店");
        shop.setShopStatus("0");
        return shop;
    }

    private BizMember member()
    {
        BizMember member = new BizMember();
        member.setMemberId(MEMBER_ID);
        member.setNickName("买家");
        return member;
    }
}
