package com.sqx.modules.discSpinning.controller;

import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.util.concurrent.RateLimiter;
import com.sqx.common.utils.DateUtils;
import com.sqx.common.utils.Result;
import com.sqx.modules.app.annotation.Login;
import com.sqx.modules.app.entity.UserEntity;
import com.sqx.modules.app.entity.UserMoney;
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.entity.DiscSpinning;
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.orders.entity.Orders;
import com.sqx.modules.orders.service.OrdersService;
import com.sqx.modules.pay.entity.CashOut;
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.taskCenter.service.TaskCenterService;
import com.sqx.modules.utils.AliPayOrderUtil;
import io.swagger.annotations.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import reactor.util.annotation.Nullable;
import springfox.documentation.annotations.ApiIgnore;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@RestController
@Api(value = "大转盘", tags = {"大转盘"})
@RequestMapping
public class DiscSpinningController {

    private final DiscSpinningService discSpinningService;
    private final CommonInfoService commonRepository;
    private final OrdersService ordersService;
    private final DiscSpinningRecordService recordService;
    private final UserMoneyService userMoneyService;
    private final UserMoneyDetailsService userMoneyDetailsService;
    private final UserService userService;
    private final CashOutService cashOutService;
    private final TaskCenterService taskCenterService;

    // 以id为键，对应的RateLimiter实例为值，用于不同id的防抖控制
    private static final ConcurrentHashMap<Long, RateLimiter> rateLimiterMap = new ConcurrentHashMap<>();


    @Autowired
    public DiscSpinningController(CommonInfoService commonRepository, DiscSpinningService discSpinningService,
                                  UserMoneyDetailsService userMoneyDetailsService, CashOutService cashOutService,
                                  OrdersService ordersService, DiscSpinningRecordService recordService,
                                  UserMoneyService userMoneyService, UserService userService,
                                  TaskCenterService taskCenterService
    ) {
        this.commonRepository = commonRepository;
        this.discSpinningService = discSpinningService;
        this.ordersService = ordersService;
        this.recordService = recordService;
        this.userMoneyService = userMoneyService;
        this.userMoneyDetailsService = userMoneyDetailsService;
        this.userService = userService;
        this.cashOutService = cashOutService;
        this.taskCenterService = taskCenterService;

    }

    @PostMapping("/discSpinning/insertDiscSpinning")
    @ApiOperation("添加大转盘")
    public Result insertDiscSpinning(@RequestBody DiscSpinning discSpinning) {
        discSpinning.setCreateTime(DateUtils.format(new Date()));
        discSpinningService.save(discSpinning);
        return Result.success();
    }

    @GetMapping("/discSpinning/{id}")
    @ApiOperation("通过Id查询详情")
    public Result selectOne(@PathVariable Integer id) {
        return Result.success().put("data", discSpinningService.getById(id));
    }

    @PostMapping("/discSpinning/updateDiscSpinning")
    @ApiOperation("修改大转盘")
    public Result updateDiscSpinning(@RequestBody DiscSpinning discSpinning) {
        List<DiscSpinning> prizes = discSpinningService.list(new QueryWrapper<DiscSpinning>().orderByAsc("type", "id"));
        BigDecimal number = BigDecimal.ZERO;
        List<DiscSpinning> prizesResult = new ArrayList<>();
        for (DiscSpinning prize : prizes) {
            if (discSpinning.getId().equals(prize.getId())) {
                number = prize.getNumber().add(discSpinning.getOdds().subtract(prize.getOdds()));
                discSpinning.setNumber(number);
                prize.setOdds(discSpinning.getOdds());
                prizesResult.add(discSpinning);
            } else if (number.compareTo(BigDecimal.ZERO) > 0) {
                number = number.add(prize.getOdds());
                prize.setNumber(number);
                prizesResult.add(prize);
            }
        }
        BigDecimal totalOdds = prizes.stream()
                .map(DiscSpinning::getOdds)
                .filter(Objects::nonNull)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        if (totalOdds.compareTo(new BigDecimal(100)) > 0) {
            return Result.error("中奖概率总和 不可超过100");
        }
        discSpinningService.updateBatchById(prizesResult);
        return Result.success();
    }

    @PostMapping("/discSpinning/deleteDiscSpinning")
    @ApiOperation("删除大转盘")
    public Result deleteDiscSpinning(Long id) {
        discSpinningService.removeById(id);
        return Result.success();
    }

    @GetMapping("/app/discSpinning/selectDiscSpinning")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "type", value = "`1`普通转盘 `2`任务转盘", dataTypeClass = String.class, paramType = "body"),
    })
    @ApiOperation("查询大转盘")
    public Result selectDiscSpinning(@RequestParam(required = false, defaultValue = "1") Integer type) {
        return Result.success().put("data", discSpinningService.page(new Page<>(1, 20),
                new QueryWrapper<DiscSpinning>().eq("disc_type", type).orderByAsc("disc_type", "odds")));
    }

    @Login
    @GetMapping("/app/discSpinning/drawCount")
    @ApiOperation("获取大转盘抽奖次数")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "source", value = "`task`任务拉起抽奖 或者 `order` 订单拉起抽奖", dataTypeClass = String.class, paramType = "body"),
    })
    @ApiResponses({
            @ApiResponse(code = 200, message = "{\"sum\":\"总抽奖次数\",\"count\":\"剩余抽奖次数\"}"),
    })
    public Result drawCount(@ApiIgnore @RequestAttribute("userId") Long userId, @Nullable @ApiIgnore @RequestBody Map maps) {
        Map<String, Object> map = new HashMap<>();
        int drawCount = Integer.parseInt(commonRepository.findOne(901).getValue());
        map.put("sum", drawCount);
        if (maps != null && maps.containsKey("source") && "task".equals(maps.get("source"))) {
            //任务可抽奖次数
            map.put("count", taskCenterService.countTaskDisc(userId));
        } else {
            int i = recordService.countDraw(userId);
            if (drawCount - i > 0) {
                map.put("count", ordersService.selectOrdersCountStatisticsByDay(userId, drawCount - i));
            } else {
                map.put("count", 0);
            }
        }
        return Result.success(map);
    }

    @Login
    @GetMapping("/app/discSpinning/draw")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "source", value = "`task`任务拉起抽奖 或者 `order` 订单拉起抽奖", dataTypeClass = String.class, paramType = "body"),
    })
    @ApiOperation("抽取大转盘")
    public Result draw(@ApiIgnore @RequestAttribute("userId") Long userId, @Nullable @ApiIgnore @RequestBody Map maps) {
        double amount = 0;
        Long orderId = null;
        if (maps == null || !maps.containsKey("source") || !"task".equals(maps.get("source"))) {
            //任务抽奖
            int drawCount = Integer.parseInt(commonRepository.findOne(901).getValue());
            Integer i = recordService.countDraw(userId);
            if (i != null && i > drawCount) {
                return Result.error("当日可抽奖次数已超限");
            }
            Orders orders = ordersService.selectOrdersByDay(userId);
            amount = orders.getPayMoney().doubleValue();
            orderId = orders.getOrdersId();
            if (orders == null) {
                return Result.error("无可抽奖机会");
            }
        }
        return new Result().put("data",
                draws(amount, orderId, userId, maps == null || maps.get("source") == null ? "order" : maps.get("source").toString()));
    }

    @PostMapping("/app/discSpinning/receive")
    @ApiOperation("大转盘奖项领取")
    public Result receive(@RequestBody DiscSpinningRecord receive) {
        // 每秒允许0.6次操作
        RateLimiter rateLimiter = rateLimiterMap.computeIfAbsent(receive.getId(), k -> RateLimiter.create(0.6));
        if (rateLimiter.tryAcquire()) {
            CompletableFuture.runAsync(() -> {
                DiscSpinningRecord record = recordService.getById(receive.getId());
                CompletableFuture.runAsync(() -> {
                    receiveAsync(record);
                });
            });
            return Result.success();
        }
        return Result.error("操作过于频繁，请稍后再试");
    }

    @Transactional
    public void receiveAsync(DiscSpinningRecord receive) {
        UserEntity userInfo = userService.queryByUserId(receive.getUserId());
        UserMoneyDetails userMoneyDetails = new UserMoneyDetails(
                receive.getUserId(),null,null,"[现金大转盘]",5,1,2,
                receive.getNumber(),"现金红包奖励" + receive.getNumber() + "元");
        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());
        }
    }


    @Transactional
    public void withdraw(UserEntity userInfo, Double money) {
        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());

        UserMoneyDetails userMoneyDetails = new UserMoneyDetails(
                userInfo.getUserId(),null,null,"[现金大转盘]",4,2,1,
                new BigDecimal(money),"现金红包自动提现" + money + "元");
        userMoneyDetailsService.save(userMoneyDetails);
        //减去余额 钱
        userMoneyService.updateAmount(2, userInfo.getUserId(), money);

        //最高提现金额
        CommonInfo one2 = commonRepository.findOne(910);
        if (one2 == null || money <= Double.parseDouble(one2.getValue())) {
            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)) {
                cashOut.setState(1);
            } else if (StringUtils.isNotBlank(baseResp.getErrorMsg())) {
                cashOut.setState(2);
                cashOut.setRefund(baseResp.getErrorMsg());
            }
        }
        cashOutService.saveBody(cashOut);
    }

    @Transactional
    public DiscSpinningRecord draws(double orderAmount, Long orderId, Long userId, String source) {
        DiscSpinning result = new DiscSpinning("谢谢惠顾", 1, null);
        List<DiscSpinning> prizes = discSpinningService.list(new QueryWrapper<DiscSpinning>().eq("disc_type", "order".equals(source) ? 1 : 2).orderByAsc("odds"));
        Random random = new Random();
        double randomDouble;
        do {
            randomDouble = random.nextDouble();
        } while (randomDouble == 0);
        BigDecimal randomNum = new BigDecimal(randomDouble).multiply(new BigDecimal(10000)).divide(new BigDecimal(100));
        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;
                    //从0 到 80-amount 直接 取 多个区间 数值越小 概率越大
                    if (prize.getType() == 2) {
                        double baseRandom = random.nextDouble();
                        if (baseRandom < 0.8) {
                            // 0.8到0.82范围内概率最大（这里示例以当前订单金额为基础，模拟类似0.8的情况）
                            resultAmount = orderAmount + (random.nextDouble() * 0.02);
                        } else if (baseRandom < 0.9) {
                            // 0.82 - 0.85范围概率小一些
                            resultAmount = orderAmount + (0.02 + (random.nextDouble() * 0.03));
                        } else if (baseRandom < 0.95) {
                            // 0.85 - 0.9概率更小
                            resultAmount = orderAmount + (0.05 + (random.nextDouble() * 0.05));
                        } else if (baseRandom < 0.99) {
                            // 0.9 - 1概率更小
                            resultAmount = orderAmount + (0.1 + (random.nextDouble() * 0.1));
                        } else {
                            // 1 - 20概率依次递减
                            // 控制最大金额为80
                            resultAmount = 1 + (random.nextDouble() * (maxAmount - 1));
                        }
                    }
                    if (resultAmount > maxAmount) {
                        resultAmount = maxAmount;
                    }
                    result = new DiscSpinning(prize.getName(), 2, new BigDecimal(resultAmount).setScale(2, RoundingMode.HALF_UP));
                } else {
                    result = prize;
                }
            }
        }
        DiscSpinningRecord record = new DiscSpinningRecord(result.getName(), orderId, userId, result.getType(),
                result.getNumber(), DateUtils.formatYMD(new Date()), DateUtils.format(new Date()), source);
        recordService.save(record);
        return record;
    }
}

