package cn.lili.modules.promotion.serviceimpl;

import cn.hutool.core.text.CharSequenceUtil;
import cn.lili.cache.Cache;
import cn.lili.common.enums.ResultCode;
import cn.lili.common.exception.ServiceException;
import cn.lili.common.security.AuthUser;
import cn.lili.common.security.context.UserContext;
import cn.lili.common.vo.PageVO;
import cn.lili.modules.promotion.entity.dos.Coupon;
import cn.lili.modules.promotion.entity.dos.MemberCoupon;
import cn.lili.modules.promotion.entity.dto.search.MemberCouponSearchParams;
import cn.lili.modules.promotion.entity.enums.CouponGetEnum;
import cn.lili.modules.promotion.entity.enums.MemberCouponStatusEnum;
import cn.lili.modules.promotion.entity.enums.PromotionsScopeTypeEnum;
import cn.lili.modules.promotion.entity.enums.PromotionsStatusEnum;
import cn.lili.modules.promotion.entity.vos.MemberCouponVO;
import cn.lili.modules.promotion.mapper.MemberCouponMapper;
import cn.lili.modules.promotion.service.CouponService;
import cn.lili.modules.promotion.service.MemberCouponService;
import cn.lili.modules.promotion.tools.PromotionTools;
import cn.lili.mybatis.util.PageUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

/**
 * 会员优惠券业务层实现
 *
 * @author Chopper
 * @since 2020/8/21
 */
@Service
@CacheConfig(cacheNames = "{MemberCoupon}")
public class MemberCouponServiceImpl extends ServiceImpl<MemberCouponMapper, MemberCoupon> implements MemberCouponService {

    /**
     * 优惠券
     */
    @Autowired
    private CouponService couponService;

    /**
     * 缓存
     */
    @Autowired
    private Cache cache;

    @Override
    public void checkCouponLimit(String couponId, String memberId) {
        Coupon coupon = couponService.getById(couponId);
        LambdaQueryWrapper<MemberCoupon> queryWrapper = new LambdaQueryWrapper<MemberCoupon>()
                .eq(MemberCoupon::getCouponId, couponId)
                .eq(MemberCoupon::getMemberId, memberId);
        long haveCoupons = this.count(queryWrapper);
        if (!PromotionsStatusEnum.START.name().equals(coupon.getPromotionStatus())) {
            throw new ServiceException(ResultCode.COUPON_RECEIVE_ERROR);
        }
        if (coupon.getPublishNum() != 0 && coupon.getReceivedNum() >= coupon.getPublishNum()) {
            throw new ServiceException(ResultCode.COUPON_NUM_INSUFFICIENT_ERROR);
        }
        if (!coupon.getCouponLimitNum().equals(0) && haveCoupons >= coupon.getCouponLimitNum()) {
            throw new ServiceException(ResultCode.COUPON_LIMIT_ERROR, "此优惠券最多领取" + coupon.getCouponLimitNum() + "张");
        }
    }

    /**
     * 领取优惠券
     *
     * @param couponId   优惠券编号
     * @param memberId   会员
     * @param memberName 会员名称
     */
    @Override
    @CacheEvict(key = "#memberId")
    @Transactional(rollbackFor = Exception.class)
    public void receiveBuyerCoupon(String couponId, String memberId, String memberName) {
        Coupon coupon = couponService.getById(couponId);
        if (coupon != null && !CouponGetEnum.FREE.name().equals(coupon.getGetType())) {
            throw new ServiceException(ResultCode.COUPON_DO_NOT_RECEIVER);
        } else if (coupon != null) {
            this.receiverCoupon(couponId, memberId, memberName, coupon);
        }

    }

    @Override
    @CacheEvict(key = "#memberId")
    @Transactional(rollbackFor = Exception.class)
    public void receiveCoupon(String couponId, String memberId, String memberName) {
        Coupon coupon = couponService.getById(couponId);
        if (coupon != null) {
            this.receiverCoupon(couponId, memberId, memberName, coupon);
        } else {
            throw new ServiceException(ResultCode.COUPON_NOT_EXIST);
        }
    }

    @Override
    public IPage<MemberCoupon> getMemberCoupons(MemberCouponSearchParams param, PageVO pageVo) {
        QueryWrapper<MemberCoupon> queryWrapper = param.queryWrapper();
        Page<MemberCoupon> page = this.page(PageUtil.initPage(pageVo), queryWrapper);
        if (page.getRecords() != null && page.getRecords().size() > 0) {
            if (page.getRecords().stream().anyMatch(i -> i.getEndTime().before(new Date()))) {
                this.expireInvalidMemberCoupon(param.getMemberId());
                return this.page(PageUtil.initPage(pageVo), queryWrapper);
            }
        }
        return page;
    }

    /**
     * 获取会员优惠券列表
     *
     * @param param 查询参数
     * @return 会员优惠券列表
     */
    @Override
    public List<MemberCoupon> getMemberCoupons(MemberCouponSearchParams param) {
        List<MemberCoupon> list = this.list(param.queryWrapper());
        if (list.stream().anyMatch(i -> i.getEndTime().before(new Date()))) {
            this.expireInvalidMemberCoupon(param.getMemberId());
            return this.list(param.queryWrapper());
        }
        return list;
    }

    /**
     * 获取当前用户的优惠券列表（优先读取缓存）
     *
     * @param memberId 会员id
     * @return 会员优惠券列表
     */
    @Override
    @Cacheable(key = "#memberId")
    public List<MemberCoupon> getMemberCoupons(String memberId) {
        MemberCouponSearchParams searchParams = new MemberCouponSearchParams();
        searchParams.setMemberId(Objects.requireNonNull(UserContext.getCurrentUser()).getId());
        searchParams.setMemberCouponStatus(MemberCouponStatusEnum.NEW.name());
        searchParams.setPromotionStatus(PromotionsStatusEnum.START.name());
        return this.getMemberCoupons(searchParams);
    }

    /**
     * 获取会员优惠券列表
     *
     * @param param  查询参数
     * @param pageVo 分页参数
     * @return 会员优惠券列表
     */
    @Override
    public IPage<MemberCoupon> getMemberCouponsByCanUse(MemberCouponSearchParams param, Double totalPrice, PageVO pageVo) {
        LambdaQueryWrapper<MemberCoupon> queryWrapper = new LambdaQueryWrapper<>();
        List<String> storeIds = new ArrayList<>(Arrays.asList(param.getStoreId().split(",")));
        storeIds.add(PromotionTools.PLATFORM_ID);
        queryWrapper.in(MemberCoupon::getStoreId, storeIds);
        queryWrapper.eq(MemberCoupon::getMemberId, param.getMemberId());
        queryWrapper.and(
                i -> i.like(MemberCoupon::getScopeId, param.getScopeId())
                        .or(j -> j.eq(MemberCoupon::getScopeType, PromotionsScopeTypeEnum.ALL.name())));
        queryWrapper.eq(MemberCoupon::getMemberCouponStatus, MemberCouponStatusEnum.NEW.name());
        queryWrapper.le(MemberCoupon::getConsumeThreshold, totalPrice);
        queryWrapper.ge(MemberCoupon::getEndTime, new Date());
        return this.page(PageUtil.initPage(pageVo), queryWrapper);
    }

    /**
     * 获取当前会员当前商品可用的会员优惠券
     *
     * @param memberId  会员Id
     * @param couponIds 优惠券id列表
     * @return 会员优惠券列表
     */
    @Override
    public List<MemberCoupon> getCurrentGoodsCanUse(String memberId, List<String> couponIds, Double totalPrice) {
        LambdaQueryWrapper<MemberCoupon> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(MemberCoupon::getMemberId, memberId);
        queryWrapper.eq(MemberCoupon::getMemberCouponStatus, MemberCouponStatusEnum.NEW.name());
        queryWrapper.in(MemberCoupon::getCouponId, couponIds);
        queryWrapper.ne(MemberCoupon::getScopeType, PromotionsScopeTypeEnum.ALL.name());
        queryWrapper.le(MemberCoupon::getConsumeThreshold, totalPrice);
        queryWrapper.ge(MemberCoupon::getEndTime, new Date());
        return this.list(queryWrapper);
    }

    /**
     * 获取当前会员全品类优惠券
     *
     * @param memberId 会员Id
     * @param storeId  店铺id
     * @return 会员优惠券列表
     */
    @Override
    public List<MemberCoupon> getAllScopeMemberCoupon(String memberId, List<String> storeId) {
        LambdaQueryWrapper<MemberCoupon> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(MemberCoupon::getMemberId, memberId);
        queryWrapper.eq(MemberCoupon::getMemberCouponStatus, MemberCouponStatusEnum.NEW.name());
        queryWrapper.eq(MemberCoupon::getScopeType, PromotionsScopeTypeEnum.ALL.name());
        queryWrapper.ge(MemberCoupon::getEndTime, new Date()).and(i -> i.in(MemberCoupon::getStoreId, storeId).or(j -> j.eq(MemberCoupon::getPlatformFlag, true)));
        return this.list(queryWrapper);
    }

    /**
     * 获取当前会员全品类优惠券
     *
     * @param param 查询参数
     * @return 会员优惠券列表
     */
    @Override
    public MemberCoupon getMemberCoupon(MemberCouponSearchParams param) {
        return this.getOne(param.queryWrapper(), false);
    }

    @Override
    public long getMemberCouponsNum() {
        AuthUser authUser = Objects.requireNonNull(UserContext.getCurrentUser());
        QueryWrapper<MemberCoupon> queryWrapper = Wrappers.query();
        queryWrapper.eq("member_id", authUser.getId());
        queryWrapper.eq("member_coupon_status", MemberCouponStatusEnum.NEW.name());
        queryWrapper.eq("delete_flag", false);
        return this.count(queryWrapper);
    }


    @Override
    @CacheEvict(key = "#memberId")
    @Transactional(rollbackFor = Exception.class)
    public void used(String memberId, List<String> ids) {
        if (ids != null && !ids.isEmpty()) {
            List<MemberCoupon> memberCoupons = this.listByIds(ids);

            //如果查出来的优惠券数量不一致
            if (memberCoupons.size() != ids.size()) {
                throw new ServiceException(ResultCode.COUPON_EXPIRED);
            }
            //循环处理
            memberCoupons.forEach(item -> {
                if (!item.getMemberCouponStatus().equals(MemberCouponStatusEnum.NEW.name())) {
                    throw new ServiceException(ResultCode.COUPON_EXPIRED);
                }
                item.setMemberCouponStatus(MemberCouponStatusEnum.USED.name());
                item.setConsumptionTime(new Date());
            });

            this.updateBatchById(memberCoupons);
        }
    }

    /**
     * 作废当前会员优惠券
     *
     * @param id id
     */
    @Override
    @CacheEvict(key = "#memberId")
    @Transactional(rollbackFor = Exception.class)
    public void cancellation(String memberId, String id) {
        LambdaUpdateWrapper<MemberCoupon> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(MemberCoupon::getId, id);
        updateWrapper.set(MemberCoupon::getMemberCouponStatus, MemberCouponStatusEnum.CLOSED.name());
        this.update(updateWrapper);
    }

    /**
     * 关闭会员优惠券
     *
     * @param couponIds 优惠券id集合
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void closeMemberCoupon(List<String> couponIds) {
        LambdaUpdateWrapper<MemberCoupon> memberCouponLambdaUpdateWrapper = new LambdaUpdateWrapper<MemberCoupon>()
                .in(MemberCoupon::getCouponId, couponIds)
                .set(MemberCoupon::getMemberCouponStatus, MemberCouponStatusEnum.CLOSED.name());
        this.cache.vagueDel("{MemberCoupon}");
        this.update(memberCouponLambdaUpdateWrapper);
    }

    @Override
    public boolean recoveryMemberCoupon(List<String> memberCouponIds) {
        LambdaUpdateWrapper<MemberCoupon> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.in(MemberCoupon::getId, memberCouponIds);
        updateWrapper.set(MemberCoupon::getMemberCouponStatus, MemberCouponStatusEnum.NEW.name());
        updateWrapper.set(MemberCoupon::getConsumptionTime, null);
        return this.update(updateWrapper);
    }

    @Override
    public void voidCoupon(String couponId) {
        LambdaUpdateWrapper<MemberCoupon> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.in(MemberCoupon::getCouponId, couponId);
        updateWrapper.set(MemberCoupon::getMemberCouponStatus, MemberCouponStatusEnum.CLOSED.name());
        updateWrapper.set(MemberCoupon::getDeleteFlag, true);
        this.update(updateWrapper);
    }

    @Override
    public Page<MemberCouponVO> getMemberCouponsPage(Page<MemberCoupon> page, MemberCouponSearchParams param) {
        QueryWrapper<MemberCouponVO> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(CharSequenceUtil.isNotEmpty(param.getMemberId()), "mc.member_id", param.getMemberId());
        queryWrapper.eq(CharSequenceUtil.isNotEmpty(param.getStoreId()), "c.store_id", param.getStoreId());
        queryWrapper.like(CharSequenceUtil.isNotEmpty(param.getMemberName()), "mc.member_name", param.getMemberName());
        queryWrapper.eq(CharSequenceUtil.isNotEmpty(param.getCouponId()), "mc.coupon_id", param.getCouponId());
        queryWrapper.like(CharSequenceUtil.isNotEmpty(param.getCouponName()), "c.coupon_name", param.getCouponName());
        queryWrapper.eq(CharSequenceUtil.isNotEmpty(param.getGetType()), "mc.get_type", param.getGetType());
        queryWrapper.eq(CharSequenceUtil.isNotEmpty(param.getScopeType()), "mc.scope_type", param.getPromotionStatus());
        queryWrapper.eq(CharSequenceUtil.isNotEmpty(param.getCouponType()), "mc.coupon_type", param.getCouponType());
        queryWrapper.eq(CharSequenceUtil.isNotEmpty(param.getMemberCouponStatus()), "mc.member_coupon_status", param.getMemberCouponStatus());
        if (param.getStartTime() != null) {
            queryWrapper.ge("mc.start_time", new Date(param.getStartTime()));
        }
        if (param.getEndTime() != null) {
            queryWrapper.le("mc.end_time", new Date(param.getEndTime()));
        }
        return this.baseMapper.getMemberCoupons(page, queryWrapper);
    }

    @Override
    public long getMemberCouponNum(String memberId, String couponId) {
        LambdaQueryWrapper<MemberCoupon> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(MemberCoupon::getMemberId, memberId);
        queryWrapper.eq(MemberCoupon::getCouponId, couponId);
        return this.count(queryWrapper);
    }

    /**
     * 清除无效的会员优惠券
     *
     * @return 是否操作成功
     */
    @Override
    @CacheEvict(key = "#memberId")
    public boolean expireInvalidMemberCoupon(String memberId) {
        //将过期优惠券变更为过期状体
        LambdaUpdateWrapper<MemberCoupon> updateWrapper = new LambdaUpdateWrapper<MemberCoupon>()
                .eq(CharSequenceUtil.isNotEmpty(memberId), MemberCoupon::getMemberId, memberId)
                .eq(MemberCoupon::getMemberCouponStatus, MemberCouponStatusEnum.NEW.name())
                .le(MemberCoupon::getEndTime, new Date())
                .set(MemberCoupon::getMemberCouponStatus, MemberCouponStatusEnum.EXPIRE.name());
        return this.update(updateWrapper);
    }

    private void receiverCoupon(String couponId, String memberId, String memberName, Coupon coupon) {
        this.checkCouponLimit(couponId, memberId);
        MemberCoupon memberCoupon = new MemberCoupon(coupon);
        memberCoupon.setMemberId(memberId);
        memberCoupon.setMemberName(memberName);
        memberCoupon.setMemberCouponStatus(MemberCouponStatusEnum.NEW.name());
        memberCoupon.setPlatformFlag((PromotionTools.PLATFORM_ID).equals(coupon.getStoreId()));
        this.save(memberCoupon);
        couponService.receiveCoupon(couponId, 1);
    }
}