package com.zsc.edu.gateway.rest.iot;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zsc.edu.gateway.MockMvcConfigBase;
import com.zsc.edu.gateway.domain.iot.ProductBuilder;
import com.zsc.edu.gateway.framework.mybatisplus.DataScopeType;
import com.zsc.edu.gateway.framework.security.UserDetailsImpl;
import com.zsc.edu.gateway.modules.iot.product.controller.ProductController;
import com.zsc.edu.gateway.modules.iot.product.dto.ProductDto;
import com.zsc.edu.gateway.modules.iot.product.entity.Product;
import com.zsc.edu.gateway.modules.iot.product.service.ProductService;
import com.zsc.edu.gateway.modules.system.entity.Dept;
import com.zsc.edu.gateway.modules.system.entity.Role;
import org.assertj.core.util.Lists;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.mockito.Spy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;

import java.util.List;
import java.util.Set;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@WebMvcTest(ProductController.class)
public class ProductControllerTest extends MockMvcConfigBase {
    @Spy
    private static Product product1;
    private static Product product2;
    private static Product product3;
    private static Product product4;
    @MockBean
    private ProductService service;

    @Autowired
    private ObjectMapper objectMapper;

    @BeforeAll
    static void beforeAll() {
        product1 = ProductBuilder.bProduct().name("Product 1").productType("Description 1").deptId(2L).createId(1L).build();
        product1.setId(1L);
        product2 = ProductBuilder.bProduct().name("Product 2").productType("Description 2").deptId(2L).createId(1L).build();
        product2.setId(2L);
        product3 = ProductBuilder.bProduct().name("Product 3").productType("Description 3").deptId(2L).createId(2L).build();
        product3.setId(3L);
        product4 = ProductBuilder.bProduct().name("Product 4").productType("Description 4").deptId(3L).createId(2L).build();
        product4.setId(4L);
    }

    private UserDetailsImpl createUserDetails(DataScopeType dataScopeType) {
        Dept dept = new Dept();
        dept.setId(2L);
        Role role = new Role();
        role.setId(50L);
        role.setDataScope(dataScopeType);
        userDetails.setId(1L);
        userDetails.setDept(dept);
        userDetails.setRole(role);
        userDetails.setDataScopeDeptIds(Set.of(2L));
        return userDetails;
    }

    @Test
    void create() throws Exception {
        ProductDto dto = new ProductDto();
        dto.setProductType(product1.getProductType());
        dto.setName(product1.getName());
        dto.setParams(null);
        when(service.create(any(ProductDto.class))).thenReturn(product1);
        mockMvc.perform(post("/api/rest/product")
                        .with(csrf().asHeader())
                        .with(user(userDetails))
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(dto))
                )
                .andExpect(status().isOk())
                .andDo(print());
        verify(service).create(any(ProductDto.class));
    }

    @Test
    void listWithDataScopeALL() throws Exception {
        // 创建测试数据
        List<Product> products = Lists.newArrayList(product1, product2, product3, product4);
        Page<Product> pageResult = new Page<>();
        pageResult.setRecords(products);
        pageResult.setTotal(products.size());
        // 模拟 service.page 方法的返回值
        when(service.page(any(Page.class), any())).thenReturn(pageResult);
        // 执行 GET 请求并验证响应
        mockMvc.perform(get("/api/rest/product")
                        .with(user(createUserDetails(DataScopeType.DATA_SCOPE_ALL))))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.records.length()").value(products.size()))
                .andExpect(jsonPath("$.total").value(products.size()))
                .andExpect(jsonPath("$.records[0].id").value(product1.getId()))
                .andExpect(jsonPath("$.records[1].id").value(product2.getId()))
                .andExpect(jsonPath("$.records[2].id").value(product3.getId()))
                .andExpect(jsonPath("$.records[3].id").value(product4.getId()))
                .andExpect(jsonPath("$.records[0].deptId").value(2L))
                .andExpect(jsonPath("$.records[1].deptId").value(2L))
                .andExpect(jsonPath("$.records[2].deptId").value(2L))
                .andExpect(jsonPath("$.records[3].deptId").value(3L))
                .andExpect(jsonPath("$.records[0].createId").value(1L))
                .andExpect(jsonPath("$.records[1].createId").value(1L))
                .andExpect(jsonPath("$.records[2].createId").value(2L))
                .andExpect(jsonPath("$.records[3].createId").value(2L))
                .andDo(print());
        // 验证 service.page 方法确实被调用了一次
        verify(service).page(any(Page.class), any());
    }

    @Test
    void listWithDataScopeDept() throws Exception {
        // 创建测试数据
        List<Product> products = Lists.newArrayList(product1, product2, product3);
        Page<Product> pageResult = new Page<>();
        pageResult.setRecords(products);
        pageResult.setTotal(products.size());
        // 模拟 service.page 方法的返回值
        when(service.page(any(Page.class), any())).thenReturn(pageResult);
        // 执行 GET 请求并验证响应
        mockMvc.perform(get("/api/rest/product")
                        .with(user(createUserDetails(DataScopeType.DATA_SCOPE_DEPT))))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.records.length()").value(products.size()))
                .andExpect(jsonPath("$.total").value(products.size()))
                .andExpect(jsonPath("$.records[0].id").value(product1.getId()))
                .andExpect(jsonPath("$.records[1].id").value(product2.getId()))
                .andExpect(jsonPath("$.records[2].id").value(product3.getId()))
                .andExpect(jsonPath("$.records[0].deptId").value(2L))
                .andExpect(jsonPath("$.records[1].deptId").value(2L))
                .andExpect(jsonPath("$.records[2].deptId").value(2L))
                .andExpect(jsonPath("$.records[0].createId").value(1L))
                .andExpect(jsonPath("$.records[1].createId").value(1L))
                .andExpect(jsonPath("$.records[2].createId").value(2L))
                .andDo(print());
        // 验证 service.page 方法确实被调用了一次
        verify(service).page(any(Page.class), any());
    }

    @Test
    void listWithDataScopeDeptAndChild() throws Exception {
        // 创建测试数据
        List<Product> products = Lists.newArrayList(product1, product2, product3, product4);
        Page<Product> pageResult = new Page<>();
        pageResult.setRecords(products);
        pageResult.setTotal(products.size());
        // 模拟 service.page 方法的返回值
        when(service.page(any(Page.class), any())).thenReturn(pageResult);
        // 执行 GET 请求并验证响应
        mockMvc.perform(get("/api/rest/product")
                        .with(user(createUserDetails(DataScopeType.DATA_SCOPE_DEPT_AND_CHILD))))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.records.length()").value(products.size()))
                .andExpect(jsonPath("$.total").value(products.size()))
                .andExpect(jsonPath("$.records[0].id").value(product1.getId()))
                .andExpect(jsonPath("$.records[1].id").value(product2.getId()))
                .andExpect(jsonPath("$.records[2].id").value(product3.getId()))
                .andExpect(jsonPath("$.records[3].id").value(product4.getId()))
                .andExpect(jsonPath("$.records[0].deptId").value(2L))
                .andExpect(jsonPath("$.records[1].deptId").value(2L))
                .andExpect(jsonPath("$.records[2].deptId").value(2L))
                .andExpect(jsonPath("$.records[3].deptId").value(3L))
                .andExpect(jsonPath("$.records[0].createId").value(1L))
                .andExpect(jsonPath("$.records[1].createId").value(1L))
                .andExpect(jsonPath("$.records[2].createId").value(2L))
                .andExpect(jsonPath("$.records[3].createId").value(2L))
                .andDo(print());
        // 验证 service.page 方法确实被调用了一次
        verify(service).page(any(Page.class), any());
    }

    @Test
    void listWithDataScopeSelf() throws Exception {
        // 创建测试数据
        List<Product> products = Lists.newArrayList(product1, product2);
        Page<Product> pageResult = new Page<>();
        pageResult.setRecords(products);
        pageResult.setTotal(products.size());
        // 模拟 service.page 方法的返回值
        when(service.page(any(Page.class), any())).thenReturn(pageResult);
        // 执行 GET 请求并验证响应
        mockMvc.perform(get("/api/rest/product")
                        .with(user(createUserDetails(DataScopeType.DATA_SCOPE_SELF))))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.records.length()").value(products.size()))
                .andExpect(jsonPath("$.total").value(products.size()))
                .andExpect(jsonPath("$.records[0].id").value(product1.getId()))
                .andExpect(jsonPath("$.records[1].id").value(product2.getId()))
                .andExpect(jsonPath("$.records[0].deptId").value(2L))
                .andExpect(jsonPath("$.records[1].deptId").value(2L))
                .andExpect(jsonPath("$.records[0].createId").value(1L))
                .andExpect(jsonPath("$.records[1].createId").value(1L))
                .andDo(print());
        // 验证 service.page 方法确实被调用了一次
        verify(service).page(any(Page.class), any());
    }

    @Test
    void update() throws Exception {
        ProductDto dto = new ProductDto();
        dto.setProductType(product1.getProductType());
        dto.setName(product1.getName());
        dto.setParams(null);
        when(service.update(any(ProductDto.class), anyLong())).thenReturn(product1);
        mockMvc.perform(patch("/api/rest/product/{id}", product1.getId())
                        .with(csrf().asHeader())
                        .with(user(userDetails))
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(dto))
                )
                .andExpect(status().isOk())
                .andDo(print());
        verify(service).update(any(ProductDto.class), anyLong());
    }
}
