package com.ruoyi.web.modules.order.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.anyList;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
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.Calendar;
import java.util.Collections;
import java.util.Date;
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.account.facade.IMemberFacade;
import com.ruoyi.web.modules.cart.facade.ICartFacade;
import com.ruoyi.web.modules.goods.mapper.BizGoodsMapper;
import com.ruoyi.web.modules.order.constant.OrderAppConstants;
import com.ruoyi.web.modules.order.constant.OrderConstants;
import com.ruoyi.web.modules.order.domain.BizOrder;
import com.ruoyi.web.modules.order.domain.BizOrderItem;
import com.ruoyi.web.modules.order.mapper.BizGoodsReviewMapper;
import com.ruoyi.web.modules.order.mapper.BizOrderAftersaleMapper;
import com.ruoyi.web.modules.order.mapper.BizOrderItemMapper;
import com.ruoyi.web.modules.order.mapper.BizOrderLogisticsTraceMapper;
import com.ruoyi.web.modules.order.mapper.BizOrderMapper;
import com.ruoyi.web.modules.order.service.impl.OrderAppServiceImpl;
import com.ruoyi.web.modules.order.support.CheckoutSupport;
import com.ruoyi.web.modules.order.support.OrderAppSupport;
import com.ruoyi.web.modules.order.vo.OrderAppBadgeVO;
import com.ruoyi.web.modules.pay.service.IWeChatPayService;
import com.ruoyi.web.modules.pay.vo.WeChatPayParamsVO;
import com.ruoyi.web.modules.store.mapper.BizShopMapper;

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

    private static final Long ORDER_ID = 900L;

    @Mock
    private IMemberFacade memberFacade;

    @Mock
    private BizOrderMapper orderMapper;

    @Mock
    private BizOrderItemMapper orderItemMapper;

    @Mock
    private BizGoodsMapper goodsMapper;

    @Mock
    private ICartFacade cartFacade;

    @Mock
    private CheckoutSupport checkoutSupport;

    @Mock
    private OrderAppSupport orderAppSupport;

    @Mock
    private BizGoodsReviewMapper reviewMapper;

    @Mock
    private BizOrderAftersaleMapper aftersaleMapper;

    @Mock
    private BizOrderLogisticsTraceMapper traceMapper;

    @Mock
    private BizShopMapper shopMapper;

    @Mock
    private IWeChatPayService weChatPayService;

    @Mock
    private IOrderPaySuccessService orderPaySuccessService;

    @Mock
    private IOrderCompletionService orderCompletionService;

    @InjectMocks
    private OrderAppServiceImpl orderAppService;

    @Test
    void list_returnsAllItems()
    {
        when(memberFacade.isMemberEnabled(MEMBER_ID)).thenReturn(true);
        BizOrder order = pendingOrder();
        order.setShopId(10L);
        order.setShopName("测试店铺");
        when(orderMapper.selectMemberAppList(eq(MEMBER_ID), any())).thenReturn(Collections.singletonList(order));

        BizOrderItem item1 = new BizOrderItem();
        item1.setItemId(1L);
        item1.setOrderId(ORDER_ID);
        item1.setGoodsId(101L);
        item1.setGoodsName("商品A");
        BizOrderItem item2 = new BizOrderItem();
        item2.setItemId(2L);
        item2.setOrderId(ORDER_ID);
        item2.setGoodsId(102L);
        item2.setGoodsName("商品B");
        when(orderItemMapper.selectByOrderIds(Collections.singletonList(ORDER_ID)))
                .thenReturn(java.util.Arrays.asList(item1, item2));
        when(reviewMapper.selectByOrderIds(Collections.singletonList(ORDER_ID))).thenReturn(Collections.emptyList());
        when(aftersaleMapper.selectAftersaleStatusByOrderIds(Collections.singletonList(ORDER_ID)))
                .thenReturn(Collections.emptyList());
        when(shopMapper.selectById(10L)).thenReturn(null);
        when(orderAppSupport.toStatusText(order.getOrderStatus())).thenReturn("待付款");
        when(orderAppSupport.toItemSummary(item1)).thenReturn(summary(item1));
        when(orderAppSupport.toItemSummary(item2)).thenReturn(summary(item2));
        when(orderAppSupport.indexReviewsByItemId(any(), any())).thenReturn(Collections.emptyMap());
        when(orderAppSupport.calcPayRemainSeconds(order)).thenReturn(3600L);
        when(orderAppSupport.resolveReviewStatus(eq(order), any(), any()))
                .thenReturn(OrderAppConstants.REVIEW_STATUS_NONE);
        when(orderAppSupport.resolveActions(order)).thenReturn(Collections.singletonList("PAY"));

        com.ruoyi.web.modules.order.vo.OrderAppListRowVO row =
                orderAppService.list(MEMBER_ID, new com.ruoyi.web.modules.order.dto.OrderAppListQueryDTO()).get(0);

        assertEquals(2, row.getItemCount());
        assertEquals(2, row.getItems().size());
        assertEquals("商品A", row.getItems().get(0).getGoodsName());
        assertEquals("商品B", row.getItems().get(1).getGoodsName());
        assertEquals(row.getItems().get(0), row.getFirstItem());
    }

    @Test
    void list_withInProgressAftersale_setsAftersaleStatus()
    {
        when(memberFacade.isMemberEnabled(MEMBER_ID)).thenReturn(true);
        BizOrder order = pendingOrder();
        order.setOrderStatus(OrderConstants.STATUS_PENDING_SHIP);
        order.setShopId(10L);
        when(orderMapper.selectMemberAppList(eq(MEMBER_ID), any())).thenReturn(Collections.singletonList(order));
        when(orderItemMapper.selectByOrderIds(Collections.singletonList(ORDER_ID)))
                .thenReturn(Collections.singletonList(singleItem()));
        when(reviewMapper.selectByOrderIds(Collections.singletonList(ORDER_ID))).thenReturn(Collections.emptyList());
        com.ruoyi.web.modules.order.dto.OrderAftersaleStatusRow statusRow =
                new com.ruoyi.web.modules.order.dto.OrderAftersaleStatusRow();
        statusRow.setOrderId(ORDER_ID);
        statusRow.setAftersaleStatus(OrderAppConstants.AFTERSALE_STATUS_IN_PROGRESS);
        when(aftersaleMapper.selectAftersaleStatusByOrderIds(Collections.singletonList(ORDER_ID)))
                .thenReturn(Collections.singletonList(statusRow));
        when(shopMapper.selectById(10L)).thenReturn(null);
        when(orderAppSupport.toStatusText(order.getOrderStatus())).thenReturn("待发货");
        when(orderAppSupport.toItemSummary(any())).thenReturn(new com.ruoyi.web.modules.order.vo.OrderAppItemSummaryVO());
        when(orderAppSupport.indexReviewsByItemId(any(), any())).thenReturn(Collections.emptyMap());
        when(orderAppSupport.calcPayRemainSeconds(order)).thenReturn(0L);
        when(orderAppSupport.resolveReviewStatus(eq(order), any(), any()))
                .thenReturn(OrderAppConstants.REVIEW_STATUS_NONE);
        when(orderAppSupport.resolveActions(order)).thenReturn(Collections.emptyList());

        com.ruoyi.web.modules.order.vo.OrderAppListRowVO row =
                orderAppService.list(MEMBER_ID, new com.ruoyi.web.modules.order.dto.OrderAppListQueryDTO()).get(0);

        assertEquals("待发货", row.getOrderStatusText());
        assertEquals(OrderAppConstants.AFTERSALE_STATUS_IN_PROGRESS, row.getAftersaleStatus());
    }

    @Test
    void list_withoutAftersale_aftersaleStatusNull()
    {
        when(memberFacade.isMemberEnabled(MEMBER_ID)).thenReturn(true);
        BizOrder order = pendingOrder();
        order.setShopId(10L);
        when(orderMapper.selectMemberAppList(eq(MEMBER_ID), any())).thenReturn(Collections.singletonList(order));
        when(orderItemMapper.selectByOrderIds(Collections.singletonList(ORDER_ID)))
                .thenReturn(Collections.singletonList(singleItem()));
        when(reviewMapper.selectByOrderIds(Collections.singletonList(ORDER_ID))).thenReturn(Collections.emptyList());
        when(aftersaleMapper.selectAftersaleStatusByOrderIds(Collections.singletonList(ORDER_ID)))
                .thenReturn(Collections.emptyList());
        when(shopMapper.selectById(10L)).thenReturn(null);
        when(orderAppSupport.toStatusText(order.getOrderStatus())).thenReturn("待付款");
        when(orderAppSupport.toItemSummary(any())).thenReturn(new com.ruoyi.web.modules.order.vo.OrderAppItemSummaryVO());
        when(orderAppSupport.indexReviewsByItemId(any(), any())).thenReturn(Collections.emptyMap());
        when(orderAppSupport.calcPayRemainSeconds(order)).thenReturn(3600L);
        when(orderAppSupport.resolveReviewStatus(eq(order), any(), any()))
                .thenReturn(OrderAppConstants.REVIEW_STATUS_NONE);
        when(orderAppSupport.resolveActions(order)).thenReturn(Collections.emptyList());

        com.ruoyi.web.modules.order.vo.OrderAppListRowVO row =
                orderAppService.list(MEMBER_ID, new com.ruoyi.web.modules.order.dto.OrderAppListQueryDTO()).get(0);

        assertEquals(null, row.getAftersaleStatus());
    }

    private BizOrderItem singleItem()
    {
        BizOrderItem item = new BizOrderItem();
        item.setItemId(1L);
        item.setOrderId(ORDER_ID);
        item.setGoodsId(101L);
        item.setGoodsName("商品A");
        return item;
    }

    private com.ruoyi.web.modules.order.vo.OrderAppItemSummaryVO summary(BizOrderItem item)
    {
        com.ruoyi.web.modules.order.vo.OrderAppItemSummaryVO vo =
                new com.ruoyi.web.modules.order.vo.OrderAppItemSummaryVO();
        vo.setItemId(item.getItemId());
        vo.setGoodsId(item.getGoodsId());
        vo.setGoodsName(item.getGoodsName());
        return vo;
    }

    @Test
    void createPay_buyNowSkipsCartRemoval()
    {
        when(memberFacade.isMemberEnabled(MEMBER_ID)).thenReturn(true);
        when(memberFacade.getWxOpenid(MEMBER_ID)).thenReturn("openid-test");
        BizOrder order = pendingOrder();
        when(orderMapper.selectByIdAndMember(ORDER_ID, MEMBER_ID)).thenReturn(order);
        WeChatPayParamsVO payParams = new WeChatPayParamsVO();
        payParams.setMock(true);
        when(weChatPayService.createJsapiPay(MEMBER_ID, order, "openid-test")).thenReturn(payParams);

        orderAppService.createPay(MEMBER_ID, ORDER_ID);

        verify(orderPaySuccessService).completeWeChatPay(eq(order.getOrderNo()), eq("MOCK" + order.getOrderNo()),
                any(Date.class));
        verify(cartFacade, never()).removeItems(any(), anyList());
    }

    @Test
    void createPay_successDeductsStockAndRemovesCart()
    {
        when(memberFacade.isMemberEnabled(MEMBER_ID)).thenReturn(true);
        when(memberFacade.getWxOpenid(MEMBER_ID)).thenReturn("openid-test");
        BizOrder order = pendingOrder();
        when(orderMapper.selectByIdAndMember(ORDER_ID, MEMBER_ID)).thenReturn(order);
        WeChatPayParamsVO payParams = new WeChatPayParamsVO();
        payParams.setMock(true);
        when(weChatPayService.createJsapiPay(MEMBER_ID, order, "openid-test")).thenReturn(payParams);

        orderAppService.createPay(MEMBER_ID, ORDER_ID);

        verify(orderPaySuccessService).completeWeChatPay(eq(order.getOrderNo()), eq("MOCK" + order.getOrderNo()),
                any(Date.class));
    }

    @Test
    void createPay_stockFailedKeepsPendingPay()
    {
        when(memberFacade.isMemberEnabled(MEMBER_ID)).thenReturn(true);
        when(memberFacade.getWxOpenid(MEMBER_ID)).thenReturn("openid-test");
        when(orderMapper.selectByIdAndMember(ORDER_ID, MEMBER_ID)).thenReturn(pendingOrder());
        WeChatPayParamsVO payParams = new WeChatPayParamsVO();
        payParams.setMock(true);
        when(weChatPayService.createJsapiPay(eq(MEMBER_ID), any(), eq("openid-test"))).thenReturn(payParams);
        org.mockito.Mockito.doThrow(new ServiceException(OrderConstants.MSG_STOCK_DEDUCT_FAILED))
                .when(orderPaySuccessService).completeWeChatPay(any(), any(), any());

        ServiceException ex = assertThrows(ServiceException.class, () -> orderAppService.createPay(MEMBER_ID, ORDER_ID));
        assertEquals(OrderConstants.MSG_STOCK_DEDUCT_FAILED, ex.getMessage());
    }

    @Test
    void cancelPay_closesOrder()
    {
        when(memberFacade.isMemberEnabled(MEMBER_ID)).thenReturn(true);
        when(orderMapper.selectByIdAndMember(ORDER_ID, MEMBER_ID)).thenReturn(pendingOrder());
        when(orderMapper.updateForCancelPay(ORDER_ID, MEMBER_ID, OrderConstants.CLOSE_TYPE_BUYER_CANCEL)).thenReturn(1);

        orderAppService.cancelPay(MEMBER_ID, ORDER_ID);

        verify(orderMapper).updateForCancelPay(ORDER_ID, MEMBER_ID, OrderConstants.CLOSE_TYPE_BUYER_CANCEL);
    }

    @Test
    void getBadges_returnsCounts()
    {
        when(memberFacade.isMemberEnabled(MEMBER_ID)).thenReturn(true);
        when(orderMapper.countMemberByStatus(MEMBER_ID, OrderConstants.STATUS_PENDING_PAY)).thenReturn(2);
        when(orderMapper.countMemberByStatus(MEMBER_ID, OrderConstants.STATUS_PENDING_SHIP)).thenReturn(1);
        when(orderMapper.countMemberByStatus(MEMBER_ID, OrderConstants.STATUS_SHIPPED)).thenReturn(3);
        when(aftersaleMapper.countInProgressByMember(MEMBER_ID)).thenReturn(1);

        OrderAppBadgeVO badges = orderAppService.getBadges(MEMBER_ID);

        assertEquals(2, badges.getPendingPayCount());
        assertEquals(3, badges.getPendingReceiveCount());
    }

    @Test
    void confirmReceive_delegatesToCompletionService()
    {
        when(memberFacade.isMemberEnabled(MEMBER_ID)).thenReturn(true);
        BizOrder order = pendingOrder();
        order.setOrderStatus(OrderConstants.STATUS_SHIPPED);
        when(orderMapper.selectByIdAndMember(ORDER_ID, MEMBER_ID)).thenReturn(order);

        orderAppService.confirmReceive(MEMBER_ID, ORDER_ID);

        verify(orderCompletionService).completeOrder(eq(order), any(Date.class), eq(String.valueOf(MEMBER_ID)),
                eq("买家确认收货"));
    }

    @Test
    void confirmReceive_notShipped_throws()
    {
        when(memberFacade.isMemberEnabled(MEMBER_ID)).thenReturn(true);
        BizOrder order = pendingOrder();
        when(orderMapper.selectByIdAndMember(ORDER_ID, MEMBER_ID)).thenReturn(order);
        doThrow(new ServiceException(OrderAppConstants.MSG_CANNOT_CONFIRM_RECEIVE))
                .when(orderCompletionService).completeOrder(eq(order), any(Date.class), any(), any());

        assertThrows(ServiceException.class, () -> orderAppService.confirmReceive(MEMBER_ID, ORDER_ID));
    }

    private BizOrder pendingOrder()
    {
        BizOrder order = new BizOrder();
        order.setOrderId(ORDER_ID);
        order.setMemberId(MEMBER_ID);
        order.setOrderStatus(OrderConstants.STATUS_PENDING_PAY);
        order.setPayAmount(new BigDecimal("25.00"));
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.HOUR, 1);
        order.setPayExpireTime(calendar.getTime());
        order.setOrderNo("O20260526000001");
        return order;
    }
}
