/*
 * Copyright (c) 2018-2999 广州亚米信息科技有限公司 All rights reserved.
 *
 * https://www.gz-yami.com/
 *
 */

package com.yami.shop.user.comment.service.impl;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yami.shop.bean.enums.ScoreLogType;
import com.yami.shop.bean.event.BatchBindCouponEvent;
import com.yami.shop.bean.model.Order;
import com.yami.shop.bean.model.User;
import com.yami.shop.bean.model.UserExtension;
import com.yami.shop.common.config.Constant;
import com.yami.shop.common.exception.GlobalException;
import com.yami.shop.common.exception.YamiShopBindException;
import com.yami.shop.dao.UserExtensionMapper;
import com.yami.shop.service.UserService;
import com.yami.shop.user.comment.dao.UserLevelMapper;
import com.yami.shop.user.comment.dto.LevelDetailDto;
import com.yami.shop.user.comment.dto.UserLevelDto;
import com.yami.shop.user.comment.enums.UserRightsInfo;
import com.yami.shop.user.comment.model.*;
import com.yami.shop.user.comment.service.*;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

/**
 * 会员等级
 *
 * @author LGH
 * @date 2020-02-26 16:03:14
 */
@Service
@AllArgsConstructor
@Slf4j
public class UserLevelServiceImpl extends ServiceImpl<UserLevelMapper, UserLevel> implements UserLevelService {

    private final UserLevelMapper userLevelMapper;
    private final UserLevelLogService userLevelLogService;
    private final UserScoreDetailService userScoreDetailService;
    private final UserScoreLogService userScoreLogService;
    private final UserGrowthLogService userGrowthLogService;
    private final UserService userService;
    private final UserExtensionMapper userExtensionMapper;
    private final ApplicationContext applicationContext;
    private final UserLevelCategoryService userLevelCategoryService;
    private final UserLevelCouponService userLevelCouponService;
    private final UserRightsService userRightsService;
    private final UserLevelRightsService userLevelRightsService;

    //    @Override
//    @Cacheable(cacheNames = "UserLevelsByLevelType", key = "#userLevelType")
//    public List<UserLevel> listUserLevelsByUserLevelType(Integer userLevelType) {
//        return userLevelMapper.selectList(new LambdaQueryWrapper<UserLevel>().eq(UserLevel::getLevelType, userLevelType));
//    }
//
    @Override
    @Cacheable(cacheNames = "UserLevelsByLevelType", key = "#userLevelType")
    public List<UserLevelDto> listUserLevelsByUserLevelType(Integer userLevelType) {
        List<UserLevelDto> userLevelList = userLevelMapper.getList(userLevelType);
//        Iterator<UserLevelDto> iterator = userLevelList.iterator();
//        while (iterator.hasNext()){
//            UserLevelDto userLevel = iterator.next();
//            if (Objects.equals(userLevel.getDiscountType(),1)){
//                List<Long> categoryIds =  userLevelCategoryService.getCategoryIdByLevelId(userLevel.getId());
//                userLevel.setCategorys(categoryIds);
//            }
//            userLevel.setCouponList(userLevelCouponService.getCouponListByLevelId(userLevel.getId()));
//        }
        return userLevelList;
    }

    @Override
    @CacheEvict(cacheNames = "UserLevelsByLevelType", key = "#userLevelType")
    public void removeUserLevelsByUserLevelTypeCache(Integer userLevelType) {

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void paySuccess(String payNo) {
        //根据内部订单号更新用户会员信息
        UserLevelLog userLevelLog = userLevelLogService.getOne(new LambdaQueryWrapper<UserLevelLog>().eq(UserLevelLog::getPayNo, payNo));
        if (userLevelLog == null) {
            log.info("订单异常,");
            return;
        }
        // 订单已支付
        if (userLevelLog.getIsPayed() == 1) {
            log.info("订单已支付，settlement.id：{}", userLevelLog.getPayNo());
            return;
        }
        UserLevel userLevel = userLevelMapper.selectLevelAndCoupons(userLevelLog.getLevel());
        if (userLevel == null) {
            log.info("订单异常,会员等级找不到!", userLevelLog.getPayNo());
            return;
        }
        //修改用户会员信息
        User user = userService.getById(userLevelLog.getUserId());
        UserExtension userExtension = userExtensionMapper.selectOne(new LambdaQueryWrapper<UserExtension>().eq(UserExtension::getUserId, userLevelLog.getUserId()));
        //修改用户等级
        updateUserLevel(user, userLevel, userExtension);
        //发放奖励
        ArrayList<UserLevel> userLevels = new ArrayList<>();
        userLevels.add(userLevel);
        levelUp(userLevels, userLevelLog, userExtension, null);

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(cacheNames = "UserLevelsByLevelType", key = "#userLevel.levelType")
    public void updateUserLevelList(UserLevel userLevel) {
        UserLevel dbUserLevel = new UserLevel();
        //根据成长值，更新已有用户的等级
        if (!Objects.isNull(userLevel.getId())) {
            dbUserLevel = userLevelMapper.selectOne(new LambdaQueryWrapper<UserLevel>()
                    .eq(UserLevel::getId, userLevel.getId())
                    .eq(UserLevel::getLevelType, userLevel.getLevelType()));
        }
        // 若是普通会员，新增或修改时成长值有变化，状态设置为未更新用户等级'
        if (Objects.equals(userLevel.getLevelType(), 0) && !Objects.equals(dbUserLevel.getNeedGrowth(), userLevel.getNeedGrowth())) {
            userLevel.setStatus(-1);
        } else {
            userLevel.setStatus(1);
        }
        this.saveOrUpdate(userLevel);
        // 插入/更新等级分类数据
        this.delOrAddCategory(userLevel, dbUserLevel);
        //插入/更新等级优惠券数据
        this.delOrAddCoupon(userLevel);
        //插入/更新等级用户权益数据
        this.delOrAddRights(userLevel);
    }

    @Override
    @CacheEvict(cacheNames = "UserLevelsByLevelType", key = "#userLevel.levelType")
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteUserLevel(UserLevel userLevel) {
        if (Objects.equals(userLevel.getLevel(), 1)) {
            throw new GlobalException("此等级不能删除，必须要保留最少一个等级");
        }
        UserLevel dbUserLevel = userLevelMapper.selectById(userLevel.getId());
        //删除等级关联权益
        userLevelRightsService.remove(new LambdaQueryWrapper<UserLevelRights>().eq(UserLevelRights::getLevelId, userLevel.getId()));
        //删除等级关联的优惠券
        userLevelCouponService.remove(new LambdaQueryWrapper<UserLevelCoupon>().eq(UserLevelCoupon::getLevelId, userLevel.getId()));
        //删除等级关联的分类
        userLevelCategoryService.remove(new LambdaQueryWrapper<UserLevelCategory>().eq(UserLevelCategory::getLevelId, userLevel.getId()));
        userService.setUserLevelBylevelId(dbUserLevel.getLevel());
        this.removeById(userLevel.getId());
        return true;
    }

    @Override
    public UserLevel getUserLevelById(Long id) {
        UserLevel userLevel = userLevelMapper.selectById(id);
        if (Objects.equals(userLevel.getDiscountType(), 1)) {
            List<Long> categoryIds = userLevelCategoryService.getCategoryIdByLevelId(userLevel.getId());
            userLevel.setCategorys(categoryIds);
        }
        userLevel.setUserRightsIds(userRightsService.getUserRightsIdListByLevelId(userLevel.getId(), 1));
        userLevel.setCouponList(userLevelCouponService.getCouponListByLevelId(userLevel.getId()));
        return userLevel;
    }

    @Override
    public List<LevelDetailDto> selectLevelAndRights(Integer levelType) {
        return userLevelMapper.selectLevelAndRights(levelType);
    }

    @Override
    @Cacheable(cacheNames = "getUserLevelName", key = "#userExtension.userId")
    public String getUserLevelName(UserExtension userExtension) {
        List<UserLevel> userLevels = list(new LambdaQueryWrapper<UserLevel>()
                .le(UserLevel::getNeedGrowth, userExtension.getGrowth()).orderByDesc(UserLevel::getNeedGrowth));
        String levelName;
        if (userExtension.getLevel() == null) {
            levelName = userLevels.get(0).getLevelName();
        } else {
            levelName = String.valueOf(userExtension.getLevel());
        }
        return levelName;
    }

    @Override
    @CacheEvict(cacheNames = "UserLevelsByLevelType", key = "#userLevel.levelType")
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateUserLevel(UserLevel userLevel) {
        List<UserLevelDto> userLevelDtoList = this.listUserLevelsByUserLevelType(userLevel.getLevelType());
        Boolean isUpdate = false;
        Integer level = 0;
        Integer minNeedGrowth = 0;
        for (UserLevelDto userLevelDto : userLevelDtoList) {
            if (isUpdate) {
                userService.setUserLevelByGrowth(level, minNeedGrowth - 1, userLevelDto.getNeedGrowth());
                isUpdate = false;
            }
            if (Objects.equals(userLevelDto.getStatus(), -1) && Objects.equals(userLevelDto.getLevel(), userLevelDtoList.size())) {
                userService.setUserLevelByGrowth(userLevelDto.getLevel(), userLevelDto.getNeedGrowth() - 1, null);
            } else if (Objects.equals(userLevelDto.getStatus(), -1)) {
                isUpdate = true;
            }
            level = userLevelDto.getLevel();
            minNeedGrowth = userLevelDto.getNeedGrowth();
        }
        userLevelMapper.setStatusByLevelType();
        return true;
    }

    /**
     * 修改用户等级中的商品分类
     *
     * @param userLevel
     * @param dbUserLevel
     */
    private void delOrAddCategory(UserLevel userLevel, UserLevel dbUserLevel) {
        List<Long> dbCategoryIds = userLevelCategoryService.getCategoryIdByLevelId(userLevel.getId());
        List<Long> categoryList = userLevel.getCategorys();
        //如果分类数据发生改变，执行方法进行增删操作
        Map<String, List<Long>> map = this.compare(categoryList, dbCategoryIds);
        List<Long> delCategory = map.get("del");
        if (delCategory.size() > 0) {
            userLevelCategoryService.delBatch(delCategory.toArray(new Long[delCategory.size()]), userLevel.getId());
        }
        List<Long> addCategory = map.get("add");
        if (addCategory.size() > 0) {
            userLevelCategoryService.insertBatch(addCategory.toArray(new Long[addCategory.size()]), userLevel.getId());
        }
    }

    /**
     * 修改用户等级中的赠送优惠券
     *
     * @param userLevel
     */
    private void delOrAddCoupon(UserLevel userLevel) {
        List<UserLevelCoupon> userLevelCouponList = userLevelCouponService.list(new LambdaQueryWrapper<UserLevelCoupon>().eq(UserLevelCoupon::getLevelId, userLevel.getId()));
        List<Long> dbCouponIds = new ArrayList<>();
        List<Long> couponIds = new ArrayList<>();
        if (!Objects.isNull(userLevelCouponList)) {
            for (UserLevelCoupon userLevelCoupon : userLevelCouponList) {
                dbCouponIds.add(userLevelCoupon.getCouponId());
            }
        }
        if (!Objects.isNull(userLevel.getCouponList())) {
            for (Coupon coupon : userLevel.getCouponList()) {
                couponIds.add(coupon.getCouponId());
            }
        }
        Map<String, List<Long>> map = this.compare(couponIds, dbCouponIds);
        List<Long> delCoupon = map.get("del");
        if (delCoupon.size() > 0) {
            userLevelCouponService.delBatchCoupon(delCoupon.toArray(new Long[delCoupon.size()]), userLevel.getId());
        }
        List<Long> addCoupon = map.get("add");
        if (addCoupon.size() > 0) {
            userLevelCouponService.insertBatchCoupon(addCoupon.toArray(new Long[addCoupon.size()]), userLevel.getId());
        }

    }

    /**
     * 修改用户等级中的权益
     *
     * @param userLevel
     */
    private void delOrAddRights(UserLevel userLevel) {
        List<Long> userRightsList = userRightsService.getUserRightsIdListByLevelId(userLevel.getId(), null);
        List<Long> dbRightsIds = new ArrayList<>();
        List<Long> rightsIds = new ArrayList<>();
        if (!Objects.isNull(userRightsList)) {
            dbRightsIds.addAll(userRightsList);
        }
        if (!Objects.isNull(userLevel.getUserRightsIds())) {
            rightsIds.addAll(userLevel.getUserRightsIds());
            //折扣
            if (!Objects.equals(userLevel.getDiscount(), 10D)) {
                rightsIds.add(UserRightsInfo.DISCOUNT.value());
            }
            //包邮
            if (Objects.equals(userLevel.getIsFreeFee(), 1)) {
                rightsIds.add(UserRightsInfo.IS_FREE_FEE.value());
            }
            //赠送积分
            if (!Objects.equals(userLevel.getPresScore(), 0)) {
                rightsIds.add(UserRightsInfo.SCORE.value());
            }
            //优惠券
            if (userLevel.getCouponList().size() > 0) {
                rightsIds.add(UserRightsInfo.COUPON.value());
            }
            //积分倍率
            if (!Objects.equals(userLevel.getRateScore(), 1D)) {
                rightsIds.add(UserRightsInfo.RATE_SCORE.value());
            }
        }
        Map<String, List<Long>> map = this.compare(rightsIds, dbRightsIds);
        List<Long> delCoupon = map.get("del");
        if (delCoupon.size() > 0) {
            userLevelRightsService.delBatchRights(delCoupon.toArray(new Long[delCoupon.size()]), userLevel.getId());
        }
        List<Long> addCoupon = map.get("add");
        if (addCoupon.size() > 0) {
            userLevelRightsService.insertBatchRights(addCoupon.toArray(new Long[addCoupon.size()]), userLevel.getId());
        }
    }


    /**
     * 得到需要新增或删除的数据
     *
     * @param ids
     * @param dbIds
     */
    private Map<String, List<Long>> compare(List<Long> ids, List<Long> dbIds) {
        if (Objects.isNull(ids)) {
            ids = new ArrayList<>();
        }
        List<Long> del = new ArrayList<>();
        List<Long> add = new ArrayList<>();
        del.addAll(dbIds);
        add.addAll(ids);
        add.removeAll(dbIds);
        del.removeAll(ids);
        Map<String, List<Long>> map = new HashMap<>();
        //需要新增的数据
        map.put("add", add);
        //需要删除的数据
        map.put("del", del);
        return map;
    }

    /**
     * 增加用户成长值和积分
     *
     * @param growthPrice 增加的成长值
     * @param score       增加的积分
     * @param order       确认收货的订单信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addGrowthAndScore(double growthPrice, Integer score, Order order, UserExtension userExtension) {
        String userId = order.getUserId();
        User user = userService.getById(userId);
        if (user.getLevel() == null) {
            user.setLevel(Constant.USER_LEVEL_INIT);
        }
        Integer growth = (int) growthPrice;

        addScoreAndGrowth(order.getOrderId(), user.getUserId(), growth, score);
        //判断是否升级
        //修改用户信息
        //用户＋增加成长值
        Integer nowGrowth = userExtension.getGrowth() == null ? growth : userExtension.getGrowth() + growth;
        userExtension.setScore(userExtension.getScore() == null ? score : userExtension.getScore() + score);
        userExtension.setGrowth(nowGrowth);
        userExtension.setUpdateTime(new Date());
        //如果用户是付费会员则不提升等级,只修改成长值
        //判断用户是否提升过等级,如果是修改用户等级并添加用户等级提升日志
        List<UserLevel> userLevels = userLevelMapper.selectListAndCoupons(nowGrowth, userExtension.getLevel());
        if (user.getLevelType() == 1 || CollectionUtils.isEmpty(userLevels)) {
            userExtensionMapper.updateById(userExtension);
        } else {
            Integer level = userLevels.get(0).getLevel();
            //修改用户等级
            user.setLevel(level);
            userService.updateById(user);
            //判断用户是否提升过等级
            Integer historyLevel = userLevelLogService.getMaxLevelByUserId(user.getUserId());
            //等级提升发放奖品及添加等级提升日志
            levelUp(userLevels, null, userExtension, historyLevel);
        }
    }

    /**
     * 添加积分明细、积分日志、成长值日志
     *
     * @param orderId 订单号
     * @param userId  用户id
     * @param growth  成长值
     * @param score   积分
     */
    private void addScoreAndGrowth(Long orderId, String userId, Integer growth, Integer score) {
        if (score > 0) {
            //添加积分明细
            UserScoreDetail addDetail = new UserScoreDetail();
            addDetail.setCreateTime(new Date());
            addDetail.setStatus(1);
            addDetail.setUserId(userId);
            addDetail.setBizId(orderId);
            addDetail.setUsableScore(score);
            userScoreDetailService.save(addDetail);

            //添加积分日志
            UserScoreLog userScoreLog = new UserScoreLog();
            userScoreLog.setUserId(userId);
            userScoreLog.setScore(score);
            userScoreLog.setBizId(orderId);
            userScoreLog.setSource(ScoreLogType.SHOP.value());
            userScoreLog.setCreateTime(new Date());
            userScoreLog.setIoType(1);
            userScoreLogService.save(userScoreLog);
        }
        if (growth > 0) {
            //添加成长值日志
            UserGrowthLog userGrowthLog = new UserGrowthLog();
            userGrowthLog.setChangeGrowth(growth);
            userGrowthLog.setBizId(orderId);
            userGrowthLog.setSource(1);
            userGrowthLog.setUserId(userId);
            userGrowthLog.setCreateTime(new Date());
            userGrowthLog.setRemarks("订单确认收货获取的成长值");
            userGrowthLogService.save(userGrowthLog);
        }
    }
//
//    private void updateUser(User user, UserLevel userLevel) {
//        Integer level = userLevel.getLevel();
//        DateTime endTime;
//        //修改用户等级
//        if(user.getVipEndTime() == null || user.getLevel() < level){
//            endTime = DateUtil.offset(new Date(), DateField.HOUR_OF_DAY, 24 * userLevel.getTerm());
//        }else{
//            endTime = DateUtil.offset(user.getVipEndTime(), DateField.HOUR_OF_DAY, 24 * userLevel.getTerm());
//        }
//        user.setVipEndTime(endTime);
//        user.setLevel(level);
//        userService.updateById(user);
//
//        //修改用户等级积分详细表
//        UserExtension extension = userExtensionMapper.selectOne(
//                new LambdaQueryWrapper<UserExtension>().eq(UserExtension::getUserId, user.getUserId()));
//        if(extension == null){
//            UserExtension userExtension = new UserExtension();
//            userExtension.setUserId(user.getUserId());
//            userExtension.setGrowth(0);
//            userExtension.setScore(0);
//            userExtension.setBalance(0.0);
//            userExtension.setVersion(0);
//            userExtension.setLevel(level);
//            userExtension.setUpdateTime(new Date());
//            userExtensionMapper.insert(userExtension);
//        }else{
//            extension.setLevel(level);
//            extension.setUpdateTime(new Date());
//            // 使用乐观锁进行更新
//            if (userExtensionMapper.updateById(extension)!=1) {
//                log.error("更新用户等级失败！");
//                throw new GlobalException("服务器繁忙,更新用户等级失败!");
//            }
//        }
//
//    }


    /**
     * 用户购买会员后提升会员等级
     *
     * @param user          用户信息
     * @param userLevel     用户等级
     * @param userExtension 用户扩展信息
     */
    private void updateUserLevel(User user, UserLevel userLevel, UserExtension userExtension) {
        Integer level = userLevel.getLevel();
        DateTime endTime;
        Map<Integer, Integer> dateMap = new HashMap<>(12);
        dateMap.put(1, 1);
        dateMap.put(2, 7);
        dateMap.put(3, 31);
        dateMap.put(4, 93);
        dateMap.put(5, 366);
        //修改用户等级
        if (user.getVipEndTime() == null || user.getLevel() < level || user.getLevel() > level) {
            endTime = DateUtil.offset(new Date(), DateField.HOUR_OF_DAY, 24 * userLevel.getTerm() * dateMap.get(userLevel.getTermType()));
        } else {
            endTime = DateUtil.offset(user.getVipEndTime(), DateField.HOUR_OF_DAY, 24 * userLevel.getTerm() * dateMap.get(userLevel.getTermType()));
        }
        user.setVipEndTime(endTime);
        user.setLevel(level);
        user.setLevelType(1);
        userService.updateById(user);
        //修改用户等级积分详细表
        userExtension.setLevel(level);
        userExtension.setLevelType(1);
        userExtension.setUpdateTime(new Date());
        // 使用乐观锁进行更新
        if (userExtensionMapper.updateById(userExtension) != 1) {
            log.error("更新用户等级失败！");
            throw new GlobalException("服务器繁忙,更新用户等级失败!");
        }
    }

    /**
     * 等级提升
     *
     * @param userLevels   多个等级，奖励发放时
     * @param userLevelLog 等级日志,在购买付费会员时存在
     * @param user         用户详细信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void levelUp(List<UserLevel> userLevels, UserLevelLog userLevelLog, UserExtension user, Integer historyLevel) {
        List<UserScoreLog> userScoreLogs = new ArrayList<>();
        List<UserScoreDetail> userScoreDetails = new ArrayList<>();
        //用户可能升多级，批量插入升级日志
        List<UserLevelLog> levelLogs = new ArrayList<>();
        List<Long> couponIds = new ArrayList<>();
        for (UserLevel level : userLevels) {
            if (ObjectUtils.isEmpty(level)) break;
            //修改用户扩展表等级
            user.setLevel(level.getLevel());
            user.setLevelType(level.getLevelType());
            boolean isLevelUp = historyLevel != null && (level.getLevel() <= historyLevel || level.getLevel().equals(user.getLevel()));
            //如果历史提升过此等级并且为普通会员就退出循环
            if (level.getLevelType() == 0 && isLevelUp) {
                break;
            }
            //升级奖励计算(积分，优惠券，商品)

            //2.优惠券
            //TODO
            if (CollectionUtils.isNotEmpty(level.getCouponIds())) {
                couponIds.addAll(level.getCouponIds());
            }
            //3.等级日志,如果不为空则为会员购买提升的等级就直接退出，修改一行
            if (userLevelLog != null) {
                userLevelLog.setIsPayed(1);
                userLevelLog.setState(1);
                userLevelLog.setLevelType(1);
                userLevelLogService.updateById(userLevelLog);
                break;
            }
            UserLevelLog levelLog = new UserLevelLog();
            levelLog.setUserId(user.getUserId());
            levelLog.setAddrOrderId(null);
            levelLog.setLevel(level.getLevel());
            levelLog.setCreateTime(DateUtil.date());
            levelLog.setState(1);
            levelLog.setLevelType(0);
            levelLog.setIsPayed(1);
            levelLogs.add(levelLog);
        }
        //修改扩展表信息
        userExtensionMapper.updateById(user);
        if (CollectionUtils.isNotEmpty(couponIds)) {
            applicationContext.publishEvent(new BatchBindCouponEvent(couponIds, user.getUserId(), 0L));
        }
        //保存积分日志、积分明细日志
        if (CollectionUtils.isNotEmpty(userScoreLogs)) {
            userScoreLogService.saveBatch(userScoreLogs);
            userScoreDetailService.saveBatch(userScoreDetails);
        }
        //批量保存等级日志
        if (CollectionUtils.isNotEmpty(levelLogs)) {
            userLevelLogService.saveBatch(levelLogs);
        }
    }


}
