package com.yonge.cooleshow.biz.dal.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yonge.cooleshow.biz.dal.dto.excel.UserWithdrawalExport;
import com.yonge.cooleshow.biz.dal.enums.FrozenTypeEnum;
import com.yonge.cooleshow.common.constant.SysConfigConstant;
import com.yonge.cooleshow.biz.dal.dao.UserBankCardDao;
import com.yonge.cooleshow.biz.dal.dto.UserAccountRecordDto;
import com.yonge.cooleshow.biz.dal.dto.req.WithdrawalReq;
import com.yonge.cooleshow.biz.dal.entity.UserAccountRecord;
import com.yonge.cooleshow.biz.dal.enums.AccountBizTypeEnum;
import com.yonge.cooleshow.biz.dal.enums.AuthStatusEnum;
import com.yonge.cooleshow.biz.dal.enums.InOrOutEnum;
import com.yonge.cooleshow.biz.dal.service.SysConfigService;
import com.yonge.cooleshow.biz.dal.service.UserAccountService;
import com.yonge.cooleshow.biz.dal.vo.UserAccountVo;
import com.yonge.cooleshow.biz.dal.vo.UserBankCardVo;
import com.yonge.cooleshow.biz.dal.vo.res.WithdrawalInfoRes;
import com.yonge.cooleshow.common.entity.HttpResponseResult;
import com.yonge.cooleshow.common.exception.BizException;
import com.yonge.toolset.utils.easyexcel.ErrMsg;
import com.yonge.toolset.utils.easyexcel.ExcelDataReaderProperty;
import com.yonge.toolset.utils.easyexcel.ExcelException;
import com.yonge.toolset.utils.string.StringUtil;
import com.yonge.toolset.utils.string.ValueUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.yonge.cooleshow.biz.dal.entity.UserWithdrawal;
import com.yonge.cooleshow.biz.dal.vo.UserWithdrawalVo;
import com.yonge.cooleshow.biz.dal.dto.search.TeacherWithdrawalSearch;
import com.yonge.cooleshow.biz.dal.dao.UserWithdrawalDao;
import com.yonge.cooleshow.biz.dal.service.UserWithdrawalService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.DigestUtils;

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


@Service
public class UserWithdrawalServiceImpl extends ServiceImpl<UserWithdrawalDao, UserWithdrawal> implements UserWithdrawalService {
    private final static Logger log = LoggerFactory.getLogger(UserWithdrawalServiceImpl.class);

    @Autowired
    private UserBankCardDao bankCardDao;
    @Autowired
    private UserAccountService userAccountService;
    @Autowired
    private SysConfigService sysConfigService;


    @Override
    public UserWithdrawalVo detail(Long id) {
        UserWithdrawalVo detail = baseMapper.detail(id);
        return detail;
    }

    @Override
    public IPage<UserWithdrawalVo> selectPage(IPage<UserWithdrawalVo> page, TeacherWithdrawalSearch query) {
        List<UserWithdrawalVo> withdrawalVoList = baseMapper.selectPage(page, query);
        withdrawalVoList.forEach(o -> o.setBankCard(ValueUtil.fuzzyBankCard(o.getBankCard())));
        return page.setRecords(withdrawalVoList);
    }

    @Override
    public List<UserWithdrawalVo> selectList(TeacherWithdrawalSearch query) {
        return baseMapper.selectList(query);
    }

    @Override
    public HttpResponseResult<WithdrawalInfoRes> getWithdrawalInfo(Long userId) {
        WithdrawalInfoRes withdrawalInfoRes = new WithdrawalInfoRes();
        UserBankCardVo defaultBank = bankCardDao.getDefaultBankByUserId(userId);
        if (null == defaultBank) {
            return HttpResponseResult.failed("用户未绑定银行卡");
        }
        defaultBank.setPhone(ValueUtil.fuzzyMobile(defaultBank.getPhone()));
        defaultBank.setBankCard(ValueUtil.fuzzyBankCard(defaultBank.getBankCard()));
        withdrawalInfoRes.setUserBankCard(defaultBank);
        //获取平台提现手续费
        BigDecimal withdrawalServiceFee = getWithdrawalServiceFee();
        withdrawalInfoRes.setWithdrawalServiceFee(withdrawalServiceFee);
        //获取用户可用于提现余额
        withdrawalInfoRes.setAmountWithdrawal(getAmountUsable(userId));
        return HttpResponseResult.succeed(withdrawalInfoRes);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public HttpResponseResult<Boolean> withdrawal(Long userId, WithdrawalReq withdrawalReq) {
        BigDecimal withdrawalServiceFee = getWithdrawalServiceFee();
        //校验金额
        if (withdrawalReq.getAmountWithdrawal().subtract(withdrawalServiceFee).floatValue() < 0) {
            return HttpResponseResult.failed("提现金额过小");
        }
        //获取用户提现金额
        BigDecimal amountWithdrawal = getAmountUsable(userId);
        //比较大小
        if (amountWithdrawal.compareTo(withdrawalReq.getAmountWithdrawal()) < 0) {
            return HttpResponseResult.failed("账户余额不足");
        }
        //获取提现银行卡
        UserBankCardVo bankCardVo = bankCardDao.getBankByUserIdAndCardId(userId, withdrawalReq.getBankCardId());
        if (null == bankCardVo) {
            return HttpResponseResult.failed("未找到用户绑卡信息");
        }
        //插入用户提现表
        UserWithdrawal userWithdrawal = insertUserWithdrawal(userId, withdrawalReq, bankCardVo, withdrawalServiceFee);
        //插入账户变更
        UserAccountRecordDto accountRecordDto = new UserAccountRecordDto(
                userId, withdrawalReq.getAmountWithdrawal(), InOrOutEnum.OUT, AccountBizTypeEnum.WITHDRAWAL,
                userWithdrawal.getId(), "老师提现", null
        );
        accountRecordDto.setFrozenType(FrozenTypeEnum.FROZEN);
        accountRecordDto.setSaveRecord(false);
        HttpResponseResult<UserAccountRecord> accountChange = userAccountService.accountChange(accountRecordDto);
        if (accountChange.getStatus()) {
            return HttpResponseResult.succeed(true);
        } else {
            throw new BizException("提现失败");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void importExcel(List<ExcelDataReaderProperty<UserWithdrawalExport>> dataList, Long userId) {
        List<ErrMsg> errMsgList = new ArrayList<>();
        dataList.sort(Comparator.comparingInt(ExcelDataReaderProperty::getRowIndex));
        List<UserWithdrawal> userWithdrawalList = new ArrayList<>();
        for (ExcelDataReaderProperty<UserWithdrawalExport> dataReaderProperty : dataList) {
            String errMsg = dataReaderProperty.getErrorMessage();
            if (!StringUtil.isEmpty(errMsg)) {
                errMsgList.add(new ErrMsg(dataReaderProperty.getRowIndex(), errMsg));
                continue;
            }
            UserWithdrawalExport data = dataReaderProperty.getClazz();
            StringBuffer buffer = new StringBuffer(data.getId().toString());
            buffer.append(data.getUserId());
            buffer.append(data.getAmount().setScale(2, RoundingMode.HALF_UP));
            buffer.append(data.getPlantformFee().setScale(2, RoundingMode.HALF_UP));
            buffer.append(data.getActualAmount().setScale(2, RoundingMode.HALF_UP));

            String sign = DigestUtils.md5DigestAsHex(buffer.toString().getBytes());
            if (!data.getSign().equals(sign)) {
                errMsgList.add(new ErrMsg(dataReaderProperty.getRowIndex(), "验签失败"));
                continue;
            }
            if (AuthStatusEnum.PASS.equals(data.getStatus()) && StringUtil.isEmpty(data.getBankFlowNo())) {
                errMsgList.add(new ErrMsg(dataReaderProperty.getRowIndex(), "审核通过时，转账流水号不能为空"));
                continue;
            }
            if (!AuthStatusEnum.DOING.equals(data.getStatus())
                    && StringUtil.isEmpty(data.getVerifyReason())) {
                errMsgList.add(new ErrMsg(dataReaderProperty.getRowIndex(), "审核理由不能为空"));
                continue;
            }
            UserWithdrawal userWithdrawal = new UserWithdrawal();
            userWithdrawal.setId(data.getId());
            userWithdrawal.setBankFlowNo(data.getBankFlowNo());
            userWithdrawal.setStatus(data.getStatus());
            userWithdrawal.setVerifyReason(data.getVerifyReason());

            userWithdrawal.setUpdateTime(new Date());
            userWithdrawalList.add(userWithdrawal);
        }
        if (!CollectionUtils.isEmpty(errMsgList)) {
            throw new ExcelException("导入异常", errMsgList);
        }
        authWithdrawalBancth(userWithdrawalList, userId);
    }

    /***
     * 审核
     * @author liweifan
     * @param: userWithdrawalList
     * @updateTime 2022/4/20 11:18
     */
    private void authWithdrawalBancth(List<UserWithdrawal> userWithdrawalList, Long userId) {
        for (UserWithdrawal userWithdrawal : userWithdrawalList) {
            UserWithdrawal old = baseMapper.selectById(userWithdrawal.getId());
            if (null == old || !AuthStatusEnum.DOING.equals(old.getStatus()) || AuthStatusEnum.DOING.equals(userWithdrawal.getStatus())) {
                continue;
            }
            userWithdrawal.setUpdateTime(new Date());

            if (AuthStatusEnum.PASS.equals(userWithdrawal.getStatus())) {
                userWithdrawal.setVerifyUserId(userId);

                //审核通过，账户解冻，入账户明细
                UserAccountRecordDto accountRecordDto = new UserAccountRecordDto(
                        old.getUserId(), old.getAmount(), InOrOutEnum.OUT, AccountBizTypeEnum.WITHDRAWAL,
                        userWithdrawal.getId(), "老师提现", null
                );
                accountRecordDto.setFrozenType(FrozenTypeEnum.FROZEN_DEDUCT);
                accountRecordDto.setSaveRecord(true);
                userAccountService.accountChange(accountRecordDto);

                userWithdrawal.setTransferTime(new Date());
            } else if (AuthStatusEnum.UNPASS.equals(userWithdrawal.getStatus())) {
                //审核不通过，账户解冻
                UserAccountRecordDto accountRecordDto = new UserAccountRecordDto(
                        old.getUserId(), old.getAmount(), InOrOutEnum.OUT, AccountBizTypeEnum.WITHDRAWAL,
                        userWithdrawal.getId(), "老师提现", null
                );
                accountRecordDto.setFrozenType(FrozenTypeEnum.FROZEN_BACK);
                accountRecordDto.setSaveRecord(false);
                userAccountService.accountChange(accountRecordDto);
            }
            baseMapper.updateById(userWithdrawal);
        }
    }

    /***
     * 入提现表
     * @author liweifan
     * @param: userId
     * @param: withdrawalReq
     * @param: bankCardVo
     * @param: userAccountRecord
     * @updateTime 2022/4/7 16:46
     */
    private UserWithdrawal insertUserWithdrawal(Long userId, WithdrawalReq withdrawalReq,
                                                UserBankCardVo bankCardVo, BigDecimal withdrawalServiceFee) {
        UserWithdrawal userWithdrawal = new UserWithdrawal();
        userWithdrawal.setUserId(userId);
        userWithdrawal.setAmount(withdrawalReq.getAmountWithdrawal());
        userWithdrawal.setPlantformFee(withdrawalServiceFee);

        BigDecimal actualAmount = withdrawalReq.getAmountWithdrawal().subtract(withdrawalServiceFee);
        userWithdrawal.setActualAmount(actualAmount);
        userWithdrawal.setBankCard(bankCardVo.getBankCard());
        userWithdrawal.setCreateTime(new Date());
        userWithdrawal.setUpdateTime(new Date());
        userWithdrawal.setStatus(AuthStatusEnum.DOING);
        baseMapper.insert(userWithdrawal);
        return userWithdrawal;
    }

    /***
     * 获取平台提现服务费
     * @author liweifan
     * @param: userId
     * @updateTime 2022/4/7 14:59
     * @return: java.math.BigDecimal
     */
    private BigDecimal getWithdrawalServiceFee() {
        BigDecimal withdrawalServiceFee;
        try {
            String configValue = sysConfigService.findConfigValue(SysConfigConstant.WITHDRAWAL_SERVICE_FEE);
            withdrawalServiceFee = new BigDecimal(configValue);
        } catch (Exception e) {
            e.printStackTrace();
            log.info("获取平台配置失败 key={}", SysConfigConstant.WITHDRAWAL_SERVICE_FEE);
            withdrawalServiceFee = new BigDecimal(0);
        }
        return withdrawalServiceFee;
    }

    /***
     * 获取用户可提现金额
     * @author liweifan
     * @param: userId
     * @param: withdrawalServiceFee
     * @updateTime 2022/4/7 15:02
     * @return: java.math.BigDecimal
     */
    private BigDecimal getAmountUsable(Long userId) {
        //查询用户账户余额
        UserAccountVo detail = userAccountService.detail(userId);
        BigDecimal subtract = detail.getAmountUsable();
        if (subtract.intValue() < 0) {
            subtract = new BigDecimal(0);
        }
        return subtract;
    }

}