package com.sonic.lion.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.sonic.common.AppRuntime;
import com.sonic.common.exception.SysException;
import com.sonic.common.utils.DateConvertUtils;
import com.sonic.lion.dao.AccountBuffDao;
import com.sonic.lion.dao.PayTradeDao;
import com.sonic.lion.dao.WithdrawRequestDao;
import com.sonic.lion.domain.bo.TradeStatusBo;
import com.sonic.lion.domain.entity.BuffRewardRecord;
import com.sonic.lion.domain.entity.PayCharge;
import com.sonic.lion.domain.entity.PayTrade;
import com.sonic.lion.domain.enums.*;
import com.sonic.lion.domain.input.*;
import com.sonic.lion.domain.output.ChannelPaymentOutput;
import com.sonic.lion.domain.output.PrePaymentOutput;
import com.sonic.lion.domain.output.TradeHandleOutput;
import com.sonic.lion.domain.req.RefundReq;
import com.sonic.lion.domain.resp.CheckoutResp;
import com.sonic.lion.enums.*;
import com.sonic.lion.service.*;
import com.sonic.lion.utils.KeyGenerator;
import com.sonic.lion.utils.TransactionUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

import static com.sonic.lion.enums.PayGenrtatorCodeType.TRADE;

/**
 * @author: code
 * @date: 2025/05/11
 * @Description:
 * @version: 1.0.0
 */
@Slf4j
@Service
public class PayTradeServiceImpl implements PayTradeService {

    @Value("${site.type}")
    private String siteType;
    @Autowired
    private AppRuntime appRuntime;
    @Autowired
    private AccountBuffService accountBuffService;
    @Autowired
    private AccountBuffDao accountBuffDao;
    @Autowired
    private PayTradeDao payTradeDao;
    @Autowired
    private PayCallChannelService payCallChannelService;
    @Autowired
    private ActivityService activityService;
    @Autowired
    private WithdrawRequestDao withdrawRequestDao;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private CheckOutService checkOutService;


    @Override
    public PayTrade getByTradeNo(String tradeNo) {
        if (StringUtils.isBlank(tradeNo)) {
            return null;
        }
        LambdaQueryWrapper<PayTrade> lambdaQueryWrapper = Wrappers.<PayTrade>lambdaQuery()
                .eq(PayTrade::getTradeNo, tradeNo);
        return payTradeDao.selectOne(lambdaQueryWrapper);
    }


    /**
     * 支付预下单 初始状态 等待支付
     *
     * @param input
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public PrePaymentOutput prePayment(@NonNull PrePaymentInput input) {
        validPrePaymentInput(input);

        String tradeNo = KeyGenerator.instance().generatorUniqueKey(TRADE);
        String outTradeNo = input.getOutTradeNo();
        if (StringUtils.isNotBlank(outTradeNo)) {
            ToastResultCode.DUPLICATE_ORDER_NUMBER.check(getByPlatformAndOuterTradeNo(input.getPlatform(), outTradeNo) != null);
        } else {
            outTradeNo = tradeNo;
        }

        PayTrade payTrade = new PayTrade();
        payTrade.setIp(input.getIp());
        payTrade.setProductId(input.getProductId());
        payTrade.setPlatform(input.getPlatform());
        payTrade.setOutTradeNo(outTradeNo);
        payTrade.setOutTradeNoRelationNo(input.getOutTradeNoRelationNo());
        payTrade.setTradeNo(tradeNo);
        payTrade.setSrcAccountId(input.getSrcAccountId());
        payTrade.setSrcAccountName(input.getSrcAccountName());
        payTrade.setDesAccountNo(input.getDesAccountNo());
        payTrade.setDesAccountName(input.getDesAccountName());
        payTrade.setBizType(input.getBizType());
        payTrade.setName(input.getName());
        payTrade.setAmount(input.getProductAmount());
        payTrade.setActualAmount(input.getActualAmount());
        payTrade.setPlatformFee(input.getPlatformFee() == null ? 0L : input.getPlatformFee());

        payTrade.setThirdFee(input.getThirdFee() == null ? 0L : input.getThirdFee());
        payTrade.setFee(input.getFee() == null ? 0L : input.getFee());
        payTrade.setPromoAmount(input.getPromoAmount() == null ? 0L : input.getPromoAmount());
        payTrade.setOccurAmount(input.getOccurAmount() == null ? 0L : input.getOccurAmount());
        payTrade.setPaymentTradeNo(input.getPaymentTradeNo());
        payTrade.setStatus(TradeStatus.WAITPAY);
        payTrade.setStatusInTime(LocalDateTime.now());
        payTrade.setCloseTime(input.getCloseTime());
        payTrade.setResourceKey(input.getResourceKey());
        payTrade.setResourceNum(input.getResourceNum());
        payTrade.setExtend(input.getExtend());
        payTrade.setClientVersion(input.getClientVersion());
        if (input.getPayChannel() != null) {
            payTrade.setPayChannel(input.getPayChannel());
        }
        if (input.getPaymentType() != null) {
            payTrade.setPaymentType(input.getPaymentType());
        }
        payTrade.setCreateTime(LocalDateTime.now());


        //充值成功 获取赠送金额
        PayCharge payCharge = activityService.getRewardTypeByUserIdV2(payTrade.getSrcAccountId(), payTrade.getCreateTime(), payTrade.getProductId(), payTrade.getClientVersion());
        if (payCharge != null) {
            //如果有赠送金额 就要加上赠送金额
            payTrade.setGiftAmount(payCharge.getGiftAmount());
        }

        payTradeDao.insert(payTrade);

        return PrePaymentOutput.builder()
                .tradeNo(payTrade.getTradeNo())
                .outTradeNo(payTrade.getOutTradeNo())
                .outTradeNoRelationNo(payTrade.getOutTradeNoRelationNo())
                .tradeStatus(TradeStatus.WAITPAY)
                .build();
    }

    @Transactional
    @Override
    public CheckoutResp checkout(@NonNull CheckoutInput input) {
        PayTrade payTrade = getByTradeNo(input.getTradeNo());
        ToastResultCode.DATA_NOT_EXITS.check(payTrade == null);
        TradeStatus tradeStatus = payTrade.getStatus();
        ToastResultCode.PAY_TRADE_STATUS_CHANGE.check(tradeStatus != TradeStatus.WAITPAY);
        ToastResultCode.PAY_TRADE_PAYER_ERROR.check(!Objects.equals(input.getUid(), payTrade.getSrcAccountId()));

        BizType bizType = payTrade.getBizType();
        TradeType tradeType = bizType.getTradeType();
        ToastResultCode.PAY_TRADE_PAYMENT_TYPE_ERROR.check(tradeType == TradeType.CHARGE && input.getPaymentType() == PaymentType.BALANCE);

        long fee = 0;
        CheckoutResp resp = new CheckoutResp();
        if (input.getPaymentType() == PaymentType.BALANCE) {
            TradeStatusBo tradeStatusBo = checkOutByBalance(input, payTrade, bizType, tradeType, fee);
            tradeStatus = tradeStatusBo.getTradeStatus();
            //将流水ID列表设置到出参对象中
            resp.setBl(tradeStatusBo.getBillIdList());
        } else if (input.getPaymentType() == PaymentType.CHANNEL) {
            fee = payCallChannelService.calculateFee(payTrade.getAmount(), input.getPayChannel(), bizType);
            Long platformFee = payCallChannelService.calculatePlatformFee(payTrade.getAmount(), bizType);
            payTrade.setPlatformFee(platformFee);
            payTrade.setThirdFee(fee - payTrade.getPlatformFee());

            updatePaymentInfo(input, payTrade, fee);

            //调用支付渠道
            ChannelPaymentInput channelPaymentInput = ChannelPaymentInput.builder()
                    .payTrade(payTrade)
                    .returnUrl(input.getReturnUrl())
                    .cancelUrl(input.getCancelUrl())
                    .ip(payTrade.getIp())
                    .build();
            ChannelPaymentOutput output = payCallChannelService.callPayment(channelPaymentInput);
            ToastResultCode.DATA_STATUS_CHANGED.check(output.getStatus() == CallChannelStatus.FAIL);
            if (output.getStatus() == CallChannelStatus.SUCC) {
                tradeStatus = TradeStatus.PAID;
            }
            resp.setSubmitId(output.getSubmitId());
            resp.setPaymentUrl(output.getPaymentUrl());
        }
        if (tradeStatus == TradeStatus.PAID) {
            //付款成功后处理
            getProxy().tradeHandle(payTrade, TradeEvent.PAYMENT);
        }
        resp.setTradeStatus(tradeStatus);
        resp.setFee(fee);
        return resp;
    }


    /**
     * 使用余额 付款
     *
     * @param input
     * @param payTrade
     * @param bizType
     * @param tradeType
     * @param fee
     * @return
     */
    private TradeStatusBo checkOutByBalance(CheckoutInput input, PayTrade payTrade, BizType bizType, TradeType tradeType, long fee) {
        //声明返回对象
        TradeStatusBo tradeStatusBo = new TradeStatusBo();

        TradeStatus tradeStatus;
        updatePaymentInfo(input, payTrade, fee);

        //扣除付款方余额
        BuffTransferInput buffTransferInput = BuffTransferInput.builder()
                .payChannel(input.getPayChannel())
                .platform(payTrade.getPlatform())
                .decUid(payTrade.getSrcAccountId())
                .decBuffType(BuffType.BALANCE)
                .decBuff(payTrade.getOccurAmount())
                .decTargetUserId(Long.valueOf(payTrade.getDesAccountNo()))
                .tradeNo(payTrade.getTradeNo())
                .bizNo(payTrade.getOutTradeNo())
                .bizNoRelationNo(payTrade.getOutTradeNoRelationNo())
                .bizType(bizType)
                .extend(payTrade.getExtend())
                .build();
        if (tradeType == TradeType.SECURED_TRANSACTION || tradeType == TradeType.C2B) {
            //转到系统余额
            buffTransferInput.setAddUid(SystemUser.SYSTEM_USER_1.getValue());
            buffTransferInput.setAddBuffType(BuffType.BALANCE);
            buffTransferInput.setAddBuff(payTrade.getOccurAmount());
        } else if (tradeType == TradeType.NON_SECURED_TRANSACTION) {
            //转到收款方待入账
            buffTransferInput.setAddUid(Long.valueOf(payTrade.getDesAccountNo()));
            buffTransferInput.setAddBuffType(BuffType.AWAITING_INCOME);
            buffTransferInput.setAddBuff(payTrade.getAmount());
        }
        //根据业务类型判断，给用户和系统账户 加钱
        if (bizType == BizType.GIFT || bizType == BizType.IMAGE_UNLOCK) {
            //转到收款方待入账
            buffTransferInput.setAddUid(Long.valueOf(payTrade.getDesAccountNo()));
            buffTransferInput.setAddBuffType(BuffType.AWAITING_INCOME);
            buffTransferInput.setAddBuff(payTrade.getAmount() - payTrade.getPlatformFee());

            //转到系统账号的收款方待入账
            buffTransferInput.setAddSystemUid(SystemUser.SYSTEM_USER_2.getValue());
            buffTransferInput.setAddSystemBuffType(BuffType.AWAITING_INCOME);
            buffTransferInput.setAddSystemBuff(payTrade.getPlatformFee());
        }
        List<Long> billIdList = accountBuffService.transfer(buffTransferInput);
        //将流水id列表设置到返回对象中
        tradeStatusBo.setBillIdList(billIdList);
        tradeStatus = TradeStatus.PAID;
        tradeStatusBo.setTradeStatus(tradeStatus);
        return tradeStatusBo;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public TradeHandleOutput tradeHandleV2(PayChannel payChannel, @NonNull String tradeNo, @NonNull TradeEvent tradeEvent) {
        PayTrade payTrade = getByTradeNo(tradeNo);
        payTrade.setPayChannel(payChannel);
        ToastResultCode.DATA_NOT_EXITS.check(payTrade == null);
        return tradeHandle(payTrade, tradeEvent);
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void refund(@NonNull RefundReq req) {
        ToastResultCode.OUT_TRADE_NO_EMPTY.check(CollectionUtils.isEmpty(req.getOutTradeNoList()));
        for (String outTradeNo : req.getOutTradeNoList()) {
            PayTrade payTrade = getByPlatformAndOuterTradeNo(req.getPlatform(), outTradeNo);
            ToastResultCode.DATA_NOT_EXITS.check(payTrade == null);
            ToastResultCode.UNSUPPORTED_BUSINESS_TYPE.check(payTrade.getBizType() != BizType.GAME);

            PaymentType paymentType = payTrade.getPaymentType();
            TradeStatus status = payTrade.getStatus();
            BuffTransferInput buffTransferInput = new BuffTransferInput();
            if (paymentType == PaymentType.BALANCE) {
                if (status == TradeStatus.PAID) {
                    //已付款状态退款,此时钱还在系统账户
                    //将系统余额转入用户余额
                    buffTransferInput = BuffTransferInput.builder()
                            .platform(payTrade.getPlatform())
                            .decUid(SystemUser.SYSTEM_USER_1.getValue())
                            .decBuffType(BuffType.BALANCE)
                            .decBuff(payTrade.getOccurAmount())
                            .addUid(payTrade.getSrcAccountId())
                            .addBuffType(BuffType.BALANCE)
                            .addBuff(payTrade.getOccurAmount())
                            .addTargetUserId(Long.valueOf(payTrade.getDesAccountNo()))
                            .tradeNo(payTrade.getTradeNo())
                            .bizNoRelationNo(payTrade.getOutTradeNoRelationNo())
                            .bizNo(payTrade.getOutTradeNo())
                            .bizType(BizType.REFUND)
                            .build();
                } else if (status == TradeStatus.FINISHED) {
                    //已完成状态退款，此时钱已到收款方账户
                    //收款方待入账金额转入付款方余额
                    buffTransferInput = BuffTransferInput.builder()
                            .platform(payTrade.getPlatform())
                            .decUid(Long.valueOf(payTrade.getDesAccountNo()))
                            .decBuffType(BuffType.AWAITING_INCOME)
                            .decBuff(payTrade.getAmount())
                            .decTargetUserId(payTrade.getSrcAccountId())
                            .addUid(payTrade.getSrcAccountId())
                            .addBuffType(BuffType.BALANCE)
                            .addBuff(payTrade.getOccurAmount())
                            .addTargetUserId(Long.valueOf(payTrade.getDesAccountNo()))
                            .tradeNo(payTrade.getTradeNo())
                            .bizNoRelationNo(payTrade.getOutTradeNoRelationNo())
                            .bizNo(payTrade.getOutTradeNo())
                            .bizType(BizType.REFUND)
                            .fullRefund(true)
                            .build();

                } else {
                    ToastResultCode.DATA_STATUS_CHANGED.check(true);
                }
                accountBuffService.transfer(buffTransferInput);
                //更新交易状态
                updateStatus(payTrade.getId(), TradeStatus.REFUNDED, status);
                //buff退款不用调用第三方系统, 可以直接完成到已退款状态同步返回退款结果
            } else {
                ToastResultCode.UNSUPPORTED_BUSINESS_TYPE.check(true);
            }
        }
    }

    @Override
    public PayTrade getByPlatformAndOuterTradeNo(String platform, String outTradeNo) {
        if (StringUtils.isBlank(platform) || StringUtils.isBlank(outTradeNo)) {
            return null;
        }
        LambdaQueryWrapper<PayTrade> queryWrapper = Wrappers.<PayTrade>lambdaQuery()
                .eq(PayTrade::getPlatform, platform)
                .eq(PayTrade::getOutTradeNo, outTradeNo);
        PayTrade payTrade = payTradeDao.selectOne(queryWrapper);
        //为兼容老订单新支付，如果找不到getPlatform换成1找找看，下个版本可删除
        if (payTrade == null) {
            LambdaQueryWrapper<PayTrade> queryWrapper2 = Wrappers.<PayTrade>lambdaQuery()
                    .eq(PayTrade::getPlatform, "1")
                    .eq(PayTrade::getOutTradeNo, outTradeNo);
            return payTradeDao.selectOne(queryWrapper2);
        }
        return payTrade;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void completeSecuredTrade(@NonNull String platform, @NonNull String outTradeNo) {

        PayTrade payTrade = getByPlatformAndOuterTradeNo(platform, outTradeNo);
        ToastResultCode.DATA_NOT_EXITS.check(payTrade == null);
        //交易已完成
        if (payTrade.getComplete()) {
            return;
        }
        ToastResultCode.PAY_TRADE_STATUS_CHANGE.check(payTrade.getStatus() != TradeStatus.PAID);

        updateStatus(payTrade.getId(), TradeStatus.FINISHED, TradeStatus.PAID);

        //将系统余额转入收款方待入账金额
        BuffTransferInput buffTransferInput = BuffTransferInput.builder()
                .platform(payTrade.getPlatform())
                .decUid(SystemUser.SYSTEM_USER_1.getValue())
                .decBuffType(BuffType.BALANCE)
                .decBuff(payTrade.getAmount())
                .addUid(Long.valueOf(payTrade.getDesAccountNo()))
                .addBuffType(BuffType.AWAITING_INCOME)
                .addBuff(payTrade.getAmount())
                .addTargetUserId(payTrade.getSrcAccountId())
                .tradeNo(payTrade.getTradeNo())
                .bizNo(payTrade.getOutTradeNo())
                .bizType(payTrade.getBizType())
                .extend(payTrade.getExtend())
                .build();
        accountBuffService.transfer(buffTransferInput);
    }


    @Transactional
    @Override
    public void closeTrade(@NonNull String platform, @NonNull String outTradeNo) {
        PayTrade payTrade = getByPlatformAndOuterTradeNo(platform, outTradeNo);
        if (payTrade == null) {
            log.warn("不能找到需要关闭的交易, platform: {}, outTradeNo: {}", platform, outTradeNo);
            return;
        }
        ToastResultCode.PAY_TRADE_STATUS_CHANGE.check(payTrade.getStatus() != TradeStatus.WAITPAY);
        updateStatus(payTrade.getId(), TradeStatus.CLOSED, TradeStatus.WAITPAY);
        //todo 关闭支付渠道交易
    }

    @Transactional
    @Override
    public int closeWaitPayTrade() {
        return payTradeDao.closeWaitPayTrade();
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public TradeHandleOutput tradeHandle(@NonNull PayTrade payTrade, @NonNull TradeEvent tradeEvent) {
        //所有交易上锁60秒
        String lockKey = appRuntime.buildPrefixKey("lock", "trade", payTrade.getTradeNo());
        boolean onLock = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, "lock", 60L, TimeUnit.SECONDS);
        ToastResultCode.IN_PROCESSING.check(!onLock);
        log.info("payTrade:{}", payTrade);
        BizType bizType = payTrade.getBizType();
        TradeType tradeType = bizType.getTradeType();
        TradeStatus status = null;
        if (tradeEvent == TradeEvent.PAYMENT) {
            status = paymentEventHandler(payTrade, tradeType);
        } else if (tradeEvent == TradeEvent.WITHDRAW) {
            //
        } else if (tradeEvent == TradeEvent.REFUND) {
            status = refundEventHandler(payTrade, tradeType);
        }
        TradeHandleOutput output = new TradeHandleOutput();
        output.setUid(payTrade.getSrcAccountId());
        output.setTradeNo(payTrade.getTradeNo());
        output.setOutTradeNo(payTrade.getOutTradeNo());
        output.setAmount(payTrade.getAmount());
        output.setBizType(bizType);
        output.setStatusInTime(LocalDateTime.now());
        output.setPaymentType(payTrade.getPaymentType());
        output.setStatus(status);
        output.setFee(payTrade.getFee());
        output.setPromoAmount(payTrade.getPromoAmount());
        output.setOccurAmount(payTrade.getOccurAmount());
        stringRedisTemplate.delete(lockKey);
        return output;
    }

    /**
     * 退款事件处理
     *
     * @param payTrade
     * @param tradeType
     * @return
     */
    private TradeStatus refundEventHandler(@NonNull PayTrade payTrade, TradeType tradeType) {
        if (tradeType == TradeType.SECURED_TRANSACTION) {
            return refundAfterHandle(payTrade);
        } else {
            throw new SysException("", "暂不支持该交易类型的退款事件处理");
        }
    }

    /**
     * 付款事件处理
     *
     * @param payTrade
     * @param tradeType
     * @return
     */
    private TradeStatus paymentEventHandler(@NonNull PayTrade payTrade, TradeType tradeType) {
        if (tradeType == TradeType.SECURED_TRANSACTION) {
            return securedPaymentAfterHandle(payTrade);
        } else if (tradeType == TradeType.NON_SECURED_TRANSACTION) {
            return nonSecuredPaymentAfterHandle(payTrade);
        } else if (tradeType == TradeType.CHARGE) {
            return chargePaymentAfterHandle(payTrade);
        } else if (tradeType == TradeType.C2B) {
            return c2bPaymentAfterHandle(payTrade);
        } else {
            throw new SysException("", "暂不支持该交易类型的支付事件处理");
        }
    }

    /**
     * 担保交易渠道处理成功后系统内处理
     *
     * @param payTrade
     */
    private TradeStatus securedPaymentAfterHandle(PayTrade payTrade) {
        updateStatus(payTrade.getId(), TradeStatus.PAID, TradeStatus.WAITPAY);
        return TradeStatus.PAID;
    }

    /**
     * 非担保交易渠道处理成功后系统内处理
     *
     * @param payTrade
     */
    private TradeStatus nonSecuredPaymentAfterHandle(PayTrade payTrade) {
        updateStatus(payTrade.getId(), TradeStatus.FINISHED, TradeStatus.WAITPAY);
        return TradeStatus.FINISHED;
    }

    /**
     * c2b交易支付后处理
     *
     * @param payTrade
     */
    private TradeStatus c2bPaymentAfterHandle(PayTrade payTrade) {
        updateStatus(payTrade.getId(), TradeStatus.FINISHED, TradeStatus.WAITPAY);
        return TradeStatus.FINISHED;
    }

    /**
     * 渠道退款处理成功后系统内处理
     *
     * @param payTrade
     */
    private TradeStatus refundAfterHandle(PayTrade payTrade) {
        updateStatus(payTrade.getId(), TradeStatus.REFUNDED, TradeStatus.REFUNDING);
        return TradeStatus.REFUNDED;
    }

    /**
     * 渠道支付成功后系统内充值处理
     *
     * @param payTrade
     */
    private TradeStatus chargePaymentAfterHandle(PayTrade payTrade) {
        //查询数据库的 paytrade 看状态是FINISHED 说明是其他渠道 再次充值。 不能再次 扣款
        PayTrade payTradeDb = payTradeDao.selectById(payTrade.getId());
        boolean isFinished = TradeStatus.FINISHED.equals(payTradeDb.getStatus());

        //将系统余额转入收款方待入账金额
        BuffTransferInput buffTransferInput = BuffTransferInput.builder()
                .platform(payTrade.getPlatform())

                .addUid(payTrade.getSrcAccountId())
                .addBuffType(BuffType.BALANCE)
                //添加实得金额
                .addBuff(payTrade.getActualAmount())
                .payChannel(payTrade.getPayChannel())
                .tradeNo(payTrade.getTradeNo())
                .bizNo(payTrade.getOutTradeNo())
                .bizType(payTrade.getBizType())
                .payMethod(payTrade.getPayMethod())
                .build();

        //充值成功 获取赠送金额
        //获取充值档位的赠送基础数据
        PayCharge payCharge = activityService.getRewardTypeByUserIdV2(payTrade.getSrcAccountId(), payTrade.getCreateTime(), payTrade.getProductId(), payTrade.getClientVersion());
        if (payCharge != null) {
            //如果有赠送金额 就要加上赠送金额
            buffTransferInput.setGiftAmount(payCharge.getGiftAmount());
            buffTransferInput.setAddBuff(payTrade.getActualAmount() + buffTransferInput.getGiftAmount());

            payTradeDao.updateGiftAmountByTradeNo(payTrade.getId(), buffTransferInput.getGiftAmount());
        } else {
            payTradeDao.updateGiftAmountByTradeNo(payTrade.getId(), 0L);
        }

        //执行转账操作
        accountBuffService.transfer(buffTransferInput);

        if (payCharge != null) {
            //TODO 对这个表的数据加监控？监控是否会多送
            Integer yearMonthInt = com.sonic.lion.utils.DateConvertUtils.formatYearMonthToInt(DateConvertUtils.toZone(LocalDateTime.now(), "Asia/Shanghai", "America/Los_Angeles"));
            BuffRewardRecord buffRewardRecord = BuffRewardRecord.builder()
                    .productId(payTrade.getProductId())
                    .rewardDate(DateConvertUtils.toZone(payTrade.getCreateTime(), "Asia/Shanghai", "America/Los_Angeles").toLocalDate())
                    .amount(buffTransferInput.getGiftAmount())
                    .uid(payTrade.getSrcAccountId()).createTime(payTrade.getCreateTime())
                    .yearMonthInt(yearMonthInt)
                    .rewardType(payCharge.getBizType()).build();
            activityService.saveRecord(buffRewardRecord);
        }

        //充值且支付逻辑 第二笔不能再扣款
        if (StringUtils.isNotBlank(payTrade.getPaymentTradeNo()) && !isFinished) {
            TransactionUtils.afterCommitSyncExecute(() -> {
                checkOutService.chargeAfterPayment(payTrade.getPaymentTradeNo(), payTrade.getSrcAccountId());
            });
        }
        updateStatus(payTrade.getId(), TradeStatus.FINISHED);
        return TradeStatus.FINISHED;
    }

    /**
     * 更新交易状态
     *
     * @param id           payTrade主键
     * @param status       更新后状态
     * @param expectStatus 期望现在的状态
     * @return
     */
    @Transactional
    @Override
    public void updateStatus(Long id, TradeStatus status, TradeStatus expectStatus) {
        LocalDateTime now = LocalDateTime.now();

        LambdaUpdateWrapper<PayTrade> updateWrapper = Wrappers.<PayTrade>lambdaUpdate()
                .eq(PayTrade::getId, id);

        PayTrade updater = PayTrade.builder()
                .status(status)
                .statusInTime(now)
                .build();

        if (expectStatus != null) {
            updateWrapper.eq(PayTrade::getStatus, expectStatus);
        }

        if (status == TradeStatus.PAID) {
            updater.setPayTime(now);
        } else if (status == TradeStatus.FINISHED) {
            if (expectStatus == TradeStatus.WAITPAY) {
                //直接由待支付状态转到完成状态，更新支付时间
                updater.setPayTime(now);
            }
            updater.setComplete(true);
            updater.setFinishTime(now);
        }

        int n = payTradeDao.update(updater, updateWrapper);
        ToastResultCode.PAY_TRADE_STATUS_CHANGE.check(n != 1);
    }


    private void updateStatus(Long id, TradeStatus status) {
        LocalDateTime now = LocalDateTime.now();

        LambdaUpdateWrapper<PayTrade> updateWrapper = Wrappers.<PayTrade>lambdaUpdate()
                .eq(PayTrade::getId, id);

        PayTrade updater = PayTrade.builder()
                .status(status)
                .statusInTime(now)
                .build();

        if (status == TradeStatus.PAID) {
            updater.setPayTime(now);
        } else if (status == TradeStatus.FINISHED) {
            updater.setComplete(true);
            updater.setFinishTime(now);
        }
        int n = payTradeDao.update(updater, updateWrapper);
    }

    /**
     * 更新交易单支付信息
     *
     * @param input
     * @param payTrade
     * @param fee
     */
    @Override
    @Transactional
    public void updatePaymentInfo(CheckoutInput input, PayTrade payTrade, long fee) {
        long occurAmount = payTrade.getAmount() + fee - payTrade.getPromoAmount();
        //TODO by code 临时需求(使用优惠码进行免单由平台付款给卖家) 即 校验bizType如果是Game的订单，支付金额是0元的话 则不作数据校验
        if (input.getPaymentType() == PaymentType.BALANCE && BizType.GAME != payTrade.getBizType()) {
            validOccurAmount(occurAmount);
        }
        //小于0判断，如果小于0则给 默认值为0
        occurAmount = occurAmount <= 0 ? 0 : occurAmount;
        payTrade.setPaymentType(input.getPaymentType());
        payTrade.setFee(fee);
        payTrade.setOccurAmount(occurAmount);
        payTrade.setPayChannel(input.getPayChannel());
        payTradeDao.updateById(payTrade);
    }

    private void validOccurAmount(long occurAmount) {
        ToastResultCode.PAY_TRADE_STATUS_CHANGE.check(occurAmount < 0);
    }

    private void validPrePaymentInput(PrePaymentInput input) {
        //礼物打赏可以自己打赏自己的AI，需要排除
        if (!BizType.GIFT.equals(input.getBizType())) {
            ToastResultCode.PAY_TRADE_PAYER_PAYEE_ERROR.check(Objects.equals(input.getSrcAccountId().toString(), input.getDesAccountNo()));
        }
        if (input.getOccurAmount() != null) {
            validOccurAmount(input.getOccurAmount());
        }
        if (input.getResourceNum() != null && input.getResourceNum() > 0) {
            ToastResultCode.RESOURCEKEY_IS_BLANK.check(StringUtils.isBlank(input.getResourceKey()));
            ToastResultCode.RESOURCENUM_EXCEEDS_THE_MAXIMUM.check(payTradeDao.countPaidByResourceKey(input.getResourceKey()) >= input.getResourceNum());
        }
    }

    private PayTradeService getProxy() {
        return (PayTradeService) AopContext.currentProxy();
    }

    @Override
    public int getToWithdrawableIncomeTime(Long userId, CoinType coinType) {
        //设置提现等待时间
        return 30;
    }

    /**
     * 平台 赠送金额
     *
     * @param
     * @return
     */

    @Transactional
    @Override
    public PayTrade platformGift(PlatformGiftInput req) {
        BizType bizType = req.getType().getBizType();
        Long gift = req.getAmount();
        BuffType targetBuffType = BuffType.valueOf(req.getType().getBuffType().name());
        String tradeNo = KeyGenerator.instance().generatorUniqueKey(PayGenrtatorCodeType.getTradeType(siteType));
        PrePaymentInput input = PrePaymentInput.builder()
                .name(req.getName())
                .outTradeNo(req.getOutTradeNo())
                .ip(req.getIp())
                .platform("Epal")
                .payChannel(PayChannel.BUFF)
                .bizType(bizType)
                .desAccountNo(req.getUid().toString())
                .desAccountName(req.getName())
                .paymentType(PaymentType.BALANCE)
                .srcAccountId(SystemUser.SYSTEM_USER_1.getValue())
                .srcAccountName("SYSTEM")
                .productAmount(gift)
                .extend(req.getExtend() == null ? "" : req.getExtend())
                .occurAmount(gift).build();

        PayTrade payTrade = new PayTrade();
        payTrade.setIp(input.getIp());
        payTrade.setPlatform(input.getPlatform());
        payTrade.setOutTradeNo(tradeNo);
        payTrade.setOutTradeNoRelationNo(input.getOutTradeNoRelationNo());
        payTrade.setTradeNo(tradeNo);
        payTrade.setSrcAccountId(input.getSrcAccountId());
        payTrade.setSrcAccountName(input.getSrcAccountName());
        payTrade.setDesAccountNo(input.getDesAccountNo());
        payTrade.setDesAccountName(input.getDesAccountName());
        payTrade.setBizType(input.getBizType());
        payTrade.setName(input.getName());
        payTrade.setAmount(input.getProductAmount());
        payTrade.setBizType(bizType);
        payTrade.setFee(input.getFee());
        payTrade.setPromoAmount(input.getPromoAmount());
        payTrade.setOccurAmount(input.getOccurAmount());
        payTrade.setPaymentTradeNo(input.getPaymentTradeNo());
        payTrade.setStatus(TradeStatus.FINISHED);
        payTrade.setStatusInTime(LocalDateTime.now());

        payTrade.setResourceKey(input.getResourceKey());
        payTrade.setResourceNum(input.getResourceNum());
        payTrade.setExtend(input.getExtend());
        if (input.getPayChannel() != null) {
            payTrade.setPayChannel(input.getPayChannel());
        }
        if (input.getPaymentType() != null) {
            payTrade.setPaymentType(input.getPaymentType());
        }


        if (req.getType().isNeedConfirm()) {  //5.9.0 版本 送的BUFF需要用户确认
            payTrade.setStatus(TradeStatus.WAITPAY);
        } else {
            payTrade.setCloseTime(input.getCloseTime());
            payTrade.setPayTime(LocalDateTime.now());
            payTrade.setFinishTime(LocalDateTime.now());
        }
        payTradeDao.insert(payTrade);

        BuffTransferInput buffTransferInput = BuffTransferInput.builder()
                .platform("Epal")
                .decUid(SystemUser.SYSTEM_USER_1.getValue())
                .decBuffType(BuffType.BALANCE)
                .decBuff(gift)
                .tradeNo(payTrade.getTradeNo())
                .bizType(bizType)
                .bizNoRelationNo(payTrade.getTradeNo())
                .bizNo(payTrade.getTradeNo())
                .errorMessage(req.getReason())
                .payChannel(payTrade.getPayChannel())
                .build();

        buffTransferInput.setAddUid(req.getUid());
        buffTransferInput.setAddBuffType(targetBuffType);
        buffTransferInput.setAddBuff(gift);

        if (!req.getType().isNeedConfirm()) {
            accountBuffService.transfer(buffTransferInput);
        }
        return payTrade;
    }
}
