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

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.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.web.modules.account.domain.BizMemberAddress;
import com.ruoyi.web.modules.freight.constant.FreightTemplateConstants;
import com.ruoyi.web.modules.freight.facade.IFreightTemplateFacade;
import com.ruoyi.web.modules.goods.domain.BizGoodsFreight;
import com.ruoyi.web.modules.goods.mapper.BizGoodsFreightMapper;
import com.ruoyi.web.modules.order.constant.CheckoutConstants;
import com.ruoyi.web.modules.order.mapper.BizOrderMapper;
import com.ruoyi.web.modules.region.domain.BizRegion;
import com.ruoyi.web.modules.region.mapper.BizRegionMapper;

@ExtendWith(MockitoExtension.class)
class CheckoutSupportTest
{
    @Mock
    private BizGoodsFreightMapper goodsFreightMapper;

    @Mock
    private IFreightTemplateFacade freightTemplateFacade;

    @Mock
    private BizRegionMapper regionMapper;

    @Mock
    private BizOrderMapper orderMapper;

    @InjectMocks
    private CheckoutSupport checkoutSupport;

    @Test
    void calcFreight_fixedAndTemplate()
    {
        CheckoutLineModel line1 = line(1L, 2);
        CheckoutLineModel line2 = line(2L, 1);
        BizGoodsFreight fixed = new BizGoodsFreight();
        fixed.setFreightType(FreightTemplateConstants.FREIGHT_TYPE_FIXED);
        fixed.setFixedAmount(new BigDecimal("5.00"));
        BizGoodsFreight template = new BizGoodsFreight();
        template.setFreightType(FreightTemplateConstants.FREIGHT_TYPE_TEMPLATE);
        template.setFreightTemplateId(10L);
        when(goodsFreightMapper.selectByGoodsId(1L)).thenReturn(fixed);
        when(goodsFreightMapper.selectByGoodsId(2L)).thenReturn(template);
        when(freightTemplateFacade.calcFreight(eq(10L), eq(101L), eq(11), any(), any(), any()))
                .thenReturn(new BigDecimal("8.00"));

        BigDecimal freight = checkoutSupport.calcFreight(101L, Arrays.asList(line1, line2), 11,
                new BigDecimal("100.00"));

        assertEquals(0, freight.compareTo(new BigDecimal("13.00")));
    }

    @Test
    void calcFreight_missingConfigThrows()
    {
        CheckoutLineModel line = line(1L, 1);
        when(goodsFreightMapper.selectByGoodsId(1L)).thenReturn(null);

        ServiceException ex = assertThrows(ServiceException.class,
                () -> checkoutSupport.calcFreight(101L, Collections.singletonList(line), 11, BigDecimal.TEN));
        assertEquals(CheckoutConstants.MSG_FREIGHT_NOT_CONFIG, ex.getMessage());
    }

    @Test
    void resolveProvinceId_fromDistrictCode()
    {
        BizMemberAddress address = new BizMemberAddress();
        address.setRegionCode("440305");
        BizRegion district = region(3, 200, 3);
        BizRegion city = region(200, 20, 2);
        BizRegion province = region(20, 0, 1);
        when(regionMapper.selectByCode(440305)).thenReturn(district);
        when(regionMapper.selectById(200)).thenReturn(city);
        when(regionMapper.selectById(20)).thenReturn(province);

        assertEquals(Integer.valueOf(20), checkoutSupport.resolveProvinceId(address));
    }

    @Test
    void nextOrderNo_incrementsSequence()
    {
        when(orderMapper.selectMaxOrderNoByPrefix("O20260526")).thenReturn("O20260526000001");

        String orderNo = checkoutSupport.nextOrderNo(new java.util.GregorianCalendar(2026, 4, 26).getTime());

        assertEquals("O20260526000002", orderNo);
    }

    @Test
    void calcFreightSingle_fixedFreight()
    {
        BizGoodsFreight fixed = new BizGoodsFreight();
        fixed.setFreightType(FreightTemplateConstants.FREIGHT_TYPE_FIXED);
        fixed.setFixedAmount(new BigDecimal("8.00"));
        when(goodsFreightMapper.selectByGoodsId(1L)).thenReturn(fixed);

        BigDecimal freight = checkoutSupport.calcFreightSingle(101L, 1L, 3, 11, new BigDecimal("30.00"));

        assertEquals(0, freight.compareTo(new BigDecimal("8.00")));
    }

    @Test
    void assertSameShop_rejectsCrossShop()
    {
        CheckoutLineModel line1 = line(1L, 1);
        line1.setShopId(101L);
        CheckoutLineModel line2 = line(2L, 1);
        line2.setShopId(102L);

        ServiceException ex = assertThrows(ServiceException.class,
                () -> checkoutSupport.assertSameShop(Arrays.asList(line1, line2)));
        assertEquals(CheckoutConstants.MSG_CROSS_SHOP, ex.getMessage());
    }

    private CheckoutLineModel line(Long goodsId, int quantity)
    {
        CheckoutLineModel line = new CheckoutLineModel();
        line.setGoodsId(goodsId);
        line.setQuantity(quantity);
        line.setSubtotal(BigDecimal.TEN);
        return line;
    }

    private BizRegion region(int id, int pid, int type)
    {
        BizRegion region = new BizRegion();
        region.setId(id);
        region.setPid(pid);
        region.setType(type);
        return region;
    }
}
