package com.sqx.modules.discSpinning.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sqx.common.utils.DateUtils;
import com.sqx.common.utils.RedisKeys;
import com.sqx.common.utils.RedisUtils;
import com.sqx.common.utils.Result;
import com.sqx.modules.app.entity.UserEntity;
import com.sqx.modules.app.entity.UserMoneyDetails;
import com.sqx.modules.app.service.UserMoneyDetailsService;
import com.sqx.modules.app.service.UserMoneyService;
import com.sqx.modules.app.service.UserService;
import com.sqx.modules.common.entity.CommonInfo;
import com.sqx.modules.common.service.CommonInfoService;
import com.sqx.modules.discSpinning.dao.DiscSpinningDao;
import com.sqx.modules.discSpinning.entity.DiscSpinning;
import com.sqx.modules.discSpinning.entity.DiscSpinningAmount;
import com.sqx.modules.discSpinning.entity.DiscSpinningRecord;
import com.sqx.modules.discSpinning.service.DiscSpinningRecordService;
import com.sqx.modules.discSpinning.service.DiscSpinningService;
import com.sqx.modules.pay.entity.CashOut;
import com.sqx.modules.pay.entity.WithdrawTypeEnum;
import com.sqx.modules.pay.service.CashOutService;
import com.sqx.modules.pay.wuyou.BaseResp;
import com.sqx.modules.pay.wuyou.WuyouPay;
import com.sqx.modules.utils.AliPayOrderUtil;
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.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadLocalRandom;

@Slf4j
@Service
public class DiscSpinningServiceImpl extends ServiceImpl<DiscSpinningDao, DiscSpinning> implements DiscSpinningService {

    private final DiscSpinningRecordService recordService;
    private final CommonInfoService commonRepository;
    private final UserMoneyService userMoneyService;
    private final UserMoneyDetailsService userMoneyDetailsService;
    private final UserService userService;
    private final CashOutService cashOutService;
    private final RedisUtils redisUtils;
    private final WuyouPay wuyouPay;
    private final PlatformTransactionManager transactionManager;


    @Autowired
    public DiscSpinningServiceImpl(CommonInfoService commonRepository,
                                   UserMoneyDetailsService userMoneyDetailsService, CashOutService cashOutService,
                                   DiscSpinningRecordService recordService,
                                   UserMoneyService userMoneyService, UserService userService,
                                   RedisUtils redisUtils,
                                   WuyouPay wuyouPay,
                                   PlatformTransactionManager transactionManager
    ) {
        this.commonRepository = commonRepository;
        this.recordService = recordService;
        this.userMoneyService = userMoneyService;
        this.userMoneyDetailsService = userMoneyDetailsService;
        this.userService = userService;
        this.cashOutService = cashOutService;
        this.redisUtils = redisUtils;
        this.wuyouPay = wuyouPay;
        this.transactionManager = transactionManager;
    }

    @Override
    @Transactional
    public void receiveAsync(DiscSpinningRecord receive) {
        if (receive.getType() != 2) {
            return;
        }
        UserEntity userInfo = userService.queryByUserId(receive.getUserId());
        if (userInfo.getStatus().equals(0)) {
            return;
        }

        UserMoneyDetails userMoneyDetails = new UserMoneyDetails(
                receive.getUserId(), null, null, "[现金大转盘]", 5, 1, 2,
                receive.getNumber(), "现金红包奖励" + receive.getNumber() + "元", 1,receive.getId());
        //上一秒
        userMoneyDetails.setCreateTime(DateUtil.format(new Date(System.currentTimeMillis() - 1000), "yyyy-MM-dd HH:mm:ss"));
        userMoneyDetailsService.save(userMoneyDetails);
        receive.setTarget("2");
        receive.setTargetId(userMoneyDetails.getId());
        recordService.updateById(receive);
        //存入余额 钱
        userMoneyService.updateAmount(1, receive.getUserId(), receive.getNumber().doubleValue());


        // 去除自动提现
//        if (receive.getNumber().compareTo(new BigDecimal("0.1")) > 0 && StringUtils.isNotBlank(userInfo.getZhiFuBao()) && StringUtils.isNotBlank(userInfo.getZhiFuBaoName())) {
//            //提现
//            withdraw(userInfo, receive.getNumber().doubleValue());
//        }
    }

    @Override
    @Transactional
    public void withdraw(UserEntity userInfo, Double money) {
        withdraw(userInfo, money, "[提现]");
    }

    @Override
    @Transactional
    public void withdraw(UserEntity userInfo, Double money, String title) {
        CashOut cashOut = new CashOut();
        cashOut.setIsOut(false);
        cashOut.setMoney(money.toString());
        cashOut.setUserId(userInfo.getUserId());
        cashOut.setZhifubao(userInfo.getZhiFuBao());
        cashOut.setZhifubaoName(userInfo.getZhiFuBaoName());
        cashOut.setState(0);
        cashOut.setRate(0.00);
        cashOut.setUserType(1);
        cashOut.setCreateAt(DateUtil.now());
        cashOut.setWithdrawType(WithdrawTypeEnum.RED_ENVELOPE.getValue());

        //最高提现金额
        CommonInfo one2 = commonRepository.findOne(910);
        if (one2 == null || money <= Double.parseDouble(one2.getValue())) {
            if (wuyouPay.checkCanCash(userInfo.getUserId(), WithdrawTypeEnum.RED_ENVELOPE, BigDecimal.valueOf(money))) {
                cashOut.setStatus(4);
                String outOrderNo = AliPayOrderUtil.createOrderId();
                cashOut.setOrderNumber(outOrderNo);
                BaseResp baseResp = wuyouPay.extractOrder(outOrderNo, cashOut.getMoney(), cashOut.getZhifubao(), cashOut.getZhifubaoName());
                if (baseResp.getStatus() != null && (baseResp.getStatus().equals(2) || baseResp.getStatus().equals(10000))) {
                    UserMoneyDetails userMoneyDetails = new UserMoneyDetails(
                            userInfo.getUserId(), null, null, title, 4, 2, 1,
                            new BigDecimal(money), "现金红包自动提现" + money + "元", 1);
                    userMoneyDetailsService.save(userMoneyDetails);
                    //减去余额 钱
                    userMoneyService.updateAmount(2, userInfo.getUserId(), money);

                    cashOut.setState(1);
                } else if (StringUtils.isNotBlank(baseResp.getErrorMsg())) {
                    cashOut.setState(2);
                    if (baseResp.getErrorMsg().contains("收款人账户号出款属性不匹配")) {
                        cashOut.setRefund("提现失败,请检查支付宝账号与收款人姓名后,重试。");
                    } else {
                        cashOut.setRefund(baseResp.getErrorMsg());
                    }
                } else if (StringUtils.isNotBlank(baseResp.getMsg())) {
                    cashOut.setState(2);
                    cashOut.setRefund("提现失败,请检查支付宝账号与收款人姓名后,重试。");
                }
            } else {
                UserMoneyDetails userMoneyDetails = new UserMoneyDetails(
                        userInfo.getUserId(), null, null, title, 4, 2, 1,
                        new BigDecimal(money), "现金红包自动提现" + money + "元", 1);
                userMoneyDetailsService.save(userMoneyDetails);
                //减去余额 钱
                userMoneyService.updateAmount(2, userInfo.getUserId(), money);
                cashOut.setState(3);
            }
        } else {
            UserMoneyDetails userMoneyDetails = new UserMoneyDetails(
                    userInfo.getUserId(), null, null, title, 4, 2, 1,
                    new BigDecimal(money), "现金红包自动提现" + money + "元", 1);
            userMoneyDetailsService.save(userMoneyDetails);
            //减去余额 钱
            userMoneyService.updateAmount(2, userInfo.getUserId(), money);
        }
        cashOutService.saveBody(cashOut);
        log.info("领取奖励执行完毕");
    }

    @Override
    @Transactional
    public void withdrawAsync(UserEntity userInfo, Double money, String title) {
        TransactionStatus transactionStatus = transactionManager.getTransaction(new DefaultTransactionDefinition());
        try {
            withdraw(userInfo, money, title);
            transactionManager.commit(transactionStatus);
        } catch (Exception e) {
            transactionManager.rollback(transactionStatus);
            throw e;
        }
    }

    @Override
    @Transactional
    public DiscSpinningRecord draws(int drawCount, double orderAmount, Long sourceId, Long userId, Integer source) {
        DiscSpinning result = new DiscSpinning("谢谢惠顾", 1, BigDecimal.ZERO);
        List<DiscSpinning> prizes = baseMapper.selectList(new QueryWrapper<DiscSpinning>().eq("disc_type", source).orderByAsc("odds"));
        if (CollectionUtil.isEmpty(prizes)) {
            DiscSpinningRecord record = new DiscSpinningRecord(result.getName(), sourceId, userId, result.getUrl(), result.getType(),
                    result.getNumber(), DateUtils.formatYMD(new Date()), DateUtils.format(new Date()), source);
            recordService.save(record);
            return record;
        }
        Random random = new Random();
        BigDecimal maxNumber = prizes.stream()
                .map(DiscSpinning::getNumber)
                .max(BigDecimal::compareTo)
                .orElse(null);
        BigDecimal randomNum = new BigDecimal(ThreadLocalRandom.current().nextDouble(0.01, maxNumber.doubleValue()));

        List<DiscSpinningAmount> amounts = new ArrayList<>();
        Map<String, List<DiscSpinningAmount>> amountMaps = redisUtils.getMapData(RedisKeys.getDateKey("spinning:amount"), "setDiscSpinningAmounts", DiscSpinningAmount.class);
        for (int i = drawCount; i >= 0; i--) {
            if (amountMaps.containsKey(i + "")) {
                amounts = amountMaps.get(i + "");
                break;
            }
        }
        for (DiscSpinning prize : prizes) {
            if (randomNum.compareTo(prize.getNumber()) < 0) {
                if (prize.getType() == 2) {
                    int maxAmount = Integer.parseInt(commonRepository.findOne(900).getValue());
                    double resultAmount = 0;
                    if (prize.getType() == 2) {
                        double baseRandom = random.nextDouble();
                        double baseAmount = 0;
                        for (DiscSpinningAmount amount : amounts) {
                            if (baseRandom < amount.getRandom()) {
                                resultAmount = baseAmount + random.nextDouble() * (amount.getMaxAmount() - baseAmount);
                                break;
                            }
                            baseAmount = amount.getMaxAmount();
                        }

                        if (resultAmount < 0.01) {
                            resultAmount = 0.01;
                        }
                        resultAmount = orderAmount + resultAmount;
                        if (resultAmount > maxAmount) {
                            resultAmount = maxAmount;
                        }
                    }
                    result = new DiscSpinning(prize.getName(), 2, new BigDecimal(resultAmount).setScale(2, RoundingMode.HALF_UP));
                    break;
                } else {
                    if (source != 1) {
                        result = prize;
                        result.setNumber(BigDecimal.ONE);
                    }
                }
            }
        }

        DiscSpinningRecord record = new DiscSpinningRecord(result.getName(), sourceId, userId, result.getUrl(), result.getType(),
                result.getNumber(), DateUtils.formatYMD(new Date()), DateUtils.format(new Date()), source);
        recordService.save(record);
        return record;
    }

    @Override
    public void receive1(DiscSpinningRecord receive) {
        if (recordService.countDraw(receive.getUserId()) > 10) {
            return;
        }
        DiscSpinningRecord record = recordService.getById(receive.getId());
        if (record.getTargetId() != null) {
            return;
        }
        CompletableFuture.runAsync(() -> {
            receiveAsync(record);
        });
    }
}

