package com.ruoyi.web.modules.industryservice.controller;

import static org.hamcrest.Matchers.containsString;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
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.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;

import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
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 org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.pagehelper.PageHelper;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.framework.web.exception.GlobalExceptionHandler;
import com.ruoyi.web.modules.industryservice.domain.BizYakHerdInventory;
import com.ruoyi.web.modules.industryservice.service.IBizYakHerdInventoryService;

/**
 * ZCZX-MYCLTB-API-001～020（MockMvc + Mock Service）
 */
@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
@DisplayName("牦牛存栏数据填报 HTTP 接口 MockMvc")
class BizYakHerdInventoryControllerApiTest
{
    private final ObjectMapper objectMapper = new ObjectMapper();

    @Mock
    private IBizYakHerdInventoryService service;

    @InjectMocks
    private BizYakHerdInventoryController controller;

    private MockMvc mockMvc;

    @BeforeEach
    void setUp()
    {
        mockMvc = MockMvcBuilders.standaloneSetup(controller)
                .setControllerAdvice(new GlobalExceptionHandler())
                .build();
        loginAsAllPermissions();
    }

    @AfterEach
    void tearDown()
    {
        SecurityContextHolder.clearContext();
        PageHelper.clearPage();
    }

    @Test
    @DisplayName("ZCZX-MYCLTB-API-001 GET /list 分页")
    void api001_list() throws Exception
    {
        when(service.selectBizYakHerdInventoryList(any())).thenReturn(Collections.singletonList(sample(1L)));
        mockMvc.perform(get("/dataModel/yakHerdInventory/list").param("pageNum", "1").param("pageSize", "10"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200))
                .andExpect(jsonPath("$.rows[0].statYear").value(2026))
                .andExpect(jsonPath("$.rows[0].townName").value("巴青乡"))
                .andExpect(jsonPath("$.rows[0].yakTotal").value(21687));
    }

    @Test
    @DisplayName("ZCZX-MYCLTB-API-002 按年份筛选")
    void api002_listByYear() throws Exception
    {
        when(service.selectBizYakHerdInventoryList(any())).thenReturn(Collections.emptyList());
        mockMvc.perform(get("/dataModel/yakHerdInventory/list").param("statYear", "2026"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200));
    }

    @Test
    @DisplayName("ZCZX-MYCLTB-API-005 详情全字段")
    void api005_detail() throws Exception
    {
        when(service.selectBizYakHerdInventoryById(1L)).thenReturn(sample(1L));
        mockMvc.perform(get("/dataModel/yakHerdInventory/1"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200))
                .andExpect(jsonPath("$.data.townName").value("巴青乡"))
                .andExpect(jsonPath("$.data.bullTotal").value(8157))
                .andExpect(jsonPath("$.data.cowYoungCount").value(2301))
                .andExpect(jsonPath("$.data.villageName").value("巴青村"));
    }

    @Test
    @DisplayName("ZCZX-MYCLTB-API-006 新增成功")
    void api006_add() throws Exception
    {
        when(service.insertBizYakHerdInventory(any())).thenReturn(1);
        mockMvc.perform(post("/dataModel/yakHerdInventory")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(sample(null))))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200));
    }

    @Test
    @DisplayName("ZCZX-MYCLTB-API-009 重复同年乡镇")
    void api009_duplicate() throws Exception
    {
        when(service.insertBizYakHerdInventory(any())).thenThrow(new ServiceException("该年份该乡镇已填报"));
        mockMvc.perform(post("/dataModel/yakHerdInventory")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(sample(null))))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(500))
                .andExpect(jsonPath("$.msg", containsString("已填报")));
    }

    @Test
    @DisplayName("ZCZX-MYCLTB-API-010 修改")
    void api010_update() throws Exception
    {
        when(service.updateBizYakHerdInventory(any())).thenReturn(1);
        BizYakHerdInventory row = sample(2L);
        row.setYakTotal(999);
        mockMvc.perform(put("/dataModel/yakHerdInventory")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(row)))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200));
    }

    @Test
    @DisplayName("ZCZX-MYCLTB-API-011 删除")
    void api011_delete() throws Exception
    {
        when(service.deleteBizYakHerdInventoryByIds(any(), eq("tester"))).thenReturn(1);
        mockMvc.perform(delete("/dataModel/yakHerdInventory/1"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200));
    }

    @Test
    @DisplayName("ZCZX-MYCLTB-API-012 年份下拉")
    void api012_yearOptions() throws Exception
    {
        when(service.listYearOptions()).thenReturn(Arrays.asList(2026, 2025, 2024, 2023, 2022, 2021));
        mockMvc.perform(get("/dataModel/yakHerdInventory/yearOptions"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200))
                .andExpect(jsonPath("$.data.length()").value(6));
    }

    @Test
    @DisplayName("ZCZX-MYCLTB-API-013 乡镇下拉")
    void api013_townOptions() throws Exception
    {
        when(service.listTownOptions()).thenReturn(Collections.singletonList(Collections.singletonMap("deptName", "巴青乡")));
        mockMvc.perform(get("/dataModel/yakHerdInventory/townOptions"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200))
                .andExpect(jsonPath("$.data[0].deptName").value("巴青乡"));
    }

    @Test
    @DisplayName("ZCZX-MYCLTB-API-021 GET /export 按年份导出")
    void api021_export() throws Exception
    {
        when(service.exportByYearMonth(eq(2026), eq(5), any(), any())).thenReturn(new byte[] { 1, 2, 3 });
        mockMvc.perform(get("/dataModel/yakHerdInventory/export").param("statYear", "2026").param("statMonth", "5"))
                .andExpect(status().isOk());
    }

    private static void loginAsAllPermissions()
    {
        SysUser u = new SysUser();
        u.setUserId(1L);
        u.setUserName("tester");
        u.setPassword("x");
        Set<String> perms = new HashSet<>();
        perms.add(Constants.ALL_PERMISSION);
        LoginUser lu = new LoginUser(1L, 100L, u, perms);
        UsernamePasswordAuthenticationToken auth =
                new UsernamePasswordAuthenticationToken(lu, "", lu.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(auth);
    }

    private static BizYakHerdInventory sample(Long id)
    {
        BizYakHerdInventory row = new BizYakHerdInventory();
        row.setId(id);
        row.setStatYear(2026);
        row.setStatMonth(5);
        row.setTownDeptId(101L);
        row.setTownName("巴青乡");
        row.setVillageDeptId(201L);
        row.setVillageName("巴青村");
        row.setVillageCount(26);
        row.setHerdsmanHouseholdCount(1605);
        row.setBreedingBullCount(504);
        row.setBullCastratedCount(7653);
        row.setBullTotal(8157);
        row.setCowYoungCount(2301);
        row.setCowAdultCount(11129);
        row.setCowTotal(13430);
        row.setInsuredYakCount(100);
        row.setYakTotal(21687);
        row.setMilkingCowCount(10);
        row.setFertileCowCount(20);
        row.setDairyTotal(30);
        row.setSheepTotal(50);
        row.setGrandTotal(21767);
        row.setYakSheepTotal(21737);
        return row;
    }
}
