package com.ylx.coupon.service.impl;


import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ylx.common.exception.ServiceException;
import com.ylx.coupon.domain.Coupon;
import com.ylx.coupon.mapper.CouponMapper;
import com.ylx.coupon.service.CouponReceiveService;
import com.ylx.coupon.service.CouponService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Map;

/**
 * 优惠券的规则信息(Coupon)表服务实现类
 *
 * @author makejava
 * @since 2024-05-13 16:32:59
 */
@Slf4j
@Service("couponService")
public class CouponServiceImpl extends ServiceImpl<CouponMapper, Coupon> implements CouponService {

    @Resource
    private CouponReceiveService couponReceiveService;

    @Override
    public BigDecimal calculateDiscountAmount(String couponId, String openId, BigDecimal orderAmount) {

        // 1. 查询优惠券领取详情
        Map<String, Object> detail = couponReceiveService.selectCouponDetailForCalc(couponId, openId);

        if (CollUtil.isEmpty(detail)) {
            throw new ServiceException("未查询到该用户的优惠券信息");
        }

        // 3. 校验优惠券状态（必须是 0：待使用）
        Integer couponStatus = (Integer) detail.get("coupon_status");
        if (couponStatus == null || couponStatus != 0) {
            throw new ServiceException("优惠券状态异常，不可使用");
        }

        // 4. 校验有效期
        LocalDate validStart = ((java.sql.Date) detail.get("valid_start_time")).toLocalDate();
        LocalDate expiration = ((java.sql.Date) detail.get("expiration_time")).toLocalDate();
        LocalDate today = LocalDate.now();
        if (today.isBefore(validStart) || today.isAfter(expiration)) {
            throw new ServiceException("优惠券不在有效期内");
        }

        // 5. 提取优惠规则字段
        Integer discountType = (Integer) detail.get("discount_type");
        BigDecimal discountValue = (BigDecimal) detail.get("discount_value");
        BigDecimal rebValue = (BigDecimal) detail.get("reb_value");
        BigDecimal thresholdAmount = (BigDecimal) detail.get("threshold_amount");

        // 6. 根据类型计算抵扣金额
        BigDecimal discountAmount = BigDecimal.ZERO;
        switch (discountType) {
            case 1: // 无门槛
                discountAmount = discountValue;
                break;
            case 2: // 折扣 (reb_value 例如 0.8 代表8折)
                // 抵扣金额 = 订单金额 * (1 - 折扣值)
                discountAmount = orderAmount.multiply(BigDecimal.ONE.subtract(rebValue));
                break;
            case 3: // 满减
                if (orderAmount.compareTo(thresholdAmount) < 0) {
                    throw new ServiceException("订单金额未达到满减门槛");
                }
                discountAmount = discountValue;
                break;
            default:
                throw new ServiceException("未知的优惠券类型: " + discountType);
        }

        // 7. 兜底逻辑：抵扣金额不能大于订单实际金额
        if (discountAmount.compareTo(orderAmount) > 0) {
            discountAmount = orderAmount;
        }

        // 8. 保留两位小数，四舍五入
        return discountAmount.setScale(2, RoundingMode.HALF_UP);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void useCoupon(String couponId, String openId, Long orderId, Integer orderType) {
        // 1. 乐观锁核销用户领取记录
        int updatedRows = couponReceiveService.useCouponOptimisticLock(couponId, openId, orderId, orderType, LocalDateTime.now());
        if (updatedRows == 0) {
            throw new ServiceException("优惠券核销失败：状态异常或已被使用");
        }

        // 2. 规则表使用数量 +1
        int ruleUpdatedRows = couponReceiveService.incrementUsedNum(couponId);
        if (ruleUpdatedRows == 0) {
            throw new ServiceException("优惠券规则状态异常，核销中止");
        }
        log.info("优惠券核销成功: couponId={}, openId={}, orderId={}", couponId, openId, orderId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void returnCoupon(String couponId, String openId, Long orderId) {
        // 1. 将已使用的券退回到待使用状态
        int rows = couponReceiveService.returnCouponOptimisticLock(couponId, openId, orderId);
        if (rows == 0) {
            log.warn("退还优惠券失败，可能券已过期或不属于该订单: couponId={}, orderId={}", couponId, orderId);
            return;
        }

        // 2. 规则表使用数量 -1
        this.couponReceiveService.decrementUsedNum(couponId);
        log.info("优惠券退还成功: couponId={}, orderId={}", couponId, orderId);
    }
}

