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.argThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
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 static org.mockito.ArgumentMatchers.argThat;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
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;
import com.ruoyi.web.modules.category.vo.CategoryLevel2OptionVO;
import com.ruoyi.web.modules.category.vo.CategoryRowVO;

/**
 * 商品分类 Service 单元测试（平台 + 店铺）
 */
@ExtendWith(MockitoExtension.class)
class CategoryServiceImplTest
{
    private static final Long SHOP_ID = 101L;
    @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 insertPlatformLevel1_success()
    {
        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());
    }

    @Test
    void selectPlatformLevel2Options_queriesShopIdNull()
    {
        BizGoodsCategory l1 = level1(1L, "肥料");
        BizGoodsCategory l2 = level2(10L, 1L);
        l2.setCategoryName("复合肥");
        when(categoryMapper.selectList(argThat(q -> q.getShopId() == null)))
                .thenReturn(Arrays.asList(l1, l2));

        List<CategoryLevel2OptionVO> options = categoryService.selectPlatformLevel2Options();
        assertEquals(1, options.size());
        assertEquals("肥料 > 复合肥", options.get(0).getLabel());
    }

    @Test
    void selectPlatformCategoryRowList_ordersChildrenUnderParent()
    {
        BizGoodsCategory l1a = level1(1L, "肥料");
        l1a.setSortNo(10);
        BizGoodsCategory l1b = level1(2L, "种子");
        l1b.setSortNo(5);
        BizGoodsCategory l2 = level2(10L, 2L);
        l2.setCategoryName("尿素");
        when(categoryMapper.selectList(any())).thenReturn(Arrays.asList(l1a, l2, l1b));

        List<CategoryRowVO> rows = categoryService.selectPlatformCategoryRowList(null);

        assertEquals(3, rows.size());
        assertEquals("种子", rows.get(0).getCategoryName());
        assertEquals("尿素", rows.get(1).getCategoryName());
        assertEquals("肥料", rows.get(2).getCategoryName());
        assertEquals("2", rows.get(1).getCategoryLevel());
        assertEquals(Long.valueOf(2L), rows.get(1).getParentId());
    }

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

        categoryService.insertPlatformCategory(dto);

        verify(categoryMapper).insert(argThat(c -> CategoryConstants.LEVEL_TWO.equals(c.getCategoryLevel())
                && Long.valueOf(1L).equals(c.getParentId())));
    }

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

        categoryService.insertPlatformCategory(dto);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    @Test
    void insertShopLevel1_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);

        assertEquals(1, categoryService.insertCategory(dto));
    }

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

        assertThrows(CategoryBatchDeleteException.class,
                () -> categoryService.deleteCategoryByIds(new Long[] { 10L }, "tester"));
        verify(categoryMapper, never()).logicDeleteByIds(any(), any(), 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.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.setParentId(parentId);
        c.setCategoryLevel(CategoryConstants.LEVEL_TWO);
        c.setCategoryName("子类" + id);
        return c;
    }
}
