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.ArgumentMatchers.eq;
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 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.home.constant.SearchAppConstants;
import com.ruoyi.web.modules.home.dto.SearchQuery;
import com.ruoyi.web.modules.home.mapper.SearchAppMapper;
import com.ruoyi.web.modules.home.service.impl.SearchAppServiceImpl;
import com.ruoyi.web.modules.home.vo.HomeHotGoodsVO;
import com.ruoyi.web.modules.home.vo.SearchResultAppVO;
import com.ruoyi.web.modules.home.vo.SearchShopAppVO;

/**
 * C 端全站搜索 Service 单元测试（CSR-UT-001～012）
 */
@ExtendWith(MockitoExtension.class)
class SearchAppServiceImplTest
{
    @Mock
    private SearchAppMapper searchAppMapper;

    @InjectMocks
    private SearchAppServiceImpl searchAppService;

    @Test
    void search_emptyKeywordRejected()
    {
        SearchQuery query = new SearchQuery();

        ServiceException ex = assertThrows(ServiceException.class, () -> searchAppService.search(query));

        assertEquals(SearchAppConstants.MSG_KEYWORD_REQUIRED, ex.getMessage());
    }

    @Test
    void search_blankKeywordRejected()
    {
        SearchQuery query = new SearchQuery();
        query.setKeyword("   ");

        ServiceException ex = assertThrows(ServiceException.class, () -> searchAppService.search(query));

        assertEquals(SearchAppConstants.MSG_KEYWORD_REQUIRED, ex.getMessage());
    }

    @Test
    void search_keywordTrimmed()
    {
        SearchQuery query = new SearchQuery();
        query.setKeyword(" 阿莫 ");
        when(searchAppMapper.selectGoodsByKeyword(eq("阿莫"), eq(SearchAppConstants.SORT_SALES_DESC)))
                .thenReturn(Collections.emptyList());
        when(searchAppMapper.selectShopsByKeyword(eq("阿莫"))).thenReturn(Collections.emptyList());

        searchAppService.search(query);

        verify(searchAppMapper).selectGoodsByKeyword("阿莫", SearchAppConstants.SORT_SALES_DESC);
        verify(searchAppMapper).selectShopsByKeyword("阿莫");
    }

    @Test
    void search_defaultSalesDescSort()
    {
        SearchQuery query = new SearchQuery();
        query.setKeyword("阿莫");
        when(searchAppMapper.selectGoodsByKeyword(eq("阿莫"), eq(SearchAppConstants.SORT_SALES_DESC)))
                .thenReturn(Collections.singletonList(goods(1L)));
        when(searchAppMapper.selectShopsByKeyword(eq("阿莫"))).thenReturn(Collections.emptyList());

        searchAppService.search(query);

        verify(searchAppMapper).selectGoodsByKeyword("阿莫", SearchAppConstants.SORT_SALES_DESC);
    }

    @Test
    void search_invalidSortRejected()
    {
        SearchQuery query = new SearchQuery();
        query.setKeyword("阿莫");
        query.setSortBy("invalid");

        ServiceException ex = assertThrows(ServiceException.class, () -> searchAppService.search(query));

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

    @Test
    void search_callsBothMappers()
    {
        SearchQuery query = new SearchQuery();
        query.setKeyword("阿莫");
        when(searchAppMapper.selectGoodsByKeyword(eq("阿莫"), eq(SearchAppConstants.SORT_SALES_DESC)))
                .thenReturn(Collections.singletonList(goods(1L)));
        when(searchAppMapper.selectShopsByKeyword(eq("阿莫")))
                .thenReturn(Collections.singletonList(shop(101L)));

        SearchResultAppVO result = searchAppService.search(query);

        verify(searchAppMapper).selectGoodsByKeyword("阿莫", SearchAppConstants.SORT_SALES_DESC);
        verify(searchAppMapper).selectShopsByKeyword("阿莫");
        assertEquals(1, result.getGoods().getRows().size());
        assertEquals(1, result.getShops().getRows().size());
    }

    @Test
    void search_resultStructure()
    {
        SearchQuery query = new SearchQuery();
        query.setKeyword("阿莫");
        when(searchAppMapper.selectGoodsByKeyword(eq("阿莫"), eq(SearchAppConstants.SORT_SALES_DESC)))
                .thenReturn(Arrays.asList(goods(1L), goods(2L)));
        when(searchAppMapper.selectShopsByKeyword(eq("阿莫")))
                .thenReturn(Collections.singletonList(shop(101L)));

        SearchResultAppVO result = searchAppService.search(query);

        assertNotNull(result.getGoods());
        assertNotNull(result.getShops());
        assertEquals(2, result.getGoods().getRows().size());
        assertEquals(1, result.getShops().getRows().size());
    }

    @Test
    void search_goodsEmptyShopsPresent()
    {
        SearchQuery query = new SearchQuery();
        query.setKeyword("阿莫");
        when(searchAppMapper.selectGoodsByKeyword(eq("阿莫"), eq(SearchAppConstants.SORT_SALES_DESC)))
                .thenReturn(Collections.emptyList());
        when(searchAppMapper.selectShopsByKeyword(eq("阿莫")))
                .thenReturn(Collections.singletonList(shop(101L)));

        SearchResultAppVO result = searchAppService.search(query);

        assertTrue(result.getGoods().getRows().isEmpty());
        assertEquals(1, result.getShops().getRows().size());
    }

    @Test
    void search_priceAscSort()
    {
        SearchQuery query = new SearchQuery();
        query.setKeyword("阿莫");
        query.setSortBy(SearchAppConstants.SORT_PRICE_ASC);
        when(searchAppMapper.selectGoodsByKeyword(eq("阿莫"), eq(SearchAppConstants.SORT_PRICE_ASC)))
                .thenReturn(Collections.emptyList());
        when(searchAppMapper.selectShopsByKeyword(eq("阿莫"))).thenReturn(Collections.emptyList());

        searchAppService.search(query);

        verify(searchAppMapper).selectGoodsByKeyword("阿莫", SearchAppConstants.SORT_PRICE_ASC);
    }

    @Test
    void search_priceDescSort()
    {
        SearchQuery query = new SearchQuery();
        query.setKeyword("阿莫");
        query.setSortBy(SearchAppConstants.SORT_PRICE_DESC);
        when(searchAppMapper.selectGoodsByKeyword(eq("阿莫"), eq(SearchAppConstants.SORT_PRICE_DESC)))
                .thenReturn(Collections.emptyList());
        when(searchAppMapper.selectShopsByKeyword(eq("阿莫"))).thenReturn(Collections.emptyList());

        searchAppService.search(query);

        verify(searchAppMapper).selectGoodsByKeyword("阿莫", SearchAppConstants.SORT_PRICE_DESC);
    }

    @Test
    void search_dualPaginationParams()
    {
        SearchQuery query = new SearchQuery();
        query.setKeyword("阿莫");
        query.setGoodsPageNum(2);
        query.setGoodsPageSize(5);
        query.setShopPageNum(3);
        query.setShopPageSize(8);
        when(searchAppMapper.selectGoodsByKeyword(eq("阿莫"), eq(SearchAppConstants.SORT_SALES_DESC)))
                .thenReturn(Collections.emptyList());
        when(searchAppMapper.selectShopsByKeyword(eq("阿莫"))).thenReturn(Collections.emptyList());

        SearchResultAppVO result = searchAppService.search(query);

        assertNotNull(result.getGoods());
        assertNotNull(result.getShops());
        verify(searchAppMapper).selectGoodsByKeyword("阿莫", SearchAppConstants.SORT_SALES_DESC);
        verify(searchAppMapper).selectShopsByKeyword("阿莫");
    }

    @Test
    void search_nullMapperResultReturnsEmptyRows()
    {
        SearchQuery query = new SearchQuery();
        query.setKeyword("阿莫");
        when(searchAppMapper.selectGoodsByKeyword(eq("阿莫"), eq(SearchAppConstants.SORT_SALES_DESC)))
                .thenReturn(null);
        when(searchAppMapper.selectShopsByKeyword(eq("阿莫"))).thenReturn(null);

        SearchResultAppVO result = searchAppService.search(query);

        assertTrue(result.getGoods().getRows().isEmpty());
        assertTrue(result.getShops().getRows().isEmpty());
    }

    private static HomeHotGoodsVO goods(Long goodsId)
    {
        HomeHotGoodsVO vo = new HomeHotGoodsVO();
        vo.setGoodsId(goodsId);
        vo.setGoodsSn("G00" + goodsId);
        vo.setGoodsName("阿莫西林");
        vo.setMainPic("/pic/g.jpg");
        vo.setSalePrice(new BigDecimal("88.00"));
        vo.setShopId(101L);
        vo.setShopName("阿莫兽药店");
        return vo;
    }

    private static SearchShopAppVO shop(Long shopId)
    {
        SearchShopAppVO vo = new SearchShopAppVO();
        vo.setShopId(shopId);
        vo.setShopName("阿莫兽药店");
        vo.setShopAvatar("/avatar/s.jpg");
        vo.setShopStatus("0");
        return vo;
    }
}
