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

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.when;

import java.math.BigDecimal;
import java.util.Collections;
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.common.exception.ServiceException;
import com.ruoyi.web.modules.industryservice.domain.vo.BreedingIndustryDashboardVo;
import com.ruoyi.web.modules.industryservice.mapper.BreedingIndustryStatsMapper;
import com.ruoyi.web.modules.industryservice.support.BreedingIndustryPastureScope;
import com.ruoyi.web.modules.industryservice.support.BreedingIndustryRules;
import com.ruoyi.web.modules.screen.domain.dto.HomeScreenHerdCowAgeSumDto;
import com.ruoyi.web.modules.screen.domain.dto.HomeScreenHerdGenderSumDto;
import com.ruoyi.web.modules.screen.domain.dto.HomeScreenStatPeriodDto;
import com.ruoyi.web.modules.screen.domain.dto.HomeScreenYearMonthBullCowDto;
import com.ruoyi.web.modules.screen.mapper.HomeScreenStatsMapper;

@ExtendWith(MockitoExtension.class)
@DisplayName("BreedingIndustryDashboardServiceImpl")
class BreedingIndustryDashboardServiceImplTest
{
    @Mock
    private BreedingIndustryStatsMapper breedingIndustryStatsMapper;

    @Mock
    private BreedingIndustryPastureScope breedingIndustryPastureScope;

    @Mock
    private HomeScreenStatsMapper homeScreenStatsMapper;

    @InjectMocks
    private BreedingIndustryDashboardServiceImpl service;

    @Test
    @DisplayName("ZCZX-YZCYSJ-UT-002 可见牧场为空仍返回填报趋势")
    void ut002_emptyScope()
    {
        stubGrassland();
        stubNoHerdReport();
        when(breedingIndustryPastureScope.resolveAllowedPastureIds()).thenReturn(Collections.emptyList());
        BreedingIndustryDashboardVo vo = service.loadDashboard(false);
        assertEquals(0, vo.getOverview().getInventoryTotal());
        assertEquals(0, vo.getInventoryChange().getBullCount());
        assertEquals(0, vo.getInventoryChange().getCowCount());
        assertEquals(0, vo.getInventoryChange().getUnknownGenderCount());
        assertEquals(12, vo.getInventoryChange().getMonthlyTrend().size());
        assertEquals(2, vo.getAgeStructure().size());
    }

    @Test
    @DisplayName("ZCZX-YZCYSJ-UT-028 产业总览与存栏取自最近填报年月")
    void ut028_overviewFromLatestReport()
    {
        stubGrassland();
        HomeScreenStatPeriodDto herd = period(2025, 12);
        HomeScreenStatPeriodDto outbound = period(2026, 5);
        when(homeScreenStatsMapper.selectLatestHerdInventoryStatPeriod()).thenReturn(herd);
        when(homeScreenStatsMapper.selectLatestOutboundReportStatPeriod()).thenReturn(outbound);
        when(homeScreenStatsMapper.sumHerdInventoryYakTotalByStatYearMonth(2025, 12)).thenReturn(50000);
        when(homeScreenStatsMapper.sumOutboundReportYakCountByStatYearMonth(2026, 5)).thenReturn(3200);
        when(homeScreenStatsMapper.sumOutboundReportFarmerHouseholdCountByStatYearMonth(2026, 5)).thenReturn(8988);
        stubHerdInventoryBlock(herd, 100, 200);
        when(breedingIndustryPastureScope.resolveAllowedPastureIds())
                .thenReturn(Collections.singletonList(1L));
        when(breedingIndustryStatsMapper.countWarningYak(any(), any(), any())).thenReturn(0);

        BreedingIndustryDashboardVo vo = service.loadDashboard(false);

        assertEquals(50000, vo.getOverview().getInventoryTotal());
        assertEquals(3200, vo.getOverview().getAnnualOutbound());
        assertEquals(8988, vo.getOverview().getPastureCount());
        assertEquals(100, vo.getInventoryChange().getBullCount());
        assertEquals(200, vo.getInventoryChange().getCowCount());
        assertEquals(0, vo.getInventoryChange().getUnknownGenderCount());
        assertEquals(12, vo.getInventoryChange().getMonthlyTrend().size());
        assertEquals(2, vo.getAgeStructure().size());
    }

    @Test
    @DisplayName("ZCZX-YZCYSJ-UT-009 公母牛取自填报无未标注")
    void ut009_genderFromReport()
    {
        stubGrassland();
        HomeScreenStatPeriodDto herd = period(2026, 7);
        when(homeScreenStatsMapper.selectLatestHerdInventoryStatPeriod()).thenReturn(herd);
        when(homeScreenStatsMapper.selectLatestOutboundReportStatPeriod()).thenReturn(null);
        when(homeScreenStatsMapper.sumHerdInventoryYakTotalByStatYearMonth(2026, 7)).thenReturn(350);
        HomeScreenHerdGenderSumDto gender = new HomeScreenHerdGenderSumDto();
        gender.setBullTotal(120);
        gender.setCowTotal(230);
        gender.setYakTotal(350);
        when(homeScreenStatsMapper.sumHerdInventoryGenderByStatYearMonth(2026, 7)).thenReturn(gender);
        HomeScreenHerdCowAgeSumDto age = new HomeScreenHerdCowAgeSumDto();
        age.setCowYoungCount(40);
        age.setCowAdultCount(60);
        when(homeScreenStatsMapper.sumHerdInventoryCowAgeByStatYearMonth(2026, 7)).thenReturn(age);
        when(homeScreenStatsMapper.selectHerdInventoryBullCowByYearMonthRange(anyInt(), anyInt()))
                .thenReturn(Collections.emptyList());
        when(breedingIndustryPastureScope.resolveAllowedPastureIds())
                .thenReturn(Collections.singletonList(1L));
        when(breedingIndustryStatsMapper.countWarningYak(any(), any(), any())).thenReturn(0);

        BreedingIndustryDashboardVo vo = service.loadDashboard(false);

        assertEquals(120, vo.getInventoryChange().getBullCount());
        assertEquals(230, vo.getInventoryChange().getCowCount());
        assertEquals(0, vo.getInventoryChange().getUnknownGenderCount());
    }

    @Test
    @DisplayName("ZCZX-YZCYSJ-API-019 聚合异常")
    void api019_loadFailed()
    {
        when(breedingIndustryPastureScope.resolveAllowedPastureIds())
                .thenThrow(new RuntimeException("db"));
        ServiceException ex = assertThrows(ServiceException.class, () -> service.loadDashboard(false));
        assertEquals(BreedingIndustryRules.MSG_LOAD_FAILED, ex.getMessage());
    }

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

    private void stubNoHerdReport()
    {
        when(homeScreenStatsMapper.selectLatestHerdInventoryStatPeriod()).thenReturn(null);
        when(homeScreenStatsMapper.selectLatestOutboundReportStatPeriod()).thenReturn(null);
        when(homeScreenStatsMapper.selectHerdInventoryBullCowByYearMonthRange(anyInt(), anyInt()))
                .thenReturn(Collections.emptyList());
    }

    private void stubHerdInventoryBlock(HomeScreenStatPeriodDto herd, int bull, int cow)
    {
        HomeScreenHerdGenderSumDto gender = new HomeScreenHerdGenderSumDto();
        gender.setBullTotal(bull);
        gender.setCowTotal(cow);
        gender.setYakTotal(bull + cow);
        when(homeScreenStatsMapper.sumHerdInventoryGenderByStatYearMonth(
                eq(herd.getStatYear()), eq(herd.getStatMonth()))).thenReturn(gender);
        HomeScreenHerdCowAgeSumDto age = new HomeScreenHerdCowAgeSumDto();
        when(homeScreenStatsMapper.sumHerdInventoryCowAgeByStatYearMonth(
                eq(herd.getStatYear()), eq(herd.getStatMonth()))).thenReturn(age);
        HomeScreenYearMonthBullCowDto row = new HomeScreenYearMonthBullCowDto();
        row.setStatYear(herd.getStatYear());
        row.setStatMonth(herd.getStatMonth());
        row.setBullCount(bull);
        row.setCowCount(cow);
        when(homeScreenStatsMapper.selectHerdInventoryBullCowByYearMonthRange(anyInt(), anyInt()))
                .thenReturn(Collections.singletonList(row));
    }

    private void stubGrassland()
    {
        when(breedingIndustryStatsMapper.countGrassland()).thenReturn(3);
        when(breedingIndustryStatsMapper.sumGrasslandAreaMu()).thenReturn(new BigDecimal("100.00"));
        when(breedingIndustryStatsMapper.countAvailableGrassland()).thenReturn(2);
        when(breedingIndustryStatsMapper.countInUseGrassland(any())).thenReturn(1);
        when(breedingIndustryStatsMapper.selectDegradationGroupCounts()).thenReturn(Collections.emptyList());
    }
}
