package com.cf.imes.module.system.controller.admin.funds.products;

import com.cf.imes.framework.common.pojo.PageParam;
import com.cf.imes.framework.common.pojo.PageResult;
import com.cf.imes.framework.security.core.util.SecurityFrameworkUtils;
import com.cf.imes.framework.security.test.WithMockLoginUser;
import com.cf.imes.framework.test.core.ut.BaseWebUnitTest;
import com.cf.imes.module.system.common.WithMockLoginUserExtension;
import com.cf.imes.module.system.controller.admin.funds.products.vo.product.ProductPageReqVO;
import com.cf.imes.module.system.controller.admin.funds.products.vo.product.ProductSaveReqVO;
import com.cf.imes.module.system.dal.dataobject.funds.products.ProductsDO;
import com.cf.imes.module.system.enums.products.ProductStatusEnum;
import com.cf.imes.module.system.service.funds.products.ProductsService;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
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.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.web.servlet.MockMvc;

import java.math.BigDecimal;
import java.util.List;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
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.put;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

/**
 * @author Gqr
 * @since 2025/10/14 11:16
 */
@WebMvcTest(controllers = ProductsController.class, excludeAutoConfiguration = SecurityAutoConfiguration.class)
@ExtendWith(WithMockLoginUserExtension.class)
class ProductsControllerTest extends  BaseWebUnitTest {
    @Autowired
    private MockMvc mockMvc;

    @MockitoBean
    private ProductsService productsService;

    @Autowired
    private ObjectMapper objectMapper;

    @Test
    void testCreateProducts() throws Exception {
        ProductSaveReqVO vo = new ProductSaveReqVO();
        vo.setProductName("新建产品");
        vo.setPrice(new BigDecimal("199"));
        vo.setStatus(ProductStatusEnum.PRODUCT_DELIS.getStatus());

        // mock Service 返回
        when(productsService.create(any())).thenReturn(100L);

        mockMvc.perform(post("/system/products")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(vo)))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.data").value(100));

        // 验证 Service 调用
        verify(productsService, times(1)).create(any());
    }

    @Test
    void testUpdateProducts() throws Exception {
        ProductSaveReqVO vo = new ProductSaveReqVO();
        vo.setId(1L);
        vo.setPrice(new BigDecimal("199"));
        vo.setProductName("更新名称");

        // mock Service 方法 void 不做实际处理
        doNothing().when(productsService).update(any());

        mockMvc.perform(put("/system/products")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(vo)))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.data").value(true));

        verify(productsService, times(1)).update(any());
    }

    @Test
    void testFreezeProducts() throws Exception {
        Long id = 1L;
        doNothing().when(productsService).update(any());

        mockMvc.perform(put("/system/products/freeze")
                        .param("id", id.toString()))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.data").value(true));

        verify(productsService, times(1)).update(argThat(vo -> vo.getId().equals(id)
                && vo.getStatus().equals(ProductStatusEnum.PRODUCT_DELIS.getStatus())));
    }

    @Test
    void testMeltProducts() throws Exception {
        Long id = 2L;
        doNothing().when(productsService).update(any());

        mockMvc.perform(put("/system/products/melt")
                        .param("id", id.toString()))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.data").value(true));

        verify(productsService, times(1)).update(argThat(vo -> vo.getId().equals(id)
                && vo.getStatus().equals(ProductStatusEnum.PRODUCT_UPDATES.getStatus())));
    }

    @Test
    void testDeleteProducts() throws Exception {
        Long id = 3L;
        doNothing().when(productsService).delete(anyLong());

        mockMvc.perform(delete("/system/products")
                        .param("id", id.toString()))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.data").value(true));

        verify(productsService, times(1)).delete(id);
    }

    @Test
    void testGetProducts() throws Exception {
        Long id = 4L;
        ProductsDO mockProduct = new ProductsDO()
                .setId(id)
                .setProductName("测试产品");
        when(productsService.get(id)).thenReturn(mockProduct);

        mockMvc.perform(get("/system/products/{id}", id))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.data.productName").value("测试产品"));

        verify(productsService, times(1)).get(id);
    }

    @Test
    void testGetProductsPage() throws Exception {
        ProductPageReqVO reqVO = new ProductPageReqVO();
        PageResult<ProductsDO> mockPage = new PageResult<>();
        mockPage.setTotal(1L);
        mockPage.setList(List.of(new ProductsDO().setId(1L).setProductName("分页产品")));
        when(productsService.getPage(any())).thenReturn(mockPage);

        mockMvc.perform(get("/system/products/page")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(reqVO)))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.data.list[0].productName").value("分页产品"));

        verify(productsService, times(1)).getPage(any());
    }

    @Test
    @WithMockLoginUser(userType = 2)
    void testGetAvailableProductsPage() throws Exception {
        ProductPageReqVO reqVO = new ProductPageReqVO();
        PageResult<ProductsDO> mockPage = new PageResult<>();
        mockPage.setTotal(1L);
        mockPage.setList(List.of(new ProductsDO().setId(1L).setProductName("已上架产品")));
        when(productsService.getPage(any())).thenReturn(mockPage);

        mockMvc.perform(get("/system/products/available")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(reqVO)))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.data.list[0].productName").value("已上架产品"));

        verify(productsService, times(1)).getPage(any());
    }

    @Test
    @WithMockLoginUser(productId = 123L)
    void testGetAvailableProductsPage_nonManager() throws Exception {
        ProductPageReqVO reqVO = new ProductPageReqVO();

        // 准备 mock 分页数据
        PageResult<ProductsDO> mockPage = new PageResult<>();
        mockPage.setTotal(1L);
        mockPage.setList(List.of(new ProductsDO().setId(123L).setProductName("非管理端产品")));
        when(productsService.getPage(any())).thenReturn(mockPage);

        // mock SecurityFrameworkUtils 的静态方法
        try (var securityMock = mockStatic(SecurityFrameworkUtils.class)) {
            // 返回非管理端
            securityMock.when(SecurityFrameworkUtils::isManageEndPoint).thenReturn(false);
            // 返回登录的产品 ID
            securityMock.when(SecurityFrameworkUtils::getProductId).thenReturn(123L);

            mockMvc.perform(get("/system/products/available")
                            .contentType(MediaType.APPLICATION_JSON)
                            .content(objectMapper.writeValueAsString(reqVO)))
                    .andExpect(status().isOk())
                    .andExpect(jsonPath("$.data.list[0].productName").value("非管理端产品"));

            // 验证 Service 调用时 pageReqVO 已经被设置 ID
            verify(productsService, times(1)).getPage(argThat(vo -> vo.getId().equals(123L)
                    && vo.getStatus().equals(ProductStatusEnum.PRODUCT_UPDATES.getStatus())
                    && vo.getPageSize().equals(PageParam.PAGE_SIZE_NONE)));
        }
    }
}
