package com.cf.imes.module.executor.controller.admin.veneer;

import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.read.builder.ExcelReaderBuilder;
import com.alibaba.excel.read.builder.ExcelReaderSheetBuilder;
import com.cf.imes.framework.common.pojo.PageResult;
import com.cf.imes.framework.test.core.ut.BaseWebUnitTest;
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.VeneerImportExcelVO;
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.enums.veneer.VeneerProductLevelEnum;
import com.cf.imes.module.executor.service.veneer.VeneerExcelListener;
import com.cf.imes.module.executor.service.veneer.VeneerService;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletResponse;
import org.junit.jupiter.api.Test;
import org.mockito.MockedConstruction;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.web.servlet.MockMvc;

import java.io.InputStream;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.List;

import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

/**
 * 贴皮管理controller 单元测试
 *
 * @author Gqr
 * @since 2026/1/7 16:58
 */
@WebMvcTest(controllers = VeneerController.class, excludeAutoConfiguration = SecurityAutoConfiguration.class)
class VeneerControllerTest extends BaseWebUnitTest {
    @Autowired
    private MockMvc mockMvc;

    @MockitoBean
    private VeneerService veneerService;

    @Autowired
    private ObjectMapper objectMapper;

    @Test
    void getVeneerPage_success() throws Exception {
        PageResult<VeneerRespVO> page = new PageResult<>();
        page.setTotal(0L);
        page.setList(Collections.emptyList());

        when(veneerService.getPage(any())).thenReturn(page);

        mockMvc.perform(get("/executor/veneer/page"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(0));
    }

    @Test
    void exportVeneerPage_success() throws Exception {
        // mock service 返回
        PageResult<VeneerRespVO> page = new PageResult<>();
        page.setList(Collections.emptyList());
        page.setTotal(0L);

        when(veneerService.getPage(any())).thenReturn(page);

        mockMvc.perform(get("/executor/veneer/export"))
                .andExpect(status().isOk());
    }

    @Test
    void getVeneer_success() throws Exception {
        VeneerRespVO resp = new VeneerRespVO();
        resp.setId(1L);

        when(veneerService.get(1L)).thenReturn(resp);

        mockMvc.perform(get("/executor/veneer/1"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.data.id").value(1L));
    }

    @Test
    void createVeneer_success() throws Exception {
        // 准备参数
        VeneerSaveReqVO veneerSaveReqVO = new VeneerSaveReqVO();
        veneerSaveReqVO.setVeneerNo("SP14126");
        veneerSaveReqVO.setClassificationName("分类名称");
        veneerSaveReqVO.setProductLevel(VeneerProductLevelEnum.A.getCode());
        veneerSaveReqVO.setVeneerName("贴皮名称");
        veneerSaveReqVO.setNum(1);
        veneerSaveReqVO.setWidth(new BigDecimal(10));
        veneerSaveReqVO.setLength(new BigDecimal(20));
        veneerSaveReqVO.setThickness(new BigDecimal(30));

        when(veneerService.create(any())).thenReturn(1L);

        mockMvc.perform(post("/executor/veneer")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(veneerSaveReqVO)))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.data").value(1L));
    }

    @Test
    void updateVeneer_success() throws Exception {
        // 准备参数
        VeneerSaveReqVO veneerSaveReqVO = new VeneerSaveReqVO();
        veneerSaveReqVO.setId(999l);
        veneerSaveReqVO.setVeneerNo("SP14126");
        veneerSaveReqVO.setClassificationName("分类名称");
        veneerSaveReqVO.setProductLevel(VeneerProductLevelEnum.A.getCode());
        veneerSaveReqVO.setVeneerName("贴皮名称");
        veneerSaveReqVO.setNum(1);
        veneerSaveReqVO.setWidth(new BigDecimal(10));
        veneerSaveReqVO.setLength(new BigDecimal(20));
        veneerSaveReqVO.setThickness(new BigDecimal(30));

        doNothing().when(veneerService).update(any());

        mockMvc.perform(put("/executor/veneer")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(veneerSaveReqVO)))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.data").value(true));
    }

    @Test
    void deleteVeneer_success() throws Exception {
        doNothing().when(veneerService).delete(1L);

        mockMvc.perform(delete("/executor/veneer/1"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.data").value(true));
    }

    @Test
    void uploadVeneerPic_success() throws Exception {
        MockMultipartFile file = new MockMultipartFile(
                "file", "a.jpg", "image/jpeg", "123".getBytes()
        );

        when(veneerService.uploadPic(eq(1L), any())).thenReturn(10L);

        mockMvc.perform(multipart("/executor/veneer/pic")
                        .file(file)
                        .param("id", "1"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.data").value(10L));
    }

    @Test
    void uploadVeneerPic_exceedMaxSize() throws Exception {
        MockMultipartFile file = new MockMultipartFile(
                "file", "large.jpg", "image/jpeg", new byte[5 * 1024 * 1024 + 1]
        );

        assertThrows(Exception.class, () -> mockMvc.perform(multipart("/executor/veneer/pic").file(file)));
        verifyNoInteractions(veneerService);
    }

    @Test
    void importExcel_success() throws Exception {
        MockMultipartFile file = new MockMultipartFile(
                "file",
                "veneer.xlsx",
                "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                "dummy excel content".getBytes()
        );

        try (
                MockedStatic<EasyExcelFactory> excelMock =
                        Mockito.mockStatic(EasyExcelFactory.class);
                MockedConstruction<VeneerExcelListener> listenerMock =
                        Mockito.mockConstruction(VeneerExcelListener.class)
        ) {

            ExcelReaderBuilder readerBuilder = Mockito.mock(ExcelReaderBuilder.class);
            ExcelReaderSheetBuilder sheetBuilder = Mockito.mock(ExcelReaderSheetBuilder.class);

            excelMock.when(() ->
                    EasyExcelFactory.read(
                            any(InputStream.class),
                            eq(VeneerImportExcelVO.class),
                            any()
                    )
            ).thenReturn(readerBuilder);

            Mockito.when(readerBuilder.headRowNumber(anyInt()))
                    .thenReturn(readerBuilder);
            Mockito.when(readerBuilder.sheet())
                    .thenReturn(sheetBuilder);
            Mockito.doNothing().when(sheetBuilder).doRead();

            mockMvc.perform(
                    multipart("/executor/veneer/import").file(file)
            ).andExpect(status().isOk());
        }
    }

    @Test
    void getVeneerImportTemplate_success() throws Exception {
        Mockito.doNothing()
                .when(veneerService)
                .importTemplateDownload(any(HttpServletResponse.class));

        mockMvc.perform(get("/executor/veneer/import/template"))
                .andExpect(status().isOk());

        Mockito.verify(veneerService)
                .importTemplateDownload(any(HttpServletResponse.class));
    }

    @Test
    void saveDefect_success() throws Exception {
        VeneerDefectSaveReqVO reqVO = new VeneerDefectSaveReqVO();
        reqVO.setId(1L);

        doNothing().when(veneerService).saveDefect(any());

        mockMvc.perform(post("/executor/veneer/defect")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(reqVO)))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.data").value(true));
    }

    @Test
    void createClassificationName_success() throws Exception {

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

        Mockito.when(veneerService.addClassificationName(any()))
                .thenReturn(1L);

        mockMvc.perform(
                        post("/executor/veneer/classificationName")
                                .contentType(MediaType.APPLICATION_JSON)
                                .content(objectMapper.writeValueAsString(reqVO))
                )
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(0))
                .andExpect(jsonPath("$.data").value(1L));

        Mockito.verify(veneerService)
                .addClassificationName(any(VeneerClassificationNameSaveReqVO.class));
    }

    @Test
    void deleteClassificationName_success() throws Exception {
        doNothing().when(veneerService).deleteClassificationName(1L);

        mockMvc.perform(delete("/executor/veneer/classificationName/1"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(0))
                .andExpect(jsonPath("$.data").value(true));

        Mockito.verify(veneerService).deleteClassificationName(1L);
    }

    @Test
    void getOrgVeneerClassificationNameList_success() throws Exception {

        VeneerClassificationNameRespVO respVO = new VeneerClassificationNameRespVO();
        respVO.setId(1L);
        respVO.setName("测试分类");

        Mockito.when(veneerService.getOrgVeneerClassificationNameList())
                .thenReturn(List.of(respVO));

        mockMvc.perform(
                        get("/executor/veneer/org/classificationName")
                )
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(0))
                .andExpect(jsonPath("$.data[0].id").value(1L))
                .andExpect(jsonPath("$.data[0].name").value("测试分类"));

        Mockito.verify(veneerService)
                .getOrgVeneerClassificationNameList();
    }

}
