package com.cf.imes.module.executor.service.veneer;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.cf.imes.framework.common.exception.ServiceException;
import com.cf.imes.framework.common.pojo.CommonResult;
import com.cf.imes.framework.common.pojo.PageResult;
import com.cf.imes.framework.common.util.json.JsonUtils;
import com.cf.imes.framework.security.test.WithMockLoginUser;
import com.cf.imes.framework.test.core.ut.BaseMockitoUnitTest;
import com.cf.imes.module.executor.common.WithMockLoginUserExtension;
import com.cf.imes.module.executor.constants.file.TemplateFilePathConstants;
import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerClassificationNameRespVO;
import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerClassificationNameSaveReqVO;
import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerDefectSaveReqVO;
import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerPageReqVO;
import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerRespVO;
import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerSaveReqVO;
import com.cf.imes.module.executor.dal.dataobject.veneer.VeneerClassificationNameDO;
import com.cf.imes.module.executor.dal.dataobject.veneer.VeneerDO;
import com.cf.imes.module.executor.dal.mysql.veneer.VeneerBatchMapper;
import com.cf.imes.module.executor.dal.mysql.veneer.VeneerClassificationNameMapper;
import com.cf.imes.module.executor.dal.mysql.veneer.VeneerMapper;
import com.cf.imes.module.executor.enums.veneer.VeneerProductLevelEnum;
import com.cf.imes.module.executor.util.file.FileHelperUtil;
import com.cf.imes.module.infra.api.file.FileApi;
import com.cf.imes.module.system.api.organ.OrganApi;
import com.cf.imes.module.system.api.organ.dto.OrganizationDTO;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.ibatis.builder.MapperBuilderAssistant;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.springframework.mock.web.MockMultipartFile;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import static com.cf.imes.framework.test.core.util.AssertUtils.assertServiceException;
import static com.cf.imes.framework.test.core.util.RandomUtils.randomPojo;
import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_NOT_EXIST;
import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_NO_EXIST;
import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_ORG_CLASSIFICATION_NAME_EXIST;
import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_ORG_CLASSIFICATION_NAME_NOT_EXIST;
import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_ORG_CLASSIFICATION_NAME_USED;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

/**
 *
 *
 * @author Gqr
 * @since 2026/1/7 15:08
 */
@ExtendWith(WithMockLoginUserExtension.class)
class VeneerServiceImplTest extends BaseMockitoUnitTest {
    @InjectMocks
    private VeneerServiceImpl veneerService;

    @Mock
    private FileApi fileApi;

    @Mock
    private OrganApi organApi;

    @Mock
    private VeneerMapper veneerMapper;

    @Mock
    private VeneerBatchMapper veneerBatchMapper;

    @Mock
    private VeneerClassificationNameMapper veneerClassificationNameMapper;

    @Mock
    private HttpServletResponse response;

    @BeforeAll
    static void init(){
        TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new MybatisConfiguration(), ""), VeneerDO.class);
    }

    @Test
    void create_success_when_veneer_no_not_exists() {
        // 模拟 Mapper 插入时生成 ID
        doAnswer(invocation -> {
            VeneerDO veneerDO = invocation.getArgument(0);
            veneerDO.setId(100L); // 模拟自增主键
            return null;
        }).when(veneerMapper).insert(any(VeneerDO.class));

        VeneerSaveReqVO reqVO = new VeneerSaveReqVO();
        reqVO.setVeneerNo("VN-001");
        reqVO.setClassificationName("测试分类");
        reqVO.setVeneerName("测试贴皮");
        reqVO.setProductLevel(VeneerProductLevelEnum.A.getCode());

        Long id = veneerService.create(reqVO);

        // 断言
        assertEquals(100L, id);
        verify(veneerMapper).insert(argThat((VeneerDO veneerDO) ->
                ObjectUtil.equals(veneerDO.getVeneerNo(), reqVO.getVeneerNo())
                        && ObjectUtil.equals(veneerDO.getBatchNo(), reqVO.getBatchNo())
                        && ObjectUtil.equals(veneerDO.getClassificationName(), reqVO.getClassificationName())
                        && ObjectUtil.equals(veneerDO.getProductLevel(), reqVO.getProductLevel())
                        && ObjectUtil.equals(veneerDO.getVeneerName(), reqVO.getVeneerName())
                        && ObjectUtil.equals(veneerDO.getNum(), reqVO.getNum())
                        && ObjectUtil.equals(veneerDO.getWidth(), reqVO.getWidth())
                        && ObjectUtil.equals(veneerDO.getLength(), reqVO.getLength())
                        && ObjectUtil.equals(veneerDO.getThickness(), reqVO.getThickness())
                        && ObjectUtil.equals(veneerDO.getColor(), reqVO.getColor())
                        && ObjectUtil.equals(veneerDO.getMaterial(), reqVO.getMaterial())
                        && ObjectUtil.equals(veneerDO.getSpec(), reqVO.getSpec())
                        && ObjectUtil.equals(veneerDO.getBrand(), reqVO.getBrand())
                        && ObjectUtil.equals(veneerDO.getFactory(), reqVO.getFactory())
                        && ObjectUtil.equals(veneerDO.getRemark(), reqVO.getRemark())
                        && ObjectUtil.equals(veneerDO.getFileId(), reqVO.getFileId())
        ));
    }

    @Test
    void create_fail_when_veneer_no_exists() {
        // 先插一条
        VeneerDO exist = new VeneerDO();
        exist.setVeneerNo("VN-002");
        exist.setClassificationName("分类");
        exist.setVeneerName("贴皮");
        exist.setProductLevel(VeneerProductLevelEnum.A.getCode());
        when(veneerMapper.selectCount(any())).thenReturn(1L);

        VeneerSaveReqVO reqVO = new VeneerSaveReqVO();
        reqVO.setVeneerNo("VN-002");

        assertServiceException(() -> veneerService.create(reqVO),
                VENEER_NO_EXIST);
    }

    @Test
    void update_success_when_same_veneer_no() {
        VeneerDO exist = new VeneerDO();
        exist.setId(100L);
        exist.setVeneerNo("VN-100");
        exist.setClassificationName("分类");
        exist.setVeneerName("贴皮");
        exist.setProductLevel(VeneerProductLevelEnum.A.getCode());
        when(veneerMapper.selectById(anyLong())).thenReturn(exist);
        when(veneerMapper.selectCount(any())).thenReturn(0L);

        VeneerSaveReqVO reqVO = new VeneerSaveReqVO();
        reqVO.setId(exist.getId());
        reqVO.setVeneerNo("VN-100"); // 不变
        reqVO.setClassificationName("新分类");
        reqVO.setVeneerName("新贴皮");
        reqVO.setProductLevel(VeneerProductLevelEnum.B.getCode());

        assertDoesNotThrow(() -> veneerService.update(reqVO));

        // 校验是否更新
        verify(veneerMapper, times(1)).updateById(any(VeneerDO.class));
    }

    @Test
    void update_fail_when_veneer_no_exists() {
        VeneerDO v1 = new VeneerDO();
        v1.setId(100L);
        v1.setVeneerNo("VN-101");
        v1.setClassificationName("分类1");
        v1.setVeneerName("贴皮1");
        v1.setProductLevel(VeneerProductLevelEnum.A.getCode());

        when(veneerMapper.selectById(anyLong())).thenReturn(v1);
        when(veneerMapper.selectCount(any())).thenReturn(1L);

        VeneerSaveReqVO reqVO = new VeneerSaveReqVO();
        reqVO.setId(v1.getId());
        reqVO.setVeneerNo("VN-101"); // 冲突

        assertServiceException(() -> veneerService.update(reqVO),
                VENEER_NO_EXIST);
    }

    @Test
    void update_fail_when_not_exists() {
        VeneerSaveReqVO reqVO = new VeneerSaveReqVO();
        reqVO.setId(999L);
        reqVO.setVeneerNo("VN-999");

        assertServiceException(() -> veneerService.update(reqVO),
                VENEER_NOT_EXIST);
    }

    @Test
    void update_fail_when_id_null(){
        VeneerSaveReqVO reqVO = new VeneerSaveReqVO();
        reqVO.setId(null);
        assertServiceException(() -> veneerService.update(reqVO),
                VENEER_NOT_EXIST);
    }

    @Test
    void delete_success() {
        VeneerDO exist = new VeneerDO();
        exist.setId(100L);
        exist.setVeneerNo("VN-200");
        exist.setClassificationName("分类");
        exist.setVeneerName("贴皮");
        exist.setProductLevel(VeneerProductLevelEnum.A.getCode());

        Long id = exist.getId();
        when(veneerMapper.selectById(id)).thenReturn(exist);

        assertDoesNotThrow(() -> veneerService.delete(id));

        verify(veneerMapper, times(1)).deleteById(id);
    }

    @Test
    void delete_fail_when_not_exists() {
        assertServiceException(() -> veneerService.delete(999L),
                VENEER_NOT_EXIST);
    }

    @Test
    @WithMockLoginUser(organId = 1L)
    void uploadPic_success() throws Exception {
        VeneerDO veneerDO = new VeneerDO();
        veneerDO.setId(1L);
        when(veneerMapper.selectById(1L)).thenReturn(veneerDO);

        // mock organApi
        OrganizationDTO dto = new OrganizationDTO();
        dto.setName("测试组织");

        when(organApi.getOrganDetails(1L))
                .thenReturn(CommonResult.success(dto));

        // mock fileApi
        when(fileApi.createFileAndReturnId(any(byte[].class), anyString()))
                .thenReturn(100L);

        // mock 文件
        MockMultipartFile file = new MockMultipartFile(
                "file",
                "test.png",
                "image/png",
                "test-content".getBytes()
        );

        Long fileId = veneerService.uploadPic(1L, file);

        assertEquals(100L, fileId);

        // 验证 fileApi 调用
        verify(fileApi).createFileAndReturnId(
                any(byte[].class),
                argThat(name -> name.contains("测试组织-veneer-pic-file-"))
        );
        verify(veneerMapper).update(argThat((LambdaUpdateWrapper<VeneerDO> wrapper) ->
                wrapper.getSqlSegment().contains("id") && wrapper.getSqlSet().contains("file_id")));
    }

    @Test
    @WithMockLoginUser(organId = 1L)
    void uploadPic_fail_when_organ_api_error() {
        VeneerDO veneerDO = new VeneerDO();
        veneerDO.setId(1L);
        when(veneerMapper.selectById(1L)).thenReturn(veneerDO);

        when(organApi.getOrganDetails(1L))
                .thenReturn(CommonResult.error(500, "组织服务异常"));

        MockMultipartFile file = new MockMultipartFile(
                "file",
                "test.jpg",
                "image/jpeg",
                "content".getBytes()
        );

        ServiceException ex = assertThrows(
                ServiceException.class,
                () -> veneerService.uploadPic(1L, file)
        );

        assertEquals(500, ex.getCode());
        assertEquals("组织服务异常", ex.getMessage());

        verify(fileApi, never()).createFileAndReturnId(any(), any());
        verify(veneerMapper, never()).update(any());
    }

    @Test
    void uploadPic_fail_when_veneer_not_exists() {
        MockMultipartFile file = new MockMultipartFile(
                "file",
                "test.jpg",
                "image/jpeg",
                "content".getBytes()
        );

        assertServiceException(() -> veneerService.uploadPic(999L, file),
                VENEER_NOT_EXIST);

        verify(organApi, never()).getOrganDetails(anyLong());
        verify(fileApi, never()).createFileAndReturnId(any(), any());
        verify(veneerMapper, never()).update(any());
    }

    @Test
    void getPage_success_when_has_data() {
        VeneerDO exist = new VeneerDO();
        exist.setVeneerNo("VN-200");
        exist.setClassificationName("分类");
        exist.setVeneerName("贴皮名称");
        exist.setProductLevel(VeneerProductLevelEnum.A.getCode());
        exist.setRemark("备注");
        exist.setWidth(new BigDecimal(10));
        exist.setThickness(new BigDecimal(9));

        VeneerPageReqVO pageReqVO = new VeneerPageReqVO();
        pageReqVO.setVeneerNo("VN-200");
        pageReqVO.setClassificationName("分类");
        pageReqVO.setVeneerName("贴皮名称");
        pageReqVO.setProductLevel(VeneerProductLevelEnum.A.getCode());
        pageReqVO.setWidth(new BigDecimal[]{new BigDecimal(0), new BigDecimal(20)});
        pageReqVO.setThickness(new BigDecimal[]{new BigDecimal(5)});
        pageReqVO.setRemark("注");

        when(veneerMapper.selectPage(pageReqVO)).thenReturn(new PageResult<>(List.of(exist), 1L));

        PageResult<VeneerRespVO> result =
                veneerService.getPage(pageReqVO);

        assertEquals(1L, result.getTotal());
        assertEquals(1, result.getList().size());
        assertEquals("VN-200", result.getList().get(0).getVeneerNo());
    }

    @Test
    void getPage_empty(){
        VeneerPageReqVO reqVO = new VeneerPageReqVO();

        when(veneerMapper.selectPage(reqVO)).thenReturn(new PageResult<>(null, 0L));
        PageResult<VeneerRespVO> result = veneerService.getPage(reqVO);

        assertNotNull(result);
        assertEquals(0L, result.getList().size());
        assertEquals(0L, result.getTotal());
    }

    @Test
    void get_success_with_defect_data() {
        String json = "{\"a\":1}";
        String zipped = JsonUtils.zipString(json);

        VeneerDO veneer = new VeneerDO();
        veneer.setId(100L);
        veneer.setVeneerNo("VN-301");
        veneer.setClassificationName("分类");
        veneer.setVeneerName("贴皮");
        veneer.setProductLevel(1);
        veneer.setDefectData(zipped);
        veneer.setOrganId(1L);

        when(veneerMapper.selectById(veneer.getId())).thenReturn(veneer);

        VeneerRespVO respVO = veneerService.get(veneer.getId());

        assertNotNull(respVO);
        assertEquals("VN-301", respVO.getVeneerNo());
        assertEquals(json, respVO.getDefectData()); // 已解压
    }

    @Test
    void get_success_without_defect_data() {
        VeneerDO veneer = new VeneerDO();
        veneer.setVeneerNo("VN-302");
        veneer.setClassificationName("分类");
        veneer.setVeneerName("贴皮");
        veneer.setProductLevel(1);
        veneer.setOrganId(1L);

        when(veneerMapper.selectById(veneer.getId())).thenReturn(veneer);

        VeneerRespVO respVO = veneerService.get(veneer.getId());

        assertNotNull(respVO);
        assertNull(respVO.getDefectData());
    }

    @Test
    void get_fail_when_not_exist() {
        assertServiceException(() -> veneerService.get(999L),
                VENEER_NOT_EXIST);
    }

    @Test
    void getOrgVeneerNo_empty() {
        when(veneerMapper.selectList(any())).thenReturn(new ArrayList<>());

        Map<String, Long> map = veneerService.getOrgVeneerNo();
        assertNotNull(map);
        assertTrue(map.isEmpty());
    }

    @Test
    void getOrgVeneerNo_success() {
        VeneerDO v1 = new VeneerDO();
        v1.setId(100L);
        v1.setVeneerNo("VN-401");
        v1.setClassificationName("分类");
        v1.setVeneerName("贴皮");
        v1.setProductLevel(VeneerProductLevelEnum.A.getCode());
        v1.setOrganId(1L);

        VeneerDO v2 = new VeneerDO();
        v2.setId(200L);
        v2.setVeneerNo("VN-402");
        v2.setClassificationName("分类");
        v2.setVeneerName("贴皮");
        v2.setProductLevel(VeneerProductLevelEnum.C.getCode());
        v2.setOrganId(1L);

        when(veneerMapper.selectList(any())).thenReturn(List.of(v1, v2));

        Map<String, Long> map = veneerService.getOrgVeneerNo();

        assertEquals(2, map.size());
        assertEquals(v1.getId(), map.get("VN-401"));
        assertEquals(v2.getId(), map.get("VN-402"));
    }

    @Test
    void batch_success() {
        VeneerDO v1 = randomPojo(VeneerDO.class, o -> {
            o.setProductLevel(VeneerProductLevelEnum.A.getCode());
        });
        VeneerDO v2 = randomPojo(VeneerDO.class, o -> {
            o.setProductLevel(VeneerProductLevelEnum.A.getCode());
        });
        List<VeneerDO> list = List.of(
                v1, v2
        );

        veneerService.batchInsert(list);

        // 验证操作
        verify(veneerBatchMapper, times(1)).insertBatchSomeColumn(list);

        veneerService.batchUpdate(list);

        verify(veneerBatchMapper, times(1)).updateBatch(list);
    }

    @Test
    void saveDefect_success_whenDefectDataNotEmpty() {
        String defectJson = "{\"x\":10,\"y\":20}";
        // 先插入贴皮
        VeneerDO veneer = randomPojo(VeneerDO.class, o -> {
            o.setProductLevel(VeneerProductLevelEnum.A.getCode());
            o.setDefectData(defectJson);
        });
        when(veneerMapper.selectById(veneer.getId())).thenReturn(veneer);

        VeneerDefectSaveReqVO req = new VeneerDefectSaveReqVO();
        req.setId(veneer.getId());
        req.setDefectData(defectJson);

        assertDoesNotThrow(() -> veneerService.saveDefect(req));

        // 验证操作
        verify(veneerMapper).update(argThat((LambdaUpdateWrapper wrapper) ->
                wrapper.getSqlSegment().contains("id")
                        && wrapper.getSqlSet().contains("defect")
                        && wrapper.getSqlSet().contains("defect_data")
        ));
    }

    @Test
    void saveDefect_throwException_whenVeneerNotExist() {
        VeneerDefectSaveReqVO req = new VeneerDefectSaveReqVO();
        req.setId(99999L);

        assertServiceException(() -> veneerService.saveDefect(req),
                VENEER_NOT_EXIST);
    }

    @Test
    void saveDefect_doNothing_whenDefectDataEmpty() {
        // 先插入贴皮
        VeneerDO veneer = randomPojo(VeneerDO.class, o -> {
            o.setProductLevel(VeneerProductLevelEnum.A.getCode());
            o.setDefectData(null);
            o.setDefect(false);
        });
        when(veneerMapper.selectById(veneer.getId())).thenReturn(veneer);

        VeneerDefectSaveReqVO req = new VeneerDefectSaveReqVO();
        req.setId(veneer.getId());
        req.setDefectData("");

        assertDoesNotThrow(() -> veneerService.saveDefect(req));

        verify(veneerMapper, never()).update(any());
    }

    @Test
    void addClassificationName_success() {

        // given
        VeneerClassificationNameSaveReqVO reqVO =
                new VeneerClassificationNameSaveReqVO();
        reqVO.setName("测试分类");

        // 唯一校验通过
        Mockito.when(veneerClassificationNameMapper.selectCount(any()))
                .thenReturn(0L);

        // mock insert 时给 id
        Mockito.doAnswer(invocation -> {
            VeneerClassificationNameDO entity = invocation.getArgument(0);
            entity.setId(1L);
            return 1;
        }).when(veneerClassificationNameMapper).insert(any(VeneerClassificationNameDO.class));

        // when
        Long id = veneerService.addClassificationName(reqVO);

        // then
        assertEquals(1L, id);

        Mockito.verify(veneerClassificationNameMapper)
                .selectCount(any());
        Mockito.verify(veneerClassificationNameMapper)
                .insert(any(VeneerClassificationNameDO.class));
    }

    @Test
    void addClassificationName_exist_throwException() {

        VeneerClassificationNameSaveReqVO reqVO =
                new VeneerClassificationNameSaveReqVO();
        reqVO.setName("重复分类");

        // 已存在
        Mockito.when(veneerClassificationNameMapper.selectCount(any()))
                .thenReturn(1L);

        assertServiceException(() -> veneerService.addClassificationName(reqVO),
                VENEER_ORG_CLASSIFICATION_NAME_EXIST);

        // insert 一定不会被调用
        Mockito.verify(veneerClassificationNameMapper, Mockito.never())
                .insert(any(VeneerClassificationNameDO.class));
    }

    @Test
    void deleteClassificationName_success() {
        VeneerClassificationNameDO entity = new VeneerClassificationNameDO();
        entity.setId(1L);
        entity.setName("分类A");

        Mockito.when(veneerClassificationNameMapper.selectById(1L))
                .thenReturn(entity);
        Mockito.when(veneerMapper.selectCount(any()))
                .thenReturn(0L);

        assertDoesNotThrow(() -> veneerService.deleteClassificationName(1L));

        Mockito.verify(veneerMapper)
                .selectCount(any());
        Mockito.verify(veneerClassificationNameMapper)
                .deleteById(1L);
    }

    @Test
    void deleteClassificationName_notExist_throwException() {
        Mockito.when(veneerClassificationNameMapper.selectById(1L))
                .thenReturn(null);

        assertServiceException(() -> veneerService.deleteClassificationName(1L),
                VENEER_ORG_CLASSIFICATION_NAME_NOT_EXIST);

        Mockito.verify(veneerMapper, Mockito.never())
                .selectCount(any());
        Mockito.verify(veneerClassificationNameMapper, Mockito.never())
                .deleteById(anyLong());
    }

    @Test
    void deleteClassificationName_used_throwException() {
        VeneerClassificationNameDO entity = new VeneerClassificationNameDO();
        entity.setId(1L);
        entity.setName("分类A");

        Mockito.when(veneerClassificationNameMapper.selectById(1L))
                .thenReturn(entity);
        Mockito.when(veneerMapper.selectCount(any()))
                .thenReturn(1L);

        assertServiceException(() -> veneerService.deleteClassificationName(1L),
                VENEER_ORG_CLASSIFICATION_NAME_USED);

        Mockito.verify(veneerClassificationNameMapper, Mockito.never())
                .deleteById(anyLong());
    }

    @Test
    void getOrgVeneerClassificationNameList_success() {

        VeneerClassificationNameDO entity = new VeneerClassificationNameDO();
        entity.setId(1L);
        entity.setName("分类A");

        Mockito.when(veneerClassificationNameMapper.selectList())
                .thenReturn(List.of(entity));

        List<VeneerClassificationNameRespVO> result =
                veneerService.getOrgVeneerClassificationNameList();

        assertEquals(1, result.size());
        assertEquals("分类A", result.get(0).getName());

        Mockito.verify(veneerClassificationNameMapper)
                .selectList();
    }

    @Test
    void importTemplateDownload_success() throws IOException {
        File mockFile = Mockito.mock(File.class);

        try (
                MockedStatic<TemplateFilePathConstants> pathMock =
                        Mockito.mockStatic(TemplateFilePathConstants.class);
                MockedStatic<FileUtil> fileUtilMock =
                        Mockito.mockStatic(FileUtil.class);
                 MockedStatic<FileHelperUtil> fileMock =
                        Mockito.mockStatic(FileHelperUtil.class);
                MockedStatic<EasyExcelFactory> excelMock =
                        Mockito.mockStatic(EasyExcelFactory.class)
        ) {

            VeneerClassificationNameDO veneerClassificationNameDO = new VeneerClassificationNameDO();
            veneerClassificationNameDO.setName("分类名称");
            when(veneerClassificationNameMapper.selectList()).thenReturn(List.of(veneerClassificationNameDO));

            pathMock.when(() ->
                    TemplateFilePathConstants.getVeneerImportTemplatePath(any())
            ).thenReturn("/mock/path");

            fileMock.when(() ->
                    FileHelperUtil.getFirstFile(anyString())
            ).thenReturn(mockFile);

            // ⭐ 关键 1：mock 模板流（不碰文件）
            fileUtilMock.when(() -> FileUtil.getInputStream(any(File.class)))
                    .thenReturn(new BufferedInputStream(new ByteArrayInputStream(new byte[]{1})));

            // mock response OutputStream
            ServletOutputStream outputStream = Mockito.mock(ServletOutputStream.class);
            Mockito.when(response.getOutputStream())
                    .thenReturn(outputStream);

            // mock EasyExcel 写链
            ExcelWriterBuilder writerBuilder = Mockito.mock(ExcelWriterBuilder.class);
            ExcelWriterSheetBuilder sheetBuilder = Mockito.mock(ExcelWriterSheetBuilder.class);

            excelMock.when(() -> EasyExcelFactory.write(any(OutputStream.class)))
                    .thenReturn(writerBuilder);

            when(writerBuilder.withTemplate(
                    any(InputStream.class)))
                    .thenReturn(writerBuilder);

            when(writerBuilder.registerWriteHandler(any()))
                    .thenReturn(writerBuilder);

            when(writerBuilder.sheet(anyInt()))
                    .thenReturn(sheetBuilder);
            doNothing().when(sheetBuilder).doWrite(anyList());

            // 执行
            assertDoesNotThrow(() ->
                    veneerService.importTemplateDownload(response));
        }
    }


    @Test
    void importTemplateDownload_fail_when_org_classification_name_list_is_empty() {
        // 1️⃣ mock 模板路径 & 文件
        File mockFile = Mockito.mock(File.class);

        try (
                MockedStatic<TemplateFilePathConstants> pathMock =
                        Mockito.mockStatic(TemplateFilePathConstants.class);
                MockedStatic<FileHelperUtil> fileMock =
                        Mockito.mockStatic(FileHelperUtil.class)
        ) {

            pathMock.when(() ->
                    TemplateFilePathConstants.getVeneerImportTemplatePath(any())
            ).thenReturn("/mock/path");

            fileMock.when(() ->
                    FileHelperUtil.getFirstFile(anyString())
            ).thenReturn(mockFile);

            // 2️⃣ mock 分类名称列表为空
            Mockito.when(veneerClassificationNameMapper.selectList())
                    .thenReturn(new ArrayList<>());

            // 3️⃣ 验证抛出异常
            assertServiceException(() -> veneerService.importTemplateDownload(response),
                    com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_ORG_CLASSIFICATION_NAME_EMPTY);
        }
    }

    @Test
    void importTemplateDownload_fail_when_io_exception_occurs() throws Exception {
        // 1️⃣ mock 模板路径 & 文件
        File mockFile = Mockito.mock(File.class);

        try (
                MockedStatic<TemplateFilePathConstants> pathMock =
                        Mockito.mockStatic(TemplateFilePathConstants.class);
                MockedStatic<FileHelperUtil> fileMock =
                        Mockito.mockStatic(FileHelperUtil.class);
                MockedStatic<EasyExcelFactory> excelMock =
                        Mockito.mockStatic(EasyExcelFactory.class)
        ) {

            pathMock.when(() ->
                    TemplateFilePathConstants.getVeneerImportTemplatePath(any())
            ).thenReturn("/mock/path");

            fileMock.when(() ->
                    FileHelperUtil.getFirstFile(anyString())
            ).thenReturn(mockFile);

            // 2️⃣ mock 分类名称列表
            VeneerClassificationNameDO entity = new VeneerClassificationNameDO();
            entity.setName("分类A");

            Mockito.when(veneerClassificationNameMapper.selectList())
                    .thenReturn(List.of(entity));

            // 3️⃣ mock response OutputStream 并抛出IOException
            Mockito.when(response.getOutputStream())
                    .thenThrow(new java.io.IOException("IO Error"));

            // 4️⃣ 验证抛出异常
            assertServiceException(() -> veneerService.importTemplateDownload(response),
                    com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_IMPORT_TEMPLATE_DOWNLOAD_ERROR);
        }
    }



    @Test
    void getFileExtension_whenEmpty() {
        assertEquals(".jpg", veneerService.getFileExtension("file."));
    }
}
