package com.ruoyi.web.modules.screen.service.impl;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.math.BigDecimal;
import java.util.Collections;
import java.util.List;
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 com.ruoyi.web.modules.industryservice.domain.BizCooperativeDevelopment;
import com.ruoyi.web.modules.industryservice.domain.BizDisposableIncome;
import com.ruoyi.web.modules.industryservice.domain.BizFamilyRanch;
import com.ruoyi.web.modules.industryservice.domain.BizFarmerHousehold;
import com.ruoyi.web.modules.industryservice.domain.BizLargeLivestockFarmer;
import com.ruoyi.web.modules.industryservice.domain.BizLivestockOwnershipHousehold;
import com.ruoyi.web.modules.industryservice.domain.BizYakHerdInventory;
import com.ruoyi.web.modules.industryservice.domain.BizYakOutboundReport;
import com.ruoyi.web.modules.industryservice.mapper.BizCooperativeDevelopmentMapper;
import com.ruoyi.web.modules.industryservice.mapper.BizDisposableIncomeMapper;
import com.ruoyi.web.modules.industryservice.mapper.BizFamilyRanchMapper;
import com.ruoyi.web.modules.industryservice.mapper.BizFarmerHouseholdMapper;
import com.ruoyi.web.modules.industryservice.mapper.BizLargeLivestockFarmerMapper;
import com.ruoyi.web.modules.industryservice.mapper.BizLivestockOwnershipHouseholdMapper;
import com.ruoyi.web.modules.industryservice.mapper.BizYakHerdInventoryMapper;
import com.ruoyi.web.modules.industryservice.mapper.BizYakOutboundReportMapper;
import com.ruoyi.web.modules.screen.domain.dto.HomeScreenCoopBusinessModelSumDto;
import com.ruoyi.web.modules.screen.domain.dto.HomeScreenDisposableIncomeYearDto;
import com.ruoyi.web.modules.screen.domain.dto.HomeScreenEnterpriseFinanceSumDto;
import com.ruoyi.web.modules.screen.domain.dto.HomeScreenHerdSpeciesSumDto;
import com.ruoyi.web.modules.screen.domain.dto.HomeScreenHerdYakStructureSumDto;
import com.ruoyi.web.modules.screen.domain.dto.HomeScreenOutboundSpeciesSumDto;
import com.ruoyi.web.modules.screen.domain.dto.HomeScreenSelfConsumptionSumDto;
import com.ruoyi.web.modules.screen.domain.dto.HomeScreenStatPeriodDto;
import com.ruoyi.web.modules.screen.domain.vo.HomeScreenCooperativeDevelopmentListItemVo;
import com.ruoyi.web.modules.screen.domain.vo.HomeScreenDashboardVo;
import com.ruoyi.web.modules.screen.domain.vo.HomeScreenFamilyRanchListItemVo;
import com.ruoyi.web.modules.screen.domain.vo.HomeScreenFarmerHouseholdListItemVo;
import com.ruoyi.web.modules.screen.domain.vo.HomeScreenLargeLivestockFarmerListItemVo;
import com.ruoyi.web.modules.screen.domain.vo.HomeScreenLivestockOwnershipHouseholdListItemVo;
import com.ruoyi.web.modules.screen.domain.vo.HomeScreenPerCapitaIncomeListItemVo;
import com.ruoyi.web.modules.screen.domain.vo.HomeScreenYakHerdInventoryListItemVo;
import com.ruoyi.web.modules.screen.domain.vo.HomeScreenBreedingEntityQuantityListItemVo;
import com.ruoyi.web.modules.screen.domain.vo.HomeScreenYakInventoryOutboundListItemVo;
import com.ruoyi.web.modules.screen.domain.vo.HomeScreenYakOutboundReportListItemVo;
import com.ruoyi.web.modules.screen.domain.vo.HomeScreenTownMapMarkerVo;
import com.ruoyi.web.modules.screen.domain.vo.HomeScreenTownMapVo;
import com.ruoyi.web.modules.screen.domain.vo.HomeScreenTownSubsidyItemVo;
import com.ruoyi.web.modules.screen.mapper.HomeScreenStandardMapper;
import com.ruoyi.web.modules.screen.mapper.HomeScreenStatsMapper;
import com.ruoyi.web.modules.industryservice.support.TownDeptSupport;

/**
 * DP-SY-UT-010 等编排测试
 */
@ExtendWith(MockitoExtension.class)
@DisplayName("HomeScreenServiceImpl")
class HomeScreenServiceImplTest
{

    @Mock
    private HomeScreenStatsMapper homeScreenStatsMapper;

    @Mock
    private HomeScreenStandardMapper homeScreenStandardMapper;

    @Mock
    private BizDisposableIncomeMapper bizDisposableIncomeMapper;

    @Mock
    private BizFarmerHouseholdMapper bizFarmerHouseholdMapper;

    @Mock
    private BizCooperativeDevelopmentMapper bizCooperativeDevelopmentMapper;

    @Mock
    private BizFamilyRanchMapper bizFamilyRanchMapper;

    @Mock
    private BizLargeLivestockFarmerMapper bizLargeLivestockFarmerMapper;

    @Mock
    private BizYakOutboundReportMapper bizYakOutboundReportMapper;

    @Mock
    private BizYakHerdInventoryMapper bizYakHerdInventoryMapper;

    @Mock
    private BizLivestockOwnershipHouseholdMapper bizLivestockOwnershipHouseholdMapper;

    @Mock
    private TownDeptSupport townDeptSupport;

    @InjectMocks
    private HomeScreenServiceImpl service;

    @Test
    @DisplayName("DP-SY-API-001 看板结构完整")
    void dashboardStructure()
    {
        stubGrasslandAndReport();
        stubOtherBlocks();
        HomeScreenDashboardVo vo = service.loadDashboard("2026");
        assertEquals(2026, vo.getStatYear());
        assertNotNull(vo.getStatDate());
        assertNotNull(vo.getIndustryOverview());
        assertEquals(2, vo.getAgeStructure().size());
        assertNotNull(vo.getHerdInventoryStructure());
        assertNotNull(vo.getLivestockOutboundStructure());
    }

    @Test
    @DisplayName("DP-SY-UT-026 地图标注取所选年最大填报月")
    void ut011_townMapMarkers()
    {
        HomeScreenStatPeriodDto herd = period(2026, 12);
        HomeScreenStatPeriodDto outbound = period(2026, 5);
        HomeScreenTownMapMarkerVo marker = new HomeScreenTownMapMarkerVo();
        marker.setTownDeptId(101L);
        marker.setTownName("巴青乡");
        marker.setYakInventoryCount(100);
        marker.setYakOutboundCount(50);
        when(homeScreenStatsMapper.selectLatestHerdInventoryStatPeriodByYear(2026)).thenReturn(herd);
        when(homeScreenStatsMapper.selectLatestOutboundReportStatPeriodByYear(2026)).thenReturn(outbound);
        when(homeScreenStatsMapper.selectTownMapMarkers(eq(2026), eq(12), eq(2026), eq(5)))
                .thenReturn(Collections.singletonList(marker));

        HomeScreenTownMapVo vo = service.loadTownMapMarkers("2026");
        assertEquals(2026, vo.getHerdStatYear().intValue());
        assertEquals(12, vo.getHerdStatMonth().intValue());
        assertEquals(2026, vo.getOutboundStatYear().intValue());
        assertEquals(5, vo.getOutboundStatMonth().intValue());
        assertEquals(1, vo.getMarkers().size());
        assertEquals(100, vo.getMarkers().get(0).getYakInventoryCount().intValue());
        assertEquals(50, vo.getMarkers().get(0).getYakOutboundCount().intValue());
    }

    @Test
    @DisplayName("DP-SY-UT-026b 仅存栏有数据时仍返回乡镇列表")
    void ut026b_townMapHerdOnly()
    {
        when(homeScreenStatsMapper.selectLatestHerdInventoryStatPeriodByYear(2026)).thenReturn(period(2026, 12));
        when(homeScreenStatsMapper.selectLatestOutboundReportStatPeriodByYear(2026)).thenReturn(null);
        when(homeScreenStatsMapper.selectTownMapMarkers(eq(2026), eq(12), eq(null), eq(null)))
                .thenReturn(Collections.emptyList());

        HomeScreenTownMapVo vo = service.loadTownMapMarkers("2026");
        assertEquals(2026, vo.getHerdStatYear().intValue());
        assertEquals(12, vo.getHerdStatMonth().intValue());
        assertNull(vo.getOutboundStatYear());
        assertEquals(0, vo.getMarkers().size());
    }

    @Test
    @DisplayName("DP-SY-UT-026c 所选年无填报时乡镇头数为0（仍查乡镇列表）")
    void ut026c_townMapEmptyWhenNoReport()
    {
        when(homeScreenStatsMapper.selectLatestHerdInventoryStatPeriodByYear(2026)).thenReturn(null);
        when(homeScreenStatsMapper.selectLatestOutboundReportStatPeriodByYear(2026)).thenReturn(null);
        when(homeScreenStatsMapper.selectTownMapMarkers(eq(null), eq(null), eq(null), eq(null)))
                .thenReturn(Collections.emptyList());

        HomeScreenTownMapVo vo = service.loadTownMapMarkers("2026");
        assertNull(vo.getHerdStatYear());
        assertNull(vo.getOutboundStatYear());
        assertEquals(0, vo.getMarkers().size());
    }

    @Test
    @DisplayName("DP-SY-UT-031 人均收入明细按年过滤并映射字段")
    void ut031_perCapitaIncomes()
    {
        BizDisposableIncome row = new BizDisposableIncome();
        row.setStatYear(2025);
        row.setRuralDisposableIncome(new BigDecimal("22190"));
        row.setIncomeIncrease(new BigDecimal("1460"));
        row.setIncomeGrowthRate(new BigDecimal("7.04"));
        row.setPovertyNetIncome(new BigDecimal("9800"));
        when(bizDisposableIncomeMapper.selectBizDisposableIncomeList(any(BizDisposableIncome.class)))
                .thenAnswer(invocation -> {
                    BizDisposableIncome q = invocation.getArgument(0);
                    assertEquals(2025, q.getStatYear().intValue());
                    return Collections.singletonList(row);
                });

        List<HomeScreenPerCapitaIncomeListItemVo> list = service.listPerCapitaIncomes(2025);
        assertEquals(1, list.size());
        assertEquals(2025, list.get(0).getStatYear().intValue());
        assertEquals(0, new BigDecimal("22190").compareTo(list.get(0).getRuralDisposableIncome()));
        assertEquals(0, new BigDecimal("1460").compareTo(list.get(0).getIncomeIncrease()));
        assertEquals(0, new BigDecimal("7.04").compareTo(list.get(0).getIncomeGrowthRate()));
        assertEquals(0, new BigDecimal("9800").compareTo(list.get(0).getPovertyNetIncome()));
    }

    @Test
    @DisplayName("DP-SY-UT-031b 人均收入明细未传年份不过滤")
    void ut031b_perCapitaIncomesNoYearFilter()
    {
        when(bizDisposableIncomeMapper.selectBizDisposableIncomeList(any(BizDisposableIncome.class)))
                .thenAnswer(invocation -> {
                    BizDisposableIncome q = invocation.getArgument(0);
                    assertNull(q.getStatYear());
                    return Collections.emptyList();
                });

        assertEquals(0, service.listPerCapitaIncomes(null).size());
    }

    @Test
    @DisplayName("DP-SY-UT-032 农牧户明细按条件过滤并映射字段")
    void ut032_farmerHouseholds()
    {
        BizFarmerHousehold row = new BizFarmerHousehold();
        row.setId(1L);
        row.setStatYear(2026);
        row.setStatMonth(3);
        row.setTownDeptId(101L);
        row.setTownName("巴青镇");
        row.setVillageDeptId(201L);
        row.setVillageName("某村");
        row.setHouseholdCount(10);
        row.setPopulationCount(30);
        row.setPastureContractArea(new BigDecimal("100.5"));
        when(bizFarmerHouseholdMapper.selectBizFarmerHouseholdList(any(BizFarmerHousehold.class)))
                .thenAnswer(invocation -> {
                    BizFarmerHousehold q = invocation.getArgument(0);
                    assertEquals(2026, q.getStatYear().intValue());
                    assertEquals(3, q.getStatMonth().intValue());
                    assertEquals(101L, q.getTownDeptId().longValue());
                    assertEquals(201L, q.getVillageDeptId().longValue());
                    return Collections.singletonList(row);
                });

        List<HomeScreenFarmerHouseholdListItemVo> list =
                service.listFarmerHouseholds(2026, 3, 101L, 201L);
        assertEquals(1, list.size());
        assertEquals(1L, list.get(0).getId().longValue());
        assertEquals("巴青镇", list.get(0).getTownName());
        assertEquals(10, list.get(0).getHouseholdCount().intValue());
        assertEquals(0, new BigDecimal("100.5").compareTo(list.get(0).getPastureContractArea()));
    }

    @Test
    @DisplayName("DP-SY-UT-032b 农牧户明细未传年月不过滤")
    void ut032b_farmerHouseholdsNoYearMonthFilter()
    {
        when(bizFarmerHouseholdMapper.selectBizFarmerHouseholdList(any(BizFarmerHousehold.class)))
                .thenAnswer(invocation -> {
                    BizFarmerHousehold q = invocation.getArgument(0);
                    assertNull(q.getStatYear());
                    assertNull(q.getStatMonth());
                    return Collections.emptyList();
                });

        assertEquals(0, service.listFarmerHouseholds(null, null, null, null).size());
    }

    @Test
    @DisplayName("DP-SY-UT-033 合作社明细按条件过滤并映射字段")
    void ut033_cooperativeDevelopments()
    {
        BizCooperativeDevelopment row = new BizCooperativeDevelopment();
        row.setId(2L);
        row.setStatYear(2026);
        row.setStatMonth(4);
        row.setTownName("巴青镇");
        row.setVillageName("某村");
        row.setCooperativeName("示范合作社");
        when(bizCooperativeDevelopmentMapper.selectBizCooperativeDevelopmentList(any(BizCooperativeDevelopment.class)))
                .thenAnswer(invocation -> {
                    BizCooperativeDevelopment q = invocation.getArgument(0);
                    assertEquals(2026, q.getStatYear().intValue());
                    assertEquals(4, q.getStatMonth().intValue());
                    assertEquals(101L, q.getTownDeptId().longValue());
                    return Collections.singletonList(row);
                });

        List<HomeScreenCooperativeDevelopmentListItemVo> list =
                service.listCooperativeDevelopments(2026, 4, 101L, null);
        assertEquals(1, list.size());
        assertEquals("示范合作社", list.get(0).getCooperativeName());
        assertEquals(2026, list.get(0).getStatYear().intValue());
    }

    @Test
    @DisplayName("DP-SY-UT-033b 合作社明细未传年月不过滤")
    void ut033b_cooperativeDevelopmentsNoYearMonthFilter()
    {
        when(bizCooperativeDevelopmentMapper.selectBizCooperativeDevelopmentList(any(BizCooperativeDevelopment.class)))
                .thenAnswer(invocation -> {
                    BizCooperativeDevelopment q = invocation.getArgument(0);
                    assertNull(q.getStatYear());
                    assertNull(q.getStatMonth());
                    return Collections.emptyList();
                });
        assertEquals(0, service.listCooperativeDevelopments(null, null, null, null).size());
    }

    @Test
    @DisplayName("DP-SY-UT-034 家庭牧场明细按条件过滤并映射字段")
    void ut034_familyRanches()
    {
        BizFamilyRanch row = new BizFamilyRanch();
        row.setId(3L);
        row.setStatYear(2026);
        row.setStatMonth(5);
        row.setTownName("巴青镇");
        row.setEnterpriseName("某某牧场");
        when(bizFamilyRanchMapper.selectBizFamilyRanchList(any(BizFamilyRanch.class)))
                .thenAnswer(invocation -> {
                    BizFamilyRanch q = invocation.getArgument(0);
                    assertEquals(2026, q.getStatYear().intValue());
                    assertEquals(5, q.getStatMonth().intValue());
                    return Collections.singletonList(row);
                });

        List<HomeScreenFamilyRanchListItemVo> list = service.listFamilyRanches(2026, 5, null, null);
        assertEquals(1, list.size());
        assertEquals("某某牧场", list.get(0).getEnterpriseName());
    }

    @Test
    @DisplayName("DP-SY-UT-034b 家庭牧场明细未传年月不过滤")
    void ut034b_familyRanchesNoYearMonthFilter()
    {
        when(bizFamilyRanchMapper.selectBizFamilyRanchList(any(BizFamilyRanch.class)))
                .thenAnswer(invocation -> {
                    BizFamilyRanch q = invocation.getArgument(0);
                    assertNull(q.getStatYear());
                    assertNull(q.getStatMonth());
                    return Collections.emptyList();
                });
        assertEquals(0, service.listFamilyRanches(null, null, null, null).size());
    }

    @Test
    @DisplayName("DP-SY-UT-035 养殖大户明细按条件过滤并映射字段")
    void ut035_largeLivestockFarmers()
    {
        BizLargeLivestockFarmer row = new BizLargeLivestockFarmer();
        row.setId(4L);
        row.setStatYear(2026);
        row.setStatMonth(6);
        row.setEnterpriseName("某某养殖大户");
        when(bizLargeLivestockFarmerMapper.selectBizLargeLivestockFarmerList(any(BizLargeLivestockFarmer.class)))
                .thenAnswer(invocation -> {
                    BizLargeLivestockFarmer q = invocation.getArgument(0);
                    assertEquals(2026, q.getStatYear().intValue());
                    assertEquals(6, q.getStatMonth().intValue());
                    assertEquals(201L, q.getVillageDeptId().longValue());
                    return Collections.singletonList(row);
                });

        List<HomeScreenLargeLivestockFarmerListItemVo> list =
                service.listLargeLivestockFarmers(2026, 6, null, 201L);
        assertEquals(1, list.size());
        assertEquals("某某养殖大户", list.get(0).getEnterpriseName());
    }

    @Test
    @DisplayName("DP-SY-UT-035b 养殖大户明细未传年月不过滤")
    void ut035b_largeLivestockFarmersNoYearMonthFilter()
    {
        when(bizLargeLivestockFarmerMapper.selectBizLargeLivestockFarmerList(any(BizLargeLivestockFarmer.class)))
                .thenAnswer(invocation -> {
                    BizLargeLivestockFarmer q = invocation.getArgument(0);
                    assertNull(q.getStatYear());
                    assertNull(q.getStatMonth());
                    return Collections.emptyList();
                });
        assertEquals(0, service.listLargeLivestockFarmers(null, null, null, null).size());
    }

    @Test
    @DisplayName("DP-SY-UT-036 牦牛出栏明细按条件过滤并映射字段")
    void ut036_yakOutboundReports()
    {
        BizYakOutboundReport row = new BizYakOutboundReport();
        row.setId(5L);
        row.setStatYear(2026);
        row.setStatMonth(7);
        row.setTownName("巴青镇");
        row.setYakOutboundCount(88);
        row.setSelfConsumptionCattleCount(10);
        when(bizYakOutboundReportMapper.selectBizYakOutboundReportList(any(BizYakOutboundReport.class)))
                .thenAnswer(invocation -> {
                    BizYakOutboundReport q = invocation.getArgument(0);
                    assertEquals(2026, q.getStatYear().intValue());
                    assertEquals(7, q.getStatMonth().intValue());
                    return Collections.singletonList(row);
                });
        List<HomeScreenYakOutboundReportListItemVo> list =
                service.listYakOutboundReports(2026, 7, null, null);
        assertEquals(1, list.size());
        assertEquals(88, list.get(0).getYakOutboundCount().intValue());
        assertEquals(10, list.get(0).getSelfConsumptionCattleCount().intValue());
    }

    @Test
    @DisplayName("DP-SY-UT-036b 牦牛出栏明细未传年月不过滤")
    void ut036b_yakOutboundReportsNoYearMonthFilter()
    {
        when(bizYakOutboundReportMapper.selectBizYakOutboundReportList(any(BizYakOutboundReport.class)))
                .thenAnswer(invocation -> {
                    BizYakOutboundReport q = invocation.getArgument(0);
                    assertNull(q.getStatYear());
                    assertNull(q.getStatMonth());
                    return Collections.emptyList();
                });
        assertEquals(0, service.listYakOutboundReports(null, null, null, null).size());
    }

    @Test
    @DisplayName("DP-SY-UT-037 牦牛存栏明细按条件过滤并映射字段")
    void ut037_yakHerdInventories()
    {
        BizYakHerdInventory row = new BizYakHerdInventory();
        row.setId(6L);
        row.setStatYear(2026);
        row.setStatMonth(8);
        row.setYakTotal(1000);
        row.setBullTotal(400);
        when(bizYakHerdInventoryMapper.selectBizYakHerdInventoryList(any(BizYakHerdInventory.class)))
                .thenAnswer(invocation -> {
                    BizYakHerdInventory q = invocation.getArgument(0);
                    assertEquals(2026, q.getStatYear().intValue());
                    assertEquals(8, q.getStatMonth().intValue());
                    return Collections.singletonList(row);
                });
        List<HomeScreenYakHerdInventoryListItemVo> list =
                service.listYakHerdInventories(2026, 8, null, null);
        assertEquals(1, list.size());
        assertEquals(1000, list.get(0).getYakTotal().intValue());
        assertEquals(400, list.get(0).getBullTotal().intValue());
    }

    @Test
    @DisplayName("DP-SY-UT-037b 牦牛存栏明细未传年月不过滤")
    void ut037b_yakHerdInventoriesNoYearMonthFilter()
    {
        when(bizYakHerdInventoryMapper.selectBizYakHerdInventoryList(any(BizYakHerdInventory.class)))
                .thenAnswer(invocation -> {
                    BizYakHerdInventory q = invocation.getArgument(0);
                    assertNull(q.getStatYear());
                    assertNull(q.getStatMonth());
                    return Collections.emptyList();
                });
        assertEquals(0, service.listYakHerdInventories(null, null, null, null).size());
    }

    @Test
    @DisplayName("DP-SY-UT-038 有无畜户明细按条件过滤并映射字段")
    void ut038_livestockOwnershipHouseholds()
    {
        BizLivestockOwnershipHousehold row = new BizLivestockOwnershipHousehold();
        row.setId(7L);
        row.setStatYear(2026);
        row.setStatMonth(9);
        row.setWithLivestockHouseholdCount(50);
        row.setWithoutLivestockHouseholdCount(5);
        when(bizLivestockOwnershipHouseholdMapper.selectBizLivestockOwnershipHouseholdList(
                any(BizLivestockOwnershipHousehold.class)))
                .thenAnswer(invocation -> {
                    BizLivestockOwnershipHousehold q = invocation.getArgument(0);
                    assertEquals(2026, q.getStatYear().intValue());
                    assertEquals(9, q.getStatMonth().intValue());
                    return Collections.singletonList(row);
                });
        List<HomeScreenLivestockOwnershipHouseholdListItemVo> list =
                service.listLivestockOwnershipHouseholds(2026, 9, null, null);
        assertEquals(1, list.size());
        assertEquals(50, list.get(0).getWithLivestockHouseholdCount().intValue());
        assertEquals(5, list.get(0).getWithoutLivestockHouseholdCount().intValue());
    }

    @Test
    @DisplayName("DP-SY-UT-038b 有无畜户明细未传年月不过滤")
    void ut038b_livestockOwnershipHouseholdsNoYearMonthFilter()
    {
        when(bizLivestockOwnershipHouseholdMapper.selectBizLivestockOwnershipHouseholdList(
                any(BizLivestockOwnershipHousehold.class)))
                .thenAnswer(invocation -> {
                    BizLivestockOwnershipHousehold q = invocation.getArgument(0);
                    assertNull(q.getStatYear());
                    assertNull(q.getStatMonth());
                    return Collections.emptyList();
                });
        assertEquals(0, service.listLivestockOwnershipHouseholds(null, null, null, null).size());
    }

    @Test
    @DisplayName("DP-SY-UT-039 牦牛存出栏情况明细按条件过滤并返回存栏出栏")
    void ut039_yakInventoryOutbounds()
    {
        HomeScreenYakInventoryOutboundListItemVo row = new HomeScreenYakInventoryOutboundListItemVo();
        row.setStatYear(2026);
        row.setStatMonth(6);
        row.setTownName("巴青镇");
        row.setVillageName("某村");
        row.setYakTotal(1200);
        row.setYakOutboundCount(80);
        when(homeScreenStatsMapper.selectYakInventoryOutboundList(eq(2026), eq(6), eq(101L), eq(201L)))
                .thenReturn(Collections.singletonList(row));
        List<HomeScreenYakInventoryOutboundListItemVo> list =
                service.listYakInventoryOutbounds(2026, 6, 101L, 201L);
        assertEquals(1, list.size());
        assertEquals(1200, list.get(0).getYakTotal().intValue());
        assertEquals(80, list.get(0).getYakOutboundCount().intValue());
    }

    @Test
    @DisplayName("DP-SY-UT-039b 牦牛存出栏情况明细未传年月不过滤（跨年全量）")
    void ut039b_yakInventoryOutboundsNoDefaultYearMonth()
    {
        when(homeScreenStatsMapper.selectYakInventoryOutboundList(
                eq(null), eq(null), eq(null), eq(null)))
                .thenReturn(Collections.emptyList());
        assertEquals(0, service.listYakInventoryOutbounds(null, null, null, null).size());
    }

    @Test
    @DisplayName("DP-SY-UT-040 养殖主体数量明细按条件过滤并返回四类数量")
    void ut040_breedingEntityQuantities()
    {
        HomeScreenBreedingEntityQuantityListItemVo row = new HomeScreenBreedingEntityQuantityListItemVo();
        row.setStatYear(2026);
        row.setStatMonth(5);
        row.setTownName("巴青镇");
        row.setVillageName("某村");
        row.setFarmerHouseholdCount(120);
        row.setCooperativeCount(3);
        row.setFamilyRanchCount(2);
        row.setLargeLivestockFarmerCount(5);
        when(homeScreenStatsMapper.selectBreedingEntityQuantityList(eq(2026), eq(5), eq(101L), eq(201L)))
                .thenReturn(Collections.singletonList(row));
        List<HomeScreenBreedingEntityQuantityListItemVo> list =
                service.listBreedingEntityQuantities(2026, 5, 101L, 201L);
        assertEquals(1, list.size());
        assertEquals(120, list.get(0).getFarmerHouseholdCount().intValue());
        assertEquals(3, list.get(0).getCooperativeCount().intValue());
        assertEquals(2, list.get(0).getFamilyRanchCount().intValue());
        assertEquals(5, list.get(0).getLargeLivestockFarmerCount().intValue());
    }

    @Test
    @DisplayName("DP-SY-UT-040b 养殖主体数量明细未传年月不过滤（跨年全量）")
    void ut040b_breedingEntityQuantitiesNoDefaultYearMonth()
    {
        when(homeScreenStatsMapper.selectBreedingEntityQuantityList(
                eq(null), eq(null), eq(null), eq(null)))
                .thenReturn(Collections.emptyList());
        assertEquals(0, service.listBreedingEntityQuantities(null, null, null, null).size());
        verify(homeScreenStatsMapper).selectBreedingEntityQuantityList(null, null, null, null);
    }

    @Test
    @DisplayName("DP-SY-UT-040c 任一组织形式有数据即可返回（其余为0）")
    void ut040c_breedingEntityQuantitiesAnyFormOnly()
    {
        HomeScreenBreedingEntityQuantityListItemVo coopOnly = new HomeScreenBreedingEntityQuantityListItemVo();
        coopOnly.setStatYear(2026);
        coopOnly.setStatMonth(5);
        coopOnly.setTownName("巴青乡");
        coopOnly.setVillageName("1村");
        coopOnly.setFarmerHouseholdCount(0);
        coopOnly.setCooperativeCount(2);
        coopOnly.setFamilyRanchCount(0);
        coopOnly.setLargeLivestockFarmerCount(0);
        when(homeScreenStatsMapper.selectBreedingEntityQuantityList(eq(2026), eq(5), eq(null), eq(null)))
                .thenReturn(Collections.singletonList(coopOnly));
        List<HomeScreenBreedingEntityQuantityListItemVo> list =
                service.listBreedingEntityQuantities(2026, 5, null, null);
        assertEquals(1, list.size());
        assertEquals(0, list.get(0).getFarmerHouseholdCount().intValue());
        assertEquals(2, list.get(0).getCooperativeCount().intValue());
        assertEquals(0, list.get(0).getFamilyRanchCount().intValue());
        assertEquals(0, list.get(0).getLargeLivestockFarmerCount().intValue());
    }

    @Test
    @DisplayName("DP-SY-UT-041 乡镇列表走全县口径")
    void ut041_townOptionsCountyWide()
    {
        java.util.Map<String, Object> item = new java.util.LinkedHashMap<>();
        item.put("deptId", 101L);
        item.put("deptName", "巴青镇");
        when(townDeptSupport.toAllOptionList()).thenReturn(Collections.singletonList(item));
        assertEquals(1, service.listTownOptions().size());
        assertEquals("巴青镇", service.listTownOptions().get(0).get("deptName"));
    }

    @Test
    @DisplayName("DP-SY-UT-042 村树走全县口径")
    void ut042_villageTreeCountyWide()
    {
        com.ruoyi.common.core.domain.TreeSelect root = new com.ruoyi.common.core.domain.TreeSelect();
        root.setId(100L);
        root.setLabel("巴青县");
        when(townDeptSupport.buildAllVillageTree()).thenReturn(Collections.singletonList(root));
        assertEquals(1, service.listVillageTree().size());
        assertEquals(100L, service.listVillageTree().get(0).getId());
    }

    @Test
    @DisplayName("DP-SY-UT-027 产业总览存栏出栏取所选年最大填报月")
    void ut027_overviewFromReportTables()
    {
        stubGrasslandAndReport();
        stubOtherBlocks();
        when(homeScreenStatsMapper.selectLatestHerdInventoryStatPeriodByYear(2024)).thenReturn(period(2024, 9));
        when(homeScreenStatsMapper.selectLatestOutboundReportStatPeriodByYear(2024)).thenReturn(period(2024, 11));
        when(homeScreenStatsMapper.sumHerdInventoryYakTotalByStatYearMonth(2024, 9)).thenReturn(50000);
        when(homeScreenStatsMapper.sumOutboundReportYakCountByStatYearMonth(2024, 11)).thenReturn(3200);

        HomeScreenDashboardVo vo = service.loadDashboard("2024");

        assertEquals(50000, vo.getIndustryOverview().getInventoryTotal());
        assertEquals(3200, vo.getIndustryOverview().getAnnualOutbound());
    }

    @Test
    @DisplayName("DP-SY-UT-032 畜群存栏结构取所选年最大填报月牦牛奶牛羊")
    void ut032_herdInventoryStructureFromYearMaxMonth()
    {
        stubGrasslandAndReport();
        stubOtherBlocks();
        when(homeScreenStatsMapper.selectLatestHerdInventoryStatPeriodByYear(2024)).thenReturn(period(2024, 9));
        HomeScreenHerdSpeciesSumDto species = new HomeScreenHerdSpeciesSumDto();
        species.setYakTotal(500);
        species.setDairyTotal(300);
        species.setSheepTotal(200);
        when(homeScreenStatsMapper.sumHerdInventorySpeciesByStatYearMonth(2024, 9)).thenReturn(species);

        HomeScreenDashboardVo vo = service.loadDashboard("2024");

        assertEquals(3, vo.getHerdInventoryStructure().size());
        assertEquals("YAK", vo.getHerdInventoryStructure().get(0).getSpeciesCode());
        assertEquals(500, vo.getHerdInventoryStructure().get(0).getCount());
        assertEquals(300, vo.getHerdInventoryStructure().get(1).getCount());
        assertEquals(200, vo.getHerdInventoryStructure().get(2).getCount());
    }

    @Test
    @DisplayName("DP-SY-UT-033 牦牛存栏结构取所选年最大填报月明细")
    void ut033_yakInventoryStructureFromYearMaxMonth()
    {
        stubGrasslandAndReport();
        stubOtherBlocks();
        when(homeScreenStatsMapper.selectLatestHerdInventoryStatPeriodByYear(2024)).thenReturn(period(2024, 9));
        HomeScreenHerdYakStructureSumDto structure = new HomeScreenHerdYakStructureSumDto();
        structure.setBreedingBullCount(11);
        structure.setBullCastratedCount(22);
        structure.setCowYoungCount(33);
        structure.setCowAdultCount(44);
        structure.setInsuredYakCount(5);
        when(homeScreenStatsMapper.sumHerdInventoryYakStructureByStatYearMonth(2024, 9)).thenReturn(structure);

        HomeScreenDashboardVo vo = service.loadDashboard("2024");

        assertEquals(3, vo.getYakInventoryStructure().size());
        assertEquals(33, vo.getYakInventoryStructure().get(0).getCount());
        assertEquals(77, vo.getYakInventoryStructure().get(1).getCount());
        assertEquals(5, vo.getYakInventoryStructure().get(2).getCount());
        assertEquals(11, vo.getYakInventoryStructure().get(0).getDetails().get(0).getCount());
        assertEquals("保险", vo.getYakInventoryStructure().get(2).getDetails().get(0).getDetailLabel());
    }

    @Test
    @DisplayName("DP-SY-UT-034 牲畜出栏与自食取所选年最大填报月")
    void ut034_livestockOutboundAndSelfConsumption()
    {
        stubGrasslandAndReport();
        stubOtherBlocks();
        when(homeScreenStatsMapper.selectLatestOutboundReportStatPeriodByYear(2024)).thenReturn(period(2024, 11));
        HomeScreenOutboundSpeciesSumDto species = new HomeScreenOutboundSpeciesSumDto();
        species.setYakOutboundCount(100);
        species.setCattleOutboundCount(50);
        species.setSheepOutboundCount(30);
        species.setGoatOutboundCount(10);
        species.setHorseMuleOutboundCount(5);
        species.setDonkeyOutboundCount(5);
        when(homeScreenStatsMapper.sumOutboundReportSpeciesByStatYearMonth(2024, 11)).thenReturn(species);
        HomeScreenSelfConsumptionSumDto self = new HomeScreenSelfConsumptionSumDto();
        self.setCattleCount(8);
        self.setSheepCount(16);
        self.setGoatCount(4);
        when(homeScreenStatsMapper.sumOutboundReportSelfConsumptionByStatYearMonth(2024, 11)).thenReturn(self);

        HomeScreenDashboardVo vo = service.loadDashboard("2024");

        assertEquals(6, vo.getLivestockOutboundStructure().size());
        assertEquals(100, vo.getLivestockOutboundStructure().get(0).getCount());
        assertEquals("匹", vo.getLivestockOutboundStructure().get(4).getUnit());
        assertEquals(2024, vo.getSelfConsumption().getStatYear());
        assertEquals(11, vo.getSelfConsumption().getStatMonth());
        assertEquals(8, vo.getSelfConsumption().getItems().get(0).getCount());
        assertEquals(16, vo.getSelfConsumption().getItems().get(1).getCount());
        assertEquals(4, vo.getSelfConsumption().getItems().get(2).getCount());
    }

    @Test
    @DisplayName("DP-SY-UT-035 家庭牧场与养殖大户财务取所选年最大填报月")
    void ut035_enterpriseFinanceFromYearMaxMonth()
    {
        stubGrasslandAndReport();
        stubOtherBlocks();
        when(homeScreenStatsMapper.selectLatestFamilyRanchStatPeriodByYear(2024)).thenReturn(period(2024, 8));
        when(homeScreenStatsMapper.selectLatestLargeLivestockFarmerStatPeriodByYear(2024)).thenReturn(period(2024, 10));
        HomeScreenEnterpriseFinanceSumDto ranch = new HomeScreenEnterpriseFinanceSumDto();
        ranch.setIncomeSum(new BigDecimal("100"));
        ranch.setExpenseSum(new BigDecimal("40"));
        ranch.setProfitSum(new BigDecimal("60"));
        ranch.setSubsidyAmountSum(new BigDecimal("10"));
        when(homeScreenStatsMapper.sumFamilyRanchFinanceByStatYearMonth(2024, 8)).thenReturn(ranch);
        HomeScreenEnterpriseFinanceSumDto farmer = new HomeScreenEnterpriseFinanceSumDto();
        farmer.setIncomeSum(new BigDecimal("200"));
        farmer.setExpenseSum(new BigDecimal("80"));
        farmer.setProfitSum(new BigDecimal("120"));
        farmer.setSubsidyAmountSum(new BigDecimal("20"));
        when(homeScreenStatsMapper.sumLargeLivestockFarmerFinanceByStatYearMonth(2024, 10)).thenReturn(farmer);

        HomeScreenDashboardVo vo = service.loadDashboard("2024");

        assertEquals(2024, vo.getFamilyRanchFinance().getStatYear());
        assertEquals(8, vo.getFamilyRanchFinance().getStatMonth());
        assertEquals(0, new BigDecimal("100.00").compareTo(vo.getFamilyRanchFinance().getItems().get(0).getAmountWan()));
        assertEquals(2024, vo.getLargeLivestockFarmerFinance().getStatYear());
        assertEquals(10, vo.getLargeLivestockFarmerFinance().getStatMonth());
        assertEquals(0, new BigDecimal("20.00").compareTo(
                vo.getLargeLivestockFarmerFinance().getItems().get(3).getAmountWan()));
    }

    @Test
    @DisplayName("DP-SY-UT-036 合作社经营模式与乡镇补助合计取所选年最大填报月")
    void ut036_coopModelAndTownSubsidy()
    {
        stubGrasslandAndReport();
        stubOtherBlocks();
        when(homeScreenStatsMapper.selectLatestCooperativeDevelopmentStatPeriodByYear(2024))
                .thenReturn(period(2024, 7));
        when(homeScreenStatsMapper.selectLatestFarmerHouseholdStatPeriodByYear(2024))
                .thenReturn(period(2024, 5));
        HomeScreenCoopBusinessModelSumDto coop = new HomeScreenCoopBusinessModelSumDto();
        coop.setWholeVillageCount(3);
        coop.setShareCount(2);
        coop.setJointCount(1);
        coop.setMultiBusinessCount(0);
        coop.setCompanyTypeCount(0);
        coop.setOtherCount(1);
        when(homeScreenStatsMapper.countCooperativeBusinessModelByStatYearMonth(2024, 7)).thenReturn(coop);
        HomeScreenTownSubsidyItemVo town = new HomeScreenTownSubsidyItemVo();
        town.setTownDeptId(101L);
        town.setTownName("巴青镇");
        town.setSubsidyTotal(new BigDecimal("12345.67"));
        when(homeScreenStatsMapper.sumFarmerHouseholdSubsidyTotalByTownStatYearMonth(2024, 5))
                .thenReturn(Collections.singletonList(town));

        HomeScreenDashboardVo vo = service.loadDashboard("2024");

        assertEquals(6, vo.getCooperativeBusinessModel().size());
        assertEquals(3, vo.getCooperativeBusinessModel().get(0).getCount());
        assertEquals(1, vo.getTownSubsidyTotals().size());
        assertEquals(101L, vo.getTownSubsidyTotals().get(0).getTownDeptId().longValue());
        assertEquals("巴青镇", vo.getTownSubsidyTotals().get(0).getTownName());
        assertEquals(0, new BigDecimal("12345.67").compareTo(vo.getTownSubsidyTotals().get(0).getSubsidyTotal()));
    }

    @Test
    @DisplayName("DP-SY-UT-037 人均收入取库中最大所属年向前共5年")
    void ut037_perCapitaIncomeTrend()
    {
        stubGrasslandAndReport();
        stubOtherBlocks();
        int endYear = 2024;
        int startYear = endYear - 4;
        when(homeScreenStatsMapper.selectLatestDisposableIncomeStatYear()).thenReturn(endYear);
        HomeScreenDisposableIncomeYearDto row = new HomeScreenDisposableIncomeYearDto();
        row.setStatYear(endYear);
        row.setRuralDisposableIncome(new BigDecimal("10000"));
        row.setPovertyNetIncome(new BigDecimal("9000"));
        when(homeScreenStatsMapper.selectDisposableIncomeByYearRange(startYear, endYear))
                .thenReturn(Collections.singletonList(row));

        HomeScreenDashboardVo vo = service.loadDashboard("2026");

        assertEquals(5, vo.getPerCapitaIncomeTrend().size());
        assertEquals(endYear, vo.getPerCapitaIncomeTrend().get(4).getStatYear());
        assertEquals(startYear, vo.getPerCapitaIncomeTrend().get(0).getStatYear());
        assertEquals(0, new BigDecimal("10000.00").compareTo(
                vo.getPerCapitaIncomeTrend().get(4).getRuralDisposableIncome()));
    }

    @Test
    @DisplayName("DP-SY-UT-037b 人均收入库中无数据则趋势为空")
    void ut037b_perCapitaIncomeTrendEmpty()
    {
        stubGrasslandAndReport();
        stubOtherBlocks();
        when(homeScreenStatsMapper.selectLatestDisposableIncomeStatYear()).thenReturn(null);

        HomeScreenDashboardVo vo = service.loadDashboard("2026");

        assertEquals(0, vo.getPerCapitaIncomeTrend().size());
    }

    @Test
    @DisplayName("DP-SY-UT-028 产业总览农牧户取所选年最大填报月")
    void ut028_farmerHouseholdFromReportTable()
    {
        stubGrasslandAndReport();
        stubOtherBlocks();
        when(homeScreenStatsMapper.selectLatestFarmerHouseholdStatPeriodByYear(2025)).thenReturn(period(2025, 8));
        when(homeScreenStatsMapper.sumFarmerHouseholdCountByStatYearMonth(2025, 8)).thenReturn(8988);

        HomeScreenDashboardVo vo = service.loadDashboard("2025");

        assertEquals(8988, vo.getIndustryOverview().getPastureCount());
    }

    @Test
    @DisplayName("DP-SY-UT-029 产业总览合作社/家庭牧场/养殖大户取所选年最大填报月")
    void ut029_entityCountsFromReportTables()
    {
        stubGrasslandAndReport();
        stubOtherBlocks();
        when(homeScreenStatsMapper.selectLatestCooperativeDevelopmentStatPeriodByYear(2025))
                .thenReturn(period(2025, 6));
        when(homeScreenStatsMapper.selectLatestFamilyRanchStatPeriodByYear(2025)).thenReturn(period(2025, 7));
        when(homeScreenStatsMapper.selectLatestLargeLivestockFarmerStatPeriodByYear(2025))
                .thenReturn(period(2025, 8));
        when(homeScreenStatsMapper.countCooperativeDevelopmentByStatYearMonth(2025, 6)).thenReturn(12);
        when(homeScreenStatsMapper.countFamilyRanchByStatYearMonth(2025, 7)).thenReturn(34);
        when(homeScreenStatsMapper.countLargeLivestockFarmerByStatYearMonth(2025, 8)).thenReturn(56);

        HomeScreenDashboardVo vo = service.loadDashboard("2025");

        assertEquals(12, vo.getIndustryOverview().getCooperativeCount());
        assertEquals(34, vo.getIndustryOverview().getFamilyRanchCount());
        assertEquals(56, vo.getIndustryOverview().getLargeLivestockFarmerCount());
    }

    @Test
    @DisplayName("DP-SY-UT-030 产业总览有畜户数与草场面积取所选年最大填报月")
    void ut030_ownershipAndPastureArea()
    {
        stubGrasslandAndReport();
        stubOtherBlocks();
        when(homeScreenStatsMapper.selectLatestLivestockOwnershipHouseholdStatPeriodByYear(2025))
                .thenReturn(period(2025, 4));
        when(homeScreenStatsMapper.sumWithLivestockHouseholdCountByStatYearMonth(2025, 4)).thenReturn(1200);
        when(homeScreenStatsMapper.selectLatestFarmerHouseholdStatPeriodByYear(2025)).thenReturn(period(2025, 9));
        when(homeScreenStatsMapper.sumFarmerHouseholdPastureContractAreaByStatYearMonth(2025, 9))
                .thenReturn(new BigDecimal("12345.678"));

        HomeScreenDashboardVo vo = service.loadDashboard("2025");

        assertEquals(1200, vo.getIndustryOverview().getWithLivestockHouseholdCount());
        assertEquals(new BigDecimal("12345.68"), vo.getIndustryOverview().getGrasslandAreaMu());
    }

    private static HomeScreenStatPeriodDto period(int year, int month)
    {
        HomeScreenStatPeriodDto dto = new HomeScreenStatPeriodDto();
        dto.setStatYear(year);
        dto.setStatMonth(month);
        return dto;
    }

    private void stubGrasslandAndReport()
    {
        lenient().when(homeScreenStatsMapper.selectLatestHerdInventoryStatPeriod()).thenReturn(null);
        lenient().when(homeScreenStatsMapper.selectLatestOutboundReportStatPeriod()).thenReturn(null);
        lenient().when(homeScreenStatsMapper.selectLatestHerdInventoryStatPeriodByYear(any(Integer.class))).thenReturn(null);
        lenient().when(homeScreenStatsMapper.selectLatestOutboundReportStatPeriodByYear(any(Integer.class))).thenReturn(null);
        lenient().when(homeScreenStatsMapper.selectLatestFarmerHouseholdStatPeriodByYear(any(Integer.class))).thenReturn(null);
        lenient().when(homeScreenStatsMapper.selectLatestCooperativeDevelopmentStatPeriodByYear(any(Integer.class)))
                .thenReturn(null);
        lenient().when(homeScreenStatsMapper.selectLatestFamilyRanchStatPeriodByYear(any(Integer.class))).thenReturn(null);
        lenient().when(homeScreenStatsMapper.selectLatestLargeLivestockFarmerStatPeriodByYear(any(Integer.class)))
                .thenReturn(null);
        lenient().when(homeScreenStatsMapper.selectLatestLivestockOwnershipHouseholdStatPeriodByYear(any(Integer.class)))
                .thenReturn(null);
        lenient().when(homeScreenStatsMapper.selectDisposableIncomeByYearRange(any(Integer.class), any(Integer.class)))
                .thenReturn(Collections.emptyList());
        lenient().when(homeScreenStatsMapper.selectLatestDisposableIncomeStatYear()).thenReturn(null);
    }

    private void stubOtherBlocks()
    {
        when(homeScreenStatsMapper.selectDistinctOutboundYears()).thenReturn(Collections.emptyList());
        when(homeScreenStatsMapper.selectDistinctAppointmentYears()).thenReturn(Collections.emptyList());
        when(homeScreenStatsMapper.selectDistinctSubsidyYears()).thenReturn(Collections.emptyList());
        when(homeScreenStatsMapper.selectDistinctStandardCreateYears()).thenReturn(Collections.emptyList());
        when(homeScreenStatsMapper.selectDistinctTechPublishYears()).thenReturn(Collections.emptyList());
        when(homeScreenStatsMapper.selectDistinctTrainingYears()).thenReturn(Collections.emptyList());
    }
}
