package com.izouma.nineth.service;

import com.izouma.nineth.config.RedisKeys;
import com.izouma.nineth.domain.*;
import com.izouma.nineth.dto.PageQuery;
import com.izouma.nineth.enums.FundChangeStatus;
import com.izouma.nineth.enums.FundChangeType;
import com.izouma.nineth.exception.BusinessException;
import com.izouma.nineth.repo.*;
import com.izouma.nineth.utils.JpaUtils;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import java.time.LocalDateTime;
import java.util.Optional;

/**
 * 资金变动记录
 */
@Service
@Slf4j
@AllArgsConstructor
public class UserFundChangeLogService {

    private UserFundChangeLogRepo userFundChangeLogRepo;


    private RechargeOrderRepo rechargeOrderRepo;

    private WithdrawApplyRepo withdrawApplyRepo;

    private PaymentInfoRepo paymentInfoRepo;

    private WalletRenewOrderRepo walletRenewOrderRepo;

    private OrderRepo orderRepo;

    public Page<UserFundChangeLog> all(PageQuery pageQuery) {
        return userFundChangeLogRepo.findAll(JpaUtils.toSpecification(pageQuery, UserFundChangeLog.class), JpaUtils.toPageRequest(pageQuery));
    }

    /**
     * 识别变动类型
     * @param orderNo
     * @return
     */
    public FundChangeType identChangeType(String orderNo){
        Integer payCount = paymentInfoRepo.countByOrderNo(orderNo);
        if(payCount > 0){
            return FundChangeType.PAYMENT;
        }

        Integer rechargeCount = rechargeOrderRepo.countByOrderNo(orderNo);
        if(rechargeCount > 0){
            return FundChangeType.RECHARGE;
        }

        Integer withdrawCount = withdrawApplyRepo.countByOrderNo(orderNo);
        if(withdrawCount > 0){
            return FundChangeType.WITHDRAW;
        }

        Integer renewCount = walletRenewOrderRepo.countByOrderNo(orderNo);
        if(renewCount > 0){
            return  FundChangeType.RENEW;
        }

        return null;
    }


    /**
     * 充值变更
     * @param rechargeOrder
     * @return
     */
    @CacheEvict(value=RedisKeys.WALLET_LIANLIAN_BALANCE_CACHE, key="#rechargeOrder.userId")
    @Transactional
    public UserFundChangeLog createByRecharge(RechargeOrder rechargeOrder){
        Optional<UserFundChangeLog> changeLogOptional = userFundChangeLogRepo.findByChangeTypeAndOrderNo(FundChangeType.RECHARGE, rechargeOrder.getOrderNo());
        if(changeLogOptional.isPresent()){
            throw new BusinessException("充值流水已存在，不允许重复添加");
        }

        String suffixNo = rechargeOrder.getBankNo().substring(rechargeOrder.getBankNo().length() - 4);
        String description = FundChangeType.RECHARGE.getDescription() + "-" + rechargeOrder.getBankName() + "("+suffixNo+")";
        UserFundChangeLog.UserFundChangeLogBuilder builder = UserFundChangeLog.builder();
        builder.userId(rechargeOrder.getUserId())
                .userFundAccountId(rechargeOrder.getUserFundAccountId())
                .orderNo(rechargeOrder.getOrderNo())
                .amount(rechargeOrder.getOrderAmount())
                .changeType(FundChangeType.RECHARGE)
                .status(FundChangeStatus.PENDING)
                .description(description)
                .orderTime(rechargeOrder.getCreatedAt())
                .finishTime(rechargeOrder.getFinishTime());
        return userFundChangeLogRepo.saveAndFlush(builder.build());
    }

    /**
     * 提现变更
     * @param withdrawApply
     * @return
     */
    @CacheEvict(value=RedisKeys.WALLET_LIANLIAN_BALANCE_CACHE, key="#withdrawApply.userId")
    @Transactional
    public UserFundChangeLog createByWithdraw(WithdrawApply withdrawApply){
        Optional<UserFundChangeLog> changeLogOptional = userFundChangeLogRepo.findByChangeTypeAndOrderNo(FundChangeType.WITHDRAW, withdrawApply.getOrderNo());
        if(changeLogOptional.isPresent()){
            throw new BusinessException("提现流水已存在，不允许重复添加");
        }

        String suffixNo = withdrawApply.getBankNo().substring(withdrawApply.getBankNo().length() - 4);
        String description = FundChangeType.WITHDRAW.getDescription() + "-" + withdrawApply.getBankName() + "("+suffixNo+")";
        UserFundChangeLog.UserFundChangeLogBuilder builder = UserFundChangeLog.builder();
        builder.userId(withdrawApply.getUserId())
                .userFundAccountId(withdrawApply.getUserFundAccountId())
                .orderNo(withdrawApply.getOrderNo())
                .amount(withdrawApply.getOrderAmount().negate())
                .changeType(FundChangeType.WITHDRAW)
                .status(FundChangeStatus.PENDING)
                .description(description)
                .orderTime(withdrawApply.getCreatedAt())
                .finishTime(withdrawApply.getFinishTime());
        return userFundChangeLogRepo.saveAndFlush(builder.build());
    }

    /**
     * 支付变更
     * @param paymentInfo
     * @return
     */
    @CacheEvict(value=RedisKeys.WALLET_LIANLIAN_BALANCE_CACHE, key="#paymentInfo.userId")
    @Transactional
    public UserFundChangeLog createByPayment(PaymentInfo paymentInfo){
        Optional<UserFundChangeLog> changeLogOptional = userFundChangeLogRepo.findByChangeTypeAndOrderNo(FundChangeType.PAYMENT, paymentInfo.getOrderNo());
        if(changeLogOptional.isPresent()){
            throw new BusinessException("支付流水已存在，不允许重复添加");
        }

        String description = FundChangeType.PAYMENT.getDescription() + "-" + paymentInfo.getGoodsTitle();
        UserFundChangeLog.UserFundChangeLogBuilder builder = UserFundChangeLog.builder();
        builder.userId(paymentInfo.getUserId())
                .userFundAccountId(paymentInfo.getUserFundAccountId())
                .orderNo(paymentInfo.getOrderNo())
                .amount(paymentInfo.getPayAmt().negate())
                .changeType(FundChangeType.PAYMENT)
                .status(FundChangeStatus.PENDING)
                .description(description)
                .orderTime(paymentInfo.getCreatedAt())
                .finishTime(paymentInfo.getPayTime());
        return userFundChangeLogRepo.saveAndFlush(builder.build());
    }

    /**
     * 支付变更
     * @param walletRenewOrder
     * @return
     */
    @CacheEvict(value=RedisKeys.WALLET_LIANLIAN_BALANCE_CACHE, key="#walletRenewOrder.userId")
    @Transactional
    public UserFundChangeLog createByRenew(WalletRenewOrder walletRenewOrder){
        Optional<UserFundChangeLog> changeLogOptional = userFundChangeLogRepo.findByChangeTypeAndOrderNo(FundChangeType.RENEW, walletRenewOrder.getOrderNo());
        if(changeLogOptional.isPresent()){
            throw new BusinessException("支付流水已存在，不允许重复添加");
        }

        String description = FundChangeType.RENEW.getDescription() + "-" + walletRenewOrder.getGoodsTitle();
        UserFundChangeLog.UserFundChangeLogBuilder builder = UserFundChangeLog.builder();
        builder.userId(walletRenewOrder.getUserId())
                .userFundAccountId(walletRenewOrder.getUserFundAccountId())
                .orderNo(walletRenewOrder.getOrderNo())
                .amount(walletRenewOrder.getPayAmt().negate())
                .changeType(FundChangeType.RENEW)
                .status(FundChangeStatus.PENDING)
                .description(description)
                .orderTime(walletRenewOrder.getCreatedAt())
                .finishTime(walletRenewOrder.getPayTime());
        return userFundChangeLogRepo.saveAndFlush(builder.build());
    }


    /**
     * 支付变更
     * @param paymentInfo
     * @return
     */
    @CacheEvict(value=RedisKeys.WALLET_LIANLIAN_BALANCE_CACHE, key="#paymentInfo.userId")
    @Transactional
    public UserFundChangeLog createByRefund(PaymentInfo paymentInfo){
        Optional<UserFundChangeLog> changeLogOptional = userFundChangeLogRepo.findByChangeTypeAndOrderNo(FundChangeType.REFUND, paymentInfo.getOrderNo());
        if(changeLogOptional.isPresent()){
            throw new BusinessException("退款流水已存在，不允许重复添加");
        }

        String description = FundChangeType.REFUND.getDescription() + "-" + paymentInfo.getGoodsTitle();
        UserFundChangeLog.UserFundChangeLogBuilder builder = UserFundChangeLog.builder();
        builder.userId(paymentInfo.getUserId())
                .userFundAccountId(paymentInfo.getUserFundAccountId())
                .orderNo(paymentInfo.getOrderNo())
                .amount(paymentInfo.getPayAmt())
                .changeType(FundChangeType.REFUND)
                .status(FundChangeStatus.PENDING)
                .description(description)
                .orderTime(paymentInfo.getApplyRefundTime())
                .finishTime(paymentInfo.getRefundTime());
        return userFundChangeLogRepo.saveAndFlush(builder.build());
    }


    /**
     * 收入变更
     * @param divMoney
     * @return
     */
    @CacheEvict(value=RedisKeys.WALLET_LIANLIAN_BALANCE_CACHE, key="#divMoney.userId")
    @Transactional
    public UserFundChangeLog createByIncome(DivMoney divMoney){
        Order order = orderRepo.findById(divMoney.getOrderId()).orElseThrow(new BusinessException("订单未找到"));
        String description = FundChangeType.INCOME.getDescription() + "-" + order.getName();
        UserFundChangeLog.UserFundChangeLogBuilder builder = UserFundChangeLog.builder();
        //直接标记为成功，不需要变更状态
        builder.userId(divMoney.getUserId())
                .userFundAccountId(divMoney.getUserFundAccountId())
                .orderNo(order.getOrderNo())
                .amount(divMoney.getAmount())
                .changeType(FundChangeType.INCOME)
                .status(FundChangeStatus.SUCCESS)
                .description(description)
                .orderTime(divMoney.getCreatedAt())
                .finishTime(divMoney.getProcessTime());
        return userFundChangeLogRepo.saveAndFlush(builder.build());
    }




    /**
     * 收入变更
     * @param transferMoneyInfo
     * @return
     */
    @CacheEvict(value=RedisKeys.WALLET_LIANLIAN_BALANCE_CACHE, key="#transferMoneyInfo.toUserId")
    @Transactional
    public UserFundChangeLog createByIncome(TransferMoneyInfo transferMoneyInfo){
        String description = FundChangeType.INCOME.getDescription() + "-" + transferMoneyInfo.getReason();
        UserFundChangeLog.UserFundChangeLogBuilder builder = UserFundChangeLog.builder();
        //直接标记为成功，不需要变更状态
        builder.userId(transferMoneyInfo.getToUserId())
                .userFundAccountId(null)
                .orderNo(transferMoneyInfo.getTradeNo())
                .amount(transferMoneyInfo.getAmount())
                .changeType(FundChangeType.INCOME)
                .status(FundChangeStatus.PENDING)
                .description(description)
                .orderTime(transferMoneyInfo.getCreatedAt())
                .finishTime(LocalDateTime.now());
        return userFundChangeLogRepo.saveAndFlush(builder.build());
    }



    /**
     * 更新状态为成功
     * @return
     */
    @CacheEvict(value=RedisKeys.WALLET_LIANLIAN_BALANCE_CACHE, key="#userId")
    @Transactional
    public UserFundChangeLog updateSuccess(Long userId, FundChangeType fundChangeType, String orderNo){
        UserFundChangeLog changeLog = userFundChangeLogRepo.findByChangeTypeAndOrderNo(fundChangeType, orderNo).orElse(null);
        if(changeLog != null && changeLog.getUserId().equals(userId)){
            changeLog.setStatus(FundChangeStatus.SUCCESS);
            changeLog.setFinishTime(LocalDateTime.now());
            userFundChangeLogRepo.saveAndFlush(changeLog);
        }else{
            log.warn("更新资金流水记录失败，userId:{}, changeType:{}, orderNo:{}", userId, fundChangeType, orderNo);
        }
        return changeLog;
    }

    /**
     * 更新状态为失败
     * @return
     */
    @CacheEvict(value=RedisKeys.WALLET_LIANLIAN_BALANCE_CACHE, key="#userId")
    @Transactional
    public UserFundChangeLog updateFailed(Long userId, FundChangeType fundChangeType, String orderNo){
        UserFundChangeLog changeLog = userFundChangeLogRepo.findByChangeTypeAndOrderNo(fundChangeType, orderNo).orElse(null);
        if(changeLog != null && changeLog.getUserId().equals(userId)){
            changeLog.setStatus(FundChangeStatus.FAILED);
            changeLog.setFinishTime(LocalDateTime.now());
            userFundChangeLogRepo.saveAndFlush(changeLog);
        }else{
            log.warn("更新资金流水记录失败，userId:{}, changeType:{}, orderNo:{}", userId, fundChangeType, orderNo);
        }
        return changeLog;
    }



}
