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

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.time.YearMonth;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.web.modules.epidemic.domain.BizEpidemicReport;
import com.ruoyi.web.modules.epidemic.domain.BizLabDetection;
import com.ruoyi.web.modules.epidemic.domain.BizRiskFactorWeight;
import com.ruoyi.web.modules.epidemic.support.EpidemicReportRules;
import com.ruoyi.web.modules.screen.domain.vo.EpidemicSituationVo;
import com.ruoyi.web.modules.screen.domain.vo.EpidemicTypeStatVo;
import com.ruoyi.web.modules.screen.domain.vo.LabDetectionMonthlyVo;
import com.ruoyi.web.modules.screen.domain.vo.LabDetectionScreenVo;
import com.ruoyi.web.modules.screen.domain.vo.RelieveDurationByTypeVo;
import com.ruoyi.web.modules.screen.domain.vo.RiskFactorWeightItemVo;
import com.ruoyi.web.modules.screen.domain.vo.RiskFactorWeightsVo;
import com.ruoyi.web.modules.screen.domain.vo.RiskLevelStatVo;
import com.ruoyi.web.modules.screen.domain.vo.SampleSourceStatVo;
import com.ruoyi.web.modules.screen.domain.vo.ScreenEpidemicReportListItemVo;
import com.ruoyi.web.modules.screen.domain.vo.TransmissionRouteStatVo;

/**
 * 大屏疫病风险 — 聚合与枚举展示（纯逻辑，便于单测）。
 * <p>
 * 检测阳性率 = positiveCount / testQuantity；解除时长按疫情类型分组求平均天数。
 */
public final class EpidemicRiskScreenSupport
{
    private static final String DATE_FMT = "yyyy-MM-dd";

    private static final int OPERATE_STATUS_NORMAL = 1;

    private static final String[] SAMPLE_SOURCE_NAMES =
            { null, "场地抽检", "疫情上报送检", "调运检疫", "日常检测" };

    private static final String[] RISK_LEVEL_NAMES = { null, "高危", "中危", "低危" };

    private static final String[] EPIDEMIC_TYPE_NAMES =
            { null, "病毒性疫病", "细菌性疫病", "寄生虫疫病", "其他类疫病" };

    private static final String[] TRANSMISSION_ROUTE_NAMES =
            { null, "调运传入", "本地扩散", "环境感染", "其他未知" };

    private EpidemicRiskScreenSupport()
    {
    }

    public static String resolveSampleSourceName(Integer sampleSource)
    {
        return labelAt(SAMPLE_SOURCE_NAMES, sampleSource, 1, 4);
    }

    public static String resolveRiskLevelName(Integer riskLevel)
    {
        return labelAt(RISK_LEVEL_NAMES, riskLevel, 1, 3);
    }

    public static String resolveEpidemicTypeName(Integer epidemicType)
    {
        return labelAt(EPIDEMIC_TYPE_NAMES, epidemicType, 1, 4);
    }

    public static String resolveTransmissionRouteName(Integer transmissionRoute)
    {
        return labelAt(TRANSMISSION_ROUTE_NAMES, transmissionRoute, 1, 4);
    }

    private static String labelAt(String[] names, Integer code, int min, int max)
    {
        if (code == null || code < min || code > max)
        {
            return "";
        }
        return names[code];
    }

    public static int safeInt(Integer value)
    {
        return value == null ? 0 : value;
    }

    public static BigDecimal calcPositiveRate(int testQuantityTotal, int positiveCountTotal)
    {
        if (testQuantityTotal <= 0)
        {
            return BigDecimal.ZERO;
        }
        return BigDecimal.valueOf(positiveCountTotal)
                .divide(BigDecimal.valueOf(testQuantityTotal), 4, RoundingMode.HALF_UP);
    }

    public static LabDetectionScreenVo buildLabDetection(List<BizLabDetection> rows, YearMonth anchor)
    {
        LabDetectionScreenVo vo = new LabDetectionScreenVo();
        int testTotal = 0;
        int positiveTotal = 0;
        Map<Integer, int[]> byYm = new HashMap<>();
        if (rows != null)
        {
            for (BizLabDetection row : rows)
            {
                int tq = safeInt(row.getTestQuantity());
                int pc = safeInt(row.getPositiveCount());
                testTotal += tq;
                positiveTotal += pc;
                int month = monthOf(row.getTestDate());
                int year = yearOf(row.getTestDate());
                if (month >= 1 && month <= 12 && year > 0)
                {
                    int key = HomeScreenSupport.yearMonthKey(YearMonth.of(year, month));
                    int[] bucket = byYm.computeIfAbsent(key, k -> new int[2]);
                    bucket[0] += tq;
                    bucket[1] += pc;
                }
            }
        }
        vo.setTestQuantityTotal(testTotal);
        vo.setPositiveCountTotal(positiveTotal);
        vo.setPositiveRate(calcPositiveRate(testTotal, positiveTotal));
        YearMonth start = anchor.minusMonths(11);
        List<LabDetectionMonthlyVo> monthly = new ArrayList<>(12);
        for (int slot = 0; slot < 12; slot++)
        {
            YearMonth ym = start.plusMonths(slot);
            int key = HomeScreenSupport.yearMonthKey(ym);
            int[] bucket = byYm.get(key);
            LabDetectionMonthlyVo item = new LabDetectionMonthlyVo();
            item.setStatYear(ym.getYear());
            item.setMonth(ym.getMonthValue());
            item.setTestQuantity(bucket != null ? bucket[0] : 0);
            item.setPositiveCount(bucket != null ? bucket[1] : 0);
            monthly.add(item);
        }
        vo.setMonthly(monthly);
        return vo;
    }

    public static List<SampleSourceStatVo> buildSampleSourceStats(List<BizLabDetection> rows, boolean hasLabData)
    {
        if (!hasLabData)
        {
            return null;
        }
        int[] qtyBySource = new int[5];
        int total = 0;
        if (rows != null)
        {
            for (BizLabDetection row : rows)
            {
                Integer src = row.getSampleSource();
                if (src == null || src < 1 || src > 4)
                {
                    continue;
                }
                int q = safeInt(row.getTestQuantity());
                qtyBySource[src] += q;
                total += q;
            }
        }
        List<SampleSourceStatVo> list = new ArrayList<>(4);
        for (int src = 1; src <= 4; src++)
        {
            SampleSourceStatVo vo = new SampleSourceStatVo();
            vo.setSampleSource(src);
            vo.setSampleSourceName(SAMPLE_SOURCE_NAMES[src]);
            vo.setTestQuantity(qtyBySource[src]);
            vo.setRatio(ratioOf(qtyBySource[src], total));
            list.add(vo);
        }
        return list;
    }

    public static List<RiskLevelStatVo> buildRiskLevelStats(List<BizEpidemicReport> reportRows)
    {
        int[] counts = new int[4];
        int total = 0;
        if (reportRows != null)
        {
            for (BizEpidemicReport row : reportRows)
            {
                Integer level = row.getRiskLevel();
                if (level == null || level < 1 || level > 3)
                {
                    continue;
                }
                counts[level]++;
                total++;
            }
        }
        List<RiskLevelStatVo> list = new ArrayList<>(3);
        for (int level = 1; level <= 3; level++)
        {
            RiskLevelStatVo vo = new RiskLevelStatVo();
            vo.setRiskLevel(level);
            vo.setRiskLevelName(RISK_LEVEL_NAMES[level]);
            vo.setCount(counts[level]);
            vo.setRatio(ratioOf(counts[level], total));
            list.add(vo);
        }
        return list;
    }

    public static List<EpidemicTypeStatVo> buildEpidemicTypeStats(List<BizEpidemicReport> reportRows)
    {
        return buildEpidemicTypeStatsInternal(reportRows);
    }

    private static List<EpidemicTypeStatVo> buildEpidemicTypeStatsInternal(List<BizEpidemicReport> reportRows)
    {
        int[] counts = new int[5];
        int total = countByField(reportRows, counts, r -> r.getEpidemicType(), 1, 4);
        List<EpidemicTypeStatVo> list = new ArrayList<>(4);
        for (int type = 1; type <= 4; type++)
        {
            EpidemicTypeStatVo vo = new EpidemicTypeStatVo();
            vo.setEpidemicType(type);
            vo.setEpidemicTypeName(EPIDEMIC_TYPE_NAMES[type]);
            vo.setCount(counts[type]);
            vo.setRatio(ratioOf(counts[type], total));
            list.add(vo);
        }
        return list;
    }

    public static List<TransmissionRouteStatVo> buildTransmissionRouteStats(List<BizEpidemicReport> reportRows)
    {
        int[] counts = new int[5];
        int total = countByField(reportRows, counts, r -> r.getTransmissionRoute(), 1, 4);
        List<TransmissionRouteStatVo> list = new ArrayList<>(4);
        for (int route = 1; route <= 4; route++)
        {
            TransmissionRouteStatVo vo = new TransmissionRouteStatVo();
            vo.setTransmissionRoute(route);
            vo.setTransmissionRouteName(TRANSMISSION_ROUTE_NAMES[route]);
            vo.setCount(counts[route]);
            vo.setRatio(ratioOf(counts[route], total));
            list.add(vo);
        }
        return list;
    }

    private interface IntFieldGetter
    {
        Integer get(BizEpidemicReport row);
    }

    private static int countByField(List<BizEpidemicReport> rows, int[] counts, IntFieldGetter getter,
            int min, int max)
    {
        int total = 0;
        if (rows == null)
        {
            return 0;
        }
        for (BizEpidemicReport row : rows)
        {
            Integer code = getter.get(row);
            if (code == null || code < min || code > max)
            {
                continue;
            }
            counts[code]++;
            total++;
        }
        return total;
    }

    public static EpidemicSituationVo buildEpidemicSituation(int availableCount, int disabledCount,
            List<BizEpidemicReport> activeRows, List<BizEpidemicReport> relievedRows)
    {
        EpidemicSituationVo vo = new EpidemicSituationVo();
        vo.setAvailableQuarantinePointCount(availableCount);
        vo.setDisabledQuarantinePointCount(disabledCount);
        vo.setActiveControlAreaCount(countDistinctLocations(activeRows));
        vo.setReleasedAreaCount(countDistinctLocations(relievedRows));
        return vo;
    }

    public static int countDistinctLocations(List<BizEpidemicReport> rows)
    {
        if (rows == null || rows.isEmpty())
        {
            return 0;
        }
        Set<String> set = new HashSet<>();
        for (BizEpidemicReport row : rows)
        {
            String track = row.getAffectedLocationTrack();
            if (StringUtils.isNotEmpty(track))
            {
                set.add(track.trim());
            }
        }
        return set.size();
    }

    public static List<RelieveDurationByTypeVo> buildRelieveDurationByType(List<BizEpidemicReport> relievedRows)
    {
        Map<Integer, List<Long>> daysByType = new HashMap<>();
        if (relievedRows != null)
        {
            for (BizEpidemicReport row : relievedRows)
            {
                Integer type = row.getEpidemicType();
                if (type == null || type < 1 || type > 4)
                {
                    continue;
                }
                Long days = relieveDays(row.getReportDate(), row.getReleaseDate());
                if (days == null)
                {
                    continue;
                }
                daysByType.computeIfAbsent(type, k -> new ArrayList<>()).add(days);
            }
        }
        List<RelieveDurationByTypeVo> list = new ArrayList<>(4);
        for (int type = 1; type <= 4; type++)
        {
            RelieveDurationByTypeVo vo = new RelieveDurationByTypeVo();
            vo.setEpidemicType(type);
            vo.setEpidemicTypeName(EPIDEMIC_TYPE_NAMES[type]);
            List<Long> days = daysByType.get(type);
            if (days == null || days.isEmpty())
            {
                vo.setAvgRelieveDays(null);
                vo.setRecordCount(0);
            }
            else
            {
                long sum = 0L;
                for (Long d : days)
                {
                    sum += d;
                }
                BigDecimal avg = BigDecimal.valueOf(sum)
                        .divide(BigDecimal.valueOf(days.size()), 1, RoundingMode.HALF_UP);
                vo.setAvgRelieveDays(avg);
                vo.setRecordCount(days.size());
            }
            list.add(vo);
        }
        return list;
    }

    /**
     * 解除日 − 上报日（自然日差，非负）。
     */
    public static Long relieveDays(Date reportDate, Date releaseDate)
    {
        if (reportDate == null || releaseDate == null)
        {
            return null;
        }
        Calendar start = truncateToDay(reportDate);
        Calendar end = truncateToDay(releaseDate);
        long diff = (end.getTimeInMillis() - start.getTimeInMillis()) / (24L * 60L * 60L * 1000L);
        return diff < 0 ? null : diff;
    }

    public static RiskFactorWeightsVo buildRiskFactorWeights(BizRiskFactorWeight config)
    {
        RiskFactorWeightsVo vo = new RiskFactorWeightsVo();
        if (config == null || config.getId() == null)
        {
            vo.setHasRiskFactorConfig(false);
            vo.setItems(Collections.emptyList());
            return vo;
        }
        vo.setHasRiskFactorConfig(true);
        List<RiskFactorWeightItemVo> items = new ArrayList<>(5);
        items.add(weightItem("breedingDensity", "养殖密度", config.getWeightBreedingDensity()));
        items.add(weightItem("trafficMobility", "交通流动性", config.getWeightTrafficMobility()));
        items.add(weightItem("quarantineIntensity", "检疫强度", config.getWeightQuarantineIntensity()));
        items.add(weightItem("meteorologicalSuitability", "气象适宜度", config.getWeightMeteorologicalSuitability()));
        items.add(weightItem("pastDiseaseLevel", "既往发病水平", config.getWeightPastDiseaseLevel()));
        vo.setItems(items);
        return vo;
    }

    private static RiskFactorWeightItemVo weightItem(String code, String name, BigDecimal weight)
    {
        RiskFactorWeightItemVo item = new RiskFactorWeightItemVo();
        item.setFactorCode(code);
        item.setFactorName(name);
        item.setWeight(weight);
        return item;
    }

    public static List<Integer> buildAvailableYears(List<String> testYears, List<String> reportYears)
    {
        Set<Integer> set = new TreeSet<>(Comparator.reverseOrder());
        addYearStrings(set, testYears);
        addYearStrings(set, reportYears);
        set.add(Calendar.getInstance().get(Calendar.YEAR));
        return new ArrayList<>(set);
    }

    private static void addYearStrings(Set<Integer> set, List<String> yearStrs)
    {
        if (yearStrs == null)
        {
            return;
        }
        for (String y : yearStrs)
        {
            if (y != null && y.matches("^\\d{4}$"))
            {
                set.add(Integer.parseInt(y));
            }
        }
    }

    public static List<ScreenEpidemicReportListItemVo> toReportListItems(List<BizEpidemicReport> rows)
    {
        if (rows == null || rows.isEmpty())
        {
            return Collections.emptyList();
        }
        List<ScreenEpidemicReportListItemVo> list = new ArrayList<>(rows.size());
        SimpleDateFormat fmt = new SimpleDateFormat(DATE_FMT);
        for (BizEpidemicReport row : rows)
        {
            ScreenEpidemicReportListItemVo vo = new ScreenEpidemicReportListItemVo();
            vo.setId(row.getId());
            vo.setReportDate(formatDate(row.getReportDate(), fmt));
            vo.setTransmissionRoute(row.getTransmissionRoute());
            vo.setTransmissionRouteName(resolveTransmissionRouteName(row.getTransmissionRoute()));
            vo.setEpidemicType(row.getEpidemicType());
            vo.setEpidemicTypeName(resolveEpidemicTypeName(row.getEpidemicType()));
            vo.setEpidemicName(row.getEpidemicName());
            vo.setRiskLevel(row.getRiskLevel());
            vo.setRiskLevelName(resolveRiskLevelName(row.getRiskLevel()));
            vo.setAffectedLocationTrack(row.getAffectedLocationTrack());
            int relieveStatus = EpidemicReportRules.resolveRelieveStatus(row.getReleaseDate());
            vo.setRelieveStatus(relieveStatus);
            vo.setReleaseDate(relieveStatus == EpidemicReportRules.RELIEVE_STATUS_DONE
                    ? formatDate(row.getReleaseDate(), fmt) : null);
            list.add(vo);
        }
        return list;
    }

    public static int monthOf(Date date)
    {
        if (date == null)
        {
            return -1;
        }
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        return cal.get(Calendar.MONTH) + 1;
    }

    public static int yearOf(Date date)
    {
        if (date == null)
        {
            return -1;
        }
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        return cal.get(Calendar.YEAR);
    }

    private static BigDecimal ratioOf(int part, int total)
    {
        if (total <= 0)
        {
            return BigDecimal.ZERO;
        }
        return BigDecimal.valueOf(part).divide(BigDecimal.valueOf(total), 4, RoundingMode.HALF_UP);
    }

    private static Calendar truncateToDay(Date date)
    {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MILLISECOND, 0);
        return cal;
    }

    private static String formatDate(Date date, SimpleDateFormat fmt)
    {
        return date == null ? null : fmt.format(date);
    }

    public static int normalOperateStatus()
    {
        return OPERATE_STATUS_NORMAL;
    }
}
