package com.usky.vpp.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.usky.vpp.constant.VppDrEventType;
import com.usky.vpp.domain.VppBiddingConfig;
import com.usky.vpp.domain.VppDrEvent;
import com.usky.vpp.domain.VppDrInvitation;
import com.usky.vpp.domain.VppResponseDeviationPrice;
import com.usky.vpp.mapper.VppBiddingConfigMapper;
import com.usky.vpp.mapper.VppResponseDeviationPriceMapper;
import com.usky.vpp.service.vo.SubsidyPredictionCalcResult;
import com.usky.vpp.util.VppAuditHelper;
import com.usky.vpp.util.VppResponseCoefficientHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.List;

/**
 * 补贴金额预测统一计算器。
 * <p>完成率/响应量系数 = 实际出清 / 预估响应容量（与偏差配置同一量纲，如 [0.6,0.8)），
 * 命中区间后取价格调整系数。补贴金额 = 预估响应容量 × 响应时长(h)
 * × 下浮系数对应竞价价格（削峰=最高价，填谷=最低价）× 价格调整系数。</p>
 */
@Component
public class VppSubsidyPredictionCalculator {

    private static final int SCALE = 4;
    private static final int RATIO_SCALE = 6;
    private static final BigDecimal MINUTES_PER_HOUR = BigDecimal.valueOf(60);
    private static final BigDecimal HUNDRED = BigDecimal.valueOf(100);

    @Autowired
    private VppResponseDeviationPriceMapper priceAdjustmentMapper;
    @Autowired
    private VppBiddingConfigMapper biddingConfigMapper;

    public List<VppResponseDeviationPrice> loadAdjustments(Integer tenantId) {
        return priceAdjustmentMapper.selectList(new LambdaQueryWrapper<VppResponseDeviationPrice>()
                .eq(VppResponseDeviationPrice::getDeleteFlag, VppAuditHelper.NOT_DELETED)
                .eq(tenantId != null, VppResponseDeviationPrice::getTenantId, tenantId)
                .orderByAsc(VppResponseDeviationPrice::getId));
    }

    public List<VppBiddingConfig> loadBiddingConfigs(Integer tenantId) {
        return biddingConfigMapper.selectList(new LambdaQueryWrapper<VppBiddingConfig>()
                .eq(VppBiddingConfig::getDeleteFlag, VppAuditHelper.NOT_DELETED)
                .eq(tenantId != null, VppBiddingConfig::getTenantId, tenantId));
    }

    public SubsidyPredictionCalcResult calculate(VppDrInvitation invitation, VppDrEvent event) {
        Integer tenantId = resolveTenantId(invitation, event);
        return calculate(invitation, event, loadAdjustments(tenantId), loadBiddingConfigs(tenantId));
    }

    /**
     * 按邀约 + 事件计算补贴预测。边界情况（预估为空/≤0、出清为空、区间未命中、竞价未唯一匹配等）金额记 0，不抛业务异常。
     */
    public SubsidyPredictionCalcResult calculate(VppDrInvitation invitation, VppDrEvent event,
                                                 List<VppResponseDeviationPrice> adjustments,
                                                 List<VppBiddingConfig> biddingConfigs) {
        SubsidyPredictionCalcResult result = new SubsidyPredictionCalcResult();
        LocalDateTime start = event == null ? null : event.getStartTime();
        LocalDateTime end = event == null ? null : event.getEndTime();
        int durationMin = resolveDurationMin(start, end);
        result.setResponseDurationMin(durationMin);

        BigDecimal estimatedKw = invitation == null ? null : invitation.getEstimatedResponseCapacityKw();
        BigDecimal clearedKw = invitation == null ? null : invitation.getActualClearedCapacityKw();
        // 预估为空或 ≤0 时无法计算完成率（分母），金额记 0
        if (estimatedKw == null || estimatedKw.compareTo(BigDecimal.ZERO) <= 0) {
            return result;
        }
        if (clearedKw == null) {
            return result;
        }

        BigDecimal estimated = estimatedKw;
        // 响应量系数/完成率口径：实际出清 ÷ 预估响应容量（如 60/100 = 0.6）
        BigDecimal coefficient = clearedKw.divide(estimated, RATIO_SCALE, RoundingMode.HALF_UP);
        result.setResponseCoefficient(coefficient);
        result.setCompletionRate(coefficient.multiply(HUNDRED).setScale(SCALE, RoundingMode.HALF_UP));
        result.setDeviationRate(BigDecimal.ONE.subtract(coefficient)
                .multiply(HUNDRED).setScale(SCALE, RoundingMode.HALF_UP));

        BigDecimal priceAdjustment = matchPriceAdjustment(coefficient, adjustments);
        result.setPriceAdjustmentCoefficient(priceAdjustment);
        if (priceAdjustment == null) {
            return result;
        }
        if (priceAdjustment.compareTo(BigDecimal.ZERO) == 0) {
            result.setEstimatedSubsidyAmount(BigDecimal.ZERO.setScale(SCALE, RoundingMode.HALF_UP));
            return result;
        }

        BigDecimal subsidyPrice = resolveSubsidyPrice(event, invitation, biddingConfigs);
        result.setSubsidyPrice(subsidyPrice);
        if (subsidyPrice == null || durationMin <= 0) {
            return result;
        }

        BigDecimal hours = BigDecimal.valueOf(durationMin).divide(MINUTES_PER_HOUR, RATIO_SCALE, RoundingMode.HALF_UP);
        // 金额 = 预估响应容量 × 时长(h) × 竞价价格 × 价格调整系数（不得用完成率替代价格调整系数）
        result.setEstimatedSubsidyAmount(estimated
                .multiply(hours)
                .multiply(subsidyPrice)
                .multiply(priceAdjustment)
                .setScale(SCALE, RoundingMode.HALF_UP));
        return result;
    }

    private static int resolveDurationMin(LocalDateTime start, LocalDateTime end) {
        if (start == null || end == null || !end.isAfter(start)) {
            return 0;
        }
        return (int) Duration.between(start, end).toMinutes();
    }

    private static BigDecimal matchPriceAdjustment(BigDecimal coefficient,
                                                   List<VppResponseDeviationPrice> adjustments) {
        if (coefficient == null || adjustments == null) {
            return null;
        }
        for (VppResponseDeviationPrice adjustment : adjustments) {
            if (VppResponseCoefficientHelper.matches(
                    adjustment.getCoeffLower(),
                    adjustment.getCoeffUpper(),
                    adjustment.getLowerInclusive(),
                    adjustment.getUpperInclusive(),
                    coefficient)) {
                return adjustment.getPriceAdjustmentCoefficient();
            }
        }
        return null;
    }

    /**
     * 用事件下浮系数匹配唯一竞价配置，再按削峰/填谷取最高价/最低价作为补贴标准。
     */
    private static BigDecimal resolveSubsidyPrice(VppDrEvent event, VppDrInvitation invitation,
                                                  List<VppBiddingConfig> biddingConfigs) {
        if (event == null || event.getFloatingCoefficient() == null || biddingConfigs == null) {
            return null;
        }
        VppBiddingConfig matched = null;
        for (VppBiddingConfig config : biddingConfigs) {
            if (config.getTypicalCoefficient() == null) {
                continue;
            }
            if (config.getTypicalCoefficient().compareTo(event.getFloatingCoefficient()) != 0) {
                continue;
            }
            if (matched != null) {
                return null;
            }
            matched = config;
        }
        if (matched == null) {
            return null;
        }
        Integer eventType = event.getEventType();
        if (eventType == null && invitation != null) {
            eventType = invitation.getTransactionType();
        }
        if (eventType == null) {
            return null;
        }
        if (eventType == VppDrEventType.PEAK) {
            return matched.getMaxPrice();
        }
        if (eventType == VppDrEventType.VALLEY) {
            return matched.getMinPrice();
        }
        return null;
    }

    private static Integer resolveTenantId(VppDrInvitation invitation, VppDrEvent event) {
        if (event != null && event.getTenantId() != null) {
            return event.getTenantId();
        }
        return invitation == null ? null : invitation.getTenantId();
    }
}
