package com.sonic.lion.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.Lists;
import com.sonic.common.exception.SysException;
import com.sonic.lion.dao.AccountBuffBillDao;
import com.sonic.lion.dao.AccountBuffDao;
import com.sonic.lion.domain.entity.AccountBuff;
import com.sonic.lion.domain.entity.AccountBuffAwaiting;
import com.sonic.lion.domain.entity.AccountBuffBill;
import com.sonic.lion.domain.entity.AppleRefundRecord;
import com.sonic.lion.domain.enums.*;
import com.sonic.lion.domain.input.BuffChangeInput;
import com.sonic.lion.domain.input.BuffTransferInput;
import com.sonic.lion.domain.output.WalletOutput;
import com.sonic.lion.enums.BizType;
import com.sonic.lion.enums.PayChannel;
import com.sonic.lion.enums.PayGenrtatorCodeType;
import com.sonic.lion.enums.ToastResultCode;
import com.sonic.lion.service.AccountBuffAwaitingService;
import com.sonic.lion.service.AccountBuffBillService;
import com.sonic.lion.service.AccountBuffService;
import com.sonic.lion.service.PayTradeService;
import com.sonic.lion.utils.KeyGenerator;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;

/**
 * @author: code
 * @date: 2025/07/16
 * @Description:
 * @version: 1.0.0
 */
@Service
@Slf4j
public class AccountBuffServiceImpl implements AccountBuffService {

    @Value("${stripe.withdrawFeeRate}")
    private BigDecimal withdrawFeeRate;

    @Autowired
    private AccountBuffDao accountBuffDao;
    @Autowired
    private AccountBuffBillDao accountBuffBillDao;
    @Autowired
    private AccountBuffBillService accountBuffBillService;
    @Autowired
    private AccountBuffAwaitingService accountBuffAwaitingService;
    @Autowired
    private PayTradeService payTradeService;

    @Override
    public AccountBuff getByUid(Long uid) {
        if (uid == null) {
            return null;
        }
        LambdaQueryWrapper<AccountBuff> queryWrapper = Wrappers.<AccountBuff>lambdaQuery()
                .eq(AccountBuff::getUid, uid);
        return accountBuffDao.selectOne(queryWrapper);
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean frozenBalance(Long uid, Long buff) {
        boolean success = accountBuffDao.frozenBalance(uid, buff) > 0;
        if (success) {
            AccountBuff accountBuff = getByUid(uid);
            //操作后总buff
            long afterTotalBuff = getCurrentTotalBuff(accountBuff);
            AccountBuffBill bill = AccountBuffBill.builder()
                    .platform("1")
                    .billNo(KeyGenerator.instance().generatorUniqueKey(PayGenrtatorCodeType.ACCOUNT_BILL))
                    .accountId(accountBuff.getId())
                    .uid(accountBuff.getUid())
                    .desUid(-1L)
                    .buff(buff)
                    .bizType(BizType.FROZEN_BALANCE)
                    .payChannel(PayChannel.BUFF)
                    .tradeNo("")
                    .bizNo("")
                    .bizNoRelationNo("")
                    .balance(afterTotalBuff)
                    .inOrOut(InOrOut.OUT)
                    .buffType(BuffType.BALANCE)
                    .reason("")
                    .buffClassify(BuffClassifyEnum.getBuffClassify(BizType.FROZEN_BALANCE, InOrOut.OUT))
                    .build();
            accountBuffBillService.save(bill);
        }
        return success;
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public AppleRefundRecord.FronzenStatus fronzenBalanceAndSendMessage(Long userId, Long getBuffAmount) {
        AccountBuff accountBuff = getByUid(userId);
        if (accountBuff.getBalance() >= getBuffAmount) {
            frozenBalance(userId, getBuffAmount); //冻结 金额并 发送消息
            return AppleRefundRecord.FronzenStatus.DEC;
        } else if (accountBuff.getBalance() > 0 && accountBuff.getBalance() < getBuffAmount) {
            frozenBalance(userId, accountBuff.getBalance()); //冻结 金额并 发送消息
            return AppleRefundRecord.FronzenStatus.FRONZEN;
        } else {
            return AppleRefundRecord.FronzenStatus.INIT;
        }
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean unFrozenBalance(Long uid, Long buff) {
        boolean success = accountBuffDao.unFrozenBalance(uid, buff) > 0;
        if (success) {
            AccountBuff accountBuff = getByUid(uid);
            //操作后总buff
            long afterTotalBuff = getCurrentTotalBuff(accountBuff);
            AccountBuffBill bill = AccountBuffBill.builder()
                    .platform("1")
                    .billNo(KeyGenerator.instance().generatorUniqueKey(PayGenrtatorCodeType.ACCOUNT_BILL))
                    .accountId(accountBuff.getId())
                    .uid(accountBuff.getUid())
                    .desUid(-1L)
                    .buff(buff)
                    .bizType(BizType.UNFROZEN_BALANCE)
                    .payChannel(PayChannel.BUFF)
                    .tradeNo("")
                    .bizNo("")
                    .bizNoRelationNo("")
                    .balance(afterTotalBuff)
                    .inOrOut(InOrOut.IN)
                    .buffType(BuffType.BALANCE)
                    .reason("")
                    .buffClassify(BuffClassifyEnum.getBuffClassify(BizType.FROZEN_BALANCE, InOrOut.IN))
                    .build();
            accountBuffBillService.save(bill);
        }
        return success;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long addAwaitingIncome(@NonNull BuffChangeInput input) {
        if (input.getBuff() == 0) {
            return null;
        }
        validInput(input);
        AccountBuff accountBuff = getByUid(input.getUid());
        if (accountBuff == null) {
            //先初始化该渠道资金账户
            accountBuff = initAccount(input.getUid());
        }

        int n = accountBuffDao.addAwaitingIncome(accountBuff.getId(), input.getBuff());
        //根据订单数量 决定 具体的待入账时间。
        int toWithdrawableIncomeTime = payTradeService.getToWithdrawableIncomeTime(input.getUid(), CoinType.BUFF);

        //记录内部账单
        input.setToWithdrawableIncomeTime(LocalDateTime.now().plusDays(toWithdrawableIncomeTime));
        Long billId = saveBill(input, accountBuff, InOrOut.IN, BuffType.AWAITING_INCOME);

        AccountBuffAwaiting accountBuffAwaiting = AccountBuffAwaiting.builder()
                .billId(billId)
                .accountId(accountBuff.getId())
                .tradeNo(input.getTradeNo())
                .buff(input.getBuff())
                .toWithdrawableIncomeTime(LocalDateTime.now().plusDays(toWithdrawableIncomeTime))
                .status(AccountBuffAwaiting.Status.PENDING)
                .build();

        accountBuffAwaitingService.save(accountBuffAwaiting);
        return billId;
    }

    @Transactional(rollbackFor = Exception.class)
    public void decAwaitingIncome(@NonNull BuffChangeInput input) {
        if (input.getBuff() == 0) {
            return;
        }

        validInput(input);
        AccountBuffAwaiting accountBuffAwaiting = accountBuffAwaitingService.getByTradeNo(input.getTradeNo());
        //未找到待入账记录

        ToastResultCode.DATA_NOT_EXITS.check(accountBuffAwaiting == null);
        ToastResultCode.PAY_TRADE_STATUS_CHANGE.check(accountBuffAwaiting.getStatus() == AccountBuffAwaiting.Status.CREDITED);
        ToastResultCode.PAY_TRADE_STATUS_CHANGE.check(accountBuffAwaiting.getStatus() == AccountBuffAwaiting.Status.REFUNDED);

        //WEB 5.16.0 看扣款的金额是否已扣完了，扣完了的话修改状态为退款，否则修改待入账的金额值
        Long awaitingBuff = accountBuffAwaiting.getBuff() - input.getBuff();

        if (awaitingBuff > 0) {
            accountBuffAwaitingService.updateAwaitingBuff(accountBuffAwaiting.getId(), awaitingBuff, AccountBuffAwaiting.Status.PENDING);
        } else {
            accountBuffAwaitingService.updateStatus(accountBuffAwaiting.getId(), AccountBuffAwaiting.Status.REFUNDED, AccountBuffAwaiting.Status.PENDING);
        }

        AccountBuff accountBuff = accountBuffDao.selectByUid(input.getUid());

        int n = accountBuffDao.decAwaitingIncome(accountBuff.getId(), input.getBuff());
        //待入账金额不足或其他异常情况
        ToastResultCode.INSUFFICIENT_BALANCE.check(n != 1);
        //记录内部账单
        saveBill(input, accountBuff, InOrOut.OUT, BuffType.AWAITING_INCOME);
        //卖家入账流水 如果全额退款，buff_type字段更新为退款类型
        if (input.getFullRefund() != null && input.getFullRefund()) {
            accountBuffBillService.updateBuffTypeToRefund(input.getTradeNo());
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void addBalance(@NonNull BuffChangeInput input) {
        if (input.getBuff() == 0) {
            return;
        }
        //入参校验
        validInput(input);
        //获取账号基础数据
        AccountBuff accountBuff = accountBuffDao.selectByUid(input.getUid());
        if (accountBuff == null) {
            //先初始化该渠道资金账户
            accountBuff = initAccount(input.getUid());
        }
        //增加钱包余额
        int n = accountBuffDao.addBalance(accountBuff.getId(), input.getBuff());
        //增加总充值金额
        if (BizType.CHARGE.equals(input.getBizType())) {
            accountBuffDao.addRechargeTotal(accountBuff.getUid(), input.getBuff());
        }
        //记录内部账单
        saveBill(input, accountBuff, InOrOut.IN, BuffType.BALANCE);
    }

    @Transactional(rollbackFor = Exception.class)
    public Long decBalance(@NonNull BuffChangeInput input) {
        if (input.getBuff() == 0) {
            return null;
        }
        //入参校验
        validInput(input);
        //获取账号基础数据
        AccountBuff accountBuff = accountBuffDao.selectByUid(input.getUid());
        if (accountBuff == null) {
            //先初始化该渠道资金账户
            accountBuff = initAccount(input.getUid());
        }
        int n = accountBuffDao.decBalance(accountBuff.getId(), input.getBuff());
        //定义钱包余额不足时的异常码
        ToastResultCode.INSUFFICIENT_BALANCE.check(n != 1);
        //记录内部账单
        Long billId = saveBill(input, accountBuff, InOrOut.OUT, BuffType.BALANCE);
        return billId;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void awaitingIncomeToWithdrawableIncome(@NonNull AccountBuffAwaiting accountBuffAwaiting) {
        ToastResultCode.DATA_STATUS_INCORRECT.check(accountBuffAwaiting.getStatus() != AccountBuffAwaiting.Status.PENDING);
        //更新待入账为已入账
        accountBuffAwaitingService.updateStatus(accountBuffAwaiting.getId(), AccountBuffAwaiting.Status.CREDITED, AccountBuffAwaiting.Status.PENDING);
        //用户总待待入账收入减，总可提现金额加
        int n = accountBuffDao.awaitingIncomeToWithdrawableIncome(accountBuffAwaiting.getAccountId(), accountBuffAwaiting.getBuff());
        ToastResultCode.INSUFFICIENT_BALANCE.check(n != 1);
        //更新流水状态为为可提现收入
        Long billId = accountBuffAwaiting.getBillId();
        if (billId != null) {
            accountBuffBillDao.updateWithdrawableIncome(BuffType.WITHDRAWABLE_INCOME.getValue(), billId);
        }
        //待入账金额不足或其他异常情况

    }


    @Transactional(rollbackFor = Exception.class)
    public void decWithdrawableIncome(@NonNull BuffChangeInput input) {
        if (input.getBuff() == 0) {
            return;
        }

        validInput(input);

        AccountBuff accountBuff = accountBuffDao.selectByUid(input.getUid());
        if (accountBuff == null) {
            //先初始化该渠道资金账户
            accountBuff = initAccount(input.getUid());
        }
        int n = accountBuffDao.decWithdrawableIncome(accountBuff.getId(), input.getBuff());
        ToastResultCode.INSUFFICIENT_BALANCE.check(n != 1);

        //减少可提现账户的 金额 进入  ongoing 账户。
        addWithdrawOnGoing(accountBuff.getId(), input.getBuff());
        //记录内部账单
        saveBill(input, accountBuff, InOrOut.OUT, BuffType.WITHDRAWABLE_INCOME);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<Long> transfer(@NonNull BuffTransferInput input) {
        //声明流水ID列表
        List<Long> billIdList = Lists.newArrayList();

        BuffChangeInput buffChangeInput = BuffChangeInput.builder()
                .platform(input.getPlatform())
                .bizType(input.getBizType())
                .tradeNo(input.getTradeNo())
                .bizNo(input.getBizNo())
                .errorMessage(input.getErrorMessage())
                .bizNoRelationNo(input.getBizNoRelationNo())
                .payChannel(input.getPayChannel())
                .payMethod(input.getPayMethod())
                .reason(input.getReason())
                .extend(input.getExtend())
                .fullRefund(input.getFullRefund())
                .build();

        //先扣款
        if (input.getDecUid() != null) {
            buffChangeInput.setUid(input.getDecUid());
            buffChangeInput.setDesUid(input.getAddUid());
            buffChangeInput.setBuff(input.getDecBuff());
            buffChangeInput.setTargetUserId(input.getDecTargetUserId());
            //校验扣款金额是否正确
            ToastResultCode.PAY_TRADE_STATUS_CHANGE.check(input.getDecBuffType() != BuffType.BALANCE && input.getDecBuff() == 0);
            if (input.getDecBuffType() == BuffType.BALANCE) {
                Long billId = decBalance(buffChangeInput);
                if (billId != null) {
                    billIdList.add(billId);
                }
            } else if (input.getDecBuffType() == BuffType.AWAITING_INCOME) {
                decAwaitingIncome(buffChangeInput);
            } else if (input.getDecBuffType() == BuffType.WITHDRAWABLE_INCOME) {
                decWithdrawableIncome(buffChangeInput);
            } else {
                throw new SysException("", "暂不之支持扣除的buff类型");
            }
        }

        //再增加
        if (input.getAddUid() != null) {
            buffChangeInput.setUid(input.getAddUid());
            buffChangeInput.setDesUid(input.getDecUid());
            buffChangeInput.setBuff(input.getAddBuff());
            buffChangeInput.setTargetUserId(input.getAddTargetUserId());
            buffChangeInput.setGiftAmount(input.getGiftAmount());
            if (input.getAddBuffType() == BuffType.BALANCE) {
                addBalance(buffChangeInput);
            } else if (input.getAddBuffType() == BuffType.AWAITING_INCOME) {
                Long billId = addAwaitingIncome(buffChangeInput);
                if (billId != null) {
                    billIdList.add(billId);
                }
            } else if (input.getAddBuffType() == BuffType.WITHDRAWABLE_INCOME) {
                addWithdrawableIncome(buffChangeInput);
            } else {
                throw new SysException("", "暂不支持扣除的buff类型");
            }
        }

        //给系统账号增加
        if (input.getAddSystemUid() != null) {
            buffChangeInput.setUid(input.getAddSystemUid());
            buffChangeInput.setDesUid(input.getDecUid());
            buffChangeInput.setBuff(input.getAddSystemBuff());
            if (input.getAddSystemBuffType() == BuffType.BALANCE) {
                addBalance(buffChangeInput);
            } else if (input.getAddSystemBuffType() == BuffType.AWAITING_INCOME) {
                Long billId = addAwaitingIncome(buffChangeInput);
                if (billId != null) {
                    billIdList.add(billId);
                }
            } else if (input.getAddSystemBuffType() == BuffType.WITHDRAWABLE_INCOME) {
                addWithdrawableIncome(buffChangeInput);
            } else {
                throw new SysException("", "暂不支持扣除的buff类型");
            }
        }

        return billIdList;
    }

    @Override
    public Long calculateIncome(AccountBuff accountBuff) {
        if (accountBuff == null) {
            return 0L;
        }
        return accountBuff.getWithdrawableIncome() + accountBuff.getAwaitingIncome() + accountBuff.getFrozenIncome();
    }


    /**
     * 这个方法仅 用于 提现回滚
     *
     * @param input
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addWithdrawableIncome(@NonNull BuffChangeInput input) {
        if (input.getBuff() == 0) {
            return;
        }
        validInput(input);
        AccountBuff accountBuff = accountBuffDao.selectByUid(input.getUid());
        if (accountBuff == null) {
            //先初始化该渠道资金账户
            accountBuff = initAccount(input.getUid());
        }
        int n = accountBuffDao.addWithdrawableIncome(accountBuff.getId(), input.getBuff());

        ToastResultCode.INSUFFICIENT_BALANCE.check(n != 1);
        //回滚账单状态为已经失败 作废，并增加原因
        rollbackBill(input.getTradeNo(), input.getErrorMessage());
    }

    private void rollbackBill(String tradeNo, String errorMessage) {
        accountBuffBillDao.updateStatus(tradeNo, BillStatusEnum.ROLL_BACK.getValue(), errorMessage);
    }

    private void validInput(BuffChangeInput input) {
        ToastResultCode.AMOUNT_LESS_THAN0.check(input.getBuff() < 0);
        ToastResultCode.TRADE_NO_BLANK.check(StringUtils.isBlank(input.getTradeNo()));
    }

    private AccountBuff initAccount(Long uid) {
        AccountBuff accountBuff = AccountBuff.builder()
                .uid(uid)
                .status(AccountBuff.Status.ENABLE)
                .balance(0L)
                .withdrawableIncome(0L)
                .awaitingIncome(0L)
                .frozenIncome(0L)
                .withdrawOnGoing(0L)
                .build();
        accountBuffDao.insert(accountBuff);
        return accountBuff;
    }

    private long getCurrentTotalBuff(AccountBuff accountBuff) {
        return accountBuff.getBalance() + accountBuff.getWithdrawableIncome() + accountBuff.getAwaitingIncome() + accountBuff.getFrozenIncome() + accountBuff.getWithdrawOnGoing();
    }


    @Transactional
    @Override
    public Long saveBill(BuffChangeInput input, AccountBuff accountBuff, InOrOut inOrOut, BuffType buffType) {
        //操作后总buff
        long afterTotalBuff = getCurrentTotalBuff(accountBuff);
        log.info("afterTotalBuff:{},input:{}", afterTotalBuff, input);
        if (inOrOut == InOrOut.IN) {
            afterTotalBuff += input.getBuff();
        } else if (inOrOut == InOrOut.OUT) {
            afterTotalBuff -= input.getBuff();
        }
        log.info("afterTotalBuff2:{},input:{}", afterTotalBuff, input);

        BizType bizType = input.getBizType();
        AccountBuffBill bill = AccountBuffBill.builder()
                .platform(input.getPlatform())
                .billNo(KeyGenerator.instance().generatorUniqueKey(PayGenrtatorCodeType.ACCOUNT_BILL))
                .accountId(accountBuff.getId())
                .uid(input.getUid())
                .desUid(input.getDesUid())
                .targetUserId(input.getTargetUserId())
                .buff(input.getBuff())
                .bizType(bizType)
                .payChannel(input.getPayChannel())
                .tradeNo(input.getTradeNo())
                .bizNo(input.getBizNo())
                .bizNoRelationNo(input.getBizNoRelationNo())
                .balance(afterTotalBuff)
                .inOrOut(inOrOut)
                .buffType(buffType)
                .giftAmount(input.getGiftAmount())
                .reason(input.getErrorMessage())
                .extend(input.getExtend())
                .payMethod(input.getPayMethod())
                .buffClassify(BuffClassifyEnum.getBuffClassify(bizType, inOrOut))
                .toWithdrawableIncomeTime(input.getToWithdrawableIncomeTime())
                .build();


        //如果是退款并且是支出，则更新buff_type为REFUND
        boolean isRefund = BizType.REFUND.equals(bizType);
        if (isRefund && InOrOut.OUT.equals(inOrOut)) {
            bill.setBuffType(BuffType.REFUND);
        }
        accountBuffBillService.save(bill);
        return bill.getId();
    }

    /**
     * 加上在途余额
     *
     * @param srcAccountId
     * @param amount
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void addWithdrawOnGoing(Long srcAccountId, Long amount) {
        accountBuffDao.addWithdrawOnGoing(srcAccountId, amount);
    }


    /**
     * 扣减 在途余额
     *
     * @param srcAccountId
     * @param amount
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean decWithdrawOnGoing(Long srcAccountId, Long amount) {
        return accountBuffDao.decWithdrawOnGoing(srcAccountId, amount) > 0;
    }


    /**
     * 在途资金 回滚到 可提现余额
     *
     * @param id
     * @param amount
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean withdrawOnGoingRollback(Long id, Long amount) {
        ToastResultCode.INSUFFICIENT_BALANCE.check(!decWithdrawOnGoing(id, amount));
        return accountBuffDao.addWithdrawableIncome(id, amount) > 0;
    }

    @Override
    public WalletOutput wallet(Long userId) {
        WalletOutput output = new WalletOutput();
        //设置提现手续费的抽成比例
        output.setWdFeeRate(withdrawFeeRate);
        AccountBuff accountBuff = getByUid(userId);
        if (accountBuff != null) {
            output.setBalance(accountBuff.getBalance());
            output.setIncome(calculateIncome(accountBuff));
            output.setRequestWithdraw(accountBuff.getWithdrawOnGoing());
            output.setWithdrawable(accountBuff.getWithdrawableIncome() == null ? 0 : accountBuff.getWithdrawableIncome());
            output.setAwaitingIncome(accountBuff.getAwaitingIncome());
        }
        return output;
    }

}
