package com.hdkj.lt.bf.service.staticgrid.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hdkj.lt.bf.constants.OptimizationConstants;
import com.hdkj.lt.bf.entity.FhzgSeCapacityDaily;
import com.hdkj.lt.bf.entity.staticgrid.StaticIndicatorMetric;
import com.hdkj.lt.bf.entity.staticgrid.StaticIndicatorResult;
import com.hdkj.lt.bf.entity.staticgrid.StaticProblemDetail;
import com.hdkj.lt.bf.entity.vo.StaticGridDashboardVO;
import com.hdkj.lt.bf.entity.vo.SeCapacityDashboardVO;
import com.hdkj.lt.bf.entity.vo.SeVoltageIndexVO;
import com.hdkj.lt.bf.mapper.FhzgSeCapacityDailyMapper;
import com.hdkj.lt.bf.mapper.staticgrid.StaticIndicatorMetricMapper;
import com.hdkj.lt.bf.mapper.staticgrid.StaticIndicatorResultMapper;
import com.hdkj.lt.bf.mapper.staticgrid.StaticProblemDetailMapper;
import com.hdkj.lt.bf.service.SeIndicatorService;
import com.hdkj.lt.bf.entity.dto.TodayOutageData;
import com.hdkj.lt.bf.service.SjztPwwyyqxtPrmPdrOutageMvDsService;
import com.hdkj.lt.bf.service.staticgrid.StaticGridDashboardService;
import com.hdkj.lt.utils.cache.RedisRepository;
import com.alibaba.fastjson.JSON;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 静态网架评估仪表盘服务实现
 * <p>
 * 支持三种节点维度查询：
 * <ul>
 *   <li>type=2 区县 — 取 county 级指标</li>
 *   <li>type=3 变电站 — 聚合该变电站下所有馈线的 feeder 级指标</li>
 *   <li>type=4 馈线 — 取单条馈线的 feeder 级指标（聚合后 bool→0/1、rate→0%/100%）</li>
 * </ul>
 * 静态指标按月份过滤，前端传 daytime（如 2026-7-16），取该月最新一批数据。
 * 停电时户数通过 SjztPwwyyqxtPrmPdrOutageMvDsService 获取。
 *
 * @author lsl
 * @since 2026-07-19
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class StaticGridDashboardServiceImpl
        extends ServiceImpl<StaticIndicatorResultMapper, StaticIndicatorResult>
        implements StaticGridDashboardService {

    private final StaticIndicatorMetricMapper metricMapper;
    private final SjztPwwyyqxtPrmPdrOutageMvDsService outageDurationService;
    private final FhzgSeCapacityDailyMapper capacityDailyMapper;
    private final StaticProblemDetailMapper problemDetailMapper;
    private final SeIndicatorService seIndicatorService;
    private final RedisRepository redisRepository;

    // ============================================================
    // 指标聚合映射表：county 级 metric_code → feeder 级 metric_code
    // ============================================================

    /** 布尔计数类：count(value="true") */
    private static final Map<String, String> BOOL_COUNT_MAP = new LinkedHashMap<>();
    static {
        BOOL_COUNT_MAP.put("same_bus_interconnection_issue_count", "has_same_bus_interconnection_issue");
        BOOL_COUNT_MAP.put("excessive_tie_point_feeder_count", "has_excessive_tie_points");
        BOOL_COUNT_MAP.put("single_radial_feeder_count", "is_single_radial_feeder");
        BOOL_COUNT_MAP.put("large_branch_issue_feeder_count", "has_large_branch_issue");
        BOOL_COUNT_MAP.put("main_trunk_lantern_issue_feeder_count", "has_main_trunk_lantern_issue");
        BOOL_COUNT_MAP.put("unreasonable_segment_count_feeder_count", "has_unreasonable_segment_count");
        BOOL_COUNT_MAP.put("supply_radius_exceed_feeder_count", "is_supply_radius_exceeded");
        BOOL_COUNT_MAP.put("extra_long_feeder_count", "is_extra_long_feeder");
        BOOL_COUNT_MAP.put("main_trunk_bottleneck_feeder_count", "has_main_trunk_bottleneck");
        BOOL_COUNT_MAP.put("connected_capacity_exceed_feeder_count", "is_connected_capacity_exceeded");
    }

    /** 布尔比率类：count(true)/total×100 */
    private static final Map<String, String> BOOL_RATE_MAP = new LinkedHashMap<>();
    static {
        BOOL_RATE_MAP.put("wiring_standardization_rate", "is_standard_wiring");
        BOOL_RATE_MAP.put("main_trunk_section_standardization_rate", "main_trunk_section_compliant");
        BOOL_RATE_MAP.put("n_minus_1_pass_rate", "n_minus_1_passed");
        BOOL_RATE_MAP.put("line_interconnection_rate", "has_tie");
        BOOL_RATE_MAP.put("inter_substation_interconnection_rate", "has_inter_substation_tie");
    }

    /** 整数求和类：sum(value) */
    private static final Map<String, String> INT_SUM_MAP = new LinkedHashMap<>();
    static {
        INT_SUM_MAP.put("feeder_head_tie_point_issue_count", "feeder_head_tie_point_issue_count");
        INT_SUM_MAP.put("same_pole_tie_point_issue_count", "same_pole_tie_point_issue_count");
        INT_SUM_MAP.put("load_group_capacity_anomaly_count", "load_group_capacity_anomaly_count");
        INT_SUM_MAP.put("main_trunk_total_count", "main_trunk_count");
        INT_SUM_MAP.put("section_compliant_main_trunk_count", "section_compliant_main_trunk_count");
    }

    // ============================================================
    // 主入口
    // ============================================================

    @Override
    public StaticGridDashboardVO queryLatestDashboard(String id, String type) {
        if (StringUtils.isBlank(id) || StringUtils.isBlank(type)) {
            log.warn("参数缺失: id={}, type={}", id, type);
            return null;
        }

        int nodeType;
        try {
            nodeType = Integer.parseInt(type);
        } catch (NumberFormatException e) {
            log.warn("type 参数非法: {}", type);
            return null;
        }

        YearMonth ym = YearMonth.now();
        String cacheKey = OptimizationConstants.CACHE_KEY_GRID_DASHBOARD_PREFIX
                + nodeType + ":" + id + ":" + ym;
        // 整包缓存：静态月数据 + 嵌运行态，15min TTL（与断面节奏对齐）
        Object cached = redisRepository.get(cacheKey);
        if (cached instanceof StaticGridDashboardVO) {
            return (StaticGridDashboardVO) cached;
        }

        StaticGridDashboardVO vo;
        if (nodeType == 2) {
            vo = queryByCounty(id, ym);
        } else if (nodeType == 3) {
            vo = queryBySubstation(id, ym);
        } else if (nodeType == 4) {
            vo = queryByFeeder(id, ym);
        } else {
            log.warn("不支持的节点类型: {}", nodeType);
            return null;
        }
        if (vo != null) {
            redisRepository.setExpire(cacheKey, vo,
                    OptimizationConstants.CACHE_TTL_GRID_DASHBOARD_MINUTES, java.util.concurrent.TimeUnit.MINUTES);
        }
        return vo;
    }

    // ============================================================
    // 三种维度的查询路径
    // ============================================================

    /**
     * type=2 区县：主表按 maintOrgId + 月份查最新，取 county 级指标
     */
    private StaticGridDashboardVO queryByCounty(String maintOrgId, YearMonth ym) {
        StaticIndicatorResult result = getLatestByMaintOrgId(maintOrgId, ym);
        if (result == null) {
            log.warn("区县无静态网架数据 maintOrgId={}, month={}", maintOrgId, ym);
            return null;
        }
        // county 级指标
        List<StaticIndicatorMetric> countyMetrics = getMetrics(result.getId(), "county", null, null);
        Map<String, String> metricMap = metricsToMap(countyMetrics);
        // 停电时户数（历史T-2 + 今日实时，分层口径）
        BigDecimal outage = queryOutageForMonth(maintOrgId, 2, ym);
        StaticGridDashboardVO vo = buildDashboardVO(result, metricMap, outage);
        fillCapacityMetrics(vo, result.getCountyId(), null, null, ym);
        // IEC用户电压指数：与运行态本月同源同算法（type=2 区县维度）
        vo.setIecUserVoltageIndex(queryVoltageIndexValue(maintOrgId, 2));
        vo.setProblemDetails(buildProblemDetails(result.getId()));
        return vo;
    }

    /**
     * type=3 变电站：先查该月所有主表批次，再按 sourceSubstationId 过滤 feeder 级指标，聚合
     */
    private StaticGridDashboardVO queryBySubstation(String substationId, YearMonth ym) {
        List<Long> resultIds = getResultIdsByMonth(ym);
        if (resultIds.isEmpty()) {
            log.warn("月份无静态网架数据 month={}", ym);
            return null;
        }
        LambdaQueryWrapper<StaticIndicatorMetric> wrapper = new LambdaQueryWrapper<StaticIndicatorMetric>()
                .in(StaticIndicatorMetric::getResultId, resultIds)
                .eq(StaticIndicatorMetric::getLevel, "feeder")
                .eq(StaticIndicatorMetric::getSourceSubstationId, substationId);
        List<StaticIndicatorMetric> feederMetrics = metricMapper.selectList(wrapper);
        if (feederMetrics.isEmpty()) {
            log.warn("变电站无 feeder 级指标 substationId={}", substationId);
            return null;
        }
        Long resultId = feederMetrics.get(0).getResultId();
        StaticIndicatorResult result = getById(resultId);
        Map<String, String> metricMap = aggregateFeederMetrics(feederMetrics);
        // 停电时户数（历史T-2 + 今日实时，分层口径）
        BigDecimal outage = queryOutageForMonth(substationId, 3, ym);
        StaticGridDashboardVO vo = buildDashboardVO(result, metricMap, outage);
        fillCapacityMetrics(vo, null, substationId, null, ym);
        // IEC用户电压指数：与运行态本月同源同算法（type=3 变电站维度）
        vo.setIecUserVoltageIndex(queryVoltageIndexValue(substationId, 3));
        vo.setProblemDetails(buildProblemDetails(resultId));
        return vo;
    }

    /**
     * type=4 馈线：按 feederId 查 feeder 级指标，聚合（单条馈线 → bool 转 0/1，rate 转 0%/100%）
     */
    private StaticGridDashboardVO queryByFeeder(String feederId, YearMonth ym) {
        List<Long> resultIds = getResultIdsByMonth(ym);
        if (resultIds.isEmpty()) {
            log.warn("月份无静态网架数据 month={}", ym);
            return null;
        }
        LambdaQueryWrapper<StaticIndicatorMetric> wrapper = new LambdaQueryWrapper<StaticIndicatorMetric>()
                .in(StaticIndicatorMetric::getResultId, resultIds)
                .eq(StaticIndicatorMetric::getLevel, "feeder")
                .eq(StaticIndicatorMetric::getFeederId, feederId);
        List<StaticIndicatorMetric> feederMetrics = metricMapper.selectList(wrapper);
        if (feederMetrics.isEmpty()) {
            log.warn("馈线无 feeder 级指标 feederId={}", feederId);
            return null;
        }
        Long resultId = feederMetrics.get(0).getResultId();
        StaticIndicatorResult result = getById(resultId);
        Map<String, String> metricMap = aggregateFeederMetrics(feederMetrics);
        // 停电时户数（历史T-2 + 今日实时，分层口径）
        BigDecimal outage = queryOutageForMonth(feederId, 4, ym);
        StaticGridDashboardVO vo = buildDashboardVO(result, metricMap, outage);
        fillCapacityMetrics(vo, null, null, feederId, ym);
        // IEC用户电压指数：与运行态本月同源同算法（type=4 馈线维度）
        vo.setIecUserVoltageIndex(queryVoltageIndexValue(feederId, 4));
        vo.setProblemDetails(buildProblemDetails(resultId));
        return vo;
    }

    /**
     * 计算 IEC 用户电压指数（运行态本月，网架图与运行态同源同算法）
     */
    private BigDecimal queryVoltageIndexValue(String id, int type) {
        try {
            SeVoltageIndexVO vo = seIndicatorService.queryVoltageIndex(id, type, "month");
            return vo != null ? vo.getVoltageIndex() : null;
        } catch (Exception e) {
            log.warn("IEC用户电压指数计算失败 id={} type={}", id, type, e);
            return null;
        }
    }

    /**
     * 网架停电时户数（分层口径，与运行态同源）：
     * 历史 T-2 查 [月初, 昨天] + 今日从实时停电表补。
     * T-2 表滞后且不含当日，直接查整月会在每月 1 号/跨月边界为空。
     */
    private BigDecimal queryOutageForMonth(String id, int type, YearMonth ym) {
        LocalDate today = LocalDate.now();
        LocalDate monthStart = ym.atDay(1);
        LocalDate histEnd = ym.atEndOfMonth().isBefore(today)
                ? ym.atEndOfMonth() : today.minusDays(1);

        BigDecimal hist = BigDecimal.ZERO;
        if (!histEnd.isBefore(monthStart)) {
            TodayOutageData histData = outageDurationService.getOutageDurationHoursHouseholds(
                    id, type, monthStart.atStartOfDay(), histEnd.atTime(23, 59, 59));
            hist = histData != null ? histData.getTotalHouseholds() : BigDecimal.ZERO;
        }

        // 今日实时（仅当查询月包含今天）
        BigDecimal todayHc = BigDecimal.ZERO;
        if (ym.getYear() == today.getYear() && ym.getMonthValue() == today.getMonthValue()) {
            TodayOutageData todayData = outageDurationService.getTodayOutageData(id, type);
            todayHc = todayData != null ? todayData.getTotalHouseholds() : BigDecimal.ZERO;
        }
        return hist.add(todayHc);
    }

    // ============================================================
    // 聚合逻辑：feeder 级指标 → county 级格式指标 Map
    // ============================================================

    /**
     * 将 feeder 级指标列表按 metric_code 聚合，
     * 输出与 county 级指标同 key 的 Map（供 buildDashboardVO 使用）
     */
    private Map<String, String> aggregateFeederMetrics(List<StaticIndicatorMetric> feederMetrics) {
        // 按 metric_code 分组
        Map<String, List<StaticIndicatorMetric>> byCode = feederMetrics.stream()
                .collect(Collectors.groupingBy(StaticIndicatorMetric::getMetricCode));

        // 馈线总数（去重 feederId）
        long totalFeeders = feederMetrics.stream()
                .map(StaticIndicatorMetric::getFeederId)
                .filter(Objects::nonNull)
                .distinct()
                .count();

        Map<String, String> result = new HashMap<>();
        result.put("feeder_total_count", String.valueOf(totalFeeders));

        // 布尔计数类：count(true)
        for (Map.Entry<String, String> entry : BOOL_COUNT_MAP.entrySet()) {
            long count = countTrue(byCode.get(entry.getValue()));
            result.put(entry.getKey(), String.valueOf(count));
        }

        // 布尔比率类：count(true)/total×100
        for (Map.Entry<String, String> entry : BOOL_RATE_MAP.entrySet()) {
            long trueCount = countTrue(byCode.get(entry.getValue()));
            if (totalFeeders > 0) {
                BigDecimal rate = new BigDecimal(trueCount)
                        .divide(new BigDecimal(totalFeeders), 6, RoundingMode.HALF_UP)
                        .multiply(new BigDecimal("100"))
                        .setScale(2, RoundingMode.HALF_UP);
                result.put(entry.getKey(), rate.toPlainString());
            }
        }

        // 整数求和类
        for (Map.Entry<String, String> entry : INT_SUM_MAP.entrySet()) {
            int sum = sumInt(byCode.get(entry.getValue()));
            result.put(entry.getKey(), String.valueOf(sum));
        }

        // 特殊：tied_feeder_total_count = count(has_tie=true)
        result.put("tied_feeder_total_count", String.valueOf(countTrue(byCode.get("has_tie"))));

        // 特殊：standard_wiring_feeder_count = count(is_standard_wiring=true)
        result.put("standard_wiring_feeder_count",
                String.valueOf(countTrue(byCode.get("is_standard_wiring"))));

        // 特殊：connected_capacity_anomaly_count = 超标准条数 + 负荷组异常数
        int capacityAnomaly = Integer.parseInt(result.getOrDefault("connected_capacity_exceed_feeder_count", "0"))
                + Integer.parseInt(result.getOrDefault("load_group_capacity_anomaly_count", "0"));
        result.put("connected_capacity_anomaly_count", String.valueOf(capacityAnomaly));

        return result;
    }

    private long countTrue(List<StaticIndicatorMetric> metrics) {
        if (metrics == null) return 0;
        return metrics.stream()
                .filter(m -> "true".equalsIgnoreCase(m.getValue()))
                .count();
    }

    private int sumInt(List<StaticIndicatorMetric> metrics) {
        if (metrics == null) return 0;
        return metrics.stream()
                .filter(m -> StringUtils.isNotBlank(m.getValue()))
                .mapToInt(m -> {
                    try {
                        return new BigDecimal(m.getValue()).intValue();
                    } catch (NumberFormatException e) {
                        return 0;
                    }
                })
                .sum();
    }

    // ============================================================
    // 构建 VO（type=2 和 type=3/4 共用）
    // ============================================================

    private StaticGridDashboardVO buildDashboardVO(StaticIndicatorResult result,
                                                    Map<String, String> metricMap,
                                                    BigDecimal outageDuration) {
        return StaticGridDashboardVO.builder()
                .resultId(result.getId())
                .batchId(result.getBatchId())
                .countyId(result.getCountyId())
                .countySid(result.getCountySid())
                .countyName(result.getCountyName())
                .createTime(result.getCreateTime())
                .dataQualityStatus(result.getDataQualityStatus())
                .dataQualityCompletenessRate(result.getDataQualityCompletenessRate())
                // 一、标准化网架
                .wiringStandardizationRate(toBigDecimal(metricMap.get("wiring_standardization_rate")))
                .mainTrunkSectionStandardizationRate(toBigDecimal(metricMap.get("main_trunk_section_standardization_rate")))
                // 二、供电可靠性
                .outageDurationHoursHouseholds(outageDuration)
                .lineNMinus1PassRate(toBigDecimal(metricMap.get("n_minus_1_pass_rate")))
                .interSubstationNoConnectionCount(calcInterSubstationNoConnectionCount(
                        metricMap.get("feeder_total_count"),
                        metricMap.get("inter_substation_interconnection_rate")))
                .sameBusInterconnectionIssueCount(toInteger(metricMap.get("same_bus_interconnection_issue_count")))
                .excessiveTiePointFeederCount(toInteger(metricMap.get("excessive_tie_point_feeder_count")))
                .singleRadialFeederCount(toInteger(metricMap.get("single_radial_feeder_count")))
                .noConnectionFeederCount(toInteger(metricMap.get("single_radial_feeder_count")))
                .largeBranchFeederCount(toInteger(metricMap.get("large_branch_issue_feeder_count")))
                .unreasonableSegmentCount(toInteger(metricMap.get("unreasonable_segment_count_feeder_count")))
                .mainTrunkSegmentDenseCount(toInteger(metricMap.get("main_trunk_lantern_issue_feeder_count")))
                // 三、电压质量
                .iecUserVoltageIndex(null)  // 预留
                .supplyRadiusExceedFeederCount(toInteger(metricMap.get("supply_radius_exceed_feeder_count")))
                .extraLongFeederCount(toInteger(metricMap.get("extra_long_feeder_count")))
                // 四、供电能力
                .regionalMaxLoadRate(null)
                .regionalAvgLoadRate(null)
                .lineOverloadCount(null)
                .mainTrunkSectionMismatchCount(toInteger(metricMap.get("main_trunk_bottleneck_feeder_count")))
                .connectedCapacityExceedFeederCount(toInteger(metricMap.get("connected_capacity_exceed_feeder_count")))
                .loadGroupCapacityAnomalyCount(toInteger(metricMap.get("load_group_capacity_anomaly_count")))
                .build();
    }

    // ============================================================
    // 主表查询辅助
    // ============================================================

    /**
     * 区县维度：按 maintOrgId + 月份查最新一条主表记录
     */
    private StaticIndicatorResult getLatestByMaintOrgId(String maintOrgId, YearMonth ym) {
        LocalDate monthStart = ym.atDay(1);
        LocalDate monthEnd = ym.atEndOfMonth();
        LambdaQueryWrapper<StaticIndicatorResult> wrapper = new LambdaQueryWrapper<StaticIndicatorResult>()
                .eq(StaticIndicatorResult::getMaintOrgId, maintOrgId)
                .ge(StaticIndicatorResult::getCreateTime, monthStart.atStartOfDay())
                .le(StaticIndicatorResult::getCreateTime, monthEnd.atTime(23, 59, 59))
                .orderByDesc(StaticIndicatorResult::getCreateTime)
                .last("LIMIT 1");
        return getOne(wrapper);
    }

    /**
     * 获取该月所有主表批次ID（type=3/4 用——先拿到月份范围内的 result_id 再查 metric）
     */
    private List<Long> getResultIdsByMonth(YearMonth ym) {
        LocalDate monthStart = ym.atDay(1);
        LocalDate monthEnd = ym.atEndOfMonth();
        LambdaQueryWrapper<StaticIndicatorResult> wrapper = new LambdaQueryWrapper<StaticIndicatorResult>()
                .ge(StaticIndicatorResult::getCreateTime, monthStart.atStartOfDay())
                .le(StaticIndicatorResult::getCreateTime, monthEnd.atTime(23, 59, 59));
        List<StaticIndicatorResult> results = list(wrapper);
        return results.stream()
                .map(StaticIndicatorResult::getId)
                .collect(Collectors.toList());
    }

    /**
     * 查 county 级或 feeder 级指标
     */
    private List<StaticIndicatorMetric> getMetrics(Long resultId, String level,
                                                    String feederId, String substationId) {
        LambdaQueryWrapper<StaticIndicatorMetric> wrapper = new LambdaQueryWrapper<StaticIndicatorMetric>()
                .eq(StaticIndicatorMetric::getResultId, resultId)
                .eq(StaticIndicatorMetric::getLevel, level);
        if (feederId != null) {
            wrapper.eq(StaticIndicatorMetric::getFeederId, feederId);
        }
        if (substationId != null) {
            wrapper.eq(StaticIndicatorMetric::getSourceSubstationId, substationId);
        }
        return metricMapper.selectList(wrapper);
    }

    private Map<String, String> metricsToMap(List<StaticIndicatorMetric> metrics) {
        Map<String, String> map = new HashMap<>();
        for (StaticIndicatorMetric m : metrics) {
            map.put(m.getMetricCode(), m.getValue());
        }
        return map;
    }

    // ============================================================
    // 工具方法
    // ============================================================

    /**
     * 解析 daytime（如 "2026-7-16"）为 YearMonth
     */
    private YearMonth parseYearMonth(String daytime) {
        if (StringUtils.isBlank(daytime)) {
            return YearMonth.now();
        }
        try {
            LocalDate date = LocalDate.parse(daytime, DateTimeFormatter.ofPattern("yyyy-M-d"));
            return YearMonth.from(date);
        } catch (DateTimeParseException e) {
            log.warn("daytime 格式非法: {}, 使用当前月份", daytime);
            return YearMonth.now();
        }
    }

    /**
     * 站间无联络线路数 = 馈线总数 × (1 - 站间联络率/100)
     */
    private Integer calcInterSubstationNoConnectionCount(String feederTotalCountStr, String interSubstationRateStr) {
        Integer total = toInteger(feederTotalCountStr);
        if (total == null || total == 0) return null;
        if (StringUtils.isBlank(interSubstationRateStr)) return total;
        try {
            BigDecimal rate = new BigDecimal(interSubstationRateStr);
            BigDecimal noConnRatio = BigDecimal.ONE.subtract(
                    rate.divide(new BigDecimal("100"), 6, RoundingMode.HALF_UP));
            return new BigDecimal(total).multiply(noConnRatio).setScale(0, RoundingMode.HALF_UP).intValue();
        } catch (NumberFormatException e) {
            return null;
        }
    }

    /**
     * 查询问题详情列表
     */
    private List<StaticGridDashboardVO.ProblemDetailItem> buildProblemDetails(Long resultId) {
        if (resultId == null) return Collections.emptyList();
        LambdaQueryWrapper<StaticProblemDetail> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(StaticProblemDetail::getResultId, resultId);
        List<StaticProblemDetail> details = problemDetailMapper.selectList(wrapper);
        if (details == null || details.isEmpty()) return Collections.emptyList();
        return details.stream().map(d -> {
            List<String> deviceIds = null;
            if (StringUtils.isNotBlank(d.getObjectId())) {
                try {
                    deviceIds = JSON.parseArray(d.getObjectId(), String.class);
                } catch (Exception e) {
                    log.warn("解析deviceIds失败 objectId={}", d.getObjectId());
                }
            }
            return StaticGridDashboardVO.ProblemDetailItem.builder()
                    .problemId(d.getProblemId())
                    .feederId(d.getFeederId())
                    .categoryName(d.getCategoryName())
                    .metricName(d.getMetricName())
                    .severity(d.getSeverity())
                    .objectType(d.getObjectType())
                    .actualValue(d.getActualValue())
                    .reason(d.getDescription())
                    .deviceIds(deviceIds)
                    .build();
        }).collect(Collectors.toList());
    }

    private BigDecimal toBigDecimal(String value) {
        if (StringUtils.isBlank(value)) return null;
        try {
            return new BigDecimal(value);
        } catch (NumberFormatException e) {
            return null;
        }
    }

    private Integer toInteger(String value) {
        if (StringUtils.isBlank(value)) return null;
        try {
            return Integer.valueOf(value);
        } catch (NumberFormatException e) {
            try {
                return new BigDecimal(value).intValue();
            } catch (NumberFormatException e2) {
                return null;
            }
        }
    }

    // ============================================================
    // 供电能力数据：从 capacity_daily 查区域/馈线负载率
    // ============================================================

    /**
     * 从 fhzg_se_capacity_daily 查询区域/馈线最大负载率、平均负载率、过载条数
     * 时间段：月初到昨天（当日数据尚未入日汇总表）
     */
    private void fillCapacityMetrics(StaticGridDashboardVO vo, String countyId,
                                      String subsId, String feederId, YearMonth ym) {
        LocalDate monthStart = ym.atDay(1);
        LocalDate monthEnd = ym.atEndOfMonth();
        LocalDate today = LocalDate.now();
        LocalDate endDate = monthEnd.isBefore(today) ? monthEnd : today.minusDays(1);

        // 按维度过滤
        LambdaQueryWrapper<FhzgSeCapacityDaily> wrapper = new LambdaQueryWrapper<FhzgSeCapacityDaily>()
                .ge(FhzgSeCapacityDaily::getStatDate, monthStart)
                .le(FhzgSeCapacityDaily::getStatDate, endDate);
        if (feederId != null) {
            wrapper.eq(FhzgSeCapacityDaily::getFeederId, feederId);
        } else if (subsId != null) {
            wrapper.eq(FhzgSeCapacityDaily::getSubsId, subsId);
        } else if (countyId != null) {
            wrapper.eq(FhzgSeCapacityDaily::getCountyId, countyId);
        }

        List<FhzgSeCapacityDaily> list = capacityDailyMapper.selectList(wrapper);

        // 区域最大负载率
        BigDecimal maxLoad = list.stream()
                .map(FhzgSeCapacityDaily::getMaxLoadRate)
                .filter(Objects::nonNull)
                .max(BigDecimal::compareTo)
                .orElse(null);

        // 区域平均负载率：对每日 avg_load_rate 取均值
        double avg = list.stream()
                .map(FhzgSeCapacityDaily::getAvgLoadRate)
                .filter(Objects::nonNull)
                .mapToDouble(BigDecimal::doubleValue)
                .average()
                .orElse(-1);
        BigDecimal avgLoad = avg >= 0
                ? BigDecimal.valueOf(avg).setScale(2, RoundingMode.HALF_UP) : null;

        // 线路重过载事件数（与运行态口径一致：日统计表存当日事件数，按月累加）
        long sumHeavy = list.stream()
                .filter(d -> d.getHeavyCount() != null)
                .mapToLong(FhzgSeCapacityDaily::getHeavyCount)
                .sum();
        long sumOverload = list.stream()
                .filter(d -> d.getOverloadCount() != null)
                .mapToLong(FhzgSeCapacityDaily::getOverloadCount)
                .sum();

        // 今日实时补（T+1 日统计表不含今天）：复用运行态 today 查询口径
        String id = feederId != null ? feederId : (subsId != null ? subsId : countyId);
        int type = feederId != null ? 4 : (subsId != null ? 3 : 2);
        if (id != null) {
            try {
                SeCapacityDashboardVO todayVo = seIndicatorService.queryCapacity(id, type, "today");
                if (todayVo != null) {
                    if (todayVo.getMaxLoadRate() != null
                            && (maxLoad == null || todayVo.getMaxLoadRate().compareTo(maxLoad) > 0)) {
                        maxLoad = todayVo.getMaxLoadRate();
                    }
                    if (todayVo.getAvgLoadRate() != null) {
                        avgLoad = todayVo.getAvgLoadRate();
                    }
                    sumHeavy += todayVo.getHeavyLoadEventCount() != null
                            ? todayVo.getHeavyLoadEventCount() : 0;
                    sumOverload += todayVo.getOverloadEventCount() != null
                            ? todayVo.getOverloadEventCount() : 0;
                }
            } catch (Exception e) {
                log.warn("网架容量今日实时补失败 id={} type={}", id, type, e);
            }
        }

        vo.setRegionalMaxLoadRate(maxLoad);
        vo.setRegionalAvgLoadRate(avgLoad);
        long totalEvents = sumHeavy + sumOverload;
        if (totalEvents > 0) {
            vo.setLineOverloadCount((int) totalEvents);
        }
    }
}
