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.dto.resp.UserCouponResp;
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.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 applicableProductsTotal = calculateApplicableProductsTotal(coupon, products);

        BigDecimal discount;
        if (coupon.getCouponType() == CouponType.PERCENTAGE) {
            // 百分比优惠券基于适用商品总价计算,而非购物车总价
            discount = applicableProductsTotal.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();
        }

        // 限制优惠金额不超过适用商品总价,防止优惠溢出到其他SKU
        if (discount.compareTo(applicableProductsTotal) > 0) {
            discount = applicableProductsTotal;
        }

        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. 检查商品类型限制（用于确定可折扣商品范围）
        List<ProductItem> discountableProducts = products;
        if (coupon.getApplicableProducts() != null && !coupon.getApplicableProducts().isEmpty()) {
            try {
                List<String> applicableProductTypes = objectMapper.readValue(
                    coupon.getApplicableProducts(), new TypeReference<List<String>>() {});

                // 空数组表示不限制商品类型，适用于所有商品
                if (applicableProductTypes == null || applicableProductTypes.isEmpty()) {
                    // 不过滤，使用全部商品
                } else {
                    discountableProducts = products.stream()
                        .filter(product -> applicableProductTypes.contains(product.getProductType().getCode()))
                        .toList();

                    if (discountableProducts.isEmpty()) {
                        return false;
                    }
                }
            } catch (Exception e) {
                log.error("解析适用商品类型失败", e);
                return false;
            }
        }

        // 4. 检查属性门槛：要求在可折扣商品范围内，任一商品出现任一属性Key
        if (coupon.getRequiredAttributeKeys() == null || coupon.getRequiredAttributeKeys().isEmpty()) {
            return true;
        }

        try {
            List<String> requiredAttributeKeys = objectMapper.readValue(
                coupon.getRequiredAttributeKeys(), new TypeReference<List<String>>() {});
            if (requiredAttributeKeys == null || requiredAttributeKeys.isEmpty()) {
                return true;
            }

            for (ProductItem product : discountableProducts) {
                List<String> attributeKeys = product.getAttributeKeys();
                if (attributeKeys == null || attributeKeys.isEmpty()) {
                    continue;
                }

                for (String requiredKey : requiredAttributeKeys) {
                    if (requiredKey == null) {
                        continue;
                    }

                    String key = requiredKey.trim();
                    if (key.isEmpty()) {
                        continue;
                    }

                    if (attributeKeys.contains(key)) {
                        return true;
                    }
                }
            }

            return false;
        } catch (Exception e) {
            log.error("解析优惠券属性门槛失败", e);
            return false;
        }
    }

    /**
     * 计算适用商品的总价
     * @param coupon 优惠券配置
     * @param products 商品列表
     * @return 适用商品的总价
     */
    private BigDecimal calculateApplicableProductsTotal(PriceCouponConfig coupon, List<ProductItem> products) {
        // 如果优惠券没有商品类型限制,返回所有商品总价
        if (coupon.getApplicableProducts() == null || coupon.getApplicableProducts().isEmpty()) {
            return products.stream()
                    .map(ProductItem::getSubtotal)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
        }

        try {
            // 解析适用商品类型列表
            List<String> applicableProductTypes = objectMapper.readValue(
                    coupon.getApplicableProducts(), new TypeReference<List<String>>() {});

            // 空数组表示不限制商品类型，返回所有商品总价
            if (applicableProductTypes == null || applicableProductTypes.isEmpty()) {
                return products.stream()
                        .map(ProductItem::getSubtotal)
                        .reduce(BigDecimal.ZERO, BigDecimal::add);
            }

            // 计算适用商品的总价
            return products.stream()
                    .filter(product -> applicableProductTypes.contains(product.getProductType().getCode()))
                    .map(ProductItem::getSubtotal)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
        } catch (Exception e) {
            log.error("解析适用商品类型失败", e);
            return BigDecimal.ZERO;
        }
    }

    @Override
    @Transactional
    public CouponUseResult useCoupon(CouponUseRequest request) {
        Date now = new Date();
        PriceCouponConfig coupon = couponConfigMapper.selectById(request.getCouponId());
        if (coupon == null || coupon.getDeleted() == 1 || !Boolean.TRUE.equals(coupon.getIsActive())) {
            throw new CouponInvalidException("优惠券不存在或已失效");
        }
        if (coupon.getValidFrom() != null && now.before(coupon.getValidFrom())) {
            throw new CouponInvalidException("优惠券尚未生效");
        }
        if (coupon.getValidUntil() != null && !now.before(coupon.getValidUntil())) {
            throw new CouponInvalidException("优惠券已过期");
        }

        List<PriceCouponClaimRecord> records = couponClaimRecordMapper.selectUserCouponRecords(
                request.getUserId(), request.getCouponId());
        
        if (records == null || records.isEmpty()) {
            throw new CouponInvalidException("用户未拥有该优惠券");
        }

        // 查找一张可用的优惠券(状态为CLAIMED)
        PriceCouponClaimRecord record = records.stream()
                .filter(r -> r.getStatus() == CouponStatus.CLAIMED)
                .filter(r -> r.getExpireTime() == null || r.getExpireTime().after(now))
                .findFirst()
                .orElse(null);
        
        if (record == null) {
            boolean hasClaimedButExpired = records.stream()
                    .anyMatch(r -> r.getStatus() == CouponStatus.CLAIMED
                            && r.getExpireTime() != null
                            && !r.getExpireTime().after(now));
            if (hasClaimedButExpired) {
                throw new CouponInvalidException("优惠券已过期");
            }
            // 如果没有可用的，抛出异常。为了错误信息准确，可以检查最后一张的状态
            CouponStatus lastStatus = records.getFirst().getStatus();
            throw new CouponInvalidException("优惠券状态无效: " + lastStatus);
        }
        
        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<UserCouponResp> getUserAvailableCoupons(Long userId, String scenicId) {
        List<PriceCouponClaimRecord> records = couponClaimRecordMapper.selectUserAvailableCoupons(userId);
        List<UserCouponResp> coupons = new ArrayList<>();

        for (PriceCouponClaimRecord record : records) {
            PriceCouponConfig config = couponConfigMapper.selectById(record.getCouponId());
            if (config == null) {
                continue;
            }
            if (scenicId != null && config.getScenicId() != null && !scenicId.equals(config.getScenicId())) {
                continue;
            }
            coupons.add(buildUserCouponResp(record, config));
        }

        return coupons;
    }

    private UserCouponResp buildUserCouponResp(PriceCouponClaimRecord record, PriceCouponConfig config) {
        UserCouponResp resp = new UserCouponResp();

        // 领取记录信息
        resp.setClaimRecordId(record.getId());
        resp.setClaimTime(record.getClaimTime());
        resp.setExpireTime(record.getExpireTime());
        resp.setStatus(record.getStatus());
        resp.setClaimScenicId(record.getScenicId());

        // 优惠券配置信息
        resp.setCouponId(config.getId());
        resp.setCouponName(config.getCouponName());
        resp.setCouponType(config.getCouponType());
        resp.setDiscountValue(config.getDiscountValue());
        resp.setMinAmount(config.getMinAmount());
        resp.setMaxDiscount(config.getMaxDiscount());
        resp.setScenicId(config.getScenicId());
        resp.setValidFrom(config.getValidFrom());
        resp.setValidUntil(config.getValidUntil());

        return resp;
    }
    
    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. 检查优惠券有效期
            Date now = new Date();
            if (coupon.getValidFrom() != null && now.before(coupon.getValidFrom())) {
                return CouponClaimResult.failure(CouponClaimResult.ERROR_COUPON_EXPIRED, "优惠券尚未生效");
            }
            if (coupon.getValidUntil() != null && now.after(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()) {
                    // 查询用户已领取的记录，返回给前端展示
                    List<PriceCouponClaimRecord> claimedRecords = couponClaimRecordMapper.selectUserCouponRecords(
                            request.getUserId(), request.getCouponId());
                    return CouponClaimResult.failureWithClaimedRecords(
                            CouponClaimResult.ERROR_CLAIM_LIMIT_REACHED,
                            "您已达到该优惠券的领取上限(" + coupon.getUserClaimLimit() + "张)",
                            claimedRecords,
                            coupon);
                }
            }

            // 7. 创建领取记录
            Date claimTime = new Date();
            PriceCouponClaimRecord claimRecord = new PriceCouponClaimRecord();
            claimRecord.setCouponId(request.getCouponId());
            claimRecord.setUserId(request.getUserId());
            claimRecord.setClaimTime(claimTime);

            // 如果配置了领取后有效天数，计算过期时间
            if (coupon.getValidDaysAfterClaim() != null && coupon.getValidDaysAfterClaim() > 0) {
                java.util.Calendar calendar = java.util.Calendar.getInstance();
                calendar.setTime(claimTime);
                calendar.add(java.util.Calendar.DAY_OF_MONTH, coupon.getValidDaysAfterClaim());
                claimRecord.setExpireTime(calendar.getTime());
            }

            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. 更新优惠券已领取数量（区分于已使用数量）
            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,
                            "优惠券已被领取完，请稍后重试");
                }
            } else {
                // 无总量限制：无条件增加已领取数量（用于统计）
                couponConfigMapper.incrementClaimedQuantity(coupon.getId());
            }
            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事务上下文，跳过回滚标记");
        }
    }
}
