/*
 *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the distribution.
 *  Neither the name of the dreamlu.net developer nor the names of its
 *  contributors may be used to endorse or promote products derived from
 *  this software without specific prior written permission.
 *  Author: Chill 庄骞 (smallchill@163.com)
 */
package org.springblade.modules.activities.service.impl;

import org.springblade.common.enums.IntegralSourceEnum;
import org.springblade.common.enums.PrizeTypeEnum;
import org.springblade.common.enums.YesOrNoEnum;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.modules.activities.entity.Prize;
import org.springblade.modules.activities.service.IPrizeRecordService;
import org.springblade.modules.activities.vo.PrizeVO;
import org.springblade.modules.activities.mapper.PrizeMapper;
import org.springblade.modules.activities.service.IPrizeService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springblade.modules.activities.wrapper.PrizeWrapper;
import org.springblade.modules.platform.service.IUserAppService;
import org.springblade.modules.platform.service.IUserIntegralService;
import org.springblade.modules.system.entity.UserApp;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 奖品表 服务实现类
 *
 * @author BladeX
 * @since 2023-05-26
 */
@Service
public class PrizeServiceImpl extends ServiceImpl<PrizeMapper, Prize> implements IPrizeService {

	@Resource
	private IPrizeRecordService prizeRecordService;
	@Lazy
	@Autowired
	private IUserAppService userAppService;
	@Lazy
	@Autowired
	private IUserIntegralService userIntegralService;

	@Override
	public IPage<PrizeVO> selectPrizePage(IPage<PrizeVO> page, PrizeVO prize) {
		return page.setRecords(baseMapper.selectPrizePage(page, prize));
	}

	@Override
	public List<Prize> getPrizeList() {
		return this.lambdaQuery().orderByDesc(Prize::getRatio).list();
	}

	@Override
	public Long getDrawCount(Long userId) {
		UserApp userApp = userAppService.getByUserId(userId);
		Assert.notNull(userApp, "用户信息不存在");
		return (long) (userApp.getIntegral() / getDrawIntegral());
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public PrizeVO draw(Long userId) {
		// 校验用户积分是否足够
		this.checkUserIntegral(userId);
		// 查询所有奖品
		List<Prize> prizeList = this.getPrizeList();
		Assert.notEmpty(prizeList, "奖品数量异常");
		// 奖品概率集合，保证跟奖品顺序一致
		List<Double> collect = new ArrayList<>();
		for (int i = 0; i < prizeList.size(); i++) {
			collect.add(prizeList.get(i).getRatio() / 100);
		}
		// 抽奖
		int index = this.lottery(collect);
		// 中奖商品
		Prize prize = prizeList.get(index);
		PrizeVO prizeVO = PrizeWrapper.build().entityVO(prize);
		// 获取转盘转动的角度
		prizeVO.setRotationAngle(this.getRotationAngle(index, prizeList.size()));

		// 扣除用户积分
		userIntegralService.reductionIntegral(getDrawIntegral(), userId, "转盘抽奖扣除");

		// 不同的奖品做不同的处理
		if (PrizeTypeEnum.INTEGRAL.getValue().equals(prize.getPrizeType())) {
			// 增加用户积分
			userIntegralService.addIntegral(Double.parseDouble(prize.getPrizeValue()), userId, IntegralSourceEnum.LOTTERY, "转盘抽奖获得");
		} else if (PrizeTypeEnum.REAL_GOODS.getValue().equals(prize.getPrizeType())) {
			// todo
		} else if (PrizeTypeEnum.THINKS.getValue().equals(prize.getPrizeType())) {
//			throw new ServiceException("谢谢参与");
		} else {
			throw new ServiceException("奖品类型异常");
		}
		// 保存抽奖信息
		prizeRecordService.savePrizeRecord(prize.getId(), userId,
				PrizeTypeEnum.REAL_GOODS.getValue().equals(prize.getPrizeType()) ? YesOrNoEnum.NO.getValue() : YesOrNoEnum.YES.getValue());
		return prizeVO;
	}

	/**
	 * 抽奖, 返回中奖的下标
	 * @param originRates 概率集合
	 * @return
	 */
	private int lottery(List<Double> originRates) {
		// 计算总概率，这样可以保证不一定总概率是1
		double sumRate = 0d;
		for (double rate : originRates) {
			sumRate += rate;
		}
		// 计算每个物品在总概率的基础下的概率情况
		List<Double> sortOriginRates = new ArrayList<>();
		double tempSumRate = 0d;
		for (double rate : originRates) {
			tempSumRate += rate;
			sortOriginRates.add(tempSumRate / sumRate);
		}
		// 获取0-1的随机值，根据区块值来获取抽取到的物品索引
		double nextDouble = Math.random();
		sortOriginRates.add(nextDouble);
		Collections.sort(sortOriginRates);
		return sortOriginRates.indexOf(nextDouble);
	}

	/**
	 * 获取旋转角度
	 * @param index 中奖商品的下标
	 * @param size 奖品数量
	 * @return
	 */
	private double getRotationAngle(int index, int size) {
		// 每个商品所占的角度
		double average = 360.0 / size;
		// 取居中的角度
		return ((index + 1) * average) - (average / 2);
	}

	/**
	 * 校验用户jifen
	 * @param userId
	 */
	private void checkUserIntegral(Long userId) {
		UserApp userApp = userAppService.getByUserId(userId);
		Assert.notNull(userApp, "用户信息不存在");
		if (userApp.getIntegral() < getDrawIntegral()) {
			throw new ServiceException("当前积分不足");
		}
	}

	/**
	 * 获取抽奖消耗的积分
	 * @return
	 */
	private Double getDrawIntegral() {
		return 100D;
	}

}
