package com.hfkj.service.rebate.impl;

import com.hfkj.common.exception.BaseException;
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.BsRebateMerAccountMapper;
import com.hfkj.entity.BsRebateMerAccount;
import com.hfkj.entity.BsRebateMerAccountExample;
import com.hfkj.entity.BsRebateMerAccountRecord;
import com.hfkj.service.rebate.BsRebateMerAccountRecordService;
import com.hfkj.service.rebate.BsRebateMerAccountService;
import com.hfkj.service.rebate.model.RebateMerAccountTradeObject;
import com.hfkj.sysenum.RebateMerAccountStatus;
import com.hfkj.sysenum.RebateMerAccountTradeTypeEnum;
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: BsRebateMerAccountServiceImpl
 * @author: HuRui
 * @date: 2023/10/11
 **/
@Service("rebateMerAccountService")
public class BsRebateMerAccountServiceImpl implements BsRebateMerAccountService {

    @Resource
    private BsRebateMerAccountMapper rebateMerAccountMapper;
    @Resource
    private BsRebateMerAccountRecordService rebateMerAccountRecordService;
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Override
    public void editData(BsRebateMerAccount merAccount) {
        if (merAccount.getId() == null) {
            merAccount.setCreateTime(new Date());
            merAccount.setUpdateTime(new Date());
            merAccount.setStatus(RebateMerAccountStatus.status1.getNumber());
            rebateMerAccountMapper.insert(merAccount);
        } else {
            merAccount.setUpdateTime(new Date());
            rebateMerAccountMapper.updateByPrimaryKey(merAccount);
        }
    }


    /**
     * 金额交易
     * @param accountNumber 账户编号
     * @param tradeType     交易类型
     * @param tradeAmount   交易金额
     * @param tradeObject   交易对象
     */
    @Transactional(propagation= Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED)
    public void priceTrade(Long accountNumber, RebateMerAccountTradeTypeEnum tradeType, BigDecimal tradeAmount, RebateMerAccountTradeObject tradeObject) {
        // 查询账户
        BsRebateMerAccount account = getAccountByNumber(accountNumber);
        if (account == null) {
            throw ErrorHelp.genException(SysCode.System, ErrorCode.COMMON_ERROR, "未知的账户");
        }
        if (!account.getStatus().equals(RebateMerAccountStatus.status1.getNumber())) {
            throw ErrorHelp.genException(SysCode.System, ErrorCode.COMMON_ERROR, "交易失败！账户不处于正常状态");
        }
        // 交易前金额
        BigDecimal amountBeforeChange = account.getAmount();

        // 计算账户金额
        if (tradeType.getNumber().equals(RebateMerAccountTradeTypeEnum.type1.getNumber())) {
            account.setAmount(account.getAmount().add(tradeAmount));

        } else if (tradeType.getNumber().equals(RebateMerAccountTradeTypeEnum.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();

        // 交易记录
        BsRebateMerAccountRecord record = new BsRebateMerAccountRecord();
        record.setAccountId(account.getId());
        record.setAccountNumber(account.getAccountNumber());
        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());
        rebateMerAccountRecordService.insert(record);
    }


    @Override
    @Transactional(propagation= Propagation.REQUIRED)
    public void income(Long accountNumber, BigDecimal tradeAmount, RebateMerAccountTradeObject tradeObject) {
        String lockKey = "REBATE_ACCOUNT_"+accountNumber;
        try {
            // 占坑锁
            Boolean lock = redisTemplate.opsForValue().setIfAbsent(lockKey, tradeAmount);
            if(lock) {
                // 金额交易
                priceTrade(accountNumber, RebateMerAccountTradeTypeEnum.type1 , tradeAmount, tradeObject);
                redisTemplate.delete(lockKey);

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

        } catch (BaseException baseException) {
            redisTemplate.delete(lockKey);
            throw ErrorHelp.genException(SysCode.System, ErrorCode.COMMON_ERROR, baseException.getErrorMsg());

        } catch (Exception e) {
            redisTemplate.delete(lockKey);
            throw ErrorHelp.genException(SysCode.System, ErrorCode.COMMON_ERROR, "交易失败！未知错误");
        }
    }

    @Override
    @Transactional(propagation= Propagation.REQUIRED)
    public void expenditure(Long accountNumber, BigDecimal tradeAmount, RebateMerAccountTradeObject tradeObject) {
        String lockKey = "REBATE_ACCOUNT_"+accountNumber;
        try {
            // 占坑锁
            Boolean lock = redisTemplate.opsForValue().setIfAbsent(lockKey, tradeAmount);
            if(lock) {
                // 金额交易
                priceTrade(accountNumber, RebateMerAccountTradeTypeEnum.type2, tradeAmount, tradeObject);
                redisTemplate.delete(lockKey);
            } else {
                // 加锁失败，重试
                Thread.sleep(100);
                expenditure(accountNumber, tradeAmount, tradeObject);
            }
        } catch (BaseException baseException) {
            redisTemplate.delete(lockKey);
            throw ErrorHelp.genException(SysCode.System, ErrorCode.COMMON_ERROR, baseException.getErrorMsg());

        } catch (Exception e) {
            redisTemplate.delete(lockKey);
            throw ErrorHelp.genException(SysCode.System, ErrorCode.COMMON_ERROR, "交易失败！未知错误");
        }
    }

    @Override
    public BsRebateMerAccount getAccountByMerId(Long merId) {
        BsRebateMerAccountExample example = new BsRebateMerAccountExample();
        example.createCriteria()
                .andMerIdEqualTo(merId)
                .andStatusNotEqualTo(RebateMerAccountStatus.status0.getNumber());
        List<BsRebateMerAccount> list = rebateMerAccountMapper.selectByExample(example);
        if (list.size() > 0) {
            return list.get(0);
        }
        return null;
    }

    @Override
    public BsRebateMerAccount getAccountByNumber(Long accountNumber) {
        BsRebateMerAccountExample example = new BsRebateMerAccountExample();
        example.createCriteria()
                .andAccountNumberEqualTo(accountNumber)
                .andStatusNotEqualTo(RebateMerAccountStatus.status0.getNumber());
        List<BsRebateMerAccount> list = rebateMerAccountMapper.selectByExample(example);
        if (list.size() > 0) {
            return list.get(0);
        }
        return null;
    }

    @Override
    public void freeze(Long accountNumber) {
        // 查询账户
        BsRebateMerAccount account = getAccountByNumber(accountNumber);
        if (account == null) {
            throw ErrorHelp.genException(SysCode.System, ErrorCode.COMMON_ERROR, "未知的账户");
        }
        if (!account.getStatus().equals(RebateMerAccountStatus.status1.getNumber())) {
            throw ErrorHelp.genException(SysCode.System, ErrorCode.COMMON_ERROR, "账户状态错误");
        }
        account.setStatus(RebateMerAccountStatus.status2.getNumber());
        editData(account);
    }

    @Override
    public void unfreeze(Long accountNumber) {
        // 查询账户
        BsRebateMerAccount account = getAccountByNumber(accountNumber);
        if (account == null) {
            throw ErrorHelp.genException(SysCode.System, ErrorCode.COMMON_ERROR, "未知的账户");
        }
        if (!account.getStatus().equals(RebateMerAccountStatus.status2.getNumber())) {
            throw ErrorHelp.genException(SysCode.System, ErrorCode.COMMON_ERROR, "账户状态错误");
        }
        account.setStatus(RebateMerAccountStatus.status1.getNumber());
        editData(account);
    }
}
