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.annotation.Transactional;

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);
        return info;
    }
}