package com.ruoyi.web.modules.screen.support;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.YearMonth;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import com.ruoyi.web.modules.industryservice.domain.dto.BreedingIndustryYakSnapshot;
import com.ruoyi.web.modules.industryservice.support.BreedingIndustryRules;
import com.ruoyi.web.modules.screen.domain.dto.HomeScreenAppointmentStatDto;
import com.ruoyi.web.modules.screen.domain.dto.HomeScreenMonthCountDto;
import com.ruoyi.web.modules.screen.domain.dto.HomeScreenTrainingMonthDto;
import com.ruoyi.web.modules.screen.domain.vo.HomeScreenAgriClassroomVo;
import com.ruoyi.web.modules.screen.domain.vo.HomeScreenAppointmentVo;
import com.ruoyi.web.modules.screen.domain.vo.HomeScreenAgeBandVo;
import com.ruoyi.web.modules.screen.domain.vo.HomeScreenInventoryMonthVo;
import com.ruoyi.web.modules.screen.domain.vo.HomeScreenOutboundMonthVo;
import com.ruoyi.web.modules.screen.domain.vo.HomeScreenCooperativeDevelopmentListItemVo;
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.HomeScreenSubsidyVo;
import com.ruoyi.web.modules.screen.domain.vo.HomeScreenTrainingTrendVo;
import com.ruoyi.web.modules.screen.domain.vo.HomeScreenYakHerdInventoryListItemVo;
import com.ruoyi.web.modules.screen.domain.vo.HomeScreenYakOutboundReportListItemVo;

/**
 * DP-SY-UT-003～018
 */
@DisplayName("HomeScreenSupport")
class HomeScreenSupportTest
{

    @Test
    @DisplayName("DP-SY-UT-004 历史年 statDate 为 12-31")
    void ut004_historicalStatDate()
    {
        assertEquals(LocalDate.of(2024, 12, 31), HomeScreenSupport.resolveStatDate(2024));
    }

    @Test
    @DisplayName("DP-SY-UT-005c 最近12个月存栏趋势")
    void ut005c_rolling12MonthBullCow()
    {
        YearMonth anchor = YearMonth.of(2026, 7);
        com.ruoyi.web.modules.screen.domain.dto.HomeScreenYearMonthBullCowDto row =
                new com.ruoyi.web.modules.screen.domain.dto.HomeScreenYearMonthBullCowDto();
        row.setStatYear(2026);
        row.setStatMonth(7);
        row.setBullCount(100);
        row.setCowCount(200);
        List<HomeScreenInventoryMonthVo> trend = HomeScreenSupport.buildRolling12MonthBullCowTrend(
                Collections.singletonList(row), anchor);
        assertEquals(12, trend.size());
        assertEquals(2026, trend.get(11).getStatYear());
        assertEquals(7, trend.get(11).getMonth());
        assertEquals(100, trend.get(11).getBullCount());
        assertEquals(200, trend.get(11).getCowCount());
        assertEquals(2025, trend.get(0).getStatYear());
        assertEquals(8, trend.get(0).getMonth());
        assertEquals(0, trend.get(0).getBullCount());
    }

    @Test
    @DisplayName("DP-SY-UT-007b 最近12个月出栏")
    void ut007b_rolling12MonthOutbound()
    {
        YearMonth anchor = YearMonth.of(2026, 7);
        com.ruoyi.web.modules.screen.domain.dto.HomeScreenYearMonthCountDto row =
                new com.ruoyi.web.modules.screen.domain.dto.HomeScreenYearMonthCountDto();
        row.setStatYear(2026);
        row.setStatMonth(5);
        row.setCount(88);
        List<HomeScreenOutboundMonthVo> list = HomeScreenSupport.buildRolling12MonthOutbound(
                Collections.singletonList(row), anchor);
        assertEquals(12, list.size());
        assertEquals(2026, list.get(9).getStatYear());
        assertEquals(5, list.get(9).getMonth());
        assertEquals(88, list.get(9).getOutboundCount());
    }

    @Test
    @DisplayName("DP-SY-UT-005b 存栏填报公母牛趋势按最大月输出 1～N")
    void ut005b_herdReportBullCowMonthlyToMaxMonth()
    {
        com.ruoyi.web.modules.screen.domain.dto.HomeScreenHerdGenderMonthDto m5 =
                new com.ruoyi.web.modules.screen.domain.dto.HomeScreenHerdGenderMonthDto();
        m5.setMonth(5);
        m5.setBullCount(10);
        m5.setCowCount(20);
        com.ruoyi.web.modules.screen.domain.dto.HomeScreenHerdGenderMonthDto m3 =
                new com.ruoyi.web.modules.screen.domain.dto.HomeScreenHerdGenderMonthDto();
        m3.setMonth(3);
        m3.setBullCount(7);
        m3.setCowCount(8);
        List<HomeScreenInventoryMonthVo> trend = HomeScreenSupport.buildBullCowMonthlyTrendFromHerdReport(
                Arrays.asList(m5, m3), LocalDate.of(2024, 12, 31));
        assertEquals(5, trend.size());
        assertEquals(1, trend.get(0).getMonth());
        assertEquals(0, trend.get(0).getBullCount());
        assertEquals(3, trend.get(2).getMonth());
        assertEquals(7, trend.get(2).getBullCount());
        assertEquals(5, trend.get(4).getMonth());
        assertEquals(10, trend.get(4).getBullCount());
        assertEquals(2024, trend.get(4).getStatYear());
    }

    @Test
    @DisplayName("DP-SY-UT-005d 存栏填报公母牛趋势无数据返回空列表")
    void ut005d_herdReportBullCowMonthlyEmpty()
    {
        assertEquals(0, HomeScreenSupport.buildBullCowMonthlyTrendFromHerdReport(
                Collections.emptyList(), LocalDate.of(2024, 12, 31)).size());
        assertEquals(0, HomeScreenSupport.buildBullCowMonthlyTrendFromHerdReport(
                null, LocalDate.of(2024, 12, 31)).size());
    }

    @Test
    @DisplayName("DP-SY-UT-006 母牛分龄两档")
    void ut006_cowAgeStructure()
    {
        com.ruoyi.web.modules.screen.domain.dto.HomeScreenHerdCowAgeSumDto sums =
                new com.ruoyi.web.modules.screen.domain.dto.HomeScreenHerdCowAgeSumDto();
        sums.setCowYoungCount(40);
        sums.setCowAdultCount(60);
        List<HomeScreenAgeBandVo> bands = HomeScreenSupport.buildCowAgeStructureFromHerdReport(sums);
        assertEquals(2, bands.size());
        assertEquals(100, bands.stream().mapToInt(b -> b.getCount()).sum());
        assertEquals(HomeScreenSupport.BAND_COW_YOUNG, bands.get(0).getBandCode());
        assertEquals(HomeScreenSupport.BAND_COW_ADULT, bands.get(1).getBandCode());
    }

    @Test
    @DisplayName("DP-SY-UT-006b 畜群存栏结构牦牛奶牛羊三档")
    void ut006b_herdInventoryStructure()
    {
        com.ruoyi.web.modules.screen.domain.dto.HomeScreenHerdSpeciesSumDto sums =
                new com.ruoyi.web.modules.screen.domain.dto.HomeScreenHerdSpeciesSumDto();
        sums.setYakTotal(50);
        sums.setDairyTotal(30);
        sums.setSheepTotal(20);
        List<com.ruoyi.web.modules.screen.domain.vo.HomeScreenHerdSpeciesItemVo> items =
                HomeScreenSupport.buildHerdInventoryStructure(sums);
        assertEquals(3, items.size());
        assertEquals(HomeScreenSupport.SPECIES_YAK, items.get(0).getSpeciesCode());
        assertEquals("牦牛", items.get(0).getSpeciesLabel());
        assertEquals(50, items.get(0).getCount());
        assertEquals(0, new BigDecimal("50.0").compareTo(items.get(0).getRatio()));
        assertEquals(HomeScreenSupport.SPECIES_DAIRY, items.get(1).getSpeciesCode());
        assertEquals(30, items.get(1).getCount());
        assertEquals(HomeScreenSupport.SPECIES_SHEEP, items.get(2).getSpeciesCode());
        assertEquals(20, items.get(2).getCount());
    }

    @Test
    @DisplayName("DP-SY-UT-006c 牦牛存栏结构公牛母牛保险及细分")
    void ut006c_yakInventoryStructure()
    {
        com.ruoyi.web.modules.screen.domain.dto.HomeScreenHerdYakStructureSumDto sums =
                new com.ruoyi.web.modules.screen.domain.dto.HomeScreenHerdYakStructureSumDto();
        sums.setBreedingBullCount(10);
        sums.setBullCastratedCount(20);
        sums.setCowYoungCount(30);
        sums.setCowAdultCount(40);
        sums.setInsuredYakCount(0);
        List<com.ruoyi.web.modules.screen.domain.vo.HomeScreenYakStructureCategoryVo> cats =
                HomeScreenSupport.buildYakInventoryStructure(sums);
        assertEquals(3, cats.size());
        assertEquals(HomeScreenSupport.YAK_CAT_BULL, cats.get(0).getCategoryCode());
        assertEquals(30, cats.get(0).getCount());
        assertEquals(2, cats.get(0).getDetails().size());
        assertEquals("种公牛", cats.get(0).getDetails().get(0).getDetailLabel());
        assertEquals(10, cats.get(0).getDetails().get(0).getCount());
        assertEquals(HomeScreenSupport.YAK_CAT_COW, cats.get(1).getCategoryCode());
        assertEquals(70, cats.get(1).getCount());
        assertEquals("0～1.5岁", cats.get(1).getDetails().get(0).getDetailLabel());
        assertEquals(HomeScreenSupport.YAK_CAT_INSURED, cats.get(2).getCategoryCode());
        assertEquals(0, cats.get(2).getCount());
    }

    @Test
    @DisplayName("DP-SY-UT-006d 牲畜出栏结构六畜及单位")
    void ut006d_livestockOutboundStructure()
    {
        com.ruoyi.web.modules.screen.domain.dto.HomeScreenOutboundSpeciesSumDto sums =
                new com.ruoyi.web.modules.screen.domain.dto.HomeScreenOutboundSpeciesSumDto();
        sums.setYakOutboundCount(40);
        sums.setCattleOutboundCount(20);
        sums.setSheepOutboundCount(20);
        sums.setGoatOutboundCount(10);
        sums.setHorseMuleOutboundCount(5);
        sums.setDonkeyOutboundCount(5);
        List<com.ruoyi.web.modules.screen.domain.vo.HomeScreenOutboundSpeciesItemVo> items =
                HomeScreenSupport.buildLivestockOutboundStructure(sums);
        assertEquals(6, items.size());
        assertEquals(HomeScreenSupport.OUTBOUND_YAK, items.get(0).getSpeciesCode());
        assertEquals(HomeScreenSupport.UNIT_HEAD, items.get(0).getUnit());
        assertEquals(HomeScreenSupport.UNIT_ONLY, items.get(2).getUnit());
        assertEquals(HomeScreenSupport.UNIT_PI, items.get(4).getUnit());
        assertEquals(40, items.get(0).getCount());
        assertEquals(0, new BigDecimal("40.0").compareTo(items.get(0).getRatio()));
    }

    @Test
    @DisplayName("DP-SY-UT-006e 自食情况含年月与三项")
    void ut006e_selfConsumption()
    {
        com.ruoyi.web.modules.screen.domain.dto.HomeScreenStatPeriodDto period =
                new com.ruoyi.web.modules.screen.domain.dto.HomeScreenStatPeriodDto();
        period.setStatYear(2024);
        period.setStatMonth(9);
        com.ruoyi.web.modules.screen.domain.dto.HomeScreenSelfConsumptionSumDto sums =
                new com.ruoyi.web.modules.screen.domain.dto.HomeScreenSelfConsumptionSumDto();
        sums.setCattleCount(12);
        sums.setSheepCount(34);
        sums.setGoatCount(56);
        com.ruoyi.web.modules.screen.domain.vo.HomeScreenSelfConsumptionVo vo =
                HomeScreenSupport.buildSelfConsumption(period, sums);
        assertEquals(2024, vo.getStatYear());
        assertEquals(9, vo.getStatMonth());
        assertEquals(3, vo.getItems().size());
        assertEquals("牛", vo.getItems().get(0).getItemLabel());
        assertEquals(12, vo.getItems().get(0).getCount());
        assertEquals(34, vo.getItems().get(1).getCount());
        assertEquals(56, vo.getItems().get(2).getCount());
    }

    @Test
    @DisplayName("DP-SY-UT-006f 企业年度收入与补助四项万元")
    void ut006f_enterpriseFinance()
    {
        com.ruoyi.web.modules.screen.domain.dto.HomeScreenStatPeriodDto period =
                new com.ruoyi.web.modules.screen.domain.dto.HomeScreenStatPeriodDto();
        period.setStatYear(2025);
        period.setStatMonth(6);
        com.ruoyi.web.modules.screen.domain.dto.HomeScreenEnterpriseFinanceSumDto sums =
                new com.ruoyi.web.modules.screen.domain.dto.HomeScreenEnterpriseFinanceSumDto();
        sums.setIncomeSum(new BigDecimal("12.345"));
        sums.setExpenseSum(new BigDecimal("5.1"));
        sums.setProfitSum(new BigDecimal("7.245"));
        sums.setSubsidyAmountSum(new BigDecimal("1"));
        com.ruoyi.web.modules.screen.domain.vo.HomeScreenEnterpriseFinanceVo vo =
                HomeScreenSupport.buildEnterpriseFinance(period, sums);
        assertEquals(2025, vo.getStatYear());
        assertEquals(6, vo.getStatMonth());
        assertEquals(4, vo.getItems().size());
        assertEquals("收入", vo.getItems().get(0).getItemLabel());
        assertEquals(0, new BigDecimal("12.35").compareTo(vo.getItems().get(0).getAmountWan()));
        assertEquals("补助资金", vo.getItems().get(3).getItemLabel());
        assertEquals(0, new BigDecimal("1.00").compareTo(vo.getItems().get(3).getAmountWan()));
    }

    @Test
    @DisplayName("DP-SY-UT-006g 合作社经营模式六类")
    void ut006g_cooperativeBusinessModel()
    {
        com.ruoyi.web.modules.screen.domain.dto.HomeScreenCoopBusinessModelSumDto sums =
                new com.ruoyi.web.modules.screen.domain.dto.HomeScreenCoopBusinessModelSumDto();
        sums.setWholeVillageCount(10);
        sums.setShareCount(20);
        sums.setJointCount(30);
        sums.setMultiBusinessCount(15);
        sums.setCompanyTypeCount(15);
        sums.setOtherCount(10);
        List<com.ruoyi.web.modules.screen.domain.vo.HomeScreenNamedCountItemVo> items =
                HomeScreenSupport.buildCooperativeBusinessModel(sums);
        assertEquals(6, items.size());
        assertEquals("整村合作社", items.get(0).getItemLabel());
        assertEquals(10, items.get(0).getCount());
        assertEquals("其他", items.get(5).getItemLabel());
        assertEquals(0, new BigDecimal("10.0").compareTo(items.get(0).getRatio()));
    }

    @Test
    @DisplayName("DP-SY-UT-006h 农牧户分类六项")
    void ut006h_farmerHouseholdClassification()
    {
        com.ruoyi.web.modules.screen.domain.dto.HomeScreenFarmerHouseholdClassSumDto sums =
                new com.ruoyi.web.modules.screen.domain.dto.HomeScreenFarmerHouseholdClassSumDto();
        sums.setPureHerderCount(1);
        sums.setNoLivestockCount(2);
        sums.setExtinctHouseholdCount(3);
        sums.setOverloadHerderCount(4);
        sums.setNeedCapCount(5);
        sums.setNeedFloorCount(6);
        List<com.ruoyi.web.modules.screen.domain.vo.HomeScreenNamedCountItemVo> items =
                HomeScreenSupport.buildFarmerHouseholdClassification(sums);
        assertEquals(6, items.size());
        assertEquals("纯牧户", items.get(0).getItemLabel());
        assertEquals(1, items.get(0).getCount());
        assertEquals("需要保底", items.get(5).getItemLabel());
        assertEquals(6, items.get(5).getCount());
    }

    @Test
    @DisplayName("DP-SY-UT-006j 草畜失衡预警明细主体类型与是否平衡")
    void ut006j_imbalanceWarningListItem()
    {
        com.ruoyi.web.modules.industryservice.domain.BizGrassLivestockImbalanceWarning row =
                new com.ruoyi.web.modules.industryservice.domain.BizGrassLivestockImbalanceWarning();
        row.setStatYear(2026);
        row.setStatMonth(3);
        row.setTownName("巴青镇");
        row.setVillageName("某村");
        row.setSubjectName("某某牧场");
        row.setDataSource(com.ruoyi.web.modules.industryservice.support.GrassLivestockImbalanceWarningRules.DATA_SOURCE_FAMILY_RANCH);
        row.setApprovedCarryingCapacity(new BigDecimal("100"));
        row.setYearEndStockSheepUnit(new BigDecimal("120"));
        com.ruoyi.web.modules.screen.domain.vo.HomeScreenImbalanceWarningListItemVo vo =
                HomeScreenSupport.toImbalanceWarningListItem(row);
        assertEquals(2026, vo.getStatYear().intValue());
        assertEquals("家庭牧场", vo.getSubjectType());
        assertEquals("否", vo.getIsGrassBalanced());
        row.setApprovedCarryingCapacity(new BigDecimal("200"));
        assertEquals("是", HomeScreenSupport.toImbalanceWarningListItem(row).getIsGrassBalanced());
    }

    @Test
    @DisplayName("DP-SY-UT-031c 人均收入明细行字段映射")
    void ut031c_toPerCapitaIncomeListItem()
    {
        com.ruoyi.web.modules.industryservice.domain.BizDisposableIncome row =
                new com.ruoyi.web.modules.industryservice.domain.BizDisposableIncome();
        row.setStatYear(2024);
        row.setRuralDisposableIncome(new BigDecimal("20000.5"));
        row.setIncomeIncrease(new BigDecimal("1000"));
        row.setIncomeGrowthRate(new BigDecimal("5.26"));
        row.setPovertyNetIncome(new BigDecimal("9000"));
        HomeScreenPerCapitaIncomeListItemVo vo = HomeScreenSupport.toPerCapitaIncomeListItem(row);
        assertEquals(2024, vo.getStatYear().intValue());
        assertEquals(0, new BigDecimal("20000.5").compareTo(vo.getRuralDisposableIncome()));
        assertEquals(0, new BigDecimal("1000").compareTo(vo.getIncomeIncrease()));
        assertEquals(0, new BigDecimal("5.26").compareTo(vo.getIncomeGrowthRate()));
        assertEquals(0, new BigDecimal("9000").compareTo(vo.getPovertyNetIncome()));
    }

    @Test
    @DisplayName("DP-SY-UT-032c 农牧户明细行字段映射")
    void ut032c_toFarmerHouseholdListItem()
    {
        com.ruoyi.web.modules.industryservice.domain.BizFarmerHousehold row =
                new com.ruoyi.web.modules.industryservice.domain.BizFarmerHousehold();
        row.setId(9L);
        row.setStatYear(2026);
        row.setStatMonth(7);
        row.setTownName("巴青镇");
        row.setVillageName("某村");
        row.setHouseholdCount(12);
        row.setPopulationCount(40);
        row.setPastureContractArea(new BigDecimal("88.8"));
        row.setIsPureHerder("是");
        HomeScreenFarmerHouseholdListItemVo vo = HomeScreenSupport.toFarmerHouseholdListItem(row);
        assertEquals(9L, vo.getId().longValue());
        assertEquals(2026, vo.getStatYear().intValue());
        assertEquals(7, vo.getStatMonth().intValue());
        assertEquals("巴青镇", vo.getTownName());
        assertEquals(12, vo.getHouseholdCount().intValue());
        assertEquals("是", vo.getIsPureHerder());
    }

    @Test
    @DisplayName("DP-SY-UT-033c 合作社明细行字段映射")
    void ut033c_toCooperativeDevelopmentListItem()
    {
        com.ruoyi.web.modules.industryservice.domain.BizCooperativeDevelopment row =
                new com.ruoyi.web.modules.industryservice.domain.BizCooperativeDevelopment();
        row.setId(11L);
        row.setStatYear(2026);
        row.setStatMonth(2);
        row.setCooperativeName("示范合作社");
        row.setBusinessModel("股份");
        HomeScreenCooperativeDevelopmentListItemVo vo =
                HomeScreenSupport.toCooperativeDevelopmentListItem(row);
        assertEquals(11L, vo.getId().longValue());
        assertEquals("示范合作社", vo.getCooperativeName());
        assertEquals("股份", vo.getBusinessModel());
    }

    @Test
    @DisplayName("DP-SY-UT-034c 家庭牧场明细行字段映射")
    void ut034c_toFamilyRanchListItem()
    {
        com.ruoyi.web.modules.industryservice.domain.BizFamilyRanch row =
                new com.ruoyi.web.modules.industryservice.domain.BizFamilyRanch();
        row.setId(12L);
        row.setStatYear(2026);
        row.setStatMonth(3);
        row.setEnterpriseName("某某牧场");
        row.setIsGrassBalanced("是");
        HomeScreenFamilyRanchListItemVo vo = HomeScreenSupport.toFamilyRanchListItem(row);
        assertEquals(12L, vo.getId().longValue());
        assertEquals("某某牧场", vo.getEnterpriseName());
        assertEquals("是", vo.getIsGrassBalanced());
    }

    @Test
    @DisplayName("DP-SY-UT-035c 养殖大户明细行字段映射")
    void ut035c_toLargeLivestockFarmerListItem()
    {
        com.ruoyi.web.modules.industryservice.domain.BizLargeLivestockFarmer row =
                new com.ruoyi.web.modules.industryservice.domain.BizLargeLivestockFarmer();
        row.setId(13L);
        row.setStatYear(2026);
        row.setStatMonth(4);
        row.setEnterpriseName("某某养殖大户");
        row.setHasSubsidy("否");
        HomeScreenLargeLivestockFarmerListItemVo vo =
                HomeScreenSupport.toLargeLivestockFarmerListItem(row);
        assertEquals(13L, vo.getId().longValue());
        assertEquals("某某养殖大户", vo.getEnterpriseName());
        assertEquals("否", vo.getHasSubsidy());
    }

    @Test
    @DisplayName("DP-SY-UT-036c 牦牛出栏明细行字段映射")
    void ut036c_toYakOutboundReportListItem()
    {
        com.ruoyi.web.modules.industryservice.domain.BizYakOutboundReport row =
                new com.ruoyi.web.modules.industryservice.domain.BizYakOutboundReport();
        row.setId(21L);
        row.setYakOutboundCount(88);
        row.setSelfConsumptionSheepCount(3);
        HomeScreenYakOutboundReportListItemVo vo = HomeScreenSupport.toYakOutboundReportListItem(row);
        assertEquals(21L, vo.getId().longValue());
        assertEquals(88, vo.getYakOutboundCount().intValue());
        assertEquals(3, vo.getSelfConsumptionSheepCount().intValue());
    }

    @Test
    @DisplayName("DP-SY-UT-037c 牦牛存栏明细行字段映射")
    void ut037c_toYakHerdInventoryListItem()
    {
        com.ruoyi.web.modules.industryservice.domain.BizYakHerdInventory row =
                new com.ruoyi.web.modules.industryservice.domain.BizYakHerdInventory();
        row.setId(22L);
        row.setYakTotal(1000);
        row.setCowTotal(600);
        HomeScreenYakHerdInventoryListItemVo vo = HomeScreenSupport.toYakHerdInventoryListItem(row);
        assertEquals(22L, vo.getId().longValue());
        assertEquals(1000, vo.getYakTotal().intValue());
        assertEquals(600, vo.getCowTotal().intValue());
    }

    @Test
    @DisplayName("DP-SY-UT-038c 有无畜户明细行字段映射")
    void ut038c_toLivestockOwnershipHouseholdListItem()
    {
        com.ruoyi.web.modules.industryservice.domain.BizLivestockOwnershipHousehold row =
                new com.ruoyi.web.modules.industryservice.domain.BizLivestockOwnershipHousehold();
        row.setId(23L);
        row.setWithLivestockHouseholdCount(50);
        row.setWithoutLivestockLaborCount(8);
        HomeScreenLivestockOwnershipHouseholdListItemVo vo =
                HomeScreenSupport.toLivestockOwnershipHouseholdListItem(row);
        assertEquals(23L, vo.getId().longValue());
        assertEquals(50, vo.getWithLivestockHouseholdCount().intValue());
        assertEquals(8, vo.getWithoutLivestockLaborCount().intValue());
    }

    @Test
    @DisplayName("DP-SY-UT-006i 人均收入最近5年缺年补零")
    void ut006i_perCapitaIncomeTrend()
    {
        com.ruoyi.web.modules.screen.domain.dto.HomeScreenDisposableIncomeYearDto row =
                new com.ruoyi.web.modules.screen.domain.dto.HomeScreenDisposableIncomeYearDto();
        row.setStatYear(2025);
        row.setRuralDisposableIncome(new BigDecimal("12345.6"));
        row.setPovertyNetIncome(new BigDecimal("8000"));
        List<com.ruoyi.web.modules.screen.domain.vo.HomeScreenPerCapitaIncomeYearVo> trend =
                HomeScreenSupport.buildPerCapitaIncomeTrend(Collections.singletonList(row), 2026);
        assertEquals(5, trend.size());
        assertEquals(2022, trend.get(0).getStatYear());
        assertEquals(0, new BigDecimal("0.00").compareTo(trend.get(0).getRuralDisposableIncome()));
        assertEquals(2025, trend.get(3).getStatYear());
        assertEquals(0, new BigDecimal("12345.60").compareTo(trend.get(3).getRuralDisposableIncome()));
        assertEquals(2026, trend.get(4).getStatYear());
    }

    @Test
    @DisplayName("DP-SY-UT-005 公母牛趋势 12 月补零")
    void ut005_bullCowMonthlyTwelve()
    {
        BreedingIndustryYakSnapshot bull = yak(1L, BreedingIndustryRules.ASSET_STATUS_NORMAL, "公", null);
        List<HomeScreenInventoryMonthVo> trend = HomeScreenSupport.buildBullCowMonthlyTrend(
                Collections.singletonList(bull), LocalDate.of(2024, 12, 31));
        assertEquals(12, trend.size());
        assertTrue(trend.get(11).getBullCount() >= 1);
    }

    @Test
    @DisplayName("DP-SY-UT-007 出栏按月按最大月输出 1～N")
    void ut007_outboundMonthly()
    {
        HomeScreenMonthCountDto m3 = monthCount(3, 1);
        HomeScreenMonthCountDto m8 = monthCount(8, 2);
        List<HomeScreenOutboundMonthVo> list = HomeScreenSupport.buildOutboundMonthly(
                Arrays.asList(m3, m8), LocalDate.of(2024, 12, 31));
        assertEquals(8, list.size());
        assertEquals(1, list.get(0).getMonth());
        assertEquals(0, list.get(0).getOutboundCount());
        assertEquals(1, list.get(2).getOutboundCount());
        assertEquals(2, list.get(7).getOutboundCount());
        assertEquals(2024, list.get(2).getStatYear());
    }

    @Test
    @DisplayName("DP-SY-UT-007c 出栏按月无数据返回空列表")
    void ut007c_outboundMonthlyEmpty()
    {
        assertEquals(0, HomeScreenSupport.buildOutboundMonthly(
                Collections.emptyList(), LocalDate.of(2024, 12, 31)).size());
        assertEquals(0, HomeScreenSupport.buildOutboundMonthly(
                null, LocalDate.of(2024, 12, 31)).size());
    }

    @Test
    @DisplayName("DP-SY-UT-011 预约排除拒绝取消")
    void ut011_appointmentAnnualMapping()
    {
        HomeScreenAppointmentStatDto vet = appt(null, 1, 2);
        HomeScreenAppointmentStatDto expert = appt(null, 3, 1);
        HomeScreenAppointmentVo vo = HomeScreenSupport.buildAppointment(
                Arrays.asList(vet, expert), Collections.emptyList(), YearMonth.of(2026, 7));
        assertEquals(2, vo.getAnnualVetCount());
        assertEquals(1, vo.getAnnualExpertCount());
        assertEquals(12, vo.getMonthlyStacked().size());
    }

    @Test
    @DisplayName("DP-SY-UT-011b 预约滚动 12 月堆叠")
    void ut011b_rolling12MonthAppointment()
    {
        YearMonth anchor = YearMonth.of(2026, 7);
        HomeScreenAppointmentStatDto row = new HomeScreenAppointmentStatDto();
        row.setStatYear(2026);
        row.setStatMonth(5);
        row.setProviderType(1);
        row.setCount(3);
        HomeScreenAppointmentVo vo = HomeScreenSupport.buildAppointment(
                Collections.emptyList(), Collections.singletonList(row), anchor);
        assertEquals(12, vo.getMonthlyStacked().size());
        assertEquals(2026, vo.getMonthlyStacked().get(9).getStatYear());
        assertEquals(5, vo.getMonthlyStacked().get(9).getMonth());
        assertEquals(3, vo.getMonthlyStacked().get(9).getVetCount());
    }

    @Test
    @DisplayName("线下服务预约：台账人数 + 所选年 1～最大月")
    void appointmentForStatYear()
    {
        HomeScreenAppointmentStatDto m3 = new HomeScreenAppointmentStatDto();
        m3.setMonth(3);
        m3.setProviderType(1);
        m3.setCount(2);
        HomeScreenAppointmentStatDto m5 = new HomeScreenAppointmentStatDto();
        m5.setMonth(5);
        m5.setProviderType(2);
        m5.setCount(1);
        HomeScreenAppointmentVo vo = HomeScreenSupport.buildAppointmentForStatYear(
                12, 8, 5, Arrays.asList(m3, m5), 2026);
        assertEquals(12, vo.getAnnualVetCount());
        assertEquals(8, vo.getAnnualExpertCount());
        assertEquals(5, vo.getAnnualOrgCount());
        assertEquals(5, vo.getMonthlyStacked().size());
        assertEquals(2026, vo.getMonthlyStacked().get(0).getStatYear());
        assertEquals(1, vo.getMonthlyStacked().get(0).getMonth());
        assertEquals(0, vo.getMonthlyStacked().get(0).getVetCount());
        assertEquals(2, vo.getMonthlyStacked().get(2).getVetCount());
        assertEquals(1, vo.getMonthlyStacked().get(4).getOrgCount());

        HomeScreenAppointmentVo empty = HomeScreenSupport.buildAppointmentForStatYear(
                0, 0, 0, Collections.emptyList(), 2026);
        assertEquals(0, empty.getMonthlyStacked().size());
    }

    @Test
    @DisplayName("DP-SY-UT-013 补贴万元换算")
    void ut013_subsidyWan()
    {
        assertEquals(new BigDecimal("1.00"), HomeScreenSupport.toAmountWan(new BigDecimal("10000")));
    }

    @Test
    @DisplayName("DP-SY-UT-014 无补贴 hasSubsidyData=false")
    void ut014_noSubsidy()
    {
        HomeScreenSubsidyVo vo = HomeScreenSupport.buildSubsidy(Collections.emptyList(), false);
        assertFalse(vo.isHasSubsidyData());
        assertEquals(3, vo.getTypeStats().size());
        assertEquals(new BigDecimal("0.00"), vo.getTypeStats().get(0).getAmountWan());
    }

    @Test
    @DisplayName("DP-SY-UT-015 农技课堂合计")
    void ut015_agriTotal()
    {
        HomeScreenAgriClassroomVo vo = HomeScreenSupport.buildAgriClassroom(2, 1);
        assertEquals(3, vo.getTrainingContentTotal());
        assertTrue(vo.isHasAgriData());
    }

    @Test
    @DisplayName("DP-SY-UT-016 农技双零")
    void ut016_agriEmpty()
    {
        HomeScreenAgriClassroomVo vo = HomeScreenSupport.buildAgriClassroom(0, 0);
        assertFalse(vo.isHasAgriData());
    }

    @Test
    @DisplayName("DP-SY-UT-017b 培训滚动 12 月")
    void ut017b_rolling12MonthTraining()
    {
        YearMonth anchor = YearMonth.of(2026, 7);
        HomeScreenTrainingMonthDto row = new HomeScreenTrainingMonthDto();
        row.setStatYear(2026);
        row.setStatMonth(3);
        row.setActivityCount(1);
        row.setEnrolledCount(20);
        HomeScreenTrainingTrendVo vo = HomeScreenSupport.buildTrainingTrend(
                Collections.singletonList(row), true, anchor);
        assertEquals(12, vo.getMonthly().size());
        assertEquals(2026, vo.getMonthly().get(7).getStatYear());
        assertEquals(3, vo.getMonthly().get(7).getMonth());
        assertEquals(1, vo.getMonthly().get(7).getActivityCount());
        assertEquals(20, vo.getMonthly().get(7).getEnrolledCount());
    }

    @Test
    @DisplayName("DP-SY-UT-018 无培训空态")
    void ut018_noTraining()
    {
        HomeScreenTrainingTrendVo vo = HomeScreenSupport.buildTrainingTrend(
                Collections.emptyList(), false, YearMonth.of(2026, 5));
        assertFalse(vo.isHasTrainingData());
        assertEquals(12, vo.getMonthly().size());
    }

    @Test
    @DisplayName("实战培训所选年 1～最大月")
    void trainingTrendForStatYear()
    {
        HomeScreenTrainingMonthDto m1 = new HomeScreenTrainingMonthDto();
        m1.setMonth(1);
        m1.setActivityCount(2);
        m1.setEnrolledCount(10);
        HomeScreenTrainingMonthDto m5 = new HomeScreenTrainingMonthDto();
        m5.setMonth(5);
        m5.setActivityCount(1);
        m5.setEnrolledCount(8);
        HomeScreenTrainingTrendVo vo = HomeScreenSupport.buildTrainingTrendForStatYear(
                Arrays.asList(m1, m5), 2026);
        assertTrue(vo.isHasTrainingData());
        assertEquals(5, vo.getMonthly().size());
        assertEquals(2026, vo.getMonthly().get(0).getStatYear());
        assertEquals(1, vo.getMonthly().get(0).getMonth());
        assertEquals(2, vo.getMonthly().get(0).getActivityCount());
        assertEquals(0, vo.getMonthly().get(2).getActivityCount());
        assertEquals(5, vo.getMonthly().get(4).getMonth());
        assertEquals(8, vo.getMonthly().get(4).getEnrolledCount());

        HomeScreenTrainingTrendVo empty = HomeScreenSupport.buildTrainingTrendForStatYear(
                Collections.emptyList(), 2026);
        assertFalse(empty.isHasTrainingData());
        assertEquals(0, empty.getMonthly().size());
    }

    @Test
    @DisplayName("DP-SY-UT-019 标准分类解析")
    void ut019_categoryParse()
    {
        assertEquals(HomeScreenStandardCategory.FEEDING, HomeScreenStandardCategory.parse("FEEDING"));
    }

    private static BreedingIndustryYakSnapshot yak(Long pastureId, int status, String gender, Integer ageMonths)
    {
        BreedingIndustryYakSnapshot s = new BreedingIndustryYakSnapshot();
        s.setPastureId(pastureId);
        s.setAssetStatus(status);
        s.setGender(gender);
        s.setAgeMonths(ageMonths);
        return s;
    }

    private static HomeScreenMonthCountDto monthCount(int month, int count)
    {
        HomeScreenMonthCountDto dto = new HomeScreenMonthCountDto();
        dto.setMonth(month);
        dto.setCount(count);
        return dto;
    }

    private static HomeScreenAppointmentStatDto appt(Integer month, int providerType, int count)
    {
        HomeScreenAppointmentStatDto dto = new HomeScreenAppointmentStatDto();
        dto.setMonth(month);
        dto.setProviderType(providerType);
        dto.setCount(count);
        return dto;
    }

}
