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

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.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import com.ruoyi.web.modules.industryservice.domain.dto.BreedingIndustryYakSnapshot;
import com.ruoyi.web.modules.industryservice.domain.vo.BreedingIndustryAgeBandVo;
import com.ruoyi.web.modules.industryservice.domain.vo.BreedingIndustryDegradationStatVo;
import com.ruoyi.web.modules.industryservice.support.BreedingIndustryCalculator;
import com.ruoyi.web.modules.industryservice.support.BreedingIndustryRules;
import com.ruoyi.web.modules.industryservice.support.GrasslandRules;
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.HomeScreenSubsidyTypeSumDto;
import com.ruoyi.web.modules.screen.domain.dto.HomeScreenTrainingMonthDto;
import com.ruoyi.web.modules.screen.domain.vo.HomeScreenAgeBandVo;
import com.ruoyi.web.modules.screen.domain.vo.HomeScreenAppointmentMonthVo;
import com.ruoyi.web.modules.screen.domain.vo.HomeScreenAppointmentVo;
import com.ruoyi.web.modules.screen.domain.vo.HomeScreenAgriClassroomVo;
import com.ruoyi.web.modules.screen.domain.vo.HomeScreenDegradationStatVo;
import com.ruoyi.web.modules.screen.domain.vo.HomeScreenGrasslandVo;
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.HomeScreenStandardListItemVo;
import com.ruoyi.web.modules.screen.domain.vo.HomeScreenSubsidyTypeStatVo;
import com.ruoyi.web.modules.screen.domain.vo.HomeScreenSubsidyVo;
import com.ruoyi.web.modules.screen.domain.vo.HomeScreenTrainingMonthVo;
import com.ruoyi.web.modules.screen.domain.vo.HomeScreenTrainingTrendVo;

/**
 * 大屏首页口径组装与纯计算（无 DB / HTTP 依赖，便于单测）。
 * <p>
 * 核心约定：{@link #resolveStatDate(int)} 决定统计截止日；当年未到的月份在趋势类方法中补 0。
 */
public final class HomeScreenSupport
{
    private static final DateTimeFormatter STAT_DATE_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd");

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

    private static final int PROVIDER_VET = 1;

    private static final int PROVIDER_ORG = 2;

    private static final int PROVIDER_EXPERT = 3;

    private static final Map<String, String> STANDARD_TYPE_NAMES;

    static
    {
        Map<String, String> map = new HashMap<>();
        map.put("001001", "繁育作业");
        map.put("001002", "饲养工作");
        map.put("001003", "免疫程序");
        map.put("001004", "环境调控");
        map.put("001005", "饲料配方");
        map.put("001006", "牦牛投喂");
        map.put("001007", "设备操作");
        map.put("001008", "设备保养");
        map.put("001009", "设备排障");
        map.put("001010", "设备维修");
        map.put("001011", "牦牛生长");
        map.put("001012", "牦牛出栏");
        map.put("001013", "疾病决策");
        STANDARD_TYPE_NAMES = Collections.unmodifiableMap(map);
    }

    private HomeScreenSupport()
    {
    }

    /**
     * 统计截止日：当前年取上海时区今天，历史年取 12-31。
     * 所有大屏模块的年区间 [{@code yearStart}, {@code yearEnd}] 均依赖此方法。
     */
    public static LocalDate resolveStatDate(int statYear)
    {
        LocalDate today = LocalDate.now(ZoneId.of(BreedingIndustryRules.ZONE_SHANGHAI));
        if (statYear == today.getYear())
        {
            return today;
        }
        return LocalDate.of(statYear, 12, 31);
    }

    public static String formatStatDate(LocalDate statDate)
    {
        return statDate.format(STAT_DATE_FMT);
    }

    /** 截止 statDate 月末仍计入存栏的牦牛数量（复用养殖产业快照口径）。 */
    public static int countInventoryAtDate(List<BreedingIndustryYakSnapshot> assets, LocalDate statDate)
    {
        int count = 0;
        for (BreedingIndustryYakSnapshot asset : assets)
        {
            if (BreedingIndustryCalculator.countsAtMonthEnd(asset, statDate))
            {
                count++;
            }
        }
        return count;
    }

    public static int[] countGenderAtDate(List<BreedingIndustryYakSnapshot> assets, LocalDate statDate)
    {
        int bull = 0;
        int cow = 0;
        for (BreedingIndustryYakSnapshot asset : assets)
        {
            if (!BreedingIndustryCalculator.countsAtMonthEnd(asset, statDate))
            {
                continue;
            }
            String gender = BreedingIndustryCalculator.normalizeGender(asset.getGender());
            if (BreedingIndustryRules.GENDER_BULL.equals(gender))
            {
                bull++;
            }
            else if (BreedingIndustryRules.GENDER_COW.equals(gender))
            {
                cow++;
            }
        }
        return new int[] { bull, cow };
    }

    /**
     * 公母牛月度存栏趋势；当前年未来月份填 0。
     *
     * @return 长度 12 的列表，{@code month} 为 1～12
     */
    public static List<HomeScreenInventoryMonthVo> buildBullCowMonthlyTrend(
            List<BreedingIndustryYakSnapshot> assets, LocalDate statDate)
    {
        List<HomeScreenInventoryMonthVo> points = new ArrayList<>();
        int statYear = statDate.getYear();
        int futureAfter = statDate.getMonthValue();
        for (int month = 1; month <= 12; month++)
        {
            HomeScreenInventoryMonthVo point = new HomeScreenInventoryMonthVo();
            point.setMonth(month);
            if (statYear == LocalDate.now(ZoneId.of(BreedingIndustryRules.ZONE_SHANGHAI)).getYear()
                    && month > futureAfter)
            {
                point.setBullCount(0);
                point.setCowCount(0);
            }
            else
            {
                LocalDate monthEnd = BreedingIndustryCalculator.resolveMonthEnd(statYear, month, statDate);
                int bull = 0;
                int cow = 0;
                for (BreedingIndustryYakSnapshot asset : assets)
                {
                    if (!BreedingIndustryCalculator.countsAtMonthEnd(asset, monthEnd))
                    {
                        continue;
                    }
                    String gender = BreedingIndustryCalculator.normalizeGender(asset.getGender());
                    if (BreedingIndustryRules.GENDER_BULL.equals(gender))
                    {
                        bull++;
                    }
                    else if (BreedingIndustryRules.GENDER_COW.equals(gender))
                    {
                        cow++;
                    }
                }
                point.setBullCount(bull);
                point.setCowCount(cow);
            }
            points.add(point);
        }
        return points;
    }

    public static List<HomeScreenAgeBandVo> buildAgeStructureAtDate(
            List<BreedingIndustryYakSnapshot> assets, LocalDate statDate)
    {
        Map<String, Integer> bandCounts = new HashMap<>();
        bandCounts.put(BreedingIndustryRules.BAND_M1_6, 0);
        bandCounts.put(BreedingIndustryRules.BAND_M7_12, 0);
        bandCounts.put(BreedingIndustryRules.BAND_M13_PLUS, 0);
        int total = 0;
        for (BreedingIndustryYakSnapshot asset : assets)
        {
            if (!BreedingIndustryCalculator.countsAtMonthEnd(asset, statDate))
            {
                continue;
            }
            total++;
            String band = BreedingIndustryCalculator.bandCodeOf(
                    BreedingIndustryCalculator.resolveAgeMonths(asset, statDate));
            bandCounts.put(band, bandCounts.get(band) + 1);
        }
        List<BreedingIndustryAgeBandVo> breedingBands =
                BreedingIndustryCalculator.buildAgeStructure(bandCounts, total);
        List<HomeScreenAgeBandVo> result = new ArrayList<>();
        for (BreedingIndustryAgeBandVo band : breedingBands)
        {
            HomeScreenAgeBandVo vo = new HomeScreenAgeBandVo();
            vo.setBandCode(band.getBandCode());
            vo.setBandLabel(band.getBandLabel());
            vo.setCount(band.getCount());
            vo.setRatio(band.getRatio());
            result.add(vo);
        }
        return result;
    }

    public static List<HomeScreenOutboundMonthVo> buildOutboundMonthly(
            List<HomeScreenMonthCountDto> rows, LocalDate statDate)
    {
        Map<Integer, Integer> byMonth = new HashMap<>();
        if (rows != null)
        {
            for (HomeScreenMonthCountDto row : rows)
            {
                if (row.getMonth() != null && row.getCount() != null)
                {
                    byMonth.put(row.getMonth(), row.getCount());
                }
            }
        }
        int futureAfter = statDate.getMonthValue();
        boolean currentYear = statDate.getYear()
                == LocalDate.now(ZoneId.of(BreedingIndustryRules.ZONE_SHANGHAI)).getYear();
        List<HomeScreenOutboundMonthVo> list = new ArrayList<>();
        for (int month = 1; month <= 12; month++)
        {
            HomeScreenOutboundMonthVo vo = new HomeScreenOutboundMonthVo();
            vo.setMonth(month);
            if (currentYear && month > futureAfter)
            {
                vo.setOutboundCount(0);
            }
            else
            {
                vo.setOutboundCount(byMonth.getOrDefault(month, 0));
            }
            list.add(vo);
        }
        return list;
    }

    public static HomeScreenGrasslandVo buildGrasslandVo(
            int grasslandTotal, int availableCount, int inUseCount,
            List<BreedingIndustryDegradationStatVo> grouped)
    {
        Map<Integer, Integer> countByLevel = new HashMap<>();
        if (grouped != null)
        {
            for (BreedingIndustryDegradationStatVo row : grouped)
            {
                countByLevel.put(row.getDegradationLevel(), row.getCount());
            }
        }
        List<HomeScreenDegradationStatVo> stats = new ArrayList<>();
        for (int level = GrasslandRules.DEGRADATION_NONE; level <= GrasslandRules.DEGRADATION_EXTREME; level++)
        {
            int count = countByLevel.getOrDefault(level, 0);
            HomeScreenDegradationStatVo stat = new HomeScreenDegradationStatVo();
            stat.setDegradationLevel(level);
            stat.setDegradationLabel(DEGRADATION_LABELS[level]);
            stat.setCount(count);
            stat.setRatio(BreedingIndustryCalculator.ratioPercent(count, grasslandTotal));
            stats.add(stat);
        }
        HomeScreenGrasslandVo vo = new HomeScreenGrasslandVo();
        vo.setAvailableCount(availableCount);
        vo.setInUseCount(inUseCount);
        vo.setDegradationStats(stats);
        return vo;
    }

    public static HomeScreenAppointmentVo buildAppointment(
            List<HomeScreenAppointmentStatDto> annualRows,
            List<HomeScreenAppointmentStatDto> monthlyRows,
            LocalDate statDate)
    {
        int vet = 0;
        int expert = 0;
        int org = 0;
        if (annualRows != null)
        {
            for (HomeScreenAppointmentStatDto row : annualRows)
            {
                int c = row.getCount() != null ? row.getCount() : 0;
                if (row.getProviderType() != null)
                {
                    if (row.getProviderType() == PROVIDER_VET)
                    {
                        vet = c;
                    }
                    else if (row.getProviderType() == PROVIDER_EXPERT)
                    {
                        expert = c;
                    }
                    else if (row.getProviderType() == PROVIDER_ORG)
                    {
                        org = c;
                    }
                }
            }
        }
        Map<String, Integer> monthlyMap = new HashMap<>();
        if (monthlyRows != null)
        {
            for (HomeScreenAppointmentStatDto row : monthlyRows)
            {
                if (row.getMonth() == null || row.getProviderType() == null || row.getCount() == null)
                {
                    continue;
                }
                monthlyMap.put(row.getMonth() + ":" + row.getProviderType(), row.getCount());
            }
        }
        int futureAfter = statDate.getMonthValue();
        boolean currentYear = statDate.getYear()
                == LocalDate.now(ZoneId.of(BreedingIndustryRules.ZONE_SHANGHAI)).getYear();
        List<HomeScreenAppointmentMonthVo> stacked = new ArrayList<>();
        for (int month = 1; month <= 12; month++)
        {
            HomeScreenAppointmentMonthVo point = new HomeScreenAppointmentMonthVo();
            point.setMonth(month);
            if (currentYear && month > futureAfter)
            {
                point.setVetCount(0);
                point.setExpertCount(0);
                point.setOrgCount(0);
            }
            else
            {
                point.setVetCount(monthlyMap.getOrDefault(month + ":" + PROVIDER_VET, 0));
                point.setExpertCount(monthlyMap.getOrDefault(month + ":" + PROVIDER_EXPERT, 0));
                point.setOrgCount(monthlyMap.getOrDefault(month + ":" + PROVIDER_ORG, 0));
            }
            stacked.add(point);
        }
        HomeScreenAppointmentVo vo = new HomeScreenAppointmentVo();
        vo.setAnnualVetCount(vet);
        vo.setAnnualExpertCount(expert);
        vo.setAnnualOrgCount(org);
        vo.setMonthlyStacked(stacked);
        return vo;
    }

    /** 按 {@code biz_subsidy_grant_record.subsidy_type} 汇总金额（万元），无记录时 hasSubsidyData=false。 */
    public static HomeScreenSubsidyVo buildSubsidy(List<HomeScreenSubsidyTypeSumDto> sums, boolean hasSubsidyData)
    {
        Map<Integer, BigDecimal> amountByType = new HashMap<>();
        if (sums != null)
        {
            for (HomeScreenSubsidyTypeSumDto row : sums)
            {
                if (row.getSubsidyType() != null && row.getAmountSum() != null)
                {
                    amountByType.put(row.getSubsidyType(), row.getAmountSum());
                }
            }
        }
        List<HomeScreenSubsidyTypeStatVo> stats = new ArrayList<>();
        stats.add(subsidyTypeStat(1, "农业生产", amountByType.get(1)));
        stats.add(subsidyTypeStat(2, "住房基建", amountByType.get(2)));
        stats.add(subsidyTypeStat(3, "民生保障", amountByType.get(3)));
        HomeScreenSubsidyVo vo = new HomeScreenSubsidyVo();
        vo.setHasSubsidyData(hasSubsidyData);
        vo.setTypeStats(stats);
        return vo;
    }

    public static BigDecimal toAmountWan(BigDecimal amountYuan)
    {
        if (amountYuan == null)
        {
            return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
        }
        return amountYuan.divide(new BigDecimal("10000"), 2, RoundingMode.HALF_UP);
    }

    public static HomeScreenAgriClassroomVo buildAgriClassroom(int videoCount, int trainingCount)
    {
        HomeScreenAgriClassroomVo vo = new HomeScreenAgriClassroomVo();
        vo.setVideoCourseCount(videoCount);
        vo.setPracticalTrainingCount(trainingCount);
        vo.setTrainingContentTotal(videoCount + trainingCount);
        vo.setHasAgriData(videoCount > 0 || trainingCount > 0);
        return vo;
    }

    public static HomeScreenTrainingTrendVo buildTrainingTrend(
            List<HomeScreenTrainingMonthDto> rows, boolean hasTrainingData, LocalDate statDate)
    {
        Map<Integer, HomeScreenTrainingMonthDto> byMonth = new HashMap<>();
        if (rows != null)
        {
            for (HomeScreenTrainingMonthDto row : rows)
            {
                if (row.getMonth() != null)
                {
                    byMonth.put(row.getMonth(), row);
                }
            }
        }
        int futureAfter = statDate.getMonthValue();
        boolean currentYear = statDate.getYear()
                == LocalDate.now(ZoneId.of(BreedingIndustryRules.ZONE_SHANGHAI)).getYear();
        List<HomeScreenTrainingMonthVo> monthly = new ArrayList<>();
        for (int month = 1; month <= 12; month++)
        {
            HomeScreenTrainingMonthVo point = new HomeScreenTrainingMonthVo();
            point.setMonth(month);
            if (!hasTrainingData)
            {
                point.setActivityCount(0);
                point.setEnrolledCount(0);
            }
            else if (currentYear && month > futureAfter)
            {
                point.setActivityCount(0);
                point.setEnrolledCount(0);
            }
            else
            {
                HomeScreenTrainingMonthDto src = byMonth.get(month);
                point.setActivityCount(src != null && src.getActivityCount() != null ? src.getActivityCount() : 0);
                point.setEnrolledCount(src != null && src.getEnrolledCount() != null ? src.getEnrolledCount() : 0);
            }
            monthly.add(point);
        }
        HomeScreenTrainingTrendVo vo = new HomeScreenTrainingTrendVo();
        vo.setHasTrainingData(hasTrainingData);
        vo.setMonthly(monthly);
        return vo;
    }

    /** 合并各数据源年份并降序，始终包含当前年供前端年份切换。 */
    public static List<Integer> buildAvailableYears(List<Integer>... yearLists)
    {
        Set<Integer> set = new TreeSet<>(Collections.reverseOrder());
        int currentYear = LocalDate.now(ZoneId.of(BreedingIndustryRules.ZONE_SHANGHAI)).getYear();
        set.add(currentYear);
        if (yearLists != null)
        {
            for (List<Integer> list : yearLists)
            {
                if (list != null)
                {
                    set.addAll(list);
                }
            }
        }
        return new ArrayList<>(set);
    }

    public static void enrichStandardTypeNames(List<HomeScreenStandardListItemVo> rows)
    {
        if (rows == null)
        {
            return;
        }
        for (HomeScreenStandardListItemVo row : rows)
        {
            String code = row.getStandardType();
            row.setStandardTypeName(STANDARD_TYPE_NAMES.getOrDefault(code, code));
        }
    }

    private static HomeScreenSubsidyTypeStatVo subsidyTypeStat(int type, String name, BigDecimal amountYuan)
    {
        HomeScreenSubsidyTypeStatVo stat = new HomeScreenSubsidyTypeStatVo();
        stat.setSubsidyType(type);
        stat.setSubsidyTypeName(name);
        stat.setAmountWan(toAmountWan(amountYuan));
        return stat;
    }
}
