package com.slodon.b2b2c.model.member;

import com.slodon.b2b2c.core.constant.MemberConst;
import com.slodon.b2b2c.core.constant.MemberTplConst;
import com.slodon.b2b2c.core.constant.MemberWithdrawConst;
import com.slodon.b2b2c.core.constant.PayConst;
import com.slodon.b2b2c.core.exception.MallException;
import com.slodon.b2b2c.core.response.PagerInfo;
import com.slodon.b2b2c.core.uid.GoodsIdGenerator;
import com.slodon.b2b2c.core.util.AssertUtil;
import com.slodon.b2b2c.core.util.StringUtil;
import com.slodon.b2b2c.core.util.TimeUtil;
import com.slodon.b2b2c.dao.read.member.MemberApplyCashReadMapper;
import com.slodon.b2b2c.dao.read.member.MemberBankAccountReadMapper;
import com.slodon.b2b2c.dao.read.member.MemberReadMapper;
import com.slodon.b2b2c.dao.write.member.MemberApplyCashWriteMapper;
import com.slodon.b2b2c.dao.write.member.MemberBalanceLogWriteMapper;
import com.slodon.b2b2c.dao.write.member.MemberBankAccountWriteMapper;
import com.slodon.b2b2c.dao.write.member.MemberWriteMapper;
import com.slodon.b2b2c.member.example.MemberApplyCashExample;
import com.slodon.b2b2c.member.example.MemberBankAccountExample;
import com.slodon.b2b2c.member.pojo.Member;
import com.slodon.b2b2c.member.pojo.MemberApplyCash;
import com.slodon.b2b2c.member.pojo.MemberBalanceLog;
import com.slodon.b2b2c.member.pojo.MemberBankAccount;
import com.slodon.b2b2c.starter.entity.SlodonTransfersRequest;
import com.slodon.b2b2c.starter.entity.SlodonTransfersResponse;
import com.slodon.b2b2c.starter.mq.entity.MessageSendProperty;
import com.slodon.b2b2c.starter.mq.entity.MessageSendVO;
import com.slodon.b2b2c.starter.pay.SlodonPay;
import com.slodon.b2b2c.system.pojo.Admin;
import com.slodon.b2b2c.util.PayPropertiesUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import static com.slodon.b2b2c.core.constant.StarterConfigConst.MQ_EXCHANGE_NAME;
import static com.slodon.b2b2c.core.constant.StarterConfigConst.MQ_QUEUE_NAME_MEMBER_MSG;

@Component
@Slf4j
public class MemberApplyCashModel {

    @Resource
    private MemberApplyCashReadMapper memberApplyCashReadMapper;
    @Resource
    private MemberApplyCashWriteMapper memberApplyCashWriteMapper;
    @Resource
    private MemberBankAccountReadMapper memberBankAccountReadMapper;
    @Resource
    private MemberBankAccountWriteMapper memberBankAccountWriteMapper;
    @Resource
    private MemberReadMapper memberReadMapper;
    @Resource
    private MemberWriteMapper memberWriteMapper;
    @Resource
    private MemberBalanceLogWriteMapper memberBalanceLogWriteMapper;
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private RabbitTemplate rabbitTemplate;
    @Resource
    private PayPropertiesUtil payPropertiesUtil;
    @Resource
    private SlodonPay slodonPay;

    /**
     * 新增会员提现申请
     *
     * @param memberApplyCash 会员提现申请信息
     * @return
     */
    public Integer saveMemberApplyCash(MemberApplyCash memberApplyCash) {
        int count = memberApplyCashWriteMapper.insert(memberApplyCash);
        if (count == 0) {
            throw new MallException("添加会员提现申请失败，请重试");
        }
        return count;
    }

    /**
     * 申请提现
     *
     * @param accountId  账号id
     * @param cashAmount 提现金额
     * @param member     会员信息
     */
    @Transactional(rollbackFor = Exception.class)
    public void applyWithdraw(Integer accountId, BigDecimal cashAmount, Member member, String accountName, String accountNumber) {
        MemberBankAccount memberBankAccount;
        if (!StringUtil.isNullOrZero(accountId)) {
            //查询提现账号信息
            memberBankAccount = memberBankAccountReadMapper.getByPrimaryKey(accountId);
            AssertUtil.notNull(memberBankAccount, "提现账号不存在");
        } else {
            //进入这里说明是pc支付宝提现，该账号不做记录
//            memberBankAccount = this.insertMemberBankAccount(member, accountName, accountNumber);
            memberBankAccount = new MemberBankAccount();
            memberBankAccount.setAccountCode(MemberWithdrawConst.ACCOUNT_CODE_ALIPAY);
            memberBankAccount.setAccountName(accountName);
            memberBankAccount.setAccountNumber(accountNumber);
            memberBankAccount.setBankName("支付宝");
        }
        //校验提现方式是否开启
        if (MemberWithdrawConst.ACCOUNT_CODE_WXPAY.equals(memberBankAccount.getAccountCode())) {
            AssertUtil.isTrue(!"1".equals(stringRedisTemplate.opsForValue().get("withdraw_wxpay_is_enable")), "微信提现未开启");
        } else if (MemberWithdrawConst.ACCOUNT_CODE_ALIPAY.equals(memberBankAccount.getAccountCode())) {
            AssertUtil.isTrue(!"1".equals(stringRedisTemplate.opsForValue().get("withdraw_alipay_is_enable")), "支付宝提现未开启");
        } else {
            throw new MallException("账号有误");
        }

        //手续费
        BigDecimal serviceFee = new BigDecimal("0.00");
        //提现手续费
        String withdrawFee = stringRedisTemplate.opsForValue().get("withdraw_fee");
        if (!StringUtil.isEmpty(withdrawFee) && !"0".equals(withdrawFee)) {
            serviceFee = cashAmount.multiply(new BigDecimal(withdrawFee)).divide(new BigDecimal("100")).setScale(2, RoundingMode.HALF_UP);
        }

        //保存申请提现信息
        MemberApplyCash memberApplyCash = new MemberApplyCash();
        memberApplyCash.setMemberId(member.getMemberId());
        memberApplyCash.setMemberName(member.getMemberName());
        memberApplyCash.setCashSn(GoodsIdGenerator.getTransferSn());
        memberApplyCash.setCashAmount(cashAmount);
        memberApplyCash.setApplyTime(new Date());
        memberApplyCash.setReceiveBank(memberBankAccount.getBankName());
        memberApplyCash.setReceiveAccount(memberBankAccount.getAccountNumber());
        memberApplyCash.setReceiveName(memberBankAccount.getAccountName());
        memberApplyCash.setReceiveType(memberBankAccount.getAccountCode());
        memberApplyCash.setWxType(memberBankAccount.getWxType());
        memberApplyCash.setState(MemberWithdrawConst.STATE_1);
        memberApplyCash.setServiceFee(serviceFee);
        this.saveMemberApplyCash(memberApplyCash);

        //修改上次提现账号状态
//        this.updateLsLast(memberBankAccount);

        //更新会员余额信息
        Member memberNew = new Member();
        memberNew.setMemberId(member.getMemberId());
        memberNew.setBalanceAvailable(member.getBalanceAvailable().subtract(cashAmount));
        memberNew.setBalanceFrozen(member.getBalanceFrozen().add(cashAmount));
        memberNew.setUpdateTime(new Date());
        int count = memberWriteMapper.updateByPrimaryKeySelective(memberNew);
        AssertUtil.isTrue(count == 0, "更新会员余额失败，请重试！");

        //记录余额日志
        MemberBalanceLog memberBalanceLog = new MemberBalanceLog();
        memberBalanceLog.setMemberId(member.getMemberId());
        memberBalanceLog.setMemberName(member.getMemberName());
        memberBalanceLog.setAfterChangeAmount(member.getBalance().subtract(cashAmount));
        memberBalanceLog.setChangeValue(BigDecimal.ZERO);
        memberBalanceLog.setFreezeAmount(memberNew.getBalanceFrozen());
        memberBalanceLog.setFreezeValue(cashAmount);
        memberBalanceLog.setCreateTime(new Date());
        memberBalanceLog.setType(MemberConst.TYPE_7);
        memberBalanceLog.setDescription("用户申请提现冻结，提现单号：" + memberApplyCash.getCashSn());
        memberBalanceLog.setAdminId(0);
        memberBalanceLog.setAdminName(member.getMemberName());
        count = memberBalanceLogWriteMapper.insert(memberBalanceLog);
        AssertUtil.isTrue(count == 0, "记录余额日志失败，请重试！");
    }

    /**
     * 更新上次提现账号状态
     *
     * @param memberBankAccount 会员提现账号信息
     */
    public void updateLsLast(MemberBankAccount memberBankAccount) {
        if (memberBankAccount.getIsLast() == MemberWithdrawConst.IS_LAST_0) {
            MemberBankAccount bankAccount = new MemberBankAccount();
            bankAccount.setAccountId(memberBankAccount.getAccountId());
            bankAccount.setIsLast(MemberWithdrawConst.IS_LAST_1);
            bankAccount.setUpdateTime(new Date());
            int count = memberBankAccountWriteMapper.updateByPrimaryKeySelective(bankAccount);
            AssertUtil.isTrue(count == 0, "更新会员提现账号信息失败，请重试");
        }
        //更新其他账号状态
        MemberBankAccount bankAccount = new MemberBankAccount();
        bankAccount.setIsLast(MemberWithdrawConst.IS_LAST_0);
        bankAccount.setUpdateTime(new Date());
        MemberBankAccountExample example = new MemberBankAccountExample();
        example.setMemberId(memberBankAccount.getMemberId());
        example.setAccountIdNotEquals(memberBankAccount.getAccountId());
        memberBankAccountWriteMapper.updateByPrimaryKeySelective(bankAccount);
    }

    /**
     * 插入支付宝账号信息
     *
     * @param member        会员信息
     * @param accountName   真实姓名
     * @param accountNumber 支付宝账号
     * @return
     */
    public MemberBankAccount insertMemberBankAccount(Member member, String accountName, String accountNumber) {
        MemberBankAccountExample example = new MemberBankAccountExample();
        example.setMemberId(member.getMemberId());
        example.setAccountName(accountName);
        example.setAccountNumber(accountNumber);
        example.setAccountCode(MemberWithdrawConst.ACCOUNT_CODE_ALIPAY);
        List<MemberBankAccount> list = memberBankAccountReadMapper.listByExample(example);
        if (!CollectionUtils.isEmpty(list)) {
            return list.get(0);
        }
        //插入支付宝账号信息
        MemberBankAccount memberBankAccount = new MemberBankAccount();
        memberBankAccount.setMemberId(member.getMemberId());
        memberBankAccount.setMemberName(member.getMemberName());
        memberBankAccount.setAccountCode(MemberWithdrawConst.ACCOUNT_CODE_ALIPAY);
        memberBankAccount.setAccountName(accountName);
        memberBankAccount.setAccountNumber(accountNumber);
        memberBankAccount.setBankName("支付宝");
        memberBankAccount.setIsLast(MemberWithdrawConst.IS_LAST_1);
        memberBankAccount.setCreateTime(new Date());
        int count = memberBankAccountWriteMapper.insert(memberBankAccount);
        AssertUtil.isTrue(count == 0, "添加会员提现账号失败，请重试");
        return memberBankAccount;
    }

    /**
     * 根据cashId删除会员提现申请
     *
     * @param cashId cashId
     * @return
     */
    public Integer deleteMemberApplyCash(Integer cashId) {
        if (StringUtils.isEmpty(cashId)) {
            throw new MallException("请选择要删除的数据");
        }
        int count = memberApplyCashWriteMapper.deleteByPrimaryKey(cashId);
        if (count == 0) {
            log.error("根据cashId：" + cashId + "删除会员提现申请失败");
            throw new MallException("删除会员提现申请失败,请重试");
        }
        return count;
    }

    /**
     * 根据cashId更新会员提现申请
     *
     * @param memberApplyCash 会员提现申请信
     * @return
     */
    public Integer updateMemberApplyCash(MemberApplyCash memberApplyCash) {
        if (StringUtils.isEmpty(memberApplyCash.getCashId())) {
            throw new MallException("请选择要修改的数据");
        }
        int count = memberApplyCashWriteMapper.updateByPrimaryKeySelective(memberApplyCash);
        if (count == 0) {
            log.error("根据cashId：" + memberApplyCash.getCashId() + "更新会员提现申请失败");
            throw new MallException("更新会员提现申请失败,请重试");
        }
        return count;
    }

    /**
     * 根据cashId获取会员提现申请详情
     *
     * @param cashId cashId
     * @return
     */
    public MemberApplyCash getMemberApplyCashByCashId(Integer cashId) {
        return memberApplyCashReadMapper.getByPrimaryKey(cashId);
    }

    /**
     * 根据条件获取会员提现申请列表
     *
     * @param example 查询条件信息
     * @param pager   分页信息
     * @return
     */
    public List<MemberApplyCash> getMemberApplyCashList(MemberApplyCashExample example, PagerInfo pager) {
        List<MemberApplyCash> memberApplyCashList;
        if (pager != null) {
            pager.setRowsCount(memberApplyCashReadMapper.countByExample(example));
            memberApplyCashList = memberApplyCashReadMapper.listPageByExample(example, pager.getStart(), pager.getPageSize());
        } else {
            memberApplyCashList = memberApplyCashReadMapper.listByExample(example);
        }
        return memberApplyCashList;
    }

    /**
     * 提现审核
     *
     * @param admin        管理员信息
     * @param cashIds      提现id集合，用英文逗号隔开
     * @param isPass       是否审核通过：true-是,false-否
     * @param refuseReason 拒绝理由
     * @param ip           ip
     */
    @SneakyThrows
    @Transactional(rollbackFor = Exception.class)
    public void withdrawAudit(Admin admin, String cashIds, Boolean isPass, String refuseReason, String ip) {
        String[] cashIdArray = cashIds.split(",");
        for (String cashId : cashIdArray) {
            MemberApplyCash memberApplyCash = memberApplyCashReadMapper.getByPrimaryKey(cashId);
            AssertUtil.notNull(memberApplyCash, "提现申请id[" + cashId + "]不存在");

            //提现金额
            BigDecimal cashAmount = memberApplyCash.getCashAmount();
            //是否成功
            boolean isSuccess = false;

            //查询会员信息
            Member member = memberReadMapper.getByPrimaryKey(memberApplyCash.getMemberId());

            //修改提现状态
            MemberApplyCash applyCash = new MemberApplyCash();
            applyCash.setCashId(memberApplyCash.getCashId());
            applyCash.setAuditTime(new Date());
            applyCash.setAdminId(admin.getAdminId());
            applyCash.setAdminName(admin.getAdminName());

            //更新会员余额信息
            Member memberNew = new Member();
            memberNew.setMemberId(member.getMemberId());
            memberNew.setUpdateTime(new Date());
            memberNew.setBalanceFrozen(member.getBalanceFrozen().subtract(cashAmount));

            //记录余额日志
            MemberBalanceLog memberBalanceLog = new MemberBalanceLog();
            memberBalanceLog.setMemberId(member.getMemberId());
            memberBalanceLog.setMemberName(member.getMemberName());

            if (isPass) {
                //审核通过
                SlodonTransfersRequest transfersRequest = new SlodonTransfersRequest();
                transfersRequest.setTransferSn(memberApplyCash.getCashSn());
                transfersRequest.setRealName(memberApplyCash.getReceiveName());
                transfersRequest.setRemark("申请提现");
                if (memberApplyCash.getReceiveType().equals(MemberWithdrawConst.ACCOUNT_CODE_ALIPAY)) {
                    //支付宝提现
                    transfersRequest.setAlipayProperties(payPropertiesUtil.getAliPayProperties());
                    transfersRequest.setTransfersMethod(PayConst.METHOD_ALIPAY);
                    transfersRequest.setLoginAccount(memberApplyCash.getReceiveAccount());
                    if ("1".equals(stringRedisTemplate.opsForValue().get("pay_test_enable"))) {
                        //支付宝提现必须大于等于一毛
                        transfersRequest.setAmount(new BigDecimal("0.1"));
                    } else {
                        transfersRequest.setAmount(memberApplyCash.getCashAmount());
                    }
                } else if (memberApplyCash.getReceiveType().equals(MemberWithdrawConst.ACCOUNT_CODE_WXPAY)) {
                    //微信提现
                    transfersRequest.setWxPayProperties(payPropertiesUtil.getWxPayProperties(memberApplyCash.getWxType()));
                    transfersRequest.setTransfersMethod(PayConst.METHOD_WX);
                    transfersRequest.setOpenId(memberApplyCash.getReceiveAccount());
                    //微信需要设置原支付金额
                    if ("1".equals(stringRedisTemplate.opsForValue().get("pay_test_enable"))) {
                        //微信提现必须大于等于三毛
                        transfersRequest.setAmount(new BigDecimal("0.3"));
                    } else {
                        transfersRequest.setAmount(memberApplyCash.getCashAmount());
                    }
                    transfersRequest.setSpBillCreateIp(ip);
                } else {
                    throw new MallException("提现方式未开启");
                }
                SlodonTransfersResponse refundResponse = slodonPay.transfers(transfersRequest);
                if (refundResponse.getSuccess()) {
                    applyCash.setFinishTime(new Date());
                    applyCash.setState(MemberWithdrawConst.STATE_2);
                    applyCash.setTransactionSn(refundResponse.getTradeSn());

                    isSuccess = true;
                    memberBalanceLog.setAfterChangeAmount(member.getBalanceAvailable().add(memberNew.getBalanceFrozen()));
                    memberBalanceLog.setChangeValue(cashAmount);
                    memberBalanceLog.setDescription("用户申请提现成功");
                    memberBalanceLog.setType(MemberConst.TYPE_4);
                } else {
                    applyCash.setState(MemberWithdrawConst.STATE_4);
                    applyCash.setFailReason(refundResponse.getMsg());

                    memberNew.setBalanceAvailable(member.getBalanceAvailable().add(cashAmount));
                    memberBalanceLog.setAfterChangeAmount(memberNew.getBalanceAvailable().add(memberNew.getBalanceFrozen()));
                    memberBalanceLog.setChangeValue(new BigDecimal("0.00"));
                    memberBalanceLog.setDescription("用户申请提现失败解冻余额，提现单号：" + memberApplyCash.getCashSn());
                    memberBalanceLog.setType(MemberConst.TYPE_8);
                }
            } else {
                //审核拒绝
                AssertUtil.notEmpty(refuseReason, "拒绝理由不能为空");

                applyCash.setState(MemberWithdrawConst.STATE_3);
                applyCash.setRefuseReason(refuseReason);

                memberNew.setBalanceAvailable(member.getBalanceAvailable().add(cashAmount));
                memberBalanceLog.setAfterChangeAmount(memberNew.getBalanceAvailable().add(memberNew.getBalanceFrozen()));
                memberBalanceLog.setChangeValue(new BigDecimal("0.00"));
                memberBalanceLog.setDescription("用户申请提现审核拒绝解冻余额，提现单号：" + memberApplyCash.getCashSn());
                memberBalanceLog.setType(MemberConst.TYPE_8);
            }
            this.updateMemberApplyCash(applyCash);

            int count = memberWriteMapper.updateByPrimaryKeySelective(memberNew);
            AssertUtil.isTrue(count == 0, "更新会员余额失败，请重试！");

            memberBalanceLog.setFreezeAmount(memberNew.getBalanceFrozen());
            memberBalanceLog.setFreezeValue(cashAmount);
            memberBalanceLog.setCreateTime(new Date());
            memberBalanceLog.setAdminId(admin.getAdminId());
            memberBalanceLog.setAdminName(admin.getAdminName());
            count = memberBalanceLogWriteMapper.insert(memberBalanceLog);
            AssertUtil.isTrue(count == 0, "记录余额日志失败，请重试！");

            //提现成功发送余额变动消息通知
            if (isSuccess) {
                this.sendMsgAccountChange(memberBalanceLog);
            }
        }
    }

    /**
     * 发送余额变动消息通知
     *
     * @param memberBalanceLog 余额变动信息
     */
    public void sendMsgAccountChange(MemberBalanceLog memberBalanceLog) {
        //日期格式转换
        String now = TimeUtil.getDateTimeString(new Date());
        String description = "余额提现";
        //消息通知
        //微信消息通知
        List<MessageSendProperty> messageSendPropertyList = new ArrayList<>();
        messageSendPropertyList.add(new MessageSendProperty("first", "您的账户发生了资金变动。"));
        messageSendPropertyList.add(new MessageSendProperty("keyword1", description));
        messageSendPropertyList.add(new MessageSendProperty("keyword2", memberBalanceLog.getChangeValue().toString()));
        messageSendPropertyList.add(new MessageSendProperty("keyword3", TimeUtil.getDateTimeString(memberBalanceLog.getCreateTime())));
        messageSendPropertyList.add(new MessageSendProperty("keyword4", memberBalanceLog.getAfterChangeAmount().toString()));
        String msgLinkInfo = "{\"type\":\"balance_change\"}";
        String[] templateParam = {now, description, memberBalanceLog.getAfterChangeAmount().toString(), memberBalanceLog.getFreezeAmount().toString()};
        MessageSendVO messageSendVO = new MessageSendVO(messageSendPropertyList, memberBalanceLog.getMemberId(),
                MemberTplConst.BALANCE_CHANGE_REMINDER, msgLinkInfo, templateParam);
        //发送到mq
        rabbitTemplate.convertAndSend(MQ_EXCHANGE_NAME, MQ_QUEUE_NAME_MEMBER_MSG, messageSendVO);
    }
}