package beilv.wx.pay.service;

import beilv.carinformation.mapper.CarInformationMapper;
import beilv.common.core.domain.AjaxResult;
import beilv.competition.mapper.CompetitionMapper;
import beilv.order.domain.StoreOrder;
import beilv.order.domain.StoreRefund;
import beilv.order.mapper.StoreOrderMapper;
import beilv.order.mapper.StoreRefundMapper;
import beilv.system.domain.SysMember;
import beilv.system.service.ISysMemberService;
import beilv.vipCardLog.domain.VipCardLog;
import beilv.vipCardLog.service.IVipCardLogService;
import beilv.voucher.domain.BeilvVoucherMember;
import beilv.voucher.mapper.BeilvVoucherMemberMapper;
import beilv.wx.pay.config.WxPayProperties;
import beilv.wx.pay.domain.vo.AppPayParam;
import beilv.wx.pay.domain.vo.AppRefundParam;
import com.github.binarywang.wxpay.bean.notify.SignatureHeader;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyV3Result;
import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyV3Result;
import com.github.binarywang.wxpay.bean.request.WxPayRefundV3Request;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request;
import com.github.binarywang.wxpay.bean.result.WxPayRefundV3Result;
import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderV3Result.JsapiResult;
import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum;
import com.github.binarywang.wxpay.constant.WxPayConstants;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.util.Date;

@Slf4j
@Service
@AllArgsConstructor
public class IWxPayService {

    @Autowired
    private CarInformationMapper carInformationMapper;
    @Autowired
    private StoreOrderMapper storeOrderMapper;
    @Autowired
    private CompetitionMapper competitionMapper;
    @Autowired
    private WxPayProperties wxPayProperties;
    @Autowired
    private ISysMemberService sysMemberService;
    @Autowired
    private StoreRefundMapper storeRefundMapper;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private IVipCardLogService vipCardLogService;
    @Autowired
    private BeilvVoucherMemberMapper beilvVoucherMemberMapper;
    private WxPayService wxService;


    // Redis中订单延迟队列的key前缀
    private static final String ORDER_DELAY_QUEUE_PREFIX = "order:delay:queue:";
    // 订单超时时间(5分钟)
    private static final long ORDER_TIMEOUT_SECONDS = 5 * 60;

    /**
     * 统一下单接口
     *
     * @return 预支付交易会话标识
     */
    public JsapiResult unifiedOrder(AppPayParam param) throws Exception {
        log.info("开始处理统一下单请求，订单号: {}", param.getOrderId());
        // 幂等性校验 - 检查订单是否已存在且已支付
        StoreOrder existingOrder = storeOrderMapper.selectStoreOrderByOrderId(param.getOrderId());
        if (existingOrder != null && existingOrder.getPaid() != null && existingOrder.getPaid() == 1) {
            log.warn("订单 {} 已支付，不能重复下单", param.getOrderId());
            throw new Exception("订单已支付，不能重复下单");
        }
        SysMember user = getSysMember(param.getUserId());
        param.setSysMember(user);
        //微信支付需要的参数
        WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request();
        request.setAppid(wxPayProperties.getAppId());
        request.setMchid(wxPayProperties.getMchId());
        request.setDescription(param.getDescription());
        request.setOutTradeNo(param.getOrderId());
        request.setNotifyUrl(wxPayProperties.getNotifyUrl());
        WxPayUnifiedOrderV3Request.Amount amount = new WxPayUnifiedOrderV3Request.Amount();
        amount.setTotal(yuanToFen(param.getPayPrice()));
        amount.setCurrency("CNY");
        request.setAmount(amount);
        request.setPayer(new WxPayUnifiedOrderV3Request.Payer().setOpenid(user.getOpenId()));
        log.info("调用微信统一下单接口，订单号: {}", param.getOrderId());
        JsapiResult jsapiResult = this.wxService.createOrderV3(TradeTypeEnum.JSAPI, request);
        if (StringUtils.isNotEmpty(jsapiResult.getPackageValue())) {
            log.info("微信统一下单成功，订单号: {}，开始创建本地订单", param.getOrderId());
            //成功之后创建订单
            generateOrder(param, jsapiResult.getPackageValue());
            //将订单添加到延迟队列中
            addOrderToDelayQueue(param.getOrderId());
            log.info("订单创建完成并加入延迟队列，订单号: {}", param.getOrderId());
        } else {
            log.warn("微信统一下单失败，订单号: {}", param.getOrderId());
        }
        return jsapiResult;
    }

    //获取当前用户信息
    private SysMember getSysMember(Long userId) {
        log.debug("获取用户信息，用户ID: {}", userId);
        return sysMemberService.selectSysMemberById(userId);
    }


    /**
     * 元转分（不四舍五入，直接截取）
     *
     * @param amount 金额（元）
     * @return 分（整数）
     */
    public static int yuanToFen(BigDecimal amount) {
        if (amount == null) return 0;
        return amount.multiply(BigDecimal.valueOf(100))
                .setScale(0, RoundingMode.DOWN)  // 不四舍五入
                .intValueExact();
    }


    /**
     * 统一下单接口
     *
     * @return 预支付交易会话标识
     */
    private void generateOrder(AppPayParam param, String payId) {
        log.info("开始生成订单，订单号: {}", param.getOrderId());
        //查看是否已经生成订单，但是未支付
        if (!isOrderInDelayQueue(param.getOrderId())) {
            // 订单不存在，创建新订单
            log.info("订单不存在，创建新订单，订单号: {}", param.getOrderId());
            StoreOrder storeOrder = new StoreOrder();
            storeOrder.setOrderId(param.getOrderId());
            storeOrder.setUid(param.getSysMember().getId());
            storeOrder.setRealName(param.getSysMember().getUsername());
            storeOrder.setUserPhone(param.getSysMember().getMobile());
            storeOrder.setUserAddress(param.getSysMember().getAddress());
            storeOrder.setTotalNum(1);
            storeOrder.setTotalPrice(param.getTotalPrice());
            storeOrder.setPayPrice(param.getPayPrice());
            storeOrder.setPayTime(new Date());
            storeOrder.setPayId(payId);
            storeOrder.setCreateTime(new Date());
            storeOrderMapper.insertStoreOrder(storeOrder);
            log.info("新订单创建成功，订单号: {}", param.getOrderId());
        } else {
            StoreOrder order = storeOrderMapper.selectStoreOrderByOrderId(param.getOrderId());
            // 订单已存在，更新支付相关信息
            log.info("订单已存在，更新支付信息，订单号: {}", param.getOrderId());
            order.setPayTime(new Date());
            order.setPayId(payId);
            storeOrderMapper.updateStoreOrder(order);
            log.info("订单支付信息更新完成，订单号: {}", param.getOrderId());
        }
    }

    /**
     * 将订单添加到延迟队列
     *
     * @param orderId 订单ID
     */
    private void addOrderToDelayQueue(String orderId) {
        log.info("将订单添加到延迟队列，订单号: {}", orderId);
        // 计算订单超时时间戳（当前时间+5分钟）
        long expireTime = System.currentTimeMillis() + (ORDER_TIMEOUT_SECONDS * 1000);
        // 将订单添加到Redis有序集合中，以超时时间戳作为score
        redisTemplate.opsForZSet().add(ORDER_DELAY_QUEUE_PREFIX + "orders", orderId, expireTime);
        log.info("订单 {} 已添加到延迟队列，超时时间: {}", orderId, new Date(expireTime));
    }

    /**
     * 从延迟队列中移除订单（支付成功时调用）
     *
     * @param orderId 订单ID
     */
    private void removeOrderFromDelayQueue(String orderId) {
        log.info("从延迟队列中移除订单，订单号: {}", orderId);
        // 从Redis有序集合中移除订单
        redisTemplate.opsForZSet().remove(ORDER_DELAY_QUEUE_PREFIX + "orders", orderId);
        log.info("订单 {} 已从延迟队列中移除", orderId);
    }

    //生成退款订单号
    public String generateOutTradeNo() {
        String time = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
        String random = String.valueOf((int) ((Math.random() * 9 + 1) * 100000)); // 6位随机
        return "REF" + time + random;
    }

    /**
     * 处理支付结果通知
     *
     * @return 处理结果
     */
    public AjaxResult handleNotify(String notifyData, HttpServletRequest request) {
        log.info("收到微信支付回调通知");
        SignatureHeader header = getRequestHeader(request);
        try {
            WxPayNotifyV3Result res = this.wxService.parseOrderNotifyV3Result(notifyData, header);
            WxPayNotifyV3Result.DecryptNotifyResult decryptRes = res.getResult();
            log.info("解析微信支付回调结果，订单号: {}, 交易状态: {}", decryptRes.getOutTradeNo(), decryptRes.getTradeState());
            if (WxPayConstants.WxpayTradeStatus.SUCCESS.equals(decryptRes.getTradeState())) {
                log.info("支付成功，开始处理订单，订单号: {}", decryptRes.getOutTradeNo());
                // 成功处理订单
                processOrderPayment(decryptRes.getOutTradeNo());
                //调用充值记录服务，更新充值记录状态
                VipCardLog vipCardLog = new VipCardLog();
                vipCardLog.setId(decryptRes.getOutTradeNo());
                vipCardLog.setPaymentStatus("payment_status_have_paid");
                vipCardLogService.updateVipCardLogByOrderId(vipCardLog);
                // 成功返回200/204，body无需有内容
                log.info("订单处理完成，订单号: {}", decryptRes.getOutTradeNo());
                return AjaxResult.success();
            } else {
                log.warn("支付失败，订单号: {}，状态: {}", decryptRes.getOutTradeNo(), decryptRes.getTradeState());
                // 失败返回4xx或5xx，且需要构造body信息
                return AjaxResult.error("订单支付失败");
            }
        } catch (WxPayException e) {
            log.error("处理微信支付回调失败", e);
            // 失败返回4xx或5xx，且需要构造body信息
            return AjaxResult.error("微信支付回调失败");
        }
    }

    /**
     * 处理订单支付完成后的业务逻辑
     *
     * @param orderId 订单号
     */
    private void processOrderPayment(String orderId) {
        log.info("开始处理订单支付完成逻辑，订单号: {}", orderId);
        
        // 使用Redis分布式锁防止并发处理
        String lockKey = "payment:lock:" + orderId;
        Boolean lockAcquired = redisTemplate.opsForValue().setIfAbsent(lockKey, "1", java.time.Duration.ofSeconds(30));
        
        if (Boolean.FALSE.equals(lockAcquired)) {
            log.warn("订单正在被其他线程处理，订单号: {}", orderId);
            return;
        }
        
        try {
            StoreOrder order = storeOrderMapper.selectStoreOrderByOrderId(orderId);
            if (order != null) {
                // 检查订单是否已被取消
                if (order.getStatus() != null && order.getStatus() == 3) {
                    log.warn("订单已超时取消，支付回调到达，订单号: {}", orderId);
                    // TODO: 这里应该调用退款接口，将钱退回给用户
                    return;
                }
                
                // 检查订单是否已经处理过
                if (order.getPaid() == null || order.getPaid() != 1) {
                    log.info("更新订单状态为已支付，订单号: {}", orderId);
                    // 更新订单状态
                    order.setPaid(1); // 设置为已支付
                    order.setStatus(1); // 设置订单状态为已支付
                    order.setPayTime(new Date()); // 设置支付时间
                    storeOrderMapper.updateStoreOrder(order);
                    // 从延迟队列中移除订单
                    removeOrderFromDelayQueue(orderId);
                    log.info("订单状态更新完成，订单号: {}", orderId);
                } else {
                    log.info("订单已处理过，无需重复处理，订单号: {}", orderId);
                }
            } else {
                log.warn("订单不存在，订单号: {}", orderId);
            }
        } finally {
            // 释放锁
            redisTemplate.delete(lockKey);
        }
    }

    /**
     * 微信支付-申请退款
     *
     */
    public WxPayRefundV3Result refund(AppRefundParam param) throws WxPayException {
        log.info("开始处理退款请求，订单号: {}", param.getOrderId());
        // 检查订单是否存在
        StoreOrder order = storeOrderMapper.selectStoreOrderByOrderId(param.getOrderId());
        if (order == null) {
            log.warn("退款失败，订单不存在，订单号: {}", param.getOrderId());
            throw new WxPayException("订单不存在");
        }

        // 检查订单是否已支付
        if (order.getPaid() == null || order.getPaid() != 1) {
            log.warn("退款失败，订单未支付，订单号: {}", param.getOrderId());
            throw new WxPayException("订单未支付，无法退款");
        }

        // 检查订单是否已退款
        if (order.getRefundStatus() != null && order.getRefundStatus() == 2) {
            log.warn("退款失败，订单已退款，订单号: {}", param.getOrderId());
            throw new WxPayException("订单已退款");
        }

        // 检查退款金额是否合法
        if (param.getRefundAmount().compareTo(order.getPayPrice()) > 0) {
            log.warn("退款失败，退款金额大于支付金额，订单号: {}", param.getOrderId());
            throw new WxPayException("退款金额不能大于支付金额");
        }
        WxPayRefundV3Request request = new WxPayRefundV3Request();
        //商户退款单号
        request.setOutRefundNo(param.getOrderId());
        //原支付商户订单号
        request.setOutTradeNo(param.getOrderId());
        //退款原因说明
        request.setReason(param.getReason());
        WxPayRefundV3Request.Amount amount = new WxPayRefundV3Request.Amount();
        //原订单实际支付金额（单位：分）
        amount.setTotal(yuanToFen(param.getRefundAmount()));
        //退款金额（单位：分）
        amount.setRefund(yuanToFen(param.getRefundAmount()));
        //货币类型，默认 "CNY"
        amount.setCurrency("CNY");
        request.setAmount(amount);
        request.setNotifyUrl(wxPayProperties.getRefundNotifyUrl());
        param.setSysMember(getSysMember(param.getUserId()));
        
        // 检查是否已存在退款记录
        StoreRefund existingRefund = storeRefundMapper.selectStoreRefundByOrderId(param.getOrderId());
        if (existingRefund != null && "2".equals(existingRefund.getRefundStatus())) {
            log.warn("退款失败，退款记录已存在且已完成，订单号: {}", param.getOrderId());
            throw new WxPayException("该订单已退款");
        }
        
        // 先调用微信退款接口
        log.info("调用微信退款接口，订单号: {}", param.getOrderId());
        WxPayRefundV3Result result = this.wxService.refundV3(request);
        log.info("微信退款接口调用成功，订单号: {}", param.getOrderId());
        
        // 微信退款成功后，再创建本地退款记录
        if (existingRefund == null) {
            log.info("微信退款成功，创建本地退款记录，订单号: {}", param.getOrderId());
            refundOrder(param);
        }
        
        return result;
    }

    /**
     * 统一退款接口
     *
     * @return 预支付交易会话标识
     */
    private void refundOrder(AppRefundParam param) {
        log.info("生成退款订单记录，订单号: {}", param.getOrderId());
        StoreRefund storeRefund = new StoreRefund();
        storeRefund.setUid(param.getSysMember().getId());
        storeRefund.setRefundNo(generateOutTradeNo());
        storeRefund.setOrderId(param.getOrderId());
        storeRefund.setRefundAmount(param.getRefundAmount());
        storeRefund.setRefundReason(param.getReason());
        storeRefund.setCreateTime(new Date());
        storeRefundMapper.insertStoreRefund(storeRefund);
        log.info("退款订单记录创建完成，订单号: {}", param.getOrderId());
    }

    /**
     * 退款结果通知
     *
     * @return 处理结果
     */
    public AjaxResult parseRefundNotify(String notifyData, HttpServletRequest request) {
        log.info("收到微信退款回调通知");
        SignatureHeader header = getRequestHeader(request);
        try {
            WxPayRefundNotifyV3Result res = this.wxService.parseRefundNotifyV3Result(notifyData, header);
            WxPayRefundNotifyV3Result.DecryptNotifyResult decryptRes = res.getResult();
            log.info("解析微信退款回调结果，订单号: {}, 退款状态: {}", decryptRes.getOutTradeNo(), decryptRes.getRefundStatus());
            // TODO 根据自己业务场景需要构造返回对象
            if (WxPayConstants.RefundStatus.SUCCESS.equals(decryptRes.getRefundStatus())) {
                log.info("退款成功，开始处理订单，订单号: {}", decryptRes.getOutTradeNo());
                // 成功处理订单
                processOrderRefund(decryptRes.getOutTradeNo());
                //成功返回200/204，body无需有内容
                log.info("退款处理完成，订单号: {}", decryptRes.getOutTradeNo());
                return AjaxResult.success();
            } else {
                log.warn("退款失败，订单号: {}，状态: {}", decryptRes.getOutTradeNo(), decryptRes.getRefundStatus());
                //失败返回4xx或5xx，且需要构造body信息
                return AjaxResult.error();
            }
        } catch (WxPayException e) {
            log.error("处理微信退款回调失败", e);
            //失败返回4xx或5xx，且需要构造body信息
            return AjaxResult.error("微信退款回调失败");
        }
    }

    /**
     * 退款完成后的业务逻辑
     *
     * @param orderId 订单号
     */
    private void processOrderRefund(String orderId) {
        log.info("开始处理订单退款完成逻辑，订单号: {}", orderId);
        StoreOrder order = storeOrderMapper.selectStoreOrderByOrderId(orderId);
        if (order != null) {
            // 检查订单是否已经处理过
            if (order.getRefundStatus() != null && order.getPaid() == 1) {
                log.info("更新订单状态为已退款，订单号: {}", orderId);
                //更新支付订单状态为退款
                order.setStatus(2); // 3表示已取消
                order.setUpdateTime(new Date());
                storeOrderMapper.updateStoreOrder(order);
                log.info("订单状态更新完成，订单号: {}", orderId);
            } else {
                log.info("订单退款状态无需更新，订单号: {}", orderId);
            }
        } else {
            log.warn("订单不存在，订单号: {}", orderId);
        }
        StoreRefund refund = storeRefundMapper.selectStoreRefundByOrderId(orderId);
        if (refund != null) {
            // 检查订单是否已经处理过
            if (refund.getRefundStatus() != null && refund.getRefundStatus().equals("1")) {
                log.info("更新退款记录状态为已退款，退款单号: {}", refund.getRefundNo());
                // 更新订单状态
                refund.setRefundStatus("2"); // 设置订单状态为已退款
                refund.setSuccessTime(new Date());
                storeRefundMapper.updateStoreRefund(refund);
                log.info("退款记录状态更新完成，退款单号: {}", refund.getRefundNo());
            } else {
                log.info("退款记录状态无需更新，退款单号: {}", refund.getRefundNo());
            }
        } else {
            log.warn("退款记录不存在，订单号: {}", orderId);
        }
    }


    /**
     * 组装请求头重的前面信息
     *
     * @param request
     * @return
     */
    private SignatureHeader getRequestHeader(HttpServletRequest request) {
        log.debug("解析微信回调请求头信息");
        // 获取通知签名
        String signature = request.getHeader("Wechatpay-Signature");
        String nonce = request.getHeader("Wechatpay-Nonce");
        String serial = request.getHeader("Wechatpay-Serial");
        String timestamp = request.getHeader("Wechatpay-Timestamp");
        SignatureHeader signatureHeader = new SignatureHeader();
        signatureHeader.setSignature(signature);
        signatureHeader.setNonce(nonce);
        signatureHeader.setSerial(serial);
        signatureHeader.setTimeStamp(timestamp);
        return signatureHeader;
    }

    /**
     * 处理超时订单
     * 定时任务调用此方法检查并处理超时订单
     */
    public void processTimeoutOrders() {
//        log.info("开始处理超时订单");
        // 获取当前时间戳
        long currentTime = System.currentTimeMillis();

        // 从Redis有序集合中获取所有已超时的订单（score小于当前时间的元素）
        redisTemplate.opsForZSet().rangeByScore(ORDER_DELAY_QUEUE_PREFIX + "orders", 0, currentTime)
                .forEach(orderId -> {
                    // 使用分布式锁防止与支付回调冲突
                    String lockKey = "payment:lock:" + orderId;
                    Boolean lockAcquired = redisTemplate.opsForValue().setIfAbsent(lockKey, "1", java.time.Duration.ofSeconds(30));
                    
                    if (Boolean.FALSE.equals(lockAcquired)) {
                        log.warn("订单正在被处理，跳过超时处理，订单号: {}", orderId);
                        return;
                    }
                    
                    try {
                        log.info("处理超时订单: {}", orderId);
                        // 查询订单信息
                        StoreOrder order = storeOrderMapper.selectStoreOrderByOrderId(orderId);
                        if (order != null && (order.getPaid() == null || order.getPaid() != 1)) {
                            // 订单未支付，执行删除操作或更新状态
                            // 这里我们更新订单状态为已取消
                            order.setStatus(3); // 3表示已取消
                            order.setUpdateTime(new Date());
                            storeOrderMapper.updateStoreOrder(order);
                            //如果使用了优惠券，释放优惠券
                            VipCardLog orderInfo = vipCardLogService.selectVipCardLogById(orderId);
                            if (StringUtils.isNotEmpty(orderInfo.getVoucherId())) {
                                beilvVoucherMemberMapper.updateBeilvVoucherMember(new BeilvVoucherMember(Long.parseLong(orderInfo.getVoucherId()), "1"));
                            }
                            log.info("订单 {} 已超时，状态已更新为已取消", orderId);
                        } else if (order != null) {
                            log.info("订单 {} 已支付，无需处理", orderId);
                        } else {
                            log.warn("订单 {} 不存在", orderId);
                        }
                        // 从延迟队列中移除订单
                        redisTemplate.opsForZSet().remove(ORDER_DELAY_QUEUE_PREFIX + "orders", orderId);
                        log.info("超时订单 {} 处理完成", orderId);
                    } catch (Exception e) {
                        log.error("处理超时订单 {} 时发生错误", orderId, e);
                    } finally {
                        // 释放锁
                        redisTemplate.delete(lockKey);
                    }
                });
//        log.info("超时订单处理完成");
    }

    /**
     * 取消订单
     *
     * @param orderId 订单ID
     * @return 处理结果
     */
    public int cancelOrder(String orderId) {
        StoreOrder order = storeOrderMapper.selectStoreOrderByOrderId(orderId);
        order.setStatus(3); // 3表示已取消
        order.setUpdateTime(new Date());
        //如果使用了优惠券，释放优惠券
        VipCardLog orderInfo = vipCardLogService.selectVipCardLogById(orderId);
        if (StringUtils.isNotEmpty(orderInfo.getVoucherId())) {
            beilvVoucherMemberMapper.updateBeilvVoucherMember(new BeilvVoucherMember(Long.parseLong(orderInfo.getVoucherId()), "1"));
        }
        return storeOrderMapper.updateStoreOrder(order);
    }

    /**
     * 检查订单是否在延迟队列中
     *
     * @param orderId 订单ID
     * @return 如果订单在延迟队列中返回true，否则返回false
     */
    public boolean isOrderInDelayQueue(String orderId) {
        // 使用rank命令检查订单是否在有序集合中
        // 如果返回null，说明订单不在集合中
        Long rank = redisTemplate.opsForZSet().rank(ORDER_DELAY_QUEUE_PREFIX + "orders", orderId);
        return rank != null;
    }

}