package com.td.boss.game.complayersattri.service;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.td.boss.common.pojo.Result;
import com.td.boss.common.service.ComConfigService;
import com.td.boss.common.service.CommonServiceImpl;
import com.td.boss.config.enums.ResultEnum;
import com.td.boss.game.complayerland.pojo.ComPlayerLand;
import com.td.boss.game.complayerland.repository.ComPlayerLandRepository;
import com.td.boss.game.complayersattri.enums.ComPlayerLuckyLogEnum;
import com.td.boss.game.complayersattri.pojo.ComPlayersAttri;
import com.td.boss.game.complayersattri.pojo.ComPlayersLucky;
import com.td.boss.game.complayersattri.repository.ComPlayersAttriRepository;
import com.td.boss.game.complayersattri.repository.ComPlayersLuckyRepository;
import com.td.boss.game.complayersattri.vo.ComPlayersLuckyLandLevelUpVo;
import com.td.boss.game.complayersattri.vo.ComPlayersLuckyToolsVo;
import com.td.boss.game.complayersattri.vo.ComPlayersLuckyVo;
import com.td.boss.game.comsnbtran.service.ComSnbTranService;
import com.td.boss.game.comsnbtran.vo.ComSnbTranVo;
import com.td.boss.game.comusers.service.ComUsersService;
import com.td.boss.game.comusers.vo.ComUsersVo;
import com.td.boss.util.RedisData;
import com.td.boss.util.RedisLock;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.util.Date;
import java.util.List;
import java.util.Optional;

@Slf4j
@Service
@Transactional
public class ComPlayersLuckyServiceImpl extends CommonServiceImpl<ComPlayersLuckyVo, ComPlayersLucky, Integer> implements ComPlayersLuckyService {

    private String toolskey = "land_tools";
    private String levelUp = "land_level";
    @PersistenceContext
    private EntityManager em;

    @Autowired
    private ComConfigService comConfigService;
    @Autowired
    private ComPlayersLuckyRepository repository;
    @Autowired
    private ComPlayersAttriRepository comPlayersAttriRepository;
    @Autowired
    private ComPlayerLandRepository comPlayerLandRepository;
    @Autowired
    private ComUsersService comUsersService;
    @Autowired
    private ComSnbTranService comSnbTranService;
    @Autowired
    private RedisLock redisLock;

    /**
     * 获取所有工具列表，方便用户选择使用哪种工具来获取幸运值
     *
     * @return
     */
    @Override
    public List<ComPlayersLuckyToolsVo> getLandTools() {
        String land_tools = comConfigService.selectByKey(toolskey);
        return JSONUtil.toList(land_tools, ComPlayersLuckyToolsVo.class);
    }

    /**
     * 获取土地升级列表
     *
     * @return
     */
    @Override
    public List<ComPlayersLuckyLandLevelUpVo> getLandLevelUp() {
        String land_tools = comConfigService.selectByKey(levelUp);
        return JSONUtil.toList(land_tools, ComPlayersLuckyLandLevelUpVo.class);
    }

    /**
     * 使用的工具 获得幸运值，12小时获取一次
     *
     * @param userId
     * @param toolId
     * @return
     */
    @Override
    public Result useLuckyTools(String userId, Integer toolId, Integer landId) {
        if (toolId == null || toolId.equals(0)) {
            return Result.of(null, false, "无效工具");
        }
        ComPlayersLuckyToolsVo tools = getLandTools().stream().filter(a -> a.getId().equals(toolId)).findFirst().orElse(null);
        if (tools == null || tools.isEnable() == false) {
            return Result.of(null, false, "无效工具");
        }

        long time = System.currentTimeMillis() + RedisData.getPlayerLandToolTimeout();
        String _redisKey = "LOCK:COM_PLAYER_LAND_TOOL:" + userId;
        if (!redisLock.lock(_redisKey, String.valueOf(time))) {
            return Result.of(null, false, ResultEnum.LAND_USE_TOOL.getMessage(), ResultEnum.LAND_USE_TOOL.getCode());
        }

        ComPlayersAttri playersAttri = comPlayersAttriRepository.findByUserId(userId).orElse(null);
        if (playersAttri == null) {
            return Result.of(null, false, "无效用户");
        }
        //验证工具是否可用：每隔一段时间才可使用
        Optional<ComPlayersLucky> lastComPlayersLuckyLog = repository.findFirstByUserIdOrderByCreateTimeDesc(userId);
        if (lastComPlayersLuckyLog.isPresent()) {
            DateTime nextTime = DateUtil.offset(lastComPlayersLuckyLog.get().getCreateTime(), DateField.HOUR_OF_DAY, tools.getUseHour());
            if (nextTime.isAfter(new Date())) {
                return Result.of(null, false, "请在" + nextTime.toDateStr() + "后使用工具");
            }
        }
        //写入使用工具明细
        ComPlayersLucky comPlayersLucky = new ComPlayersLucky();
        comPlayersLucky.setAfterLucky(playersAttri.getLucky() + tools.getToolLucky());
        comPlayersLucky.setBeforeLucky(playersAttri.getLucky());
        comPlayersLucky.setAmount(tools.getToolLucky());
        comPlayersLucky.setLuckyType(ComPlayerLuckyLogEnum.tool.getCode());
        comPlayersLucky.setToolsId(toolId);
        comPlayersLucky.setUserId(userId);
        comPlayersLucky.setLevelId(0);
        comPlayersLucky.setLandId(landId);
        comPlayersLucky.setLuckyName(tools.getToolName());
        comPlayersLucky.setCreateTime(new Date());
        repository.save(comPlayersLucky);

        //更新幸运值
        playersAttri.setLucky(playersAttri.getLucky() + tools.getToolLucky());
        comPlayersAttriRepository.save(playersAttri);
        return Result.of(null, true, "使用工具成功");
    }

    /**
     * 土地升级
     *
     * @param userId
     * @param levelId
     * @param landId
     * @return
     */
    @Override
    public Result<?> landLevelUp(String userId, Integer levelId, Integer landId) {

        ComPlayersAttri playersAttri = comPlayersAttriRepository.findByUserId(userId).orElse(null);
        if (playersAttri == null) {
            return Result.of(null, false, "无效领取");
        }
        Optional<ComPlayersLuckyLandLevelUpVo> levelLand = getLandLevelUp().stream().filter(a -> a.getId().equals(levelId)).findFirst();
        if (levelLand.isPresent() == false) {
            return Result.of(null, false, "升级失败，无效升级！");
        }
        ComPlayerLand land = comPlayerLandRepository.findByUserIdAndConfigLandId(userId, landId).orElse(null);
        if (land == null) {
            return Result.of(null, false, "升级失败，无效土地！");
        }
        if (land.getLandLevel() >= levelId) {
            return Result.of(null, false, "升级失败，无效级别！");
        }
        //土地租赁到期了，不能升级
        if (getEndTime(land.getLeaseTime(), land.getLeaseDate()).before(new Date())) {
            return Result.of(null, false, "升级失败，土地已到期！");
        }
        //剩余幸运值
        int sub = Convert.toInt(NumberUtil.sub(playersAttri.getLucky(), levelLand.get().getCostLucky()));
        if (sub < 0) {
            return Result.of(null, false, "升级失败，幸运值不足！");
        }
        long time = System.currentTimeMillis() + RedisData.getPlayerLandLevelupTimeout();
        String _redisKey = "LOCK:COM_PLAYER_LAND_LEVELUP:" + userId;

        if (!redisLock.lock(_redisKey, String.valueOf(time))) {
            return Result.of(null, false, ResultEnum.LAND_LAND_LEVELUPPING.getMessage(), ResultEnum.LAND_LAND_LEVELUPPING.getCode());
        }
        //写入使用工具明细
        ComPlayersLucky comPlayersLucky = new ComPlayersLucky();
        comPlayersLucky.setAfterLucky(sub);
        comPlayersLucky.setBeforeLucky(playersAttri.getLucky());
        comPlayersLucky.setAmount(levelLand.get().getCostLucky());
        comPlayersLucky.setLuckyType(ComPlayerLuckyLogEnum.level.getCode());
        comPlayersLucky.setLuckyName(levelLand.get().getName());
        comPlayersLucky.setLevelId(levelId);
        comPlayersLucky.setLandId(landId);
        comPlayersLucky.setUserId(userId);
        comPlayersLucky.setCreateTime(new Date());
        repository.save(comPlayersLucky);

        //更新用户剩余幸运值
        playersAttri.setLucky(sub);
        comPlayersAttriRepository.save(playersAttri);

        //更新土地级别
        land.setLandLevel(levelId);
        comPlayerLandRepository.save(land);
        return Result.of(null, true, "升级成功");
    }

    /**
     * 兑换snb
     *
     * @param userId
     * @param luckNum 消耗幸运值数量
     * @return
     */
    @Override
    public Result<?> toSNB(String userId, Integer luckNum) {
        //兑换snb倍数
        int snb = Convert.toInt(comConfigService.selectByKey("land_luck_tosnb"), 0);
        if (luckNum < 1000) {
            return Result.of(null, false, "幸运值满1000才能兑换SNB");
        }
        if (luckNum > 100000) {
            return Result.of(null, false, "幸运值最多不能大于100000");
        }
        if (luckNum % snb != 0) {
            return Result.of(null, false, StrUtil.format("幸运值必须为{}的倍数", snb));
        }
        ComUsersVo comUsersVo = comUsersService.findByUserId(userId);
        if (comUsersVo == null || StrUtil.isBlank(comUsersVo.getAddress())) {
            return Result.of(null, false, "未找到用户或钱包地址");
        }
        long time = System.currentTimeMillis() + RedisData.getSnbTimeout();
        String redisKey = StrUtil.format("SNB_SAVE_{}", userId);
        // redis锁
        if (!redisLock.lock(redisKey, String.valueOf(time))) {
            return Result.of(null, false, ResultEnum.SEED_SALE_SAVE_LOCK.getMessage(), ResultEnum.SEED_SALE_SAVE_LOCK.getCode());
        }
        //获得snb用户对象
        ComUsersVo player = comUsersService.findByUserId(userId);
        //获得幸运值用户对象
        ComPlayersAttri playersAttri = comPlayersAttriRepository.findByUserId(userId).orElse(null);
        if (playersAttri == null || player == null) {
            return Result.of(null, false, "无效用户");
        }
        //剩余幸运值
        int num = playersAttri.getLucky() - luckNum;
        if (num < 0) {
            return Result.of(null, false, "幸运值不足！");
        }
        //兑换snb数量
        int snbNum = luckNum / snb;
        //插入明细
        mingxi(userId, luckNum, playersAttri.getLucky(), player.getSnb(), snbNum);

        //更新用户snb
        player.setSnb(player.getSnb() + snbNum);
        comUsersService.save(player);

        //更新用户幸运值
        playersAttri.setLucky(num);
        comPlayersAttriRepository.save(playersAttri);
        return Result.of(null, true, StrUtil.format("成功兑换{}个snb，消耗{}个幸运值", snbNum, luckNum));
    }

    /**
     * @param userId
     * @param luckNum    消耗的幸运值
     * @param beforeLuck 兑换前的幸运值
     * @param beforeSNB  兑换前的snb数量
     * @param snbNum     兑换snb的数量
     */
    private void mingxi(String userId, int luckNum, int beforeLuck, int beforeSNB, int snbNum) {
        //记录幸运值明细
        ComPlayersLucky comPlayersLucky = new ComPlayersLucky();
        comPlayersLucky.setAfterLucky(beforeLuck - luckNum);
        comPlayersLucky.setBeforeLucky(beforeLuck);
        comPlayersLucky.setAmount(luckNum);
        comPlayersLucky.setLuckyType(ComPlayerLuckyLogEnum.cnb.getCode());
        comPlayersLucky.setLuckyName(ComPlayerLuckyLogEnum.cnb.getMsg());
        comPlayersLucky.setLevelId(0);
        comPlayersLucky.setLandId(0);
        comPlayersLucky.setToolsId(0);
        comPlayersLucky.setUserId(userId);
        comPlayersLucky.setCreateTime(new Date());
        comPlayersLucky = repository.save(comPlayersLucky);


        //记录snb明细
        ComSnbTranVo snbTran = new ComSnbTranVo();
        //记录兑换id
        snbTran.setTranId(Convert.toStr(comPlayersLucky.getId()));
        snbTran.setUserId(userId);
        snbTran.setTranName("兑换SNB");
        snbTran.setTranType(9);
        snbTran.setTranAmount(snbNum);
        snbTran.setTranDescribe(StrUtil.format("user幸运值兑换SNB、本次兑换：{},消耗：{}幸运值", snbNum, luckNum));
        snbTran.setIsAdd(1);
        snbTran.setBeforeSnb(beforeSNB);
        snbTran.setTranSnb(snbNum);
        snbTran.setAfterSnb(beforeSNB + snbNum);
        snbTran.setCreateTime(new Date());
        snbTran.setTranSnbPart(0d);
        snbTran.setAfterSnbPart(0d);
        snbTran.setBeforeSnbPart(0d);
        snbTran.setTranAmountPart(0d);
        snbTran.setTranPrice(0);
        comSnbTranService.save(snbTran);
    }

    /**
     * 获取土地到期时间
     *
     * @param beginTime 租赁时间
     * @param type      //租赁的日期，1是3个月，2是1年，3是5年
     * @return
     */
    private Date getEndTime(Date beginTime, int type) {
        if (type == 1) {
            return DateUtil.date(beginTime).offset(DateField.MONTH, 3);
        } else if (type == 2) {
            return DateUtil.date(beginTime).offset(DateField.YEAR, 1);
        } else if (type == 3) {
            return DateUtil.date(beginTime).offset(DateField.YEAR, 5);
        } else {
            return beginTime;
        }
    }
}
