package com.ruoyi.web.modules.goodsservice.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.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
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.goodsservice.constant.GoodsServiceConstants;
import com.ruoyi.web.modules.goodsservice.domain.BizGoodsService;
import com.ruoyi.web.modules.goodsservice.exception.GoodsServiceBatchDeleteException;
import com.ruoyi.web.modules.goodsservice.facade.IGoodsSnapshotFacade;
import com.ruoyi.web.modules.goodsservice.mapper.BizGoodsServiceMapper;
import com.ruoyi.web.modules.goodsservice.service.impl.GoodsServiceCatalogServiceImpl;

/**
 * 商品服务 Catalog Service 单元测试（GSV-UT）
 */
@ExtendWith(MockitoExtension.class)
class GoodsServiceCatalogServiceImplTest
{
    @Mock
    private BizGoodsServiceMapper goodsServiceMapper;

    @Mock
    private IGoodsSnapshotFacade goodsSnapshotFacade;

    @InjectMocks
    private GoodsServiceCatalogServiceImpl catalogService;

    @Test
    void insert_duplicateName_rejected()
    {
        BizGoodsService row = baseRow();
        when(goodsServiceMapper.countByName(eq("正品保证"), eq(null))).thenReturn(1);

        ServiceException ex = assertThrows(ServiceException.class, () -> catalogService.insertGoodsService(row));
        assertEquals("服务名称已存在", ex.getMessage());
    }

    @Test
    void insert_defaultShowFlag_defaultsToYes()
    {
        BizGoodsService row = baseRow();
        row.setDefaultShowFlag(null);
        when(goodsServiceMapper.countByName(any(), eq(null))).thenReturn(0);
        when(goodsServiceMapper.insert(any())).thenReturn(1);

        catalogService.insertGoodsService(row);

        assertEquals(GoodsServiceConstants.DEFAULT_SHOW_YES, row.getDefaultShowFlag());
    }

    @Test
    void insert_emptyName_rejected()
    {
        BizGoodsService row = baseRow();
        row.setServiceName("");

        assertThrows(ServiceException.class, () -> catalogService.insertGoodsService(row));
    }

    @Test
    void insert_onlyName_allowed()
    {
        BizGoodsService row = new BizGoodsService();
        row.setServiceName("仅名称");
        when(goodsServiceMapper.countByName(any(), eq(null))).thenReturn(0);
        when(goodsServiceMapper.insert(any())).thenReturn(1);

        catalogService.insertGoodsService(row);

        assertEquals("", row.getServiceIntro());
        assertEquals("", row.getServiceIcon());
        assertEquals(GoodsServiceConstants.DEFAULT_SHOW_YES, row.getDefaultShowFlag());
        assertEquals(0, row.getSortNo());
    }

    @Test
    void insert_introTooLong_rejected()
    {
        BizGoodsService row = baseRow();
        row.setServiceIntro(String.join("", Collections.nCopies(501, "a")));

        ServiceException ex = assertThrows(ServiceException.class, () -> catalogService.insertGoodsService(row));
        assertEquals("简介长度不能超过500个字符", ex.getMessage());
    }

    @Test
    void insert_iconTooLong_rejected()
    {
        BizGoodsService row = baseRow();
        row.setServiceIcon(String.join("", Collections.nCopies(513, "a")));

        ServiceException ex = assertThrows(ServiceException.class, () -> catalogService.insertGoodsService(row));
        assertEquals("图标地址长度不能超过512个字符", ex.getMessage());
    }

    @Test
    void insert_nameTooLong_rejected()
    {
        BizGoodsService row = baseRow();
        row.setServiceName(String.join("", Collections.nCopies(65, "a")));

        ServiceException ex = assertThrows(ServiceException.class, () -> catalogService.insertGoodsService(row));
        assertEquals("服务名称长度不能超过64个字符", ex.getMessage());
    }

    @Test
    void insert_negativeSort_rejected()
    {
        BizGoodsService row = baseRow();
        row.setSortNo(-1);

        assertThrows(ServiceException.class, () -> catalogService.insertGoodsService(row));
    }

    @Test
    void update_nameConflict_rejected()
    {
        BizGoodsService existing = baseRow();
        existing.setServiceId(1L);
        BizGoodsService row = baseRow();
        row.setServiceId(1L);
        row.setServiceName("极速退款");
        when(goodsServiceMapper.selectById(1L)).thenReturn(existing);
        when(goodsServiceMapper.countByName(eq("极速退款"), eq(1L))).thenReturn(1);

        assertThrows(ServiceException.class, () -> catalogService.updateGoodsService(row));
    }

    @Test
    void delete_withGoodsRef_rejected()
    {
        BizGoodsService row = baseRow();
        row.setServiceId(1L);
        when(goodsServiceMapper.selectById(1L)).thenReturn(row);
        when(goodsSnapshotFacade.existsGoodsRef(1L)).thenReturn(true);

        GoodsServiceBatchDeleteException ex = assertThrows(GoodsServiceBatchDeleteException.class,
                () -> catalogService.deleteGoodsServiceByIds(new Long[] { 1L }, "admin"));
        assertEquals(1, ex.getReasons().size());
        verify(goodsServiceMapper, never()).logicDeleteByIds(any(), any());
    }

    @Test
    void delete_success()
    {
        BizGoodsService row = baseRow();
        row.setServiceId(1L);
        when(goodsServiceMapper.selectById(1L)).thenReturn(row);
        when(goodsSnapshotFacade.existsGoodsRef(1L)).thenReturn(false);

        catalogService.deleteGoodsServiceByIds(new Long[] { 1L }, "admin");

        verify(goodsServiceMapper).logicDeleteByIds(eq(Collections.singletonList(1L)), eq("admin"));
    }

    @Test
    void delete_batchPartialFail_wholeBatchCancelled()
    {
        BizGoodsService row1 = baseRow();
        row1.setServiceId(1L);
        row1.setServiceName("A");
        BizGoodsService row2 = baseRow();
        row2.setServiceId(2L);
        row2.setServiceName("B");
        when(goodsServiceMapper.selectById(1L)).thenReturn(row1);
        when(goodsServiceMapper.selectById(2L)).thenReturn(row2);
        when(goodsSnapshotFacade.existsGoodsRef(1L)).thenReturn(false);
        when(goodsSnapshotFacade.existsGoodsRef(2L)).thenReturn(true);

        assertThrows(GoodsServiceBatchDeleteException.class,
                () -> catalogService.deleteGoodsServiceByIds(new Long[] { 1L, 2L }, "admin"));
        verify(goodsServiceMapper, never()).logicDeleteByIds(any(), any());
    }

    @Test
    void delete_batchAllPass()
    {
        BizGoodsService row1 = baseRow();
        row1.setServiceId(1L);
        BizGoodsService row2 = baseRow();
        row2.setServiceId(2L);
        row2.setServiceName("极速退款");
        when(goodsServiceMapper.selectById(1L)).thenReturn(row1);
        when(goodsServiceMapper.selectById(2L)).thenReturn(row2);
        when(goodsSnapshotFacade.existsGoodsRef(any())).thenReturn(false);

        catalogService.deleteGoodsServiceByIds(new Long[] { 1L, 2L }, "admin");

        verify(goodsServiceMapper).logicDeleteByIds(eq(Arrays.asList(1L, 2L)), eq("admin"));
    }

    @Test
    void validateFields_invalidDefaultShowFlag()
    {
        BizGoodsService row = baseRow();
        row.setDefaultShowFlag("2");

        assertThrows(ServiceException.class, () -> catalogService.insertGoodsService(row));
    }

    private BizGoodsService baseRow()
    {
        BizGoodsService row = new BizGoodsService();
        row.setServiceName("正品保证");
        row.setServiceIntro("平台认证正品");
        row.setServiceIcon("/profile/upload/s1.png");
        row.setDefaultShowFlag("1");
        row.setSortNo(0);
        return row;
    }
}
