/*
 * 文 件 名:  CarbonCalculationService
 * 版    权:  华设设计集团股份有限公司
 * 描    述:  <描述>
 * 修 改 人:  lvwenbin
 * 修改时间:  2025/6/25
 * 跟踪单号:  <跟踪单号>
 * 修改单号:  <修改单号>
 * 修改内容:  <修改内容>
 */
package com.ruoyi.ems.service.analysis;

import com.ruoyi.ems.domain.CaEmissionForecast;
import com.ruoyi.ems.domain.CaMeterD;
import com.ruoyi.ems.domain.ElecPgSupplyH;
import com.ruoyi.ems.domain.EmissionFactor;
import com.ruoyi.ems.domain.PlantCarbonSink;
import com.ruoyi.ems.mapper.CaEmissionForecastMapper;
import com.ruoyi.ems.mapper.CaMeterDMapper;
import com.ruoyi.ems.model.PvSupplyVO;
import com.ruoyi.ems.model.QueryMeter;
import com.ruoyi.ems.service.IElecPgSupplyHService;
import com.ruoyi.ems.service.IElecPvSupplyHService;
import com.ruoyi.ems.service.IEmissionFactorService;
import com.ruoyi.ems.service.IPlantCarbonSinkService;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 碳计算任务
 * <功能详细描述>
 *
 * @author lvwenbin
 * @version [版本号, 2025/6/25]
 * @see [相关类/方法]
 * @since [产品/模块版本]
 */
@Service
public class CarbonCalculationService {
    private static final Logger log = LoggerFactory.getLogger(CarbonCalculationService.class);

    private final DateTimeFormatter dateForm = DateTimeFormatter.ofPattern("yyyy-MM-dd");

    private final DateTimeFormatter monthForm = DateTimeFormatter.ofPattern("yyyy-MM");

    // 历史数据权重配置
    private static final double WEIGHT_CURRENT_MONTH = 0.5;  // 最近一个月权重

    private static final double WEIGHT_PREVIOUS_MONTH = 0.3; // 前一个月权重

    private static final double WEIGHT_OLDEST_MONTH = 0.2;   // 最早一个月权重

    @Resource
    private CaMeterDMapper caMeterDMapper;

    @Resource
    private CaEmissionForecastMapper caEmissionForecastMapper;

    @Resource
    private IElecPgSupplyHService pgSupplyHService;

    @Resource
    private IElecPvSupplyHService pvSupplyHService;

    @Resource
    private IEmissionFactorService emissionFactorService;

    @Autowired
    private IPlantCarbonSinkService service;

    /**
     * 计算日碳排量
     * <功能详细描述>
     *
     * @param areaCode 行政区划代码
     * @param date     日期
     */
    public void calculateDailyCarbonEmission(String areaCode, LocalDate date) {
        try {
            CaMeterD caMeterD = new CaMeterD();
            caMeterD.setAreaCode(areaCode);
            caMeterD.setDate(Date.from(date.atStartOfDay(ZoneId.systemDefault()).toInstant()));

            // 计算碳排放量（基于电网用电数据）
            ElecPgSupplyH totalElectricity = getDailyElectricityConsumption(areaCode, date);
            BigDecimal emission = calculateCarbonEmission(
                totalElectricity != null ? totalElectricity.getUseElecQuantity() : null);
            caMeterD.setCaEmissionQuantity(emission.doubleValue());

            // 计算碳汇量（基于光伏产能数据）
            PvSupplyVO totalPVProduction = getDailyPVProduction(areaCode, date);
            BigDecimal pvSink = calculatePvCarbonSink(totalPVProduction);
            BigDecimal plantSink = calculatePlantCarbonSink(areaCode);
            caMeterD.setCaSinkQuantity(pvSink.add(plantSink).doubleValue());

            caMeterDMapper.deleteCaMeterDByArea(areaCode, date.format(dateForm));
            caMeterDMapper.insertCaMeterD(caMeterD);
        }
        catch (Exception e) {
            log.error("calculate daily carbon emission error! areaCode:{}, date:{}", areaCode, date.format(dateForm),
                e);
        }
    }

    public void calculateMonthCarbonForecast(String areaCode, LocalDate date) {
        try {
            List<ElecPgSupplyH> totalElectricitys = getMonthElectricityConsumption(areaCode, date);
            Map<String, Double> monthElectricityMap = totalElectricitys.stream()
                .collect(Collectors.toMap(ElecPgSupplyH::getStartRecTime, ElecPgSupplyH::getUseElecQuantity));
            Double totalElectricity = calculateForecastElectricity(monthElectricityMap);
            Double emission = calculateCarbonEmission(totalElectricity).doubleValue();

            CaEmissionForecast caEmissionForecast = new CaEmissionForecast();
            caEmissionForecast.setAreaCode(areaCode);
            caEmissionForecast.setMonth(date.format(monthForm));
            caEmissionForecast.setCaEmission(emission);

            caEmissionForecastMapper.deleteCaMeterDByArea(areaCode, date.format(monthForm));
            caEmissionForecastMapper.insertCaEmissionForecast(caEmissionForecast);
        }
        catch (Exception e) {
            log.error("calculate month carbon forecast error! areaCode:{}, date:{}", areaCode, date.format(dateForm),
                e);
        }
    }

    private PvSupplyVO getDailyPVProduction(String areaCode, LocalDate date) {
        QueryMeter queryMeter = new QueryMeter();
        queryMeter.setAreaCode(areaCode);
        queryMeter.setDate(date.format(dateForm));
        queryMeter.setTimeDimension("day");
        List<PvSupplyVO> res = pvSupplyHService.selectPvSupplyList(queryMeter);
        return res.isEmpty() ? null : res.get(0);
    }

    private ElecPgSupplyH getDailyElectricityConsumption(String areaCode, LocalDate date) {
        QueryMeter queryMeter = new QueryMeter();
        queryMeter.setAreaCode(areaCode);
        queryMeter.setDate(date.format(dateForm));
        return pgSupplyHService.calDaySupplyByH(queryMeter);
    }

    private List<ElecPgSupplyH> getMonthElectricityConsumption(String areaCode, LocalDate date) {
        LocalDate startTime = date.minusMonths(3).withDayOfMonth(1);
        LocalDate endTime = date.minusMonths(1).with(TemporalAdjusters.lastDayOfMonth());

        ElecPgSupplyH queryMeter = new ElecPgSupplyH();
        queryMeter.setAreaCode(areaCode);
        queryMeter.setStartRecTime(startTime.format(dateForm));
        queryMeter.setEndRecTime(endTime.format(dateForm));
        return pgSupplyHService.calMonthSupplyByH(queryMeter);
    }

    // 计算碳排放量
    private BigDecimal calculateCarbonEmission(Double useElecQuantity) {
        EmissionFactor factor = emissionFactorService.selectByRegion("CN", 1);

        if (null == factor || useElecQuantity == null || factor.getFactorValue() == null) {
            return BigDecimal.ZERO;
        }

        return round(useElecQuantity * factor.getFactorValue(), 2);
    }

    /**
     * 计算光伏碳汇量
     *
     * @param totalPVProduction 光伏发电数据
     * @return 碳汇量
     */
    private BigDecimal calculatePvCarbonSink(PvSupplyVO totalPVProduction) {
        EmissionFactor factor = emissionFactorService.selectByRegion("CN", 1);

        if (null == factor || totalPVProduction == null || factor.getFactorValue() == null
            || totalPVProduction.getGenElecQuantity() == null) {
            return BigDecimal.ZERO;
        }

        return round(totalPVProduction.getGenElecQuantity().doubleValue() * factor.getFactorValue(), 2);
    }

    /**
     * 计算绿植碳汇量
     *
     * @return 碳汇量
     */
    private BigDecimal calculatePlantCarbonSink(String areaCode) {
        List<PlantCarbonSink> sinkCfgs = service.getAreaCfg(areaCode);

        // 初始值应为0，但建议命名为totalCarbonSink提升可读性
        BigDecimal totalCarbonSink = BigDecimal.ZERO;

        if (CollectionUtils.isNotEmpty(sinkCfgs)) {
            for (PlantCarbonSink sinkCfg : sinkCfgs) {
                // 推荐使用valueOf避免字符串转换
                BigDecimal q = BigDecimal.valueOf(sinkCfg.getCarbonQuantity());
                BigDecimal a = BigDecimal.valueOf(sinkCfg.getPlantSize());

                // 必须接收累加结果
                totalCarbonSink = totalCarbonSink.add(q.multiply(a));
            }

            totalCarbonSink = totalCarbonSink.divide(BigDecimal.valueOf(365), 2, RoundingMode.HALF_UP);
        }

        return totalCarbonSink;
    }

    // 数值四舍五入保留指定小数位
    private BigDecimal round(double value, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("scale must be non-negative");
        }
        BigDecimal bd = BigDecimal.valueOf(value);
        return bd.setScale(scale, RoundingMode.HALF_UP);
    }

    // 计算预测电网购电量
    private double calculateForecastElectricity(Map<String, Double> historicalData) {
        // 按月份排序（从近到远）
        List<Map.Entry<String, Double>> sortedData = historicalData.entrySet().stream()
            .sorted(Map.Entry.<String, Double>comparingByKey().reversed()).collect(Collectors.toList());

        double forecast = 0.0;

        // 加权平均计算
        if (sortedData.size() >= 3) {
            forecast = sortedData.get(0).getValue() * WEIGHT_CURRENT_MONTH
                + sortedData.get(1).getValue() * WEIGHT_PREVIOUS_MONTH
                + sortedData.get(2).getValue() * WEIGHT_OLDEST_MONTH;
        }
        else if (sortedData.size() == 2) {
            // 如果只有两个月数据，平均分配权重
            forecast = sortedData.get(0).getValue() * 0.6 + sortedData.get(1).getValue() * 0.4;
        }
        else if (sortedData.size() == 1) {
            // 如果只有一个月数据，直接使用
            forecast = sortedData.get(0).getValue();
        }

        return forecast;
    }
}