package com.ruoyi.system.service.impl;

import java.math.BigDecimal;
import java.util.Collections;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.system.domain.PosFood;
import com.ruoyi.system.domain.PromotionActivity;
import com.ruoyi.system.domain.PromotionActivityRule;
import com.ruoyi.system.domain.PromotionCouponBatch;
import com.ruoyi.system.domain.PromotionCouponRule;
import com.ruoyi.system.mapper.PosFoodMapper;
import com.ruoyi.system.service.ISysConfigService;

/**
 * 优惠幅度限制校验器（防止恶意竞争，平台开关控制）。
 *
 * 为什么只做"创建侧单项校验"：订单级的总优惠上限由算价侧预算统一封顶
 * （见 PromotionCalcServiceImpl），这里只负责在商家新增/修改时拦住
 * 明显超比例的设置，避免"挂大招牌、结账被砍"的虚假宣传。
 *
 * 口径（specs/008-promotion-coupon spec.md FR-024）：
 * - 满减促销每档 / 满减券 / 免配送费券：减免 ≤ 比例 × 门槛
 * - 折扣促销每商品 / 商品折扣券：折扣率 ≥ 1 − 比例（如20%时最低8折）
 * - 商品抵用券：减免 ≤ 比例 × 商品价格
 * - 第二杯半价、新客立减：创建侧不校验（前者结构性封顶25%，后者无门槛无分母、由算价预算管）
 */
@Component
public class PromotionDiscountLimitChecker
{
    /** 开关参数：sys_config，true=启用限制 */
    private static final String CONFIG_ENABLED = "promotion.discount.limit.enabled";

    /** 比例参数：sys_config，单位百分比（20 = 20%） */
    private static final String CONFIG_RATIO = "promotion.discount.limit.ratio";

    /** 比例缺省值（百分比），参数缺失或非法时兜底，保证开关+参数未配置时限制不生效/不误伤 */
    private static final BigDecimal DEFAULT_RATIO_PERCENT = new BigDecimal("20");

    private static final BigDecimal HUNDRED = new BigDecimal("100");

    @Autowired
    private ISysConfigService configService;

    @Autowired
    private PosFoodMapper posFoodMapper;

    /**
     * 校验促销活动规则（活动创建/修改时调用）。开关关闭时不做任何校验。
     */
    public void checkActivity(PromotionActivity activity, List<PromotionActivityRule> rules)
    {
        if (!isEnabled() || activity == null || activity.getType() == null
                || rules == null || rules.isEmpty())
        {
            return;
        }
        Integer type = activity.getType();
        if (type == 1)
        {
            checkManjianRules(rules);
        }
        else if (type == 2)
        {
            checkDiscountRules(rules);
        }
        // type=3 第二杯半价 / type=4 新客立减：创建侧不校验
    }

    /**
     * 校验优惠券规则（券批次创建/规则被替换时调用）。开关关闭时不做任何校验。
     */
    public void checkCouponBatch(PromotionCouponBatch batch, PromotionCouponRule rule)
    {
        checkCouponBatch(batch, rule == null ? null : Collections.singletonList(rule));
    }

    /**
     * 校验优惠券规则（多商品券）。开关关闭时不做任何校验。
     */
    public void checkCouponBatch(PromotionCouponBatch batch, List<PromotionCouponRule> rules)
    {
        if (!isEnabled() || batch == null || batch.getCouponType() == null
                || rules == null || rules.isEmpty())
        {
            return;
        }
        Integer type = batch.getCouponType();
        if (type == 1)
        {
            checkManjianCoupons(rules);
        }
        else if (type == 2)
        {
            checkProductCoupons(rules);
        }
        else if (type == 3)
        {
            checkFreeDeliveryCoupons(rules);
        }
    }

    /**
     * 满减促销：每个档位 减免 ≤ 比例 × 门槛。
     * 无门槛或无减免的档位没有比例锚点，跳过（算价预算仍会封顶）。
     */
    private void checkManjianRules(List<PromotionActivityRule> rules)
    {
        BigDecimal percent = getRatioPercent();
        BigDecimal ratio = percent.movePointLeft(2);
        for (PromotionActivityRule rule : rules)
        {
            if (rule == null || rule.getThreshold() == null || rule.getReduceAmount() == null
                    || rule.getThreshold().compareTo(BigDecimal.ZERO) <= 0)
            {
                continue;
            }
            BigDecimal maxReduce = rule.getThreshold().multiply(ratio);
            if (rule.getReduceAmount().compareTo(maxReduce) > 0)
            {
                throw new ServiceException(String.format(
                        "满减档位(满%s减%s)超过最大优惠幅度%s%%，最多可减%s",
                        fmt(rule.getThreshold()), fmt(rule.getReduceAmount()),
                        fmt(percent), fmt(maxReduce)));
            }
        }
    }

    /**
     * 折扣促销：每个商品折扣率 ≥ 1 − 比例（如20%时最低8折）。
     */
    private void checkDiscountRules(List<PromotionActivityRule> rules)
    {
        BigDecimal percent = getRatioPercent();
        BigDecimal floor = BigDecimal.ONE.subtract(percent.movePointLeft(2));
        for (PromotionActivityRule rule : rules)
        {
            if (rule == null || rule.getDiscountRate() == null)
            {
                continue;
            }
            if (rule.getDiscountRate().compareTo(floor) < 0)
            {
                throw new ServiceException(String.format(
                        "商品折扣(%s折)低于最低限制%s折（最大优惠幅度%s%%）",
                        zhe(rule.getDiscountRate()), zhe(floor), fmt(percent)));
            }
        }
    }

    /**
     * 满减券：减免 ≤ 比例 × 使用门槛。
     * 无门槛满减券没有比例锚点，创建侧放行（算价预算按订单总账封顶）。
     */
    private void checkManjianCoupons(List<PromotionCouponRule> rules)
    {
        BigDecimal percent = getRatioPercent();
        BigDecimal ratio = percent.movePointLeft(2);
        for (PromotionCouponRule rule : rules)
        {
            if (rule == null || rule.getAmount() == null
                    || rule.getThreshold() == null || rule.getThreshold().compareTo(BigDecimal.ZERO) <= 0)
            {
                continue;
            }
            BigDecimal maxReduce = rule.getThreshold().multiply(ratio);
            if (rule.getAmount().compareTo(maxReduce) > 0)
            {
                throw new ServiceException(String.format(
                        "优惠券减免金额%s超过最大优惠幅度%s%%（门槛%s下最多减%s）",
                        fmt(rule.getAmount()), fmt(percent),
                        fmt(rule.getThreshold()), fmt(maxReduce)));
            }
        }
    }

    /**
     * 商品券：折扣券 折扣率 ≥ 1 − 比例；抵用券 减免 ≤ 比例 × 商品价格。
     * 商品不存在/无价格时券在算价侧命中不了任何商品（减0），不构成实际优惠，跳过校验。
     */
    private void checkProductCoupons(List<PromotionCouponRule> rules)
    {
        BigDecimal percent = getRatioPercent();
        BigDecimal ratio = percent.movePointLeft(2);
        BigDecimal floor = BigDecimal.ONE.subtract(ratio);
        for (PromotionCouponRule rule : rules)
        {
            if (rule == null)
            {
                continue;
            }
            if (rule.getDiscountRate() != null)
            {
                if (rule.getDiscountRate().compareTo(floor) < 0)
                {
                    throw new ServiceException(String.format(
                            "商品券折扣(%s折)低于最低限制%s折（最大优惠幅度%s%%）",
                            zhe(rule.getDiscountRate()), zhe(floor), fmt(percent)));
                }
                continue;
            }
            if (rule.getAmount() == null || rule.getAmount().compareTo(BigDecimal.ZERO) <= 0
                    || rule.getProductId() == null)
            {
                continue;
            }
            PosFood food = posFoodMapper.selectById(rule.getProductId());
            if (food == null || food.getPrice() == null
                    || food.getPrice().compareTo(BigDecimal.ZERO) <= 0)
            {
                continue;
            }
            BigDecimal maxReduce = food.getPrice().multiply(ratio);
            if (rule.getAmount().compareTo(maxReduce) > 0)
            {
                throw new ServiceException(String.format(
                        "抵用券减免金额%s超过商品(%s, %s元)最大优惠幅度%s%%，最多可减%s",
                        fmt(rule.getAmount()), rule.getProductId(), fmt(food.getPrice()),
                        fmt(percent), fmt(maxReduce)));
            }
        }
    }

    /**
     * 免配送费券：必须设使用门槛，且减免 ≤ 比例 × 门槛。
     * 无门槛时没有比例锚点，而现有算价把免配送费券当普通减免从餐费里扣，
     * 无门槛大额券等同无上限满减券，所以这里直接拒绝并提示设门槛。
     */
    private void checkFreeDeliveryCoupons(List<PromotionCouponRule> rules)
    {
        BigDecimal percent = getRatioPercent();
        BigDecimal ratio = percent.movePointLeft(2);
        for (PromotionCouponRule rule : rules)
        {
            if (rule == null || rule.getAmount() == null)
            {
                continue;
            }
            if (rule.getThreshold() == null || rule.getThreshold().compareTo(BigDecimal.ZERO) <= 0)
            {
                throw new ServiceException("免配送费券需设置使用门槛，无门槛时无法限定优惠幅度");
            }
            BigDecimal maxReduce = rule.getThreshold().multiply(ratio);
            if (rule.getAmount().compareTo(maxReduce) > 0)
            {
                throw new ServiceException(String.format(
                        "免配送费券减免金额%s超过最大优惠幅度%s%%（门槛%s下最多减%s）",
                        fmt(rule.getAmount()), fmt(percent),
                        fmt(rule.getThreshold()), fmt(maxReduce)));
            }
        }
    }

    /**
     * 开关是否启用。参数未配置（SQL 未执行）视为关闭，行为与现状完全一致。
     * 算价侧（PromotionCalcServiceImpl）复用同一开关决定预算是否生效。
     */
    public boolean isEnabled()
    {
        return "true".equalsIgnoreCase(configService.selectConfigByKey(CONFIG_ENABLED));
    }

    /**
     * 读取优惠上限比例（百分比）。参数缺失或非法时回退默认20，避免运营改错参数导致校验瘫痪。
     * 算价侧复用：预算 = 比例 × 订单商品原价合计。
     */
    public BigDecimal getRatioPercent()
    {
        try
        {
            String value = configService.selectConfigByKey(CONFIG_RATIO);
            if (value != null && !value.trim().isEmpty())
            {
                BigDecimal percent = new BigDecimal(value.trim());
                if (percent.compareTo(BigDecimal.ZERO) > 0 && percent.compareTo(HUNDRED) < 0)
                {
                    return percent;
                }
            }
        }
        catch (NumberFormatException ignored)
        {
            // 非法值走默认比例
        }
        return DEFAULT_RATIO_PERCENT;
    }

    /** 金额展示：去掉无意义的尾零（40.00 → 40） */
    private String fmt(BigDecimal value)
    {
        return value.stripTrailingZeros().toPlainString();
    }

    /** 折扣率转"折"展示：0.80 → 8 */
    private String zhe(BigDecimal rate)
    {
        return rate.multiply(BigDecimal.TEN).stripTrailingZeros().toPlainString();
    }
}
