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

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.never;
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 java.util.List;
import java.util.Map;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
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.category.facade.IGoodsFacade;
import com.ruoyi.web.modules.category.facade.IShopFacade;
import com.ruoyi.web.modules.category.support.SellerShopContext;
import com.ruoyi.web.modules.freight.constant.FreightTemplateConstants;
import com.ruoyi.web.modules.freight.domain.BizShopFreightTemplate;
import com.ruoyi.web.modules.freight.domain.BizShopFreightTemplateRegion;
import com.ruoyi.web.modules.freight.dto.FreightTemplateRegionRuleDTO;
import com.ruoyi.web.modules.freight.dto.FreightTemplateRuleDTO;
import com.ruoyi.web.modules.freight.dto.FreightTemplateSaveDTO;
import com.ruoyi.web.modules.freight.exception.FreightTemplateBatchDeleteException;
import com.ruoyi.web.modules.freight.mapper.BizShopFreightTemplateMapper;
import com.ruoyi.web.modules.freight.mapper.BizShopFreightTemplateRegionMapper;
import com.ruoyi.web.modules.freight.service.impl.FreightTemplateServiceImpl;
import com.ruoyi.web.modules.freight.vo.FreightTemplateDetailVO;
import com.ruoyi.web.modules.freight.vo.FreightTemplateListVO;
import com.ruoyi.web.modules.region.domain.BizRegion;
import com.ruoyi.web.modules.region.mapper.BizRegionMapper;

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

    @Mock
    private BizShopFreightTemplateMapper templateMapper;

    @Mock
    private BizShopFreightTemplateRegionMapper templateRegionMapper;

    @Mock
    private BizRegionMapper bizRegionMapper;

    @Mock
    private IGoodsFacade goodsFacade;

    @Mock
    private IShopFacade shopFacade;

    @InjectMocks
    private FreightTemplateServiceImpl freightTemplateService;

    @BeforeEach
    void setUp()
    {
        SellerShopContext.setShopId(SHOP_ID);
        lenient().doNothing().when(shopFacade).assertShopMaintable(SHOP_ID);
    }

    @AfterEach
    void tearDown()
    {
        SellerShopContext.clear();
    }

    @Test
    void selectTemplateList_scopedToShop()
    {
        BizShopFreightTemplate template = template("全国包邮", FreightTemplateConstants.FREE_FLAG_YES, null);
        template.setIsDefault(FreightTemplateConstants.DEFAULT_YES);
        when(templateMapper.selectList(any())).thenReturn(Collections.singletonList(template));

        List<FreightTemplateListVO> rows = freightTemplateService.selectTemplateList(null);

        assertEquals(1, rows.size());
        assertEquals("包邮", rows.get(0).getFreeFlagLabel());
        assertEquals("—", rows.get(0).getPricingTypeLabel());
    }

    @Test
    void requireShopId_missingFails()
    {
        SellerShopContext.clear();
        ServiceException ex = assertThrows(ServiceException.class,
                () -> freightTemplateService.selectTemplateList(null));
        assertTrue(ex.getMessage().contains("请先选择当前店铺"));
    }

    @Test
    void insertTemplate_freeShipping_success()
    {
        when(templateMapper.countByName(eq(SHOP_ID), eq("全国包邮"), isNull())).thenReturn(0);
        when(templateMapper.insert(any())).thenAnswer(invocation -> {
            BizShopFreightTemplate t = invocation.getArgument(0);
            t.setTemplateId(1L);
            return 1;
        });

        FreightTemplateSaveDTO dto = freeShippingDto("全国包邮");

        assertEquals(1, freightTemplateService.insertTemplate(dto, "tester"));
        verify(templateRegionMapper, never()).insert(any());
    }

    @Test
    void insertTemplate_paidWithRegion_success()
    {
        when(templateMapper.countByName(eq(SHOP_ID), eq("江浙沪"), isNull())).thenReturn(0);
        when(templateMapper.insert(any())).thenAnswer(invocation -> {
            BizShopFreightTemplate t = invocation.getArgument(0);
            t.setTemplateId(2L);
            return 1;
        });
        when(templateRegionMapper.insert(any())).thenReturn(1);
        when(bizRegionMapper.selectByIds(anyList())).thenReturn(Arrays.asList(
                province(10, "江苏省"),
                province(11, "浙江省")));

        assertEquals(1, freightTemplateService.insertTemplate(paidDto("江浙沪"), "tester"));
        verify(templateRegionMapper).insert(any());
    }

    @Test
    void insertTemplate_duplicateNameFails()
    {
        when(templateMapper.countByName(eq(SHOP_ID), eq("全国包邮"), isNull())).thenReturn(1);
        ServiceException ex = assertThrows(ServiceException.class,
                () -> freightTemplateService.insertTemplate(freeShippingDto("全国包邮"), "tester"));
        assertTrue(ex.getMessage().contains("运费模版名称已存在"));
    }

    @Test
    void insertTemplate_regionOverlapFails()
    {
        FreightTemplateSaveDTO dto = paidDto("重叠地区");
        FreightTemplateRegionRuleDTO r1 = regionRule(Arrays.asList(10, 11));
        FreightTemplateRegionRuleDTO r2 = regionRule(Arrays.asList(11, 12));
        dto.setRegionRules(Arrays.asList(r1, r2));
        when(bizRegionMapper.selectByIds(anyList())).thenAnswer(invocation -> {
            @SuppressWarnings("unchecked")
            List<Integer> ids = invocation.getArgument(0);
            List<BizRegion> list = new java.util.ArrayList<>();
            for (Integer id : ids)
            {
                list.add(province(id, "省" + id));
            }
            return list;
        });

        ServiceException ex = assertThrows(ServiceException.class,
                () -> freightTemplateService.insertTemplate(dto, "tester"));
        assertTrue(ex.getMessage().contains("指定地区存在重叠"));
    }

    @Test
    void setDefaultTemplate_success()
    {
        when(templateMapper.selectById(5L, SHOP_ID)).thenReturn(template("默认", "0", "1"));
        Map<String, Object> result = freightTemplateService.setDefaultTemplate(5L, "tester");
        assertEquals(5L, result.get("templateId"));
        assertEquals(FreightTemplateConstants.DEFAULT_YES, result.get("isDefault"));
        verify(templateMapper).clearDefaultByShopId(SHOP_ID, 5L);
        verify(templateMapper).updateIsDefault(5L, SHOP_ID, FreightTemplateConstants.DEFAULT_YES, "tester");
    }

    @Test
    void deleteTemplateByIds_inUseFails()
    {
        BizShopFreightTemplate template = template("引用中", "0", "1");
        template.setTemplateId(8L);
        when(templateMapper.selectById(8L, SHOP_ID)).thenReturn(template);
        when(goodsFacade.existsByFreightTemplateId(8L)).thenReturn(true);

        FreightTemplateBatchDeleteException ex = assertThrows(FreightTemplateBatchDeleteException.class,
                () -> freightTemplateService.deleteTemplateByIds(new Long[] { 8L }, "tester"));
        assertTrue(ex.getReasons().get(0).contains("已被商品使用"));
    }

    @Test
    void deleteTemplateByIds_success()
    {
        BizShopFreightTemplate template = template("可删", "0", "1");
        template.setTemplateId(9L);
        when(templateMapper.selectById(9L, SHOP_ID)).thenReturn(template);
        when(goodsFacade.existsByFreightTemplateId(9L)).thenReturn(false);

        freightTemplateService.deleteTemplateByIds(new Long[] { 9L }, "tester");

        verify(templateRegionMapper).deleteByTemplateId(9L);
        verify(templateMapper).logicDeleteByIds(anyList(), eq(SHOP_ID), eq("tester"));
    }

    @Test
    void selectTemplateDetail_loadsRegions()
    {
        BizShopFreightTemplate template = template("按件", "0", "1");
        template.setTemplateId(3L);
        template.setFirstUnit(new BigDecimal("1"));
        template.setFirstFee(new BigDecimal("8"));
        template.setAddUnit(new BigDecimal("1"));
        template.setAddFee(new BigDecimal("2"));
        when(templateMapper.selectById(3L, SHOP_ID)).thenReturn(template);
        BizShopFreightTemplateRegion region = new BizShopFreightTemplateRegion();
        region.setProvinceIds("10,11");
        region.setProvinceNames("江苏省,浙江省");
        region.setFirstUnit(new BigDecimal("1"));
        region.setFirstFee(new BigDecimal("12"));
        region.setAddUnit(new BigDecimal("1"));
        region.setAddFee(new BigDecimal("5"));
        when(templateRegionMapper.selectByTemplateId(3L)).thenReturn(Collections.singletonList(region));

        FreightTemplateDetailVO detail = freightTemplateService.selectTemplateDetail(3L);

        assertEquals("按件", detail.getTemplateName());
        assertEquals(2, detail.getRegionRules().get(0).getProvinceIds().size());
    }

    private FreightTemplateSaveDTO freeShippingDto(String name)
    {
        FreightTemplateSaveDTO dto = new FreightTemplateSaveDTO();
        dto.setTemplateName(name);
        dto.setShipTime("24小时内发货");
        dto.setFreeFlag(FreightTemplateConstants.FREE_FLAG_YES);
        dto.setIsDefault(FreightTemplateConstants.DEFAULT_NO);
        return dto;
    }

    private FreightTemplateSaveDTO paidDto(String name)
    {
        FreightTemplateSaveDTO dto = new FreightTemplateSaveDTO();
        dto.setTemplateName(name);
        dto.setShipTime("48小时内发货");
        dto.setFreeFlag(FreightTemplateConstants.FREE_FLAG_NO);
        dto.setPricingType(FreightTemplateConstants.PRICING_TYPE_PIECE);
        dto.setIsDefault(FreightTemplateConstants.DEFAULT_NO);
        dto.setDefaultRule(rule("1", "8", "1", "2", null));
        dto.setRegionRules(Collections.singletonList(regionRule(Arrays.asList(10, 11))));
        return dto;
    }

    private FreightTemplateRegionRuleDTO regionRule(List<Integer> provinceIds)
    {
        FreightTemplateRegionRuleDTO rule = new FreightTemplateRegionRuleDTO();
        rule.setProvinceIds(provinceIds);
        rule.setFirstUnit(new BigDecimal("1"));
        rule.setFirstFee(new BigDecimal("12"));
        rule.setAddUnit(new BigDecimal("1"));
        rule.setAddFee(new BigDecimal("5"));
        return rule;
    }

    private FreightTemplateRuleDTO rule(String firstUnit, String firstFee, String addUnit, String addFee,
            String threshold)
    {
        FreightTemplateRuleDTO rule = new FreightTemplateRuleDTO();
        rule.setFirstUnit(new BigDecimal(firstUnit));
        rule.setFirstFee(new BigDecimal(firstFee));
        rule.setAddUnit(new BigDecimal(addUnit));
        rule.setAddFee(new BigDecimal(addFee));
        if (threshold != null)
        {
            rule.setFreeThreshold(new BigDecimal(threshold));
        }
        return rule;
    }

    private BizShopFreightTemplate template(String name, String freeFlag, String pricingType)
    {
        BizShopFreightTemplate template = new BizShopFreightTemplate();
        template.setTemplateName(name);
        template.setShipTime("24小时内发货");
        template.setFreeFlag(freeFlag);
        template.setPricingType(pricingType);
        template.setIsDefault(FreightTemplateConstants.DEFAULT_NO);
        return template;
    }

    private BizRegion province(int id, String name)
    {
        BizRegion region = new BizRegion();
        region.setId(id);
        region.setName(name);
        region.setType(FreightTemplateConstants.REGION_TYPE_PROVINCE);
        return region;
    }
}
