package com.jeequan.jeepay.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jeequan.jeepay.core.entity.AccountOperate;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.model.DBDefaultConfig;
import com.jeequan.jeepay.core.utils.SeqKit;
import com.jeequan.jeepay.db.entity.*;
import com.jeequan.jeepay.service.mapper.AccountChangeInfoMapper;
import com.jeequan.jeepay.service.mapper.AccountFundInfoMapper;
import com.jeequan.jeepay.service.mapper.AccountInfoMapper;
import com.jeequan.jeepay.service.mapper.TransferOrderMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * <p>
 * 账户表 服务实现类
 * </p>
 *
 * @author [mybatis plus generator]
 * @since 2023-12-11
 */
@Service
@Slf4j
public class AccountInfoService extends ServiceImpl<AccountInfoMapper, AccountInfo> {

    @Autowired
    private AccountChangeInfoMapper accountChangeInfoMapper;
    @Autowired
    private SysConfigService sysConfigService;
    @Autowired
    private MchApplymentService mchApplymentService;
    @Autowired
    private AccountFundInfoMapper accountFundInfoMapper;
    @Autowired
    private TransferOrderMapper transferOrderMapper;

    /**
     * 获取账户信息
     *
     * @param info
     * @param currentMchNo
     * @return
     */
    public List<AccountInfo> getAccountInfo(AccountInfo info, String currentMchNo) {
        LambdaQueryWrapper<AccountInfo> wrapper = AccountInfo.gw(info).eq(AccountInfo::getInfoId, currentMchNo);
        wrapper.orderByAsc(AccountInfo::getType);
        List<AccountInfo> list = super.list(wrapper);
        if (list.isEmpty()) {
            return initAccount(currentMchNo);
        }
        if (list.size() < AccountInfo.Type.values().length) {
            return initAccount(currentMchNo);
        }
        return list;
    }

    /**
     * 根据用户号和账户类型 获取账户信息
     *
     * @param mchNo
     * @param type
     * @return
     */
    public AccountInfo getAccountInfo(String mchNo, byte type) {
        LambdaQueryWrapper<AccountInfo> wrapper = AccountInfo.gw().eq(AccountInfo::getInfoId, mchNo).eq(AccountInfo::getType, type);
        AccountInfo account = super.getOne(wrapper);
        if (account == null) {
            throw new BizException("账户信息异常");
        }
        return account;
    }

    /**
     * 初始化账户信息
     *
     * @param currentMchNo
     * @return
     */
    public List<AccountInfo> initAccount(String currentMchNo) {
        List<AccountInfo> result = new ArrayList<>();
        AccountInfo.Type[] values = AccountInfo.Type.values();
        for (AccountInfo.Type type : values) {
            AccountInfo info = new AccountInfo();
            info.setInfoId(currentMchNo);
            info.setType(type.getType());
            AccountInfo ext = baseMapper.selectOne(AccountInfo.gw(info));
            if (ext == null) {
                info.setAccountNo(SeqKit.genAccountNo());
                info.setBalacne(0L);
                info.setFreeze(0L);
                baseMapper.insert(info);
            } else {
                info = ext;
            }
            result.add(info);
        }
        return result;
    }

    /**
     * 校验余额
     *
     * @param accountNo
     * @param amount
     */
    public void checkBalance(String accountNo, Long amount) {
        if (StringUtils.isEmpty(accountNo)) {
            throw new BizException("账户编号号不能为空");
        }
        if (amount == null) {
            throw new BizException("金额不能为空");
        }
        AccountInfo data = baseMapper.selectById(accountNo);
        if (data == null) {
            throw new BizException("账户编号非法");
        }
        AccountInfo.Type val = AccountInfo.Type.getVal(data.getType());
        if ((data.getBalacne() - data.getFreeze()) < amount) {
            throw new BizException(val.getDesc() + "余额不足，请前往充值");
        }
    }

    public void checkBalance(AccountInfo accountInfo, Long amount) {
        if ((accountInfo.getBalacne() - accountInfo.getFreeze()) < amount) {
            throw new BizException("账户可用余额不足，请先充值");
        }
    }

    public void checkBalance(String infoId, AccountInfo.Type accountType, Long amount) {
        AccountInfo account = getAccountInfo(infoId, accountType.getType());
        checkBalance(account.getAccountNo(), amount);
    }

    /**
     * 设置预警规则
     *
     * @param info
     */
    public void setWarnRule(AccountInfo info) {
        info.preRuleCheck();
        AccountInfo ext = baseMapper.selectById(info.getAccountNo());
        if (ext == null) {
            throw new BizException("账户信息异常");
        }
        if (ext.getLastNoticeTime() == null) {
            info.setLastNoticeTime(new Date());
        }
        baseMapper.updateById(info);
    }

    /**
     * 账户变动
     *
     * @param operate
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public synchronized AccountFundInfo upAccount(AccountOperate operate) {
        operate.preCheck();
        String orderId = operate.getOrderId();
        AccountFundInfo fundInfo = accountFundInfoMapper.selectById(orderId);
        AccountInfo account = null;
        boolean isInsert = true;
        if (fundInfo == null) {
            if (operate.getChangeType() == null) {
                log.info("未找到订单{}, 操作类型有误", orderId);
                return null;
            }
            AccountFundInfo.ChangeType changeType = AccountFundInfo.ChangeType.getVal(operate.getChangeType());
            account = baseMapper.selectById(operate.getAccountNo());
            switch (changeType) {
                case RECHARGE:
                    operate.preRechargeCheck();
                    AccountFundInfo.ChangeMethod changeMethod = AccountFundInfo.ChangeMethod.getVal(operate.getChangeMethod());
                    switch (changeMethod) {
                        case SCAN:
                            DBDefaultConfig dbDefaultConfig = sysConfigService.getDBDefaultConfig();
                            if (StringUtils.isEmpty(dbDefaultConfig.getSysPaymentMchNo())) {
                                throw new BizException("系统未配置收款商户号，暂时无法使用");
                            }
                            MchApplyment applyment = mchApplymentService.getById(dbDefaultConfig.getSysPaymentMchNo());
                            if (applyment == null) {
                                throw new BizException("系统配置的商户号数据异常");
                            }
                            operate.setMchExtNo(applyment.getApplyId());
                            operate.setFinalAmt(new BigDecimal(operate.getAmount()).multiply(AccountOperate.SCAN_REALITY_SCALE).longValue());
                            break;
                    }
                    operate.setChangeAmt(operate.getFinalAmt());
                    break;
                case CASH:
                    Long useAmt = account.getBalacne() - account.getFreeze();
                    if (operate.getAmount() > useAmt) {
                        throw new BizException("当前可提现余额不足");
                    }
                    //提现金额取反
                    operate.setChangeAmt(BigDecimal.valueOf(operate.getFinalAmt()).negate().longValue());
                case REFUND:
                    //退款金额取反
                    operate.setChangeAmt(BigDecimal.valueOf(operate.getFinalAmt()).negate().longValue());
                    //如果是退款账户 则需要检验账户余额
                    if (AccountInfo.Type.REFUND.getType().byteValue() == account.getType()) {
                        Long useRefundAmt = account.getBalacne() - account.getFreeze();
                        if (operate.getAmount() > useRefundAmt) {
                            throw new BizException("当前退款账户余额不足");
                        }
                        //如果是手续费账户 退款直接返回手续费余额
                    } else if (AccountInfo.Type.SERVICE_CHARGE.getType().byteValue() == account.getType()) {
                        operate.setChangeAmt(operate.getFinalAmt());
                    }
                    //如果是手续费账户 则不校验账户余额 退款直接返还手续费
                    break;
                case DEDUCT:
                    operate.setChangeAmt(BigDecimal.valueOf(operate.getFinalAmt()).negate().longValue());
                    break;
                case TRANSFER:
                    operate.setChangeAmt(BigDecimal.valueOf(operate.getFinalAmt()).negate().longValue());
                    break;
            }
            fundInfo = new AccountFundInfo(operate, account);
        } else {
            isInsert = false;
            account = baseMapper.selectById(fundInfo.getAccountNo());
            fundInfo.setState(operate.getState());
            AccountFundInfo.ChangeType val = AccountFundInfo.ChangeType.getVal(fundInfo.getChangeType());
            fundInfo.setRemark(StringUtils.isEmpty(operate.getRemark()) ? val.getDesc() + "失败,退回费用" : operate.getRemark());
            operate.setChangeAmt(fundInfo.getFinalAmount());
            operate.setChangeType(fundInfo.getChangeType());
            operate.setChangeMethod(AccountFundInfo.ChangeMethod.FAIL.getValue());
        }
        if (account == null) {
            throw new BizException("账户信息异常");
        }
        try {
            if (operate.getIsUpdateAccount()) {
                AccountChangeInfo changeInfo = new AccountChangeInfo(operate, account);
                accountChangeInfoMapper.insert(changeInfo);
                baseMapper.updateAccount(operate.getChangeAmt(), account.getAccountNo());
            }
            if (isInsert) {
                accountFundInfoMapper.insert(fundInfo);
            } else {
                accountFundInfoMapper.updateById(fundInfo);
            }
            return fundInfo;
        } catch (Exception e) {
            log.info("账户数据更新异常", e);
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            throw new BizException("系统异常");
        }
    }

    /**
     * 发起提现
     *
     * @param info
     */
    public void cash(AccountInfo info) {
        info.preCashCheck();
        String orderId = SeqKit.genCashoutRecordId();
        List<AccountFundInfo> list = accountFundInfoMapper.listNowByAuditData(info.getAccountNo());
        if (!list.isEmpty()) {
            throw new BizException("当前账户存在待处理的数据，暂不支持提现");
        }
        AccountOperate operate = new AccountOperate(info.getAccountNo(), info.getCashInfo().getCashAmt(), AccountFundInfo.ChangeType.CASH.getValue(), AccountFundInfo.ChangeMethod.ACCOUNT_CASH.getValue(), orderId, "账户提现");
        operate.setIsUpdateAccount(true);
        operate.setState(AccountFundInfo.AUDIT);
        operate.setExtra((JSONObject) JSON.toJSON(info.getCashInfo()));
        upAccount(operate);
    }

    /**
     * 转账扣除手续费
     *
     * @param orderId 转账流水号
     */
    public void tranferFeeAccount(String orderId) {
        try {
            TransferOrderEntity entity = transferOrderMapper.selectById(orderId);
            if (entity != null && entity.getMchOrderFeeAmount() != null) {
                AccountInfo account = getAccountInfo(entity.getMchNo(), AccountInfo.Type.SERVICE_CHARGE.getType());
                if (account != null) {
                    AccountOperate operate = new AccountOperate(account.getAccountNo(), entity.getAmount(), AccountFundInfo.ChangeType.DEDUCT.getValue(), AccountFundInfo.ChangeMethod.DF.getValue(), orderId, "代发手续费抵扣");
                    operate.setIsUpdateAccount(true);
                    operate.setFinalAmt(entity.getMchOrderFeeAmount());
                    upAccount(operate);
                }
            }
        } catch (Exception e) {
            log.error("转账扣除手续费异常,订单号：{}，异常信息:{}", orderId, e.getMessage());
        }
    }
}
