package com.td.boss.game.complayercultivate.serivce;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUtil;
import com.td.boss.common.pojo.Result;
import com.td.boss.common.service.ComConfigService;
import com.td.boss.config.enums.ResultEnum;
import com.td.boss.game.comconfigland.pojo.ComConfigLand;
import com.td.boss.game.comconfigland.service.ComConfigLandService;
import com.td.boss.game.comexplainland.service.ComExplainLandService;
import com.td.boss.game.comexplainland.vo.ComExplainLandVo;
import com.td.boss.game.complayercultivate.pojo.ComPlayerCultivate;
import com.td.boss.game.complayercultivate.repository.ComPlayerCultivateRepository;
import com.td.boss.game.complayercultivate.vo.ComPlayerCultivateSeedVo;
import com.td.boss.game.complayercultivate.vo.ComPlayerCultivateVo;
import com.td.boss.game.complayerland.pojo.ComPlayerDisaster;
import com.td.boss.game.complayerland.pojo.ComPlayerLand;
import com.td.boss.game.complayerland.service.ComPlayerLandService;
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.DappUtil;
import com.td.boss.util.DoubleUtil;
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.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Service
@Slf4j
@Transactional
public class ComPlayerCultivateSerivceImpl implements ComPlayerCultivateSerivce {

    @Autowired
    private ComPlayerLandService comPlayerLandService;
    @Autowired
    private ComExplainLandService comExplainLandService;
    @Autowired
    private ComConfigLandService comConfigLandService;
    @Autowired
    private ComPlayerCultivateRepository cultivateRepository;
    @Autowired
    private ComConfigService comConfigService;
    @Autowired
    private ComUsersService comUsersService;
    @Autowired
    private RedisLock redisLock;

    /**
     * 解锁某个土地 【牧场养殖渔场】
     * @param userId
     * @param configLandId
     * @return
     */
    @Transactional
    @Override
    public Result<?> payLand(String userId, Integer configLandId) {
        //根据土地config id查找，对应ConfigLandId ,
        ComConfigLand comConfigLand = comConfigLandService.findById(configLandId);
        //如果土地等于不能初始化，或者已经种植，不能进行购买
        if (comConfigLand == null || comConfigLand.getIsInit().equals(0) || comConfigLand.getIsPlant().equals(1)) {
            return Result.of(null, false, ResultEnum.LAND_DATA_ERROR.getMessage(), ResultEnum.LAND_DATA_ERROR.getCode());
        }
        ComPlayerLand comPlayerLand = comPlayerLandService.findByUserIdAndLandId(userId, configLandId);

        String _redisKey = comPlayerLand.getPlantFlag();
        if (!StringUtils.hasText(_redisKey)) {
            //return Result.of(null, false, ResultEnum.LAND_PLANT_FLAG_IS_NULL.getMessage(), ResultEnum.LAND_PLANT_FLAG_IS_NULL.getCode());
            _redisKey = comPlayerLand.getUserId() + "or" + comPlayerLand.getConfigLandId();
        }
        long landTime = System.currentTimeMillis() + RedisData.getLandTimeout();
        if (!redisLock.lock(_redisKey, String.valueOf(landTime))) {
            log.info("锁住key：" + _redisKey);
            return ErrorResult("操作频繁！");
        }
        try {
            if (getTimes(userId) <= 0) {
                return Result.of(null, false, ResultEnum.LAND_NO_TIMES_ERROR.getMessage(), ResultEnum.LAND_NO_TIMES_ERROR.getCode());
            }
            if (comPlayerLand == null) {
                //创建新土地
                comPlayerLand = new ComPlayerLand();
                comPlayerLand.setConfigLandId(comConfigLand.getId());
                comPlayerLand.setUserId(userId);
                comPlayerLand.setName(comConfigLand.getName());
                //todo 续租问题，如果用户续租，保留之前的状态
                //初始化一下未种植参数
                comPlayerLand.setIsPlant(0);
                comPlayerLand.setCreateTime(new Date());
                comPlayerLand.setPlantSteal(1);
                comPlayerLand.setMallType(comConfigLand.getMallType());

                comPlayerLand.setLandDescribe("每次租赁1个月");
                comPlayerLand.setLeaseMultiple(1);
                comPlayerLand.setLeaseDate(1);
                //设置租赁日期
                comPlayerLand.setLeaseTime(DateUtil.offset(new Date(), DateField.MONTH, 1));
                //租赁土地
                comPlayerLand.setIsLease(1);
                comPlayerLand.setUpdateTime(new Date());
                //记录一下租赁费用
                comPlayerLand.setRentalExpenses(1.0d);
                comPlayerLandService.AddComPlayerLand(comPlayerLand);
            }
            return Result.of(comPlayerLand, true, "租赁成功");
        } catch (Exception e) {
        } finally {
            redisLock.unlock(_redisKey, String.valueOf(landTime));
        }
        return ErrorResult("租赁失败！");
    }

    /**
     * 获取租赁机会
     *
     * @param userId
     * @return
     */
    @Override
    public Integer getTimes(String userId) {
        Integer totalcount = DappUtil.getChildrenBuyLandAmount(userId);
        long nowcount = comPlayerLandService.findAllByUserId(userId).stream()
                .filter(a -> a.getMallType() != null && a.getMallType() > 0).count();
        ComExplainLandVo comExplainLandVo = new ComExplainLandVo();
        comExplainLandVo.setUserId(userId);
        Result<List<ComExplainLandVo>> list = comExplainLandService.list(comExplainLandVo);
        long historycount = list.getData().stream().filter(a -> a.getMallType() != null && a.getMallType() > 0).count();
        return totalcount - Convert.toInt(nowcount) - Convert.toInt(historycount);
    }

    /**
     * 喂粮
     *
     * @param userId
     * @param configLandId
     * @return
     */
    @Transactional
    @Override
    public Result<?> buyFood(String userId, Integer configLandId) {
        //获得本次喂的数量
        ComPlayerLand comPlayerLand = comPlayerLandService.findByUserIdAndLandId(userId, configLandId);
        //土地数据不存在
        if (comPlayerLand == null) {
            return Result.of(null, false, ResultEnum.LAND_DATA_ERROR.getMessage(), ResultEnum.LAND_DATA_ERROR.getCode());
        }
        if (!comPlayerLand.getIsLease().equals(1)) {
            return Result.of(null, false, ResultEnum.LAND_NOT_LEASE.getMessage(), ResultEnum.LAND_NOT_LEASE.getCode());
        }
        if (Integer.valueOf(0).equals(comPlayerLand.getMallType()) ) {
            return Result.of(null, false, ResultEnum.FOOD_NO_TYPE_ERROR.getMessage(), ResultEnum.FOOD_NO_TYPE_ERROR.getCode());
        }
        Integer buyAmount = comPlayerLand.getLeaseMultiple();
        //todo 操作snb数量
        ComUsersVo comUsersVo = comUsersService.findByUserId(userId);
        if (comUsersVo.getSnb() <= 0) {
            return Result.of(null, false, ResultEnum.WALLET_SNB_INSUFFICIENT_QUANTITY.getMessage(), ResultEnum.WALLET_SNB_INSUFFICIENT_QUANTITY.getCode());
        }
        if (Integer.valueOf(0).equals(comPlayerLand.getIsPlant())) {
            return Result.of(null, false, ResultEnum.LAND_NO_PLANT_ERROR.getMessage(), ResultEnum.LAND_NO_PLANT_ERROR.getCode());
        }
        //通过判断土地类型，获取喂粮饲料
        List<ComPlayerCultivateSeedVo> SeedList = comConfigService.selectByKey("cultivate_food", ComPlayerCultivateSeedVo.class);
        ComPlayerCultivateSeedVo seedVo = SeedList.stream().filter(a -> a.getSeedId().equals(comPlayerLand.getMallType())).findFirst().orElse(null);
        if (seedVo == null) {
            return Result.of(null, false, ResultEnum.FOOD_NO_ERROR.getMessage(), ResultEnum.FOOD_NO_ERROR.getCode());
        }
        //判断今天是否已经喂过
        Date begin = DateUtil.beginOfDay(new Date());
        Date end = DateUtil.endOfDay(new Date());
        if (exists(userId, comPlayerLand.getMallType(), configLandId, begin, end)) {
            return Result.of(null, false, ResultEnum.FOOD_REPEAT_ERROR.getMessage(), ResultEnum.FOOD_REPEAT_ERROR.getCode());
        }
        long time = System.currentTimeMillis() + RedisData.getPlayerCultivateBuyFoodTimeout();
        String _redisKey = "LOCK:COM_PLAYER_CULTIVATE_BUYFOOD:" + userId;
        if (!redisLock.lock(_redisKey, String.valueOf(time))) {
            log.info("锁住key：" + _redisKey);
            return ErrorResult("操作频繁！");
        }
        try {
            ComSnbTranVo _snbTran = new ComSnbTranVo();
            _snbTran.setBeforeSnb(comUsersVo.getSnb());
            _snbTran.setBeforeSnbPart(comUsersVo.getSnbPart());
            //计算
            Double _allSnb = DoubleUtil.add(comUsersVo.getSnb().doubleValue(), comUsersVo.getSnbPart());
            //如果有snb，扣除对应的snb
            Integer _allSnbMaxInt = (int) Math.floor(_allSnb);
            //这个价格需要从配置里取
            double _needPrice = buyAmount * seedVo.getSeedSNB();
            if (_allSnbMaxInt < _needPrice) {
                return Result.of(null, false, ResultEnum.WALLET_SNB_INSUFFICIENT_QUANTITY.getMessage(), ResultEnum.WALLET_SNB_INSUFFICIENT_QUANTITY.getCode());
            }
            double _tranSnb = _allSnbMaxInt - _needPrice;
            comUsersVo.setSnb(Convert.toInt(_tranSnb));
            Double _SnbPart = DoubleUtil.sub(_allSnb, Math.floor(_allSnb));
            comUsersVo.setSnbPart(_SnbPart);
            //记录修改的数据
            comUsersService.save(comUsersVo);

            ComPlayerCultivate comPlayerCultivate = new ComPlayerCultivate();
            comPlayerCultivate.setLandId(comPlayerLand.getId());
            comPlayerCultivate.setLandType(comPlayerLand.getMallType());
            comPlayerCultivate.setUserId(userId);
            comPlayerCultivate.setCreateTime(new Date());
            comPlayerCultivate.setConfigLandId(configLandId);
            comPlayerCultivate.setAmount(buyAmount);
            comPlayerCultivate.setActivtyTime(new Date());
            comPlayerCultivate.setSnb(_needPrice);
            cultivateRepository.save(comPlayerCultivate);
            return Result.of(null, true, "成功");
        } catch (Exception e) {
        } finally {
            redisLock.unlock(_redisKey, String.valueOf(time));
        }
        return ErrorResult("喂养失败！");
    }

    /**
     * 获得养殖类种子实际成熟日期：正常成熟天数+延期天数（一次不喂延迟2天）
     * 成熟时间的具体值 （取时间差（今天-种植当天）-喂的次数）*2
     *
     * @return 延期后的成熟期
     */
    @Override
    public Date getHarvestTime(String userId, Integer configLandId) {
        ComPlayerLand comPlayerLand = comPlayerLandService.findByUserIdAndLandId(userId, configLandId);
        Integer harvestDays = getHarvestDays(userId, configLandId);
        return DateUtil.offset(comPlayerLand.getPlantStart(), DateField.DAY_OF_YEAR, harvestDays);
    }

    @Override
    public Integer getHarvestDays(String userId, Integer configLandId) {
        ComPlayerLand comPlayerLand = comPlayerLandService.findByUserIdAndLandId(userId, configLandId);
        //实际上一共喂几次
        long times = cultivateRepository.countComPlayerCultivateByUserIdAndLandTypeAndConfigLandId(userId, comPlayerLand.getMallType(), configLandId);
        //从当天到现在。一共过了几天
        long days = DateUtil.betweenDay(new Date(), comPlayerLand.getPlantStart(), true)+1;
        //延期追加天数
        long l = (days - times) * 2;
        //延期后的日期=成熟期+追加天数
        //当天也算一天。过了0点就是新的一天
        return Convert.toInt(l, 0) + comPlayerLand.getPlantMature()-1;
    }

    private boolean exists(String userId, Integer landType, Integer configLandId, Date begin, Date end) {
        Specification specification = new Specification<ComPlayerDisaster>() {
            @Override
            public Predicate toPredicate(Root<ComPlayerDisaster> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
                List<Predicate> predicates = new ArrayList<>();
                predicates.add(criteriaBuilder.between(root.get("createTime"), begin, end));
                predicates.add(criteriaBuilder.equal(root.get("userId"), userId));
                predicates.add(criteriaBuilder.equal(root.get("landType"), landType));
                predicates.add(criteriaBuilder.equal(root.get("configLandId"), configLandId));
                return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
            }
        };
        return cultivateRepository.count(specification) > 0;
    }

    private Result<?> ErrorResult(String msg) {
        return Result.of(null, false, msg);
    }
}
