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

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.ruoyi.web.modules.industryservice.support.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.common.exception.ServiceException;
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.BreedingIndustryOverviewVo;
import com.ruoyi.web.modules.industryservice.mapper.BreedingIndustryStatsMapper;
import com.ruoyi.web.modules.industryservice.service.IBreedingIndustryDashboardService;
import com.ruoyi.web.modules.prosperity.support.AchievementReportValidation;
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.vo.HomeScreenYakStructureCategoryVo;
import com.ruoyi.web.modules.screen.mapper.HomeScreenStatsMapper;
import com.ruoyi.web.modules.screen.support.HomeScreenSupport;

/**
 * 养殖产业看板：产业总量（存栏/出栏/农牧户/草场面积）、存栏变动、牦牛存栏结构与大屏首页口径一致；
 * 默认所选年为当前自然年。
 */
@Service
public class BreedingIndustryDashboardServiceImpl implements IBreedingIndustryDashboardService
{
    private static final DateTimeFormatter STAT_DATE_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd");

    private static final String[] DEGRADATION_LABELS = {
            "", "未退化", "轻度", "中度", "重度", "极重度"
    };

    @Autowired
    private BreedingIndustryStatsMapper breedingIndustryStatsMapper;

    @Autowired
    private BreedingIndustryPastureScope breedingIndustryPastureScope;

    @Autowired
    private HomeScreenStatsMapper homeScreenStatsMapper;

    @Override
    public BreedingIndustryDashboardVo loadDashboard(boolean forceRefresh)
    {
        return loadDashboard(forceRefresh, null);
    }

    @Override
    public BreedingIndustryDashboardVo loadDashboard(boolean forceRefresh, String statYear)
    {
        try
        {
            int year = resolveStatYear(statYear);
            LocalDate statDate = HomeScreenSupport.resolveStatDate(year);
            List<Long> allowedPastureIds = breedingIndustryPastureScope.resolveAllowedPastureIds();
            BreedingIndustryDashboardVo dashboard = new BreedingIndustryDashboardVo();
            dashboard.setStatDate(statDate.format(STAT_DATE_FMT));
            dashboard.setStatYear(year);

            HomeScreenStatPeriodDto yearHerd =
                    homeScreenStatsMapper.selectLatestHerdInventoryStatPeriodByYear(year);
            HomeScreenStatPeriodDto yearOutbound =
                    homeScreenStatsMapper.selectLatestOutboundReportStatPeriodByYear(year);
            HomeScreenStatPeriodDto yearFarmerHousehold =
                    homeScreenStatsMapper.selectLatestFarmerHouseholdStatPeriodByYear(year);
            HomeScreenStatPeriodDto yearCooperative =
                    homeScreenStatsMapper.selectLatestCooperativeDevelopmentStatPeriodByYear(year);
            HomeScreenStatPeriodDto yearFamilyRanch =
                    homeScreenStatsMapper.selectLatestFamilyRanchStatPeriodByYear(year);
            HomeScreenStatPeriodDto yearLargeFarmer =
                    homeScreenStatsMapper.selectLatestLargeLivestockFarmerStatPeriodByYear(year);
            HomeScreenStatPeriodDto yearOwnership =
                    homeScreenStatsMapper.selectLatestLivestockOwnershipHouseholdStatPeriodByYear(year);

            BreedingIndustryOverviewVo overview = new BreedingIndustryOverviewVo();
            overview.setGrasslandCount(breedingIndustryStatsMapper.countGrassland());
            overview.setInventoryTotal(sumHerdYakAtPeriod(yearHerd));
            overview.setAnnualOutbound(sumOutboundYakAtPeriod(yearOutbound));
            overview.setPastureCount(sumFarmerHouseholdAtPeriod(yearFarmerHousehold));
            overview.setGrasslandAreaMu(sumFarmerHouseholdPastureAreaAtPeriod(yearFarmerHousehold));
            overview.setCooperativeCount(countAtPeriod(yearCooperative,
                    homeScreenStatsMapper::countCooperativeDevelopmentByStatYearMonth));
            overview.setFamilyRanchCount(countAtPeriod(yearFamilyRanch,
                    homeScreenStatsMapper::countFamilyRanchByStatYearMonth));
            overview.setLargeLivestockFarmerCount(countAtPeriod(yearLargeFarmer,
                    homeScreenStatsMapper::countLargeLivestockFarmerByStatYearMonth));
            overview.setWithLivestockHouseholdCount(countAtPeriod(yearOwnership,
                    homeScreenStatsMapper::sumWithLivestockHouseholdCountByStatYearMonth));
            fillWarningYakCount(overview, allowedPastureIds, statDate);
            dashboard.setOverview(overview);

            BreedingIndustryInventoryChangeVo inventoryChange = new BreedingIndustryInventoryChangeVo();
            List<HomeScreenYakStructureCategoryVo> yakInventoryStructure;
            if (yearHerd == null)
            {
                inventoryChange.setBullCount(0);
                inventoryChange.setCowCount(0);
                inventoryChange.setUnknownGenderCount(0);
                yakInventoryStructure = HomeScreenSupport.buildYakInventoryStructure(null);
            }
            else
            {
                HomeScreenHerdGenderSumDto genderSum = homeScreenStatsMapper.sumHerdInventoryGenderByStatYearMonth(
                        yearHerd.getStatYear(), yearHerd.getStatMonth());
                int bull = genderSum != null && genderSum.getBullTotal() != null ? genderSum.getBullTotal() : 0;
                int cow = genderSum != null && genderSum.getCowTotal() != null ? genderSum.getCowTotal() : 0;
                int yakTotal = genderSum != null && genderSum.getYakTotal() != null ? genderSum.getYakTotal() : 0;
                inventoryChange.setBullCount(bull);
                inventoryChange.setCowCount(cow);
                inventoryChange.setUnknownGenderCount(Math.max(0, yakTotal - bull - cow));
                yakInventoryStructure = HomeScreenSupport.buildYakInventoryStructure(
                        homeScreenStatsMapper.sumHerdInventoryYakStructureByStatYearMonth(
                                yearHerd.getStatYear(), yearHerd.getStatMonth()));
            }
            inventoryChange.setMonthlyTrend(BreedingIndustryDashboardSupport.toMonthlyTrend(
                    HomeScreenSupport.buildBullCowMonthlyTrendFromHerdReport(
                            homeScreenStatsMapper.selectHerdInventoryBullCowByMonth(year), statDate)));

            dashboard.setInventoryChange(inventoryChange);
            dashboard.setYakInventoryStructure(yakInventoryStructure);
            dashboard.setGrassland(buildGrasslandVo(overview.getGrasslandCount(), statDate));
            return dashboard;
        }
        catch (ServiceException ex)
        {
            throw ex;
        }
        catch (Exception ex)
        {
            throw new ServiceException(BreedingIndustryRules.MSG_LOAD_FAILED);
        }
    }

    private static int resolveStatYear(String statYear)
    {
        if (statYear == null || statYear.trim().isEmpty())
        {
            return HomeScreenSupport.currentYearMonth().getYear();
        }
        return AchievementReportValidation.parseStatYear(statYear);
    }

    private void fillWarningYakCount(BreedingIndustryOverviewVo overview, List<Long> allowedPastureIds,
            LocalDate statDate)
    {
        if (allowedPastureIds == null || allowedPastureIds.isEmpty())
        {
            overview.setWarningYakCount(0);
            return;
        }
        Date yearStart = toDate(statDate.withDayOfYear(1));
        Date statDateEnd = toDateEndOfDay(statDate);
        overview.setWarningYakCount(
                breedingIndustryStatsMapper.countWarningYak(allowedPastureIds, yearStart, statDateEnd));
    }

    private int sumHerdYakAtPeriod(HomeScreenStatPeriodDto period)
    {
        if (period == null || period.getStatYear() == null || period.getStatMonth() == null)
        {
            return 0;
        }
        return homeScreenStatsMapper.sumHerdInventoryYakTotalByStatYearMonth(
                period.getStatYear(), period.getStatMonth());
    }

    private int sumOutboundYakAtPeriod(HomeScreenStatPeriodDto period)
    {
        if (period == null || period.getStatYear() == null || period.getStatMonth() == null)
        {
            return 0;
        }
        return homeScreenStatsMapper.sumOutboundReportYakCountByStatYearMonth(
                period.getStatYear(), period.getStatMonth());
    }

    private int sumFarmerHouseholdAtPeriod(HomeScreenStatPeriodDto period)
    {
        if (period == null || period.getStatYear() == null || period.getStatMonth() == null)
        {
            return 0;
        }
        return homeScreenStatsMapper.sumFarmerHouseholdCountByStatYearMonth(
                period.getStatYear(), period.getStatMonth());
    }

    private BigDecimal sumFarmerHouseholdPastureAreaAtPeriod(HomeScreenStatPeriodDto period)
    {
        if (period == null || period.getStatYear() == null || period.getStatMonth() == null)
        {
            return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
        }
        BigDecimal area = homeScreenStatsMapper.sumFarmerHouseholdPastureContractAreaByStatYearMonth(
                period.getStatYear(), period.getStatMonth());
        return area != null ? area.setScale(2, RoundingMode.HALF_UP) : BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
    }

    private int countAtPeriod(HomeScreenStatPeriodDto period, YearMonthCountQuery query)
    {
        if (period == null || period.getStatYear() == null || period.getStatMonth() == null)
        {
            return 0;
        }
        return query.apply(period.getStatYear(), period.getStatMonth());
    }

    @FunctionalInterface
    private interface YearMonthCountQuery
    {
        int apply(int statYear, int statMonth);
    }

    private BreedingIndustryGrasslandVo buildGrasslandVo(int grasslandTotal, LocalDate statDate)
    {
        BreedingIndustryGrasslandVo vo = new BreedingIndustryGrasslandVo();
        vo.setAvailableCount(breedingIndustryStatsMapper.countAvailableGrassland());
        vo.setInUseCount(breedingIndustryStatsMapper.countInUseGrassland(toDate(statDate)));
        vo.setDegradationStats(buildDegradationStats(grasslandTotal));
        return vo;
    }

    private List<BreedingIndustryDegradationStatVo> buildDegradationStats(int grasslandTotal)
    {
        List<BreedingIndustryDegradationStatVo> grouped = breedingIndustryStatsMapper.selectDegradationGroupCounts();
        Map<Integer, Integer> countByLevel = new HashMap<>();
        if (grouped != null)
        {
            for (BreedingIndustryDegradationStatVo row : grouped)
            {
                countByLevel.put(row.getDegradationLevel(), row.getCount());
            }
        }
        List<BreedingIndustryDegradationStatVo> result = new ArrayList<>();
        for (int level = GrasslandRules.DEGRADATION_NONE; level <= GrasslandRules.DEGRADATION_EXTREME; level++)
        {
            int count = countByLevel.getOrDefault(level, 0);
            BreedingIndustryDegradationStatVo stat = new BreedingIndustryDegradationStatVo();
            stat.setDegradationLevel(level);
            stat.setDegradationLabel(DEGRADATION_LABELS[level]);
            stat.setCount(count);
            stat.setRatio(BreedingIndustryCalculator.ratioPercent(count, grasslandTotal));
            result.add(stat);
        }
        return result;
    }

    private static Date toDate(LocalDate localDate)
    {
        return Date.from(localDate.atStartOfDay(ZoneId.of(BreedingIndustryRules.ZONE_SHANGHAI)).toInstant());
    }

    private static Date toDateEndOfDay(LocalDate localDate)
    {
        return Date.from(localDate.atTime(23, 59, 59)
                .atZone(ZoneId.of(BreedingIndustryRules.ZONE_SHANGHAI)).toInstant());
    }
}
