package com.ruoyi.web.modules.category.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.doNothing;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.verify;
import static org.mockito.ArgumentMatchers.anyLong;
import java.util.Arrays;
import java.util.Collections;
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.constant.CategoryConstants;
import com.ruoyi.web.modules.category.domain.BizGoodsCategory;
import com.ruoyi.web.modules.category.exception.CategoryBatchDeleteException;
import com.ruoyi.web.modules.category.facade.IGoodsFacade;
import com.ruoyi.web.modules.category.facade.IShopFacade;
import com.ruoyi.web.modules.category.mapper.BizGoodsCategoryMapper;
import com.ruoyi.web.modules.category.service.impl.CategoryServiceImpl;
import com.ruoyi.web.modules.category.support.SellerShopContext;

/**
 * 商品分类 Service 单元测试（CAT-UT）
 */
@ExtendWith(MockitoExtension.class)
class CategoryServiceImplTest
{
    private static final Long SHOP_ID = 1L;

    @Mock
    private BizGoodsCategoryMapper categoryMapper;

    @Mock
    private IGoodsFacade goodsFacade;

    @Mock
    private IShopFacade shopFacade;

    @InjectMocks
    private CategoryServiceImpl categoryService;

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

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

    @Test
    void insertLevel1_success()
    {
        BizGoodsCategory dto = baseDto();
        dto.setCategoryLevel(CategoryConstants.LEVEL_ONE);
        when(categoryMapper.countByName(eq(SHOP_ID), eq(CategoryConstants.PARENT_ROOT), eq("肥料"), eq(null))).thenReturn(0);
        when(categoryMapper.insert(any())).thenReturn(1);

        int rows = categoryService.insertCategory(dto);

        assertEquals(1, rows);
        verify(categoryMapper).insert(any());
    }

    @Test
    void insertLevel2_parentMustBeLevel1()
    {
        BizGoodsCategory parent = level1(1L, "肥料");
        BizGoodsCategory dto = baseDto();
        dto.setCategoryLevel(CategoryConstants.LEVEL_TWO);
        dto.setParentId(1L);
        when(categoryMapper.selectById(1L, SHOP_ID)).thenReturn(parent);
        when(categoryMapper.countByName(eq(SHOP_ID), eq(1L), eq("肥料"), eq(null))).thenReturn(0);
        when(categoryMapper.insert(any())).thenReturn(1);

        categoryService.insertCategory(dto);

        verify(categoryMapper).insert(any());
    }

    @Test
    void insertLevel2_parentIsLevel2_rejected()
    {
        BizGoodsCategory parent = level2(10L, 1L);
        BizGoodsCategory dto = baseDto();
        dto.setCategoryLevel(CategoryConstants.LEVEL_TWO);
        dto.setParentId(10L);
        when(categoryMapper.selectById(10L, SHOP_ID)).thenReturn(parent);

        ServiceException ex = assertThrows(ServiceException.class, () -> categoryService.insertCategory(dto));
        assertEquals("不支持三级分类", ex.getMessage());
    }

    @Test
    void insert_duplicateName_rejected()
    {
        BizGoodsCategory dto = baseDto();
        dto.setCategoryLevel(CategoryConstants.LEVEL_ONE);
        when(categoryMapper.countByName(eq(SHOP_ID), eq(CategoryConstants.PARENT_ROOT), eq("肥料"), eq(null))).thenReturn(1);

        assertThrows(ServiceException.class, () -> categoryService.insertCategory(dto));
    }

    @Test
    void deleteLevel2_withGoods_rejected()
    {
        BizGoodsCategory cat = level2(10L, 1L);
        when(categoryMapper.selectById(10L, SHOP_ID)).thenReturn(cat);
        when(goodsFacade.existsByCategoryId(10L)).thenReturn(true);

        assertThrows(CategoryBatchDeleteException.class,
                () -> categoryService.deleteCategoryByIds(new Long[] { 10L }, "tester"));
        verify(categoryMapper, never()).logicDeleteByIds(any(), any(), any());
    }

    @Test
    void deleteLevel2_noGoods_success()
    {
        BizGoodsCategory cat = level2(10L, 1L);
        when(categoryMapper.selectById(10L, SHOP_ID)).thenReturn(cat);
        when(goodsFacade.existsByCategoryId(10L)).thenReturn(false);
        when(categoryMapper.logicDeleteByIds(any(), eq(SHOP_ID), eq("tester"))).thenReturn(1);

        categoryService.deleteCategoryByIds(new Long[] { 10L }, "tester");

        verify(categoryMapper).logicDeleteByIds(any(), eq(SHOP_ID), eq("tester"));
    }

    @Test
    void deleteLevel1_cascadeChildren()
    {
        BizGoodsCategory l1 = level1(1L, "肥料");
        when(categoryMapper.selectById(1L, SHOP_ID)).thenReturn(l1);
        when(categoryMapper.selectChildIds(SHOP_ID, 1L)).thenReturn(Arrays.asList(10L, 11L));
        when(categoryMapper.selectById(10L, SHOP_ID)).thenReturn(level2(10L, 1L));
        when(categoryMapper.selectById(11L, SHOP_ID)).thenReturn(level2(11L, 1L));
        when(goodsFacade.existsByCategoryId(any())).thenReturn(false);

        categoryService.deleteCategoryByIds(new Long[] { 1L }, "tester");

        verify(categoryMapper).logicDeleteByIds(any(), eq(SHOP_ID), eq("tester"));
    }

    @Test
    void deleteLevel1_childHasGoods_rejected()
    {
        BizGoodsCategory l1 = level1(1L, "肥料");
        when(categoryMapper.selectById(1L, SHOP_ID)).thenReturn(l1);
        when(categoryMapper.selectChildIds(SHOP_ID, 1L)).thenReturn(Collections.singletonList(10L));
        when(categoryMapper.selectById(10L, SHOP_ID)).thenReturn(level2(10L, 1L));
        when(goodsFacade.existsByCategoryId(10L)).thenReturn(true);

        assertThrows(CategoryBatchDeleteException.class,
                () -> categoryService.deleteCategoryByIds(new Long[] { 1L }, "tester"));
    }

    @Test
    void insertPlatformLevel1_shopIdNull()
    {
        BizGoodsCategory dto = baseDto();
        dto.setCategoryLevel(CategoryConstants.LEVEL_ONE);
        when(categoryMapper.countByName(eq(null), eq(CategoryConstants.PARENT_ROOT), eq("肥料"), eq(null))).thenReturn(0);
        when(categoryMapper.insert(any())).thenReturn(1);

        int rows = categoryService.insertPlatformCategory(dto);

        assertEquals(1, rows);
        verify(categoryMapper).insert(any());
    }

    private BizGoodsCategory baseDto()
    {
        BizGoodsCategory c = new BizGoodsCategory();
        c.setCategoryName("肥料");
        c.setCategoryPic("/profile/upload/c.jpg");
        c.setSortNo(0);
        c.setShowFlag("1");
        c.setHotFlag("0");
        return c;
    }

    private BizGoodsCategory level1(Long id, String name)
    {
        BizGoodsCategory c = new BizGoodsCategory();
        c.setCategoryId(id);
        c.setShopId(SHOP_ID);
        c.setParentId(0L);
        c.setCategoryLevel(CategoryConstants.LEVEL_ONE);
        c.setCategoryName(name);
        return c;
    }

    private BizGoodsCategory level2(Long id, Long parentId)
    {
        BizGoodsCategory c = new BizGoodsCategory();
        c.setCategoryId(id);
        c.setShopId(SHOP_ID);
        c.setParentId(parentId);
        c.setCategoryLevel(CategoryConstants.LEVEL_TWO);
        c.setCategoryName("子类" + id);
        return c;
    }
}
