package com.hdkj.lt.bf.scheduler;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.hdkj.lt.bf.entity.FhzgSeLineLossDaily;
import com.hdkj.lt.bf.entity.FhzgSeLineLossDetail;
import com.hdkj.lt.bf.mapper.FhzgSeLineLossDailyMapper;
import com.hdkj.lt.bf.mapper.FhzgSeLineLossDetailMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 线路线损日汇总定时任务
 * 每天凌晨1点，将昨天的线损明细聚合写入 fhzg_se_line_loss_daily
 *
 * @author lsl
 * @since 2026-07-30
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class SeLineLossDailyScheduler {

    private final FhzgSeLineLossDetailMapper lineLossDetailMapper;
    private final FhzgSeLineLossDailyMapper lineLossDailyMapper;

    /**
     * 每天凌晨1点执行
     */
    @Scheduled(cron = "0 0 1 * * ?")
    public void aggregateYesterdayLineLoss() {
        LocalDate yesterday = LocalDate.now().minusDays(1);
        log.info("[线路线损日汇总] 开始处理 {} 数据", yesterday);

        try {
            String result = aggregateForDate(yesterday);
            log.info("[线路线损日汇总] {} {}", yesterday, result);
        } catch (Exception e) {
            log.error("[线路线损日汇总] {} 处理失败", yesterday, e);
        }
    }

    /**
     * 聚合指定日期的线损明细到日汇总表（手动触发时调用）
     */
    public String aggregateForDate(LocalDate date) {
        LocalDateTime startTime = date.atStartOfDay();
        LocalDateTime endTime = date.atTime(23, 59, 59);

        // 查当天所有线损明细
        LambdaQueryWrapper<FhzgSeLineLossDetail> wrapper = new LambdaQueryWrapper<FhzgSeLineLossDetail>()
                .ge(FhzgSeLineLossDetail::getSnapTime, startTime)
                .le(FhzgSeLineLossDetail::getSnapTime, endTime);

        List<FhzgSeLineLossDetail> details = lineLossDetailMapper.selectList(wrapper);

        if (details.isEmpty()) {
            log.info("[线路线损日汇总] {} 无线损明细数据，跳过", date);
            return date + " 无线损明细数据";
        }

        // 按馈线分组聚合
        Map<String, DailyAccumulator> accumulatorMap = new HashMap<>();
        for (FhzgSeLineLossDetail d : details) {
            String feederId = d.getFeederId();
            if (feederId == null) continue;

            DailyAccumulator acc = accumulatorMap.computeIfAbsent(feederId, k -> new DailyAccumulator());
            acc.countyId = d.getCountyId();
            acc.subsId = d.getSubsId();

            BigDecimal loss = d.getLineLossKw();
            if (loss != null) {
                // 线损电量 kWh = 线损功率 kW × 断面间隔(15min = 0.25h)
                BigDecimal energy = loss.multiply(BigDecimal.valueOf(0.25));
                acc.sumKwh = acc.sumKwh.add(energy);
                if (loss.compareTo(acc.maxKw) > 0) {
                    acc.maxKw = loss;
                }
                acc.sumKw = acc.sumKw.add(loss);
                acc.count++;
            }
        }

        // 先删当天已有数据（避免重复运行时叠加）
        LambdaQueryWrapper<FhzgSeLineLossDaily> deleteWrapper = new LambdaQueryWrapper<FhzgSeLineLossDaily>()
                .eq(FhzgSeLineLossDaily::getStatDate, date);
        lineLossDailyMapper.delete(deleteWrapper);

        // 批量插入
        List<FhzgSeLineLossDaily> dailyRecords = new ArrayList<>();
        for (Map.Entry<String, DailyAccumulator> entry : accumulatorMap.entrySet()) {
            String feederId = entry.getKey();
            DailyAccumulator acc = entry.getValue();

            BigDecimal avgKw = acc.count > 0
                    ? acc.sumKw.divide(BigDecimal.valueOf(acc.count), 4, RoundingMode.HALF_UP)
                    : BigDecimal.ZERO;

            FhzgSeLineLossDaily daily = FhzgSeLineLossDaily.builder()
                    .statDate(date)
                    .feederId(feederId)
                    .feederName(null) // 日内无条件单独查名称
                    .countyId(acc.countyId)
                    .subsId(acc.subsId)
                    .lineLossKwh(acc.sumKwh.setScale(2, RoundingMode.HALF_UP))
                    .maxLineLossKw(acc.maxKw.compareTo(BigDecimal.ZERO) > 0 ? acc.maxKw.setScale(4, RoundingMode.HALF_UP) : BigDecimal.ZERO)
                    .avgLineLossKw(avgKw.setScale(4, RoundingMode.HALF_UP))
                    .totalSnapshots(acc.count)
                    .createTime(LocalDateTime.now())
                    .updateTime(LocalDateTime.now())
                    .build();
            dailyRecords.add(daily);
        }

        for (FhzgSeLineLossDaily record : dailyRecords) {
            lineLossDailyMapper.insert(record);
        }

        String result = date + " 处理完成: " + dailyRecords.size() + " 条馈线线损日汇总";
        log.info("[线路线损日汇总] {}", result);
        return result;
    }

    /**
     * 日聚合累加器
     */
    private static class DailyAccumulator {
        String countyId;
        String subsId;
        BigDecimal sumKwh = BigDecimal.ZERO;
        BigDecimal maxKw = BigDecimal.ZERO;
        BigDecimal sumKw = BigDecimal.ZERO;
        int count = 0;
    }
}
