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

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
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 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.facade.ICategoryFacade;
import com.ruoyi.web.modules.category.mapper.BizGoodsCategoryMapper;
import com.ruoyi.web.modules.category.vo.CategoryVisibleChildVO;
import com.ruoyi.web.modules.category.vo.CategoryVisibleVO;
import com.ruoyi.web.modules.home.constant.CategoryAppConstants;
import com.ruoyi.web.modules.home.dto.CategoryGoodsQuery;
import com.ruoyi.web.modules.home.mapper.CategoryAppMapper;
import com.ruoyi.web.modules.home.service.impl.CategoryAppServiceImpl;
import com.ruoyi.web.modules.home.vo.HomeHotGoodsVO;

/**
 * C 端商品分类 Service 单元测试（CGC-UT-001～012）
 */
@ExtendWith(MockitoExtension.class)
class CategoryAppServiceImplTest
{
    @Mock
    private ICategoryFacade categoryFacade;

    @Mock
    private BizGoodsCategoryMapper categoryMapper;

    @Mock
    private CategoryAppMapper categoryAppMapper;

    @InjectMocks
    private CategoryAppServiceImpl categoryAppService;

    @Test
    void listCategoryTree_delegatesPlatformFacade()
    {
        CategoryVisibleVO l1 = visibleLevel1(1L, "兽药");
        when(categoryFacade.listVisibleByShopId(null)).thenReturn(Collections.singletonList(l1));

        List<CategoryVisibleVO> result = categoryAppService.listCategoryTree();

        verify(categoryFacade).listVisibleByShopId(null);
        assertEquals(1, result.size());
        assertEquals("兽药", result.get(0).getCategoryName());
    }

    @Test
    void listCategoryTree_emptyListWhenNoData()
    {
        when(categoryFacade.listVisibleByShopId(null)).thenReturn(Collections.emptyList());

        List<CategoryVisibleVO> result = categoryAppService.listCategoryTree();

        assertNotNull(result);
        assertTrue(result.isEmpty());
    }

    @Test
    void listCategoryTree_nullFacadeResultReturnsEmpty()
    {
        when(categoryFacade.listVisibleByShopId(null)).thenReturn(null);

        List<CategoryVisibleVO> result = categoryAppService.listCategoryTree();

        assertNotNull(result);
        assertTrue(result.isEmpty());
    }

    @Test
    void listLevel2Tabs_returnsVisibleChildren()
    {
        when(categoryMapper.selectByIdIgnoreShop(1L)).thenReturn(platformLevel1(1L));
        CategoryVisibleChildVO tab1 = level2Tab(11L, "抗生素", 0);
        CategoryVisibleChildVO tab2 = level2Tab(12L, "驱虫药", 1);
        when(categoryAppMapper.selectPlatformLevel2Tabs(1L)).thenReturn(Arrays.asList(tab1, tab2));

        List<CategoryVisibleChildVO> result = categoryAppService.listLevel2Tabs(1L);

        assertEquals(2, result.size());
        assertEquals("抗生素", result.get(0).getCategoryName());
    }

    @Test
    void listLevel2Tabs_hiddenLevel1Rejected()
    {
        BizGoodsCategory hidden = platformLevel1(99L);
        hidden.setShowFlag(CategoryConstants.SHOW_NO);
        when(categoryMapper.selectByIdIgnoreShop(99L)).thenReturn(hidden);

        ServiceException ex = assertThrows(ServiceException.class,
                () -> categoryAppService.listLevel2Tabs(99L));

        assertEquals(CategoryAppConstants.MSG_CATEGORY_INVISIBLE, ex.getMessage());
    }

    @Test
    void listCategoryGoods_missingCategoryIdRejected()
    {
        CategoryGoodsQuery query = new CategoryGoodsQuery();

        ServiceException ex = assertThrows(ServiceException.class,
                () -> categoryAppService.listCategoryGoods(query));

        assertEquals(CategoryAppConstants.MSG_CATEGORY_REQUIRED, ex.getMessage());
    }

    @Test
    void listCategoryGoods_invisibleCategoryRejected()
    {
        CategoryGoodsQuery query = new CategoryGoodsQuery();
        query.setCategoryId(20L);
        when(categoryFacade.isCategoryVisible(20L)).thenReturn(false);

        ServiceException ex = assertThrows(ServiceException.class,
                () -> categoryAppService.listCategoryGoods(query));

        assertEquals(CategoryAppConstants.MSG_CATEGORY_INVISIBLE, ex.getMessage());
    }

    @Test
    void listCategoryGoods_defaultSalesDescSort()
    {
        CategoryGoodsQuery query = new CategoryGoodsQuery();
        query.setCategoryId(11L);
        when(categoryFacade.isCategoryVisible(11L)).thenReturn(true);
        when(categoryAppMapper.selectGoodsByCategory(11L, CategoryAppConstants.DEFAULT_SORT))
                .thenReturn(Collections.singletonList(goods(1L, "G001")));

        categoryAppService.listCategoryGoods(query);

        verify(categoryAppMapper).selectGoodsByCategory(11L, CategoryAppConstants.SORT_SALES_DESC);
    }

    @Test
    void listCategoryGoods_priceAscSort()
    {
        CategoryGoodsQuery query = new CategoryGoodsQuery();
        query.setCategoryId(11L);
        query.setSortBy(CategoryAppConstants.SORT_PRICE_ASC);
        when(categoryFacade.isCategoryVisible(11L)).thenReturn(true);
        when(categoryAppMapper.selectGoodsByCategory(11L, CategoryAppConstants.SORT_PRICE_ASC))
                .thenReturn(Collections.singletonList(goods(1L, "G001")));

        categoryAppService.listCategoryGoods(query);

        verify(categoryAppMapper).selectGoodsByCategory(11L, CategoryAppConstants.SORT_PRICE_ASC);
    }

    @Test
    void listCategoryGoods_invalidSortRejected()
    {
        CategoryGoodsQuery query = new CategoryGoodsQuery();
        query.setCategoryId(11L);
        query.setSortBy("invalid");
        when(categoryFacade.isCategoryVisible(11L)).thenReturn(true);

        ServiceException ex = assertThrows(ServiceException.class,
                () -> categoryAppService.listCategoryGoods(query));

        assertEquals(CategoryAppConstants.MSG_SORT_INVALID, ex.getMessage());
    }

    @Test
    void listCategoryGoods_preservesMapperOrder()
    {
        CategoryGoodsQuery query = new CategoryGoodsQuery();
        query.setCategoryId(11L);
        when(categoryFacade.isCategoryVisible(11L)).thenReturn(true);
        HomeHotGoodsVO g1 = goods(1L, "G001");
        HomeHotGoodsVO g3 = goods(3L, "G003");
        when(categoryAppMapper.selectGoodsByCategory(11L, CategoryAppConstants.DEFAULT_SORT))
                .thenReturn(Arrays.asList(g1, g3));

        List<HomeHotGoodsVO> result = categoryAppService.listCategoryGoods(query);

        assertEquals(1L, result.get(0).getGoodsId());
        assertEquals(3L, result.get(1).getGoodsId());
    }

    @Test
    void listCategoryGoods_emptyListWhenNoGoods()
    {
        CategoryGoodsQuery query = new CategoryGoodsQuery();
        query.setCategoryId(11L);
        when(categoryFacade.isCategoryVisible(11L)).thenReturn(true);
        when(categoryAppMapper.selectGoodsByCategory(11L, CategoryAppConstants.DEFAULT_SORT))
                .thenReturn(Collections.emptyList());

        List<HomeHotGoodsVO> result = categoryAppService.listCategoryGoods(query);

        assertNotNull(result);
        assertTrue(result.isEmpty());
    }

    private static CategoryVisibleVO visibleLevel1(Long id, String name)
    {
        CategoryVisibleVO vo = new CategoryVisibleVO();
        vo.setCategoryId(id);
        vo.setCategoryName(name);
        vo.setSortNo(0);
        return vo;
    }

    private static BizGoodsCategory platformLevel1(Long id)
    {
        BizGoodsCategory c = new BizGoodsCategory();
        c.setCategoryId(id);
        c.setShopId(null);
        c.setCategoryLevel(CategoryConstants.LEVEL_ONE);
        c.setShowFlag(CategoryConstants.SHOW_YES);
        c.setDelFlag(CategoryConstants.DEL_FLAG_NORMAL);
        return c;
    }

    private static CategoryVisibleChildVO level2Tab(Long id, String name, int sortNo)
    {
        CategoryVisibleChildVO vo = new CategoryVisibleChildVO();
        vo.setCategoryId(id);
        vo.setCategoryName(name);
        vo.setSortNo(sortNo);
        return vo;
    }

    private static HomeHotGoodsVO goods(Long goodsId, String goodsSn)
    {
        HomeHotGoodsVO vo = new HomeHotGoodsVO();
        vo.setGoodsId(goodsId);
        vo.setGoodsSn(goodsSn);
        vo.setGoodsName("商品" + goodsSn);
        vo.setMainPic("/pic/" + goodsSn + ".jpg");
        vo.setSalePrice(new BigDecimal("88.50"));
        vo.setShopId(101L);
        vo.setShopName("测试店");
        return vo;
    }
}
