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

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.when;
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.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import java.math.BigDecimal;
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.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.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.vo.BreedingIndustryDashboardVo;
import com.ruoyi.web.modules.industryservice.domain.vo.BreedingIndustryDegradationStatVo;
import com.ruoyi.web.modules.industryservice.domain.vo.BreedingIndustryGrasslandVo;
import com.ruoyi.web.modules.industryservice.domain.vo.BreedingIndustryInventoryChangeVo;
import com.ruoyi.web.modules.industryservice.domain.vo.BreedingIndustryMonthlyTrendPointVo;
import com.ruoyi.web.modules.industryservice.domain.vo.BreedingIndustryOverviewVo;
import com.ruoyi.web.modules.industryservice.service.IBreedingIndustryDashboardService;
import com.ruoyi.web.modules.industryservice.support.BreedingIndustryRules;
import com.ruoyi.web.modules.screen.domain.vo.HomeScreenYakStructureCategoryVo;
import com.ruoyi.web.modules.screen.domain.vo.HomeScreenYakStructureDetailVo;

@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
@DisplayName("养殖产业数据 HTTP MockMvc")
class BreedingIndustryDashboardControllerApiTest
{
    @Mock
    private IBreedingIndustryDashboardService breedingIndustryDashboardService;

    @InjectMocks
    private BreedingIndustryDashboardController controller;

    private MockMvc mockMvc;

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

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

    @Test
    @DisplayName("ZCZX-YZCYSJ-API-001 GET /dashboard")
    void api001_dashboard() throws Exception
    {
        when(breedingIndustryDashboardService.loadDashboard(eq(false), isNull())).thenReturn(sampleDashboard());
        mockMvc.perform(get("/dataModel/breedingIndustry/dashboard"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200))
                .andExpect(jsonPath("$.data.statDate").value("2026-05-20"))
                .andExpect(jsonPath("$.data.overview.inventoryTotal").value(120))
                .andExpect(jsonPath("$.data.inventoryChange.bullCount").value(45))
                .andExpect(jsonPath("$.data.yakInventoryStructure[0].categoryCode").value("BULL"))
                .andExpect(jsonPath("$.data.grassland.availableCount").value(50));
    }

    @Test
    @DisplayName("ZCZX-YZCYSJ-API-010 forceRefresh")
    void api010_forceRefresh() throws Exception
    {
        when(breedingIndustryDashboardService.loadDashboard(eq(true), isNull())).thenReturn(sampleDashboard());
        mockMvc.perform(get("/dataModel/breedingIndustry/dashboard").param("forceRefresh", "true"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200));
    }

    @Test
    @DisplayName("ZCZX-YZCYSJ-API-011 指定统计年")
    void api011_statYear() throws Exception
    {
        when(breedingIndustryDashboardService.loadDashboard(eq(false), eq("2025"))).thenReturn(sampleDashboard());
        mockMvc.perform(get("/dataModel/breedingIndustry/dashboard").param("statYear", "2025"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200))
                .andExpect(jsonPath("$.data.statYear").value(2026));
    }

    @Test
    @DisplayName("ZCZX-YZCYSJ-API-017 聚合失败")
    void api017_loadFailed() throws Exception
    {
        when(breedingIndustryDashboardService.loadDashboard(anyBoolean(), any()))
                .thenThrow(new ServiceException(BreedingIndustryRules.MSG_LOAD_FAILED));
        mockMvc.perform(get("/dataModel/breedingIndustry/dashboard"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(500))
                .andExpect(jsonPath("$.msg").value(BreedingIndustryRules.MSG_LOAD_FAILED));
    }

    @Test
    @DisplayName("ZCZX-YZCYSJ-API-021 无 POST 维护")
    void api021_noPost() throws Exception
    {
        mockMvc.perform(post("/dataModel/breedingIndustry/dashboard"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(500));
    }

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

    private static BreedingIndustryDashboardVo sampleDashboard()
    {
        BreedingIndustryDashboardVo vo = new BreedingIndustryDashboardVo();
        vo.setStatDate("2026-05-20");
        vo.setStatYear(2026);
        BreedingIndustryOverviewVo overview = new BreedingIndustryOverviewVo();
        overview.setInventoryTotal(120);
        overview.setAnnualOutbound(8);
        overview.setCooperativeCount(6);
        overview.setFamilyRanchCount(4);
        overview.setLargeLivestockFarmerCount(3);
        overview.setPastureCount(12);
        overview.setWithLivestockHouseholdCount(10);
        overview.setWarningYakCount(5);
        overview.setGrasslandCount(80);
        overview.setGrasslandAreaMu(new BigDecimal("12500.50"));
        vo.setOverview(overview);
        BreedingIndustryInventoryChangeVo inv = new BreedingIndustryInventoryChangeVo();
        inv.setBullCount(45);
        inv.setCowCount(60);
        inv.setUnknownGenderCount(0);
        BreedingIndustryMonthlyTrendPointVo point = new BreedingIndustryMonthlyTrendPointVo();
        point.setStatYear(2026);
        point.setMonth(5);
        point.setBullCount(40);
        point.setCowCount(55);
        inv.setMonthlyTrend(Collections.singletonList(point));
        vo.setInventoryChange(inv);
        HomeScreenYakStructureCategoryVo cat = new HomeScreenYakStructureCategoryVo();
        cat.setCategoryCode("BULL");
        cat.setCategoryLabel("公牛");
        cat.setCount(45);
        cat.setRatio(new BigDecimal("37.5"));
        HomeScreenYakStructureDetailVo detail = new HomeScreenYakStructureDetailVo();
        detail.setDetailCode("BREEDING_BULL");
        detail.setDetailLabel("种公牛");
        detail.setCount(20);
        detail.setRatio(new BigDecimal("16.7"));
        cat.setDetails(Collections.singletonList(detail));
        vo.setYakInventoryStructure(Collections.singletonList(cat));
        BreedingIndustryGrasslandVo grass = new BreedingIndustryGrasslandVo();
        grass.setAvailableCount(50);
        grass.setInUseCount(40);
        BreedingIndustryDegradationStatVo deg = new BreedingIndustryDegradationStatVo();
        deg.setDegradationLevel(1);
        deg.setDegradationLabel("未退化");
        deg.setCount(30);
        deg.setRatio(new BigDecimal("37.5"));
        grass.setDegradationStats(Collections.singletonList(deg));
        vo.setGrassland(grass);
        return vo;
    }
}
