package com.hfkj.service.rebate.impl;

import com.hfkj.common.exception.ErrorCode;
import com.hfkj.common.exception.ErrorHelp;
import com.hfkj.common.exception.SysCode;
import com.hfkj.common.utils.DateUtil;
import com.hfkj.dao.BsRebateActivityAccountMapper;
import com.hfkj.entity.*;
import com.hfkj.service.rebate.BsRebateActivityAccountRecordService;
import com.hfkj.service.rebate.BsRebateActivityAccountService;
import com.hfkj.service.rebate.BsRebateActivityService;
import com.hfkj.service.rebate.BsRebateMerAccountService;
import com.hfkj.service.rebate.model.RebateActivityAccountTradeObject;
import com.hfkj.service.rebate.model.RebateMerAccountTradeObject;
import com.hfkj.sysenum.RebateActivityAccountObjectTypeEnum;
import com.hfkj.sysenum.RebateActivityAccountTradeTypeEnum;
import com.hfkj.sysenum.RebateActivityStatusEnum;
import com.hfkj.sysenum.RebateMerAccountObjectTypeEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

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

/**
 * @className: BsRebateActivityAccountServiceImpl
 * @author: HuRui
 * @date: 2023/10/18
 **/
@Service("rebateActivityAccountService")
public class BsRebateActivityAccountServiceImpl implements BsRebateActivityAccountService {

    @Resource
    private BsRebateActivityAccountMapper rebateActivityAccountMapper;
    @Resource
    private BsRebateActivityAccountRecordService rebateActivityAccountRecordService;
    @Resource
    private BsRebateActivityService rebateActivityService;
    @Resource
    private BsRebateMerAccountService rebateMerAccountService;
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    private final static String REBATE_ACTIVITY_ACCOUNT = "REBATE_ACTIVITY_ACCOUNT";
    @Override
    public void editData(BsRebateActivityAccount account) {
        if (account.getId() == null) {
            account.setCreateTime(new Date());
            account.setUpdateTime(new Date());
            rebateActivityAccountMapper.insert(account);
        } else {
            account.setUpdateTime(new Date());
            rebateActivityAccountMapper.updateByPrimaryKey(account);
        }

        redisTemplate.opsForValue().set(REBATE_ACTIVITY_ACCOUNT + ":" +account.getActivityNo(), account);
    }

    @Override
    public void createAccount(BsRebateActivityAccount account) {
        editData(account);
    }

    @Override
    public BsRebateActivityAccount getAccountByActivityNo(String activityNo) {
        Object obj = redisTemplate.opsForValue().get(REBATE_ACTIVITY_ACCOUNT + ":" + activityNo);
        if (obj != null) {
            return (BsRebateActivityAccount) obj;
        }
        BsRebateActivityAccountExample example = new BsRebateActivityAccountExample();
        example.createCriteria().andActivityNoEqualTo(activityNo).andStatusNotEqualTo(0);
        List<BsRebateActivityAccount> list = rebateActivityAccountMapper.selectByExample(example);
        if (list.size() > 0) {
            BsRebateActivityAccount account = list.get(0);
            redisTemplate.opsForValue().set(REBATE_ACTIVITY_ACCOUNT + ":" +account.getActivityNo(), account);
            return account;
        }
        return null;
    }

    @Override
    @Transactional(propagation= Propagation.REQUIRED)
    public void recharge(String activityNo, BigDecimal amount) {
        // 查询活动
        BsRebateActivity activity = rebateActivityService.getActivityByNo(activityNo);
        if (activity == null) {
            throw ErrorHelp.genException(SysCode.System, ErrorCode.COMMON_ERROR, "未找到活动");
        }
        if (activity.getStatus().equals(RebateActivityStatusEnum.status3.getNumber())) {
            throw ErrorHelp.genException(SysCode.System, ErrorCode.COMMON_ERROR, "无法充值，活动已结束");
        }
        // 查询商户账户
        BsRebateMerAccount account = rebateMerAccountService.getAccountByMerId(activity.getMerId());
        if (account == null) {
            throw ErrorHelp.genException(SysCode.System, ErrorCode.COMMON_ERROR, "未找到商户账户");
        }

        // 商户账户
        RebateMerAccountTradeObject merAccountTradeObject = new RebateMerAccountTradeObject();
        merAccountTradeObject.setTradeObjectType(RebateMerAccountObjectTypeEnum.type3);
        merAccountTradeObject.setTradeObjectId(activity.getId());
        merAccountTradeObject.setTradeObjectContent("活动编号:" + activityNo + ",充值金额：" + amount + "元");
        rebateMerAccountService.expenditure(account.getAccountNumber(), amount, merAccountTradeObject);

        // 活动账户
        RebateActivityAccountTradeObject accountTradeObject = new RebateActivityAccountTradeObject();
        accountTradeObject.setTradeObjectType(RebateActivityAccountObjectTypeEnum.type1);
        accountTradeObject.setTradeObjectId(activity.getId());
        accountTradeObject.setTradeObjectContent("充值金额：" + amount + "元");
        Boolean tradeResult = accountTrade(activity.getActivityNo(), RebateActivityAccountTradeTypeEnum.type1, amount, accountTradeObject);
        if (!tradeResult) {
            throw ErrorHelp.genException(SysCode.System, ErrorCode.COMMON_ERROR, "充值失败！");
        }

    }

    @Override
    @Transactional(propagation= Propagation.REQUIRES_NEW)
    public Boolean recover(String activityNo) {
        String lockKey = "REBATE_ACTIVITY_ACCOUNT_" + activityNo;
        try {
            // 占坑锁
            Boolean lock = redisTemplate.opsForValue().setIfAbsent(lockKey, null);
            if(lock) {
                // 查询活动
                BsRebateActivity activity = rebateActivityService.getActivityByNo(activityNo);
                if (activity == null) {
                    throw ErrorHelp.genException(SysCode.System, ErrorCode.COMMON_ERROR, "未找到活动");
                }
                // 查询活动账户
                BsRebateActivityAccount activityAccount = getAccountByActivityNo(activityNo);
                if (activityAccount == null) {
                    throw ErrorHelp.genException(SysCode.System, ErrorCode.COMMON_ERROR, "未找到活动账户");
                }
                if (activityAccount.getAmount().compareTo(new BigDecimal("0")) == 0) {
                    throw ErrorHelp.genException(SysCode.System, ErrorCode.COMMON_ERROR, "活动账户余额不足");
                }
                // 查询商户账户
                BsRebateMerAccount merAccount = rebateMerAccountService.getAccountByMerId(activity.getMerId());
                if (merAccount == null) {
                    throw ErrorHelp.genException(SysCode.System, ErrorCode.COMMON_ERROR, "未找到商户账户");
                }
                // 交易前金额
                BigDecimal amountBeforeChange = activityAccount.getAmount();
                // 回收金额
                BigDecimal tradeAmount = activityAccount.getAmount();
                activityAccount.setAmount(new BigDecimal("0"));
                editData(activityAccount);

                // 交易后金额
                BigDecimal amountAfterChange = activityAccount.getAmount();

                // 交易记录
                BsRebateActivityAccountRecord record = new BsRebateActivityAccountRecord();
                record.setActivityId(activityAccount.getActivityId());
                record.setActivityNo(activityAccount.getActivityNo());
                record.setAccountId(activityAccount.getId());
                record.setTradeType(RebateActivityAccountTradeTypeEnum.type2.getNumber());
                record.setTradeNumber(DateUtil.date2String(new Date(), "yyyyMMddHHmmss")+(new Random().nextInt(8999) + 1000));
                record.setTradeAmount(tradeAmount);
                record.setAmountBeforeChange(amountBeforeChange);
                record.setAmountAfterChange(amountAfterChange);
                record.setObjectType(RebateActivityAccountObjectTypeEnum.type2.getNumber());
                record.setObjectId(activity.getId());
                record.setObjectContent("活动余额回收");
                rebateActivityAccountRecordService.insert(record);

                // 商户账户入账
                RebateMerAccountTradeObject merAccountTradeObject = new RebateMerAccountTradeObject();
                merAccountTradeObject.setTradeObjectId(activity.getId());
                merAccountTradeObject.setTradeObjectType(RebateMerAccountObjectTypeEnum.type4);
                merAccountTradeObject.setTradeObjectContent("活动编号：" + activityNo + "，回收余额:" + tradeAmount + "元");
                rebateMerAccountService.income(merAccount.getAccountNumber(), tradeAmount, merAccountTradeObject);

                redisTemplate.delete(lockKey);
                return true;

            } else {
                // 加锁失败，重试
                Thread.sleep(100);
                recover(activityNo);
            }

        } catch (Exception e) {
            redisTemplate.delete(lockKey);
        }
        return false;
    }

    /**
     * 金额交易
     * @param activityNo    活动编号
     * @param tradeType     交易类型
     * @param tradeAmount   交易金额
     * @param tradeObject   交易对象
     */
    @Transactional(propagation= Propagation.REQUIRES_NEW, isolation = Isolation.READ_COMMITTED)
    public Boolean accountTrade(String activityNo, RebateActivityAccountTradeTypeEnum tradeType, BigDecimal tradeAmount, RebateActivityAccountTradeObject tradeObject) {
        String lockKey = "REBATE_ACTIVITY_ACCOUNT_" + activityNo;
        try {
            // 占坑锁
            Boolean lock = redisTemplate.opsForValue().setIfAbsent(lockKey, tradeAmount);
            if(lock) {
                // 查询账户
                BsRebateActivityAccount account = getAccountByActivityNo(activityNo);
                if (account == null) {
                    throw ErrorHelp.genException(SysCode.System, ErrorCode.COMMON_ERROR, "未找到活动账户");
                }
                // 交易前金额
                BigDecimal amountBeforeChange = account.getAmount();
                // 计算账户金额
                if (tradeType.getNumber().equals(RebateActivityAccountTradeTypeEnum.type1.getNumber())) {
                    account.setAmount(account.getAmount().add(tradeAmount));

                } else if (tradeType.getNumber().equals(RebateActivityAccountTradeTypeEnum.type2.getNumber())) {
                    // 扣款金额是否大于账户余额
                    if (tradeAmount.compareTo(account.getAmount()) > 0) {
                        throw ErrorHelp.genException(SysCode.System, ErrorCode.COMMON_ERROR, "账户余额不足");
                    }
                    account.setAmount(account.getAmount().subtract(tradeAmount));
                }
                editData(account);

                // 交易后金额
                BigDecimal amountAfterChange = account.getAmount();

                // 交易记录
                BsRebateActivityAccountRecord record = new BsRebateActivityAccountRecord();
                record.setActivityId(account.getActivityId());
                record.setActivityNo(account.getActivityNo());
                record.setAccountId(account.getId());
                record.setTradeType(tradeType.getNumber());
                record.setTradeNumber(DateUtil.date2String(new Date(), "yyyyMMddHHmmss")+(new Random().nextInt(8999) + 1000));
                record.setTradeAmount(tradeAmount);
                record.setAmountBeforeChange(amountBeforeChange);
                record.setAmountAfterChange(amountAfterChange);
                record.setObjectType(tradeObject.getTradeObjectType().getNumber());
                record.setObjectId(tradeObject.getTradeObjectId());
                record.setObjectContent(tradeObject.getTradeObjectContent());
                rebateActivityAccountRecordService.insert(record);

                redisTemplate.delete(lockKey);
                return true;

            } else {
                // 加锁失败，重试
                Thread.sleep(100);
                accountTrade(activityNo, tradeType, tradeAmount, tradeObject);
            }

        } catch (Exception e) {
            redisTemplate.delete(lockKey);
        }
        return false;
    }

}
