package com.td.WaDa.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.ArrayUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.td.WaDa.common.pojo.CultivateKeys;
import com.td.WaDa.common.pojo.Result;
import com.td.WaDa.common.service.ComConfigService;
import com.td.WaDa.common.service.CommonServiceImpl;
import com.td.WaDa.config.enums.ResultEnum;
import com.td.WaDa.game.complayerland.pojo.ComPlayerLand;
import com.td.WaDa.game.complayerland.repository.ComPlayerLandRepository;
import com.td.WaDa.game.complayersattri.enums.ComPlayerLuckyLogEnum;
import com.td.WaDa.game.complayersattri.pojo.ComPlayersAttri;
import com.td.WaDa.game.complayersattri.pojo.ComPlayersLucky;
import com.td.WaDa.game.complayersattri.repository.ComPlayersAttriRepository;
import com.td.WaDa.game.complayersattri.repository.ComPlayersLuckyRepository;
import com.td.WaDa.game.complayersattri.vo.ComPlayersAttriSimpleVo;
import com.td.WaDa.game.complayersattri.vo.ComPlayersLuckyLandLevelUpVo;
import com.td.WaDa.game.complayersattri.vo.ComPlayersLuckyToolsVo;
import com.td.WaDa.game.complayersattri.vo.ComPlayersLuckyVo;
import com.td.WaDa.game.comsnbtran.service.ComSnbTranService;
import com.td.WaDa.game.comsnbtran.vo.ComSnbTranVo;
import com.td.WaDa.game.comusers.service.ComUsersService;
import com.td.WaDa.game.comusers.vo.ComUsersSimpleVo;
import com.td.WaDa.game.comusers.vo.ComUsersVo;
import com.td.WaDa.util.CopyUtil;
import com.td.WaDa.util.RedisData;
import com.td.WaDa.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 java.util.*;
import java.util.stream.Collectors;

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

    private String toolskey = "land_tools";
    private String levelUp = "land_level";

    @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;

    /**
     * 使用的工具 获得幸运值，12小时获取一次
     *
     * @param userId
     * @param toolId
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result useLuckyTools(String userId, Integer toolId, Integer landId) {
        //验证工具是否存在
        ComPlayersLuckyToolsVo tools = getLandTools(userId).stream().filter(a -> a.getId().equals(toolId)).findFirst().orElse(null);
        if (tools == null || tools.isEnable() == false) {
            return Result.of(null, false, "无效工具");
        }
        //验证土地是否存在
        ComPlayerLand comPlayerLand = comPlayerLandRepository.findById(landId).orElse(null);
        if (comPlayerLand == null || comPlayerLand.getUserId().equals(userId) == false) {
            return Result.of(null, false, "使用工具失败，无效土地！");
        }
        //土地租赁到期了，不能使用工具
        if (comPlayerLand.getLeaseTime().before(new Date())) {
            return Result.of(null, false, "使用工具失败，土地已到期！");
        }
        //验证用户是否存在
        ComPlayersAttri playersAttri = comPlayersAttriRepository.findByUserId(userId).orElse(null);
        if (playersAttri == null) {
            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());
        }
        //验证工具是否可用：每隔一段时间才可使用
        Optional<ComPlayersLucky> lastComPlayersLuckyLog = repository.findFirstByUserIdAndToolsIdOrderByCreateTimeDesc(userId, toolId);
        if (lastComPlayersLuckyLog.isPresent()) {
            DateTime nextTime = DateUtil.offset(lastComPlayersLuckyLog.get().getCreateTime(), DateField.HOUR_OF_DAY, tools.getUseHour());
            if (nextTime.isAfter(new Date())) {
                redisLock.unlock(_redisKey, String.valueOf(time));
                return Result.of(null, false, "请在" + nextTime.toString() + "后使用工具");
            }
        }
        int afterLucky = playersAttri.getLucky() + tools.getToolLucky();
        //写入使用工具明细
        ComPlayersLucky comPlayersLucky = new ComPlayersLucky();
        comPlayersLucky.setAfterLucky(afterLucky);
        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(afterLucky);
        comPlayersAttriRepository.save(playersAttri);
        Map map = new HashMap();
        ComPlayersAttriSimpleVo comPlayersAttriSimpleVo = CopyUtil.copy(playersAttri, ComPlayersAttriSimpleVo.class);
        map.put("playerAttribute", comPlayersAttriSimpleVo);

        redisLock.unlock(_redisKey, String.valueOf(time));
        return Result.of(map, true, "使用工具成功");
    }

    /**
     * 土地升级
     *
     * @param userId
     * @param configLandId
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result<?> landLevelUp(String userId, Integer configLandId) {
        //验证土地级别是否存在
        //ComPlayersLuckyLandLevelUpVo landLevelUpVo = getLandLevelUp().stream().filter(a -> a.getId().equals(levelId)).findFirst().orElse(null);
        //if (landLevelUpVo == null) {
        //    return Result.of(null, false, "升级失败，无效升级！");
        //}
        //验证土地是否存在
        ComPlayerLand comPlayerLand = comPlayerLandRepository.findByUserIdAndConfigLandId(userId, configLandId).orElse(null);
        if (comPlayerLand == null || comPlayerLand.getUserId().equals(userId) == false) {
            return Result.of(null, false, "升级失败，无效土地！");
        }
        if(ArrayUtil.contains(CultivateKeys.mallType,comPlayerLand.getMallType())){
            return Result.of(null, false, "升级失败，无效土地类型！");
        }
        //土地租赁到期了，不能升级
        if (comPlayerLand.getLeaseTime().before(new Date())) {
            return Result.of(null, false, "升级失败，土地已到期！");
        }
        //Slambb added code
        //直接获取升级的 levelId
        Integer levelId = comPlayerLand.getLandLevel() == null ? 2 : comPlayerLand.getLandLevel() + 1;
        ComPlayersLuckyLandLevelUpVo landLevelUpVo = getLandLevelUp().stream().filter(a -> a.getId().equals(levelId)).findFirst().orElse(null);
        if (landLevelUpVo == null) {
            return Result.of(null, false, "升级失败，无效升级！");
        }
        //验证 将要升级的土地级别是否大于当前土地级别
        //if (comPlayerLand.getLandLevel() >= levelId) {
        //    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());
        }
        //验证用户是否存在
        ComPlayersAttri playersAttri = comPlayersAttriRepository.findByUserId(userId).orElse(null);
        if (playersAttri == null) {
            return Result.of(null, false, "升级失败");
        }
        //验证幸运值是否够用
        int sub = Convert.toInt(NumberUtil.sub(playersAttri.getLucky(), landLevelUpVo.getCostLucky()));
        if (sub < 0) {
            return Result.of(null, false, "升级失败，幸运值不足！");
        }
        //写入使用工具明细
        ComPlayersLucky comPlayersLucky = new ComPlayersLucky();
        comPlayersLucky.setAfterLucky(sub);
        comPlayersLucky.setBeforeLucky(playersAttri.getLucky());
        comPlayersLucky.setAmount(landLevelUpVo.getCostLucky());
        comPlayersLucky.setLuckyType(ComPlayerLuckyLogEnum.level.getCode());
        comPlayersLucky.setLuckyName(landLevelUpVo.getName());
        comPlayersLucky.setLevelId(levelId);
        comPlayersLucky.setLandId(configLandId);
        comPlayersLucky.setUserId(userId);
        comPlayersLucky.setCreateTime(new Date());
        repository.save(comPlayersLucky);

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

        //更新土地级别
        comPlayerLand.setLandLevel(levelId);
        comPlayerLandRepository.save(comPlayerLand);

        Map map = new HashMap();
        ComPlayersAttriSimpleVo comPlayersAttriSimpleVo = CopyUtil.copy(playersAttri, ComPlayersAttriSimpleVo.class);
        map.put("playerAttribute", comPlayersAttriSimpleVo);
        map.put("landLevel", landLevelUpVo);
        return Result.of(map, true, "升级成功");
    }

    /**
     * 兑换snb
     *
     * @param userId
     * @param luckNum 消耗幸运值数量
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    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 || comUsersVo == 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(), comUsersVo.getSnb(), snbNum,comUsersVo.getSnbPart());

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

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

        Map map = new HashMap();
        ComPlayersAttriSimpleVo comPlayersAttriSimpleVo = CopyUtil.copy(playersAttri, ComPlayersAttriSimpleVo.class);
        map.put("playerAttribute",comPlayersAttriSimpleVo);
        map.put("userInfo", CopyUtil.copy(comUsersVo, ComUsersSimpleVo.class));

        return Result.of(map, 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,double snbPart) {
        //记录幸运值明细
        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(StrUtil.format("幸运值{}兑换SNB", luckNum));
        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(snbPart);
        snbTran.setBeforeSnbPart(snbPart);
        snbTran.setTranAmountPart(0d);
        snbTran.setTranPrice(0);
        comSnbTranService.save(snbTran);
    }

    /**
     * 获取所有工具列表，方便用户选择使用哪种工具来获取幸运值
     *
     * @return
     */
    @Override
    public List<ComPlayersLuckyToolsVo> getLandTools(String userId) {
        String land_tools = comConfigService.selectByKey(toolskey);
        List<ComPlayersLuckyToolsVo> comPlayersLuckyToolsVos = JSONUtil.toList(land_tools, ComPlayersLuckyToolsVo.class).stream().filter(a->a.isEnable()).collect(Collectors.toList());
        List<ComPlayersLucky> ComPlayersLuckyList = repository.findAllByUserId(userId);

        for (ComPlayersLuckyToolsVo comPlayersLuckyToolsVo : comPlayersLuckyToolsVos) {
            long canUseTime = 0;
            //获取使用工具历史中最后一次记录
            ComPlayersLucky firstByUserIdOrderByCreateTimeDesc = ComPlayersLuckyList.stream()
                    .filter(a -> comPlayersLuckyToolsVo.getId().equals(a.getToolsId()))
                    .sorted(Comparator.comparing(ComPlayersLucky::getCreateTime).reversed())
                    .findFirst().orElse(null);

            if (firstByUserIdOrderByCreateTimeDesc != null) {
                DateTime offset = DateUtil.offset(firstByUserIdOrderByCreateTimeDesc.getCreateTime(), DateField.HOUR, comPlayersLuckyToolsVo.getUseHour());
                if (offset.after(new Date())) {
                    canUseTime = DateUtil.betweenMs(offset, new Date());
                }
            }
            comPlayersLuckyToolsVo.setActivtyTime(canUseTime);
        }
        return comPlayersLuckyToolsVos;
    }

    /**
     * 获取土地升级列表
     *
     * @return
     */
    @Override
    public List<ComPlayersLuckyLandLevelUpVo> getLandLevelUp() {
        return comConfigService.selectLandLevel();
    }
}
