package com.ycwl.basic.pricing.service.impl;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ycwl.basic.pricing.dto.*;
import com.ycwl.basic.pricing.entity.PriceCouponClaimRecord;
import com.ycwl.basic.pricing.entity.PriceCouponConfig;
import com.ycwl.basic.pricing.enums.CouponStatus;
import com.ycwl.basic.pricing.enums.CouponType;
import com.ycwl.basic.pricing.enums.ProductType;
import com.ycwl.basic.pricing.exception.CouponInvalidException;
import com.ycwl.basic.pricing.mapper.PriceCouponClaimRecordMapper;
import com.ycwl.basic.pricing.mapper.PriceCouponConfigMapper;
import com.ycwl.basic.pricing.service.ICouponService;
import lombok.RequiredArgsConstructor;

import java.util.Date;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.NoTransactionException;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

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

/**
 * 优惠券服务实现
 */
@Slf4j
@Service("pricingCouponServiceImpl")
@RequiredArgsConstructor
public class CouponServiceImpl implements ICouponService {
    
    private final PriceCouponConfigMapper couponConfigMapper;
    private final PriceCouponClaimRecordMapper couponClaimRecordMapper;
    private final ObjectMapper objectMapper;
    
    @Override
    public CouponInfo selectBestCoupon(Long userId, List<ProductItem> products, BigDecimal totalAmount) {
        List<PriceCouponClaimRecord> userCoupons = couponClaimRecordMapper.selectUserAvailableCoupons(userId);
        if (userCoupons.isEmpty()) {
            return null;
        }
        
        CouponInfo bestCoupon = null;
        BigDecimal maxDiscount = BigDecimal.ZERO;
        
        for (PriceCouponClaimRecord record : userCoupons) {
            PriceCouponConfig coupon = couponConfigMapper.selectById(record.getCouponId());
            if (coupon == null || !isCouponApplicable(coupon, products, totalAmount)) {
                continue;
            }
            
            BigDecimal discount = calculateCouponDiscount(coupon, products, totalAmount);
            if (discount.compareTo(maxDiscount) > 0) {
                maxDiscount = discount;
                bestCoupon = buildCouponInfo(coupon, discount);
            }
        }
        
        return bestCoupon;
    }
    
    @Override
    public BigDecimal calculateCouponDiscount(PriceCouponConfig coupon, List<ProductItem> products, BigDecimal totalAmount) {
        if (!isCouponApplicable(coupon, products, totalAmount)) {
            return BigDecimal.ZERO;
        }
        
        BigDecimal discount;
        if (coupon.getCouponType() == CouponType.PERCENTAGE) {
            discount = totalAmount.multiply(coupon.getDiscountValue().divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP));
            if (coupon.getMaxDiscount() != null && discount.compareTo(coupon.getMaxDiscount()) > 0) {
                discount = coupon.getMaxDiscount();
            }
        } else {
            discount = coupon.getDiscountValue();
        }
        
        return discount.setScale(2, RoundingMode.HALF_UP);
    }
    
    @Override
    public boolean isCouponApplicable(PriceCouponConfig coupon, List<ProductItem> products, BigDecimal totalAmount) {
        // 1. 检查最小使用金额
        if (totalAmount.compareTo(coupon.getMinAmount()) < 0) {
            return false;
        }
        
        // 2. 检查景区限制
        if (coupon.getScenicId() != null && !coupon.getScenicId().isEmpty()) {
            boolean hasMatchingScenicProduct = false;
            for (ProductItem product : products) {
                if (coupon.getScenicId().equals(product.getScenicId())) {
                    hasMatchingScenicProduct = true;
                    break;
                }
            }
            if (!hasMatchingScenicProduct) {
                log.debug("优惠券景区限制不匹配: 优惠券景区={}, 商品景区={}", 
                    coupon.getScenicId(), 
                    products.stream().map(ProductItem::getScenicId).distinct().toList());
                return false;
            }
        }
        
        // 3. 检查商品类型限制
        if (coupon.getApplicableProducts() == null || coupon.getApplicableProducts().isEmpty()) {
            return true;
        }
        
        try {
            List<String> applicableProductTypes = objectMapper.readValue(
                    coupon.getApplicableProducts(), new TypeReference<List<String>>() {});
            
            for (ProductItem product : products) {
                if (applicableProductTypes.contains(product.getProductType().getCode())) {
                    return true;
                }
            }
            return false;
        } catch (Exception e) {
            log.error("解析适用商品类型失败", e);
            return false;
        }
    }
    
    @Override
    @Transactional
    public CouponUseResult useCoupon(CouponUseRequest request) {
        PriceCouponClaimRecord record = couponClaimRecordMapper.selectUserCouponRecord(
                request.getUserId(), request.getCouponId());
        
        if (record == null) {
            throw new CouponInvalidException("用户未拥有该优惠券");
        }
        
        if (record.getStatus() != CouponStatus.CLAIMED) {
            throw new CouponInvalidException("优惠券状态无效: " + record.getStatus());
        }
        
        int updateCount = couponConfigMapper.incrementUsedQuantity(request.getCouponId());
        if (updateCount == 0) {
            throw new CouponInvalidException("优惠券使用失败，可能已达到使用上限");
        }
        
        Date useTime = new Date();
        
        // 设置使用时间、订单信息和景区信息
        record.setStatus(CouponStatus.USED);
        record.setUseTime(useTime);
        record.setOrderId(request.getOrderId());
        record.setUpdateTime(new Date());
        
        // 如果请求中包含景区ID，记录到使用记录中
        if (request.getScenicId() != null && !request.getScenicId().isEmpty()) {
            record.setScenicId(request.getScenicId());
        }
        
        couponClaimRecordMapper.updateCouponStatus(
                record.getId(), CouponStatus.USED, useTime, request.getOrderId(), request.getScenicId());
        
        CouponUseResult result = new CouponUseResult();
        result.setCouponId(request.getCouponId());
        result.setUserId(request.getUserId());
        result.setOrderId(request.getOrderId());
        result.setUseTime(useTime);
        result.setDiscountAmount(request.getDiscountAmount());
        
        return result;
    }
    
    @Override
    public List<CouponInfo> getUserAvailableCoupons(Long userId) {
        List<PriceCouponClaimRecord> records = couponClaimRecordMapper.selectUserAvailableCoupons(userId);
        List<CouponInfo> coupons = new ArrayList<>();
        
        for (PriceCouponClaimRecord record : records) {
            PriceCouponConfig config = couponConfigMapper.selectById(record.getCouponId());
            if (config != null) {
                coupons.add(buildCouponInfo(config, null));
            }
        }
        
        return coupons;
    }
    
    private CouponInfo buildCouponInfo(PriceCouponConfig coupon, BigDecimal actualDiscountAmount) {
        CouponInfo info = new CouponInfo();
        info.setCouponId(coupon.getId());
        info.setCouponName(coupon.getCouponName());
        info.setDiscountType(coupon.getCouponType());
        info.setDiscountValue(coupon.getDiscountValue());
        info.setActualDiscountAmount(actualDiscountAmount);
        info.setUserClaimLimit(coupon.getUserClaimLimit());
        return info;
    }
    
    @Override
    @Transactional
    public CouponClaimResult claimCoupon(CouponClaimRequest request) {
        log.info("开始领取优惠券: userId={}, couponId={}, scenicId={}", 
                request.getUserId(), request.getCouponId(), request.getScenicId());
        
        try {
            // 1. 参数验证
            if (request.getUserId() == null || request.getCouponId() == null) {
                return CouponClaimResult.failure(CouponClaimResult.ERROR_INVALID_PARAMS, "用户ID和优惠券ID不能为空");
            }
            
            // 2. 查询优惠券配置
            PriceCouponConfig coupon = couponConfigMapper.selectById(request.getCouponId());
            if (coupon == null || coupon.getDeleted() == 1) {
                return CouponClaimResult.failure(CouponClaimResult.ERROR_COUPON_NOT_FOUND, "优惠券不存在");
            }
            
            // 3. 检查优惠券是否启用
            if (!Boolean.TRUE.equals(coupon.getIsActive())) {
                return CouponClaimResult.failure(CouponClaimResult.ERROR_COUPON_INACTIVE, "优惠券已停用");
            }
            
            // 4. 检查优惠券有效期
            LocalDateTime now = LocalDateTime.now();
            if (coupon.getValidFrom() != null && now.isBefore(coupon.getValidFrom())) {
                return CouponClaimResult.failure(CouponClaimResult.ERROR_COUPON_EXPIRED, "优惠券尚未生效");
            }
            if (coupon.getValidUntil() != null && now.isAfter(coupon.getValidUntil())) {
                return CouponClaimResult.failure(CouponClaimResult.ERROR_COUPON_EXPIRED, "优惠券已过期");
            }
            
            // 5. 检查库存（如果有总量限制）
            // totalQuantity为NULL或0表示不限制总量
            if (coupon.getTotalQuantity() != null && coupon.getTotalQuantity() > 0) {
                int currentClaimed = (coupon.getClaimedQuantity() == null ? 0 : coupon.getClaimedQuantity());
                if (currentClaimed >= coupon.getTotalQuantity()) {
                    return CouponClaimResult.failure(CouponClaimResult.ERROR_COUPON_OUT_OF_STOCK, "优惠券已领完");
                }
            }

            // 6. 检查用户领取数量限制
            if (coupon.getUserClaimLimit() != null && coupon.getUserClaimLimit() > 0) {
                int userClaimCount = couponClaimRecordMapper.countUserCouponClaims(
                        request.getUserId(), request.getCouponId());
                // countUserCouponClaims 使用 FOR UPDATE + 复合索引，确保并发下的计数准确
                if (userClaimCount >= coupon.getUserClaimLimit()) {
                    return CouponClaimResult.failure(
                            CouponClaimResult.ERROR_CLAIM_LIMIT_REACHED,
                            "您已达到该优惠券的领取上限(" + coupon.getUserClaimLimit() + "张)");
                }
            }

            // 7. 创建领取记录
            Date claimTime = new Date();
            PriceCouponClaimRecord claimRecord = new PriceCouponClaimRecord();
            claimRecord.setCouponId(request.getCouponId());
            claimRecord.setUserId(request.getUserId());
            claimRecord.setClaimTime(claimTime);
            claimRecord.setStatus(CouponStatus.CLAIMED);
            claimRecord.setScenicId(request.getScenicId());
            claimRecord.setCreateTime(claimTime);
            claimRecord.setUpdateTime(claimTime);
            claimRecord.setDeleted(0);

            // 8. 插入领取记录
            int insertResult = couponClaimRecordMapper.insert(claimRecord);
            if (insertResult <= 0) {
                log.error("插入优惠券领取记录失败: userId={}, couponId={}",
                        request.getUserId(), request.getCouponId());
                return CouponClaimResult.failure(CouponClaimResult.ERROR_SYSTEM_ERROR, "领取失败，请稍后重试");
            }

            // 9. 更新优惠券已领取数量（区分于已使用数量）
            // 仅在有总量限制时才更新claimedQuantity(totalQuantity为正整数)
            if (coupon.getTotalQuantity() != null && coupon.getTotalQuantity() > 0) {
                int affected = couponConfigMapper.incrementClaimedQuantityIfAvailable(coupon.getId());
                if (affected == 0) {
                    throw new CouponInvalidException(
                            CouponClaimResult.ERROR_COUPON_OUT_OF_STOCK,
                            "优惠券已被领取完，请稍后重试");
                }
                int updatedClaimedQuantity = (coupon.getClaimedQuantity() == null ? 0 : coupon.getClaimedQuantity()) + 1;
                coupon.setClaimedQuantity(updatedClaimedQuantity);
            }

            log.info("优惠券领取成功: userId={}, couponId={}, claimRecordId={}",
                    request.getUserId(), request.getCouponId(), claimRecord.getId());

            // 10. 返回成功结果
            return CouponClaimResult.success(claimRecord, coupon);
            
        } catch (CouponInvalidException e) {
            markRollbackOnly();
            log.warn("领取优惠券失败(业务校验不通过): userId={}, couponId={}, reason={}",
                    request.getUserId(), request.getCouponId(), e.getMessage());
            String errorCode = e.getErrorCode() == null
                    ? CouponClaimResult.ERROR_SYSTEM_ERROR
                    : e.getErrorCode();
            return CouponClaimResult.failure(errorCode, e.getMessage());
        } catch (Exception e) {
            markRollbackOnly();
            log.error("领取优惠券失败: userId={}, couponId={}",
                    request.getUserId(), request.getCouponId(), e);
            return CouponClaimResult.failure(CouponClaimResult.ERROR_SYSTEM_ERROR, "系统错误，领取失败：" + e.getMessage());
        }
    }

    private void markRollbackOnly() {
        try {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        } catch (NoTransactionException ex) {
            log.debug("未检测到Spring事务上下文，跳过回滚标记");
        }
    }
}
