/*
 * Copyright (c) 2018-2999 广州亚米信息科技有限公司 All rights reserved.
 *
 * https://www.gz-yami.com/
 *
 * 未经允许，不可做商业用途！
 *
 * 版权所有，侵权必究！
 */

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

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.yami.shop.bean.app.dto.CouponDto;
import com.yami.shop.bean.app.dto.CouponOrderDto;
import com.yami.shop.bean.app.dto.ProductDto;
import com.yami.shop.bean.enums.CouponProdType;
import com.yami.shop.bean.enums.OfflineHandleEventStatus;
import com.yami.shop.bean.enums.OfflineHandleEventType;
import com.yami.shop.bean.event.ScoreOrderEvent;
import com.yami.shop.bean.model.OfflineHandleEvent;
import com.yami.shop.bean.param.OfflineHandleEventAuditParam;
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.common.util.PageParam;
import com.yami.shop.coupon.comment.Enums.CouponStatusEnum;
import com.yami.shop.coupon.comment.dao.CouponMapper;
import com.yami.shop.coupon.comment.dao.CouponProdMapper;
import com.yami.shop.coupon.comment.dao.CouponUserMapper;
import com.yami.shop.coupon.comment.model.Coupon;
import com.yami.shop.coupon.comment.model.CouponProd;
import com.yami.shop.coupon.comment.model.CouponUser;
import com.yami.shop.coupon.comment.service.CouponService;
import com.yami.shop.coupon.comment.service.CouponUserService;
import com.yami.shop.dao.ProductMapper;
import com.yami.shop.service.OfflineHandleEventService;
import com.yami.shop.service.UserService;
import lombok.AllArgsConstructor;
import ma.glasnost.orika.MapperFacade;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.time.DateUtils;
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 javax.validation.Valid;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author lgh on 2018/12/27.
 */
@Service
@AllArgsConstructor
public class CouponServiceImpl extends ServiceImpl<CouponMapper, Coupon> implements CouponService {

    private final CouponMapper couponMapper;
    private final UserService userService;
    private final MapperFacade mapperFacade;
    private final CouponProdMapper couponProdMapper;
    private final CouponUserMapper couponUserMapper;
    private final CouponUserService couponUserService;
    private final ProductMapper productMapper;
    private final OfflineHandleEventService offlineHandleEventService;
    private ApplicationContext applicationContext;

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(cacheNames = "couponAndCouponProds", key = "#coupon.shopId")
    public void updateCouponAndCouponProds(@Valid Coupon coupon) {
        Integer suitableProdType = coupon.getSuitableProdType();
        // 判断该商品的类型
        if ((Objects.equals(CouponProdType.PROD_IN.value(), coupon.getSuitableProdType())
                || Objects.equals(CouponProdType.PROD_NO_IN.value(), coupon.getSuitableProdType()))
                && CollectionUtils.isEmpty(coupon.getCouponProds())) {
            throw new GlobalException("商品不能为空");
        }
        // 结束时间小于等于当前时间
        if (coupon.getValidTimeType() == 1) {
            Date nowTime = new Date();
            if (coupon.getEndTime().getTime() < nowTime.getTime()) {
                coupon.setOverdueStatus(0);
                coupon.setPutonStatus(0);
            } else {
                coupon.setOverdueStatus(1);
            }
        }
        couponMapper.updateById(coupon);
        // 删除该优惠券下所有商品
        couponProdMapper.deleteCouponProdsByCouponId(coupon.getCouponId());
        if (suitableProdType != 0) {
            List<Long> prodIds = coupon.getCouponProds().stream().map(CouponProd::getProdId).collect(Collectors.toList());
            // 插入所有商品
            couponProdMapper.insertCouponProdsBatch(coupon.getCouponId(), prodIds);
        }
    }

    @Override
    @CacheEvict(cacheNames = "couponAndCouponProds", key = "#shopId")
    public void removeCouponAndCouponProdsCache(Long shopId) {
    }

    @Override
    public IPage<CouponDto> getCouponList(PageParam<CouponDto> page,String userId) {
        //获取优惠券列表
        IPage<CouponDto> couponDtoIPage =  couponMapper.getCouponPageByCouponDto(page);
        //根据适用商品类型，进行分组
        List<CouponDto> records = couponDtoIPage.getRecords();
        List<CouponDto> couponForAllProd = Lists.newArrayList();
        List<Long> couponForProd = Lists.newArrayList();
        List<CouponDto> couponForElseProd = Lists.newArrayList();
        for (CouponDto coupon:records){
            if (Objects.equals(coupon.getSuitableProdType(),0)){
                couponForAllProd.add(coupon);
            }else if (Objects.equals(coupon.getSuitableProdType(),1)){
                couponForProd.add(coupon.getCouponId());
            }else{
                couponForElseProd.add(coupon);
            }
        }
        List<CouponDto> couponDtoList = null;
        if (CollUtil.isNotEmpty(couponForAllProd) || CollUtil.isNotEmpty(couponForProd) || CollUtil.isNotEmpty(couponForElseProd) ){
            couponDtoList =  couponMapper.getCouponList(couponForAllProd,couponForProd,couponForElseProd,userId);
            for (CouponDto couponDto : couponDtoList) {
                for (CouponDto record : records) {
                    if (Objects.equals(couponDto.getShopId(), record.getShopId())) {
                        couponDto.setShopLogo(record.getShopLogo());
                        couponDto.setShopName(record.getShopName());
                    }
                }
            }

        }
        couponDtoIPage.setRecords(couponDtoList);
        return couponDtoIPage;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(cacheNames = "couponAndCouponProds", key = "#shopId")
    public void batchBindCouponByIds(List<Long> couponIds, String userId,Long shopId) {
        List<CouponUser> couponUsers = new ArrayList<>();
        Collection<Coupon> coupons = listByIds(couponIds);
        if(CollectionUtils.isEmpty(coupons)){
            return;
        }
        for (Coupon coupon : coupons) {
            Date nowTime = new Date();
            // 当优惠券状态不为投放时
            if (coupon.getOverdueStatus() == 0 || coupon.getPutonStatus() != 1 || coupon.getStocks() == 0) {
                log.warn("该券无法被领取或者该券领完了！");
                continue;
            }
            int count = couponUserMapper.selectCount(new LambdaQueryWrapper<CouponUser>().eq(CouponUser::getUserId, userId).eq(CouponUser::getCouponId, coupon.getCouponId()));
            if (count >= coupon.getLimitNum()) {
                log.warn("该券已达个人领取上限，无法继续领取！");
                continue;
            }
            CouponUser couponUser = new CouponUser();
            couponUser.setUserId(userId);
            couponUser.setStatus(1);
            couponUser.setCouponId(coupon.getCouponId());
            couponUser.setReceiveTime(nowTime);
            // 生效时间类型为固定时间
            if (coupon.getValidTimeType() == 1) {
                couponUser.setUserStartTime(coupon.getStartTime());
                couponUser.setUserEndTime(coupon.getEndTime());
            }
            // 生效时间类型为领取后生效
            if (coupon.getValidTimeType() == 2) {
                if (coupon.getAfterReceiveDays() == null) {
                    coupon.setAfterReceiveDays(0);
                }
                if (coupon.getValidDays() == null) {
                    coupon.setValidDays(0);
                }
                couponUser.setUserStartTime(DateUtils.addDays(DateUtil.beginOfDay(nowTime), coupon.getAfterReceiveDays()));
                couponUser.setUserEndTime(DateUtils.addDays(couponUser.getUserStartTime(), coupon.getValidDays() + 1));
            }
            couponUser.setIsDelete(0);
            couponUsers.add(couponUser);
        }
        couponUserService.saveBatch(couponUsers);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(cacheNames = "couponAndCouponProds", key = "#coupon.shopId")
    public void insertCouponAndCouponProds(@Valid Coupon coupon) {
        Integer suitableProdType = coupon.getSuitableProdType();
        if ((Objects.equals(CouponProdType.PROD_IN.value(), coupon.getSuitableProdType())
                || Objects.equals(CouponProdType.PROD_NO_IN.value(), coupon.getSuitableProdType()))
                && CollectionUtils.isEmpty(coupon.getCouponProds())) {
            throw new GlobalException("商品不能为空");
        }
        if (coupon.getLimitNum() <= 0) {
            throw new GlobalException("优惠券限领数量需大于0");
        }
        if (coupon.getValidTimeType() == 1 && coupon.getStartTime().getTime() > coupon.getEndTime().getTime()) {
            throw new GlobalException("开始时间需大于结束时间");
        }
        // 结束时间小于等于当前时间
        Date nowTime = new Date();
        if (coupon.getValidTimeType() == 1 && coupon.getEndTime().getTime() < nowTime.getTime()) {
            coupon.setOverdueStatus(0);
            coupon.setPutonStatus(0);
        }
        couponMapper.insert(coupon);
        if (suitableProdType != 0) {
            List<Long> prodIds = coupon.getCouponProds().stream().map(CouponProd::getProdId).collect(Collectors.toList());
            // 插入所有商品
            couponProdMapper.insertCouponProdsBatch(coupon.getCouponId(), prodIds);
        }
    }

    @Override
    public Coupon getCouponAndCouponProdsByCouponId(Long id) {
        return couponMapper.getCouponAndCouponProdsByCouponId(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(cacheNames = "couponAndCouponProds", key = "#coupon.shopId")
    public void receive(Coupon coupon, String userId) {
        //如果是积分商品，就扣除用户相应的积分
        if (Objects.equals(coupon.getCouponType(), 3)){
            applicationContext.publishEvent(new ScoreOrderEvent(coupon.getCouponId(),null,coupon.getScorePrice()));
        }
        CouponUser couponUser = getCouponUser(coupon, userId);
        couponUserMapper.insert(couponUser);
        // 更新库存
        if (couponMapper.updateCouponStocksAndVersion(coupon.getCouponId()) < 1) {
            throw new GlobalException("优惠券库存不足");
        }
    }

    private CouponUser getCouponUser(Coupon coupon, String userId){
        Date nowTime = new Date();
        // 当优惠券状态不为投放时
        if (coupon.getOverdueStatus() == 0 || coupon.getPutonStatus() != 1) {
            throw new GlobalException("该券无法被领取");
        }
        // 当优惠券不在规定时间内(类型为固定时间) 优惠券使用时间，不是领取时间，一般优惠券都是可以领，还不能用的
//        if (coupon.getValidTimeType() == 1 && (coupon.getEndTime().getTime() < nowTime.getTime() || coupon.getStartTime().getTime() > nowTime.getTime())) {
//            throw new GlobalException("不在可领取的时间范围内");
//        }
        // 当优惠券无库存时
        if (coupon.getStocks() == 0) {
            throw new GlobalException("该券领完了！");
        }
        int count = couponUserMapper.selectCount(new LambdaQueryWrapper<CouponUser>().eq(CouponUser::getUserId, userId).eq(CouponUser::getCouponId, coupon.getCouponId()));
        if (count >= coupon.getLimitNum()) {
            throw new GlobalException("该券已达个人领取上限，无法继续领取");
        }
        CouponUser couponUser = new CouponUser();
        couponUser.setUserId(userId);
        couponUser.setCouponId(coupon.getCouponId());
        couponUser.setStatus(1);
        couponUser.setReceiveTime(nowTime);
        // 生效时间类型为固定时间
        if (coupon.getValidTimeType() == 1) {
            couponUser.setUserStartTime(coupon.getStartTime());
            couponUser.setUserEndTime(coupon.getEndTime());
        }
        // 生效时间类型为领取后生效
        if (coupon.getValidTimeType() == 2) {
            if (coupon.getAfterReceiveDays() == null) {
                coupon.setAfterReceiveDays(0);
            }
            if (coupon.getValidDays() == null) {
                coupon.setValidDays(0);
            }
            couponUser.setUserStartTime(DateUtils.addDays(DateUtil.beginOfDay(nowTime), coupon.getAfterReceiveDays()));
            couponUser.setUserEndTime(DateUtils.addDays(couponUser.getUserStartTime(), coupon.getValidDays() + 1));
        }
        couponUser.setIsDelete(0);
        return couponUser;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void changeCoupon(Date now) {
        // 设置店铺的过期优惠券为过期状态
        couponMapper.updateOverdueStatusByTime(now);
        // 设置店铺库存为0优惠券的状态(设为未投放状态) 没库存就消失么？
        // 像京东会有没库存的优惠券，一直展示大半天，也不消失，所以消失是用户手动下线的
//        couponMapper.updatePutonStatusByStocks();
    }

    @Override
    public void putonCoupon(Date now) {
        couponMapper.putonCoupon(now);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteByCouponId(Long couponId) {
        // 获取未被删除的优惠券数量
        Integer useCount = couponUserMapper.selectCount(new LambdaQueryWrapper<CouponUser>().eq(CouponUser::getCouponId, couponId).eq(CouponUser::getIsDelete, 0));
        if (useCount > 0) {
            throw new GlobalException("该优惠券已被领取，删除失败");
        }

        Coupon coupon = couponMapper.selectById(couponId);
        if (Objects.equals(coupon.getPutonStatus(), 1)) {
            throw new GlobalException("该优惠券已投放，删除失败");
        }
        //删除优惠券关联的商品
        couponProdMapper.delete(new LambdaQueryWrapper<CouponProd>().eq(CouponProd::getCouponId, couponId));
        //删除优惠券
        couponMapper.deleteById(couponId);
    }

    @Override
    public List<CouponDto> generalCouponList(String userId) {
        return couponMapper.generalCouponList(userId);
    }

    @Override
    public IPage<CouponDto> getCouponListByStatus(IPage<CouponDto> page, String userId, Integer status) {
        IPage<CouponDto> couponDtoIPage = couponMapper.getCouponListByStatus(page, userId, status);
        List<CouponDto> couponDtoList = couponDtoIPage.getRecords();
        for (CouponDto couponDto : couponDtoList) {
            if (Objects.equals(couponDto.getValidTimeType(), 2)) {
                Integer afterReceiveDays = couponDto.getAfterReceiveDays();
                if (afterReceiveDays == null) {
                    afterReceiveDays = 0;
                }
                Integer validDays = couponDto.getValidDays();
                if (validDays == null) {
                    validDays = 0;
                }
                couponDto.setStartTime(DateUtil.offsetDay(couponDto.getReceiveTime(), afterReceiveDays));
                couponDto.setEndTime(DateUtil.offsetDay(couponDto.getStartTime(), validDays));
            }
        }
        return couponDtoIPage;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(cacheNames = "CouponAndCouponUser", key = "#userId")
    public void deleteUserCouponByCouponId(String userId, Long couponId) {
        couponUserMapper.deleteUserCoupon(userId, couponId);
    }

    @Override
    public List<CouponOrderDto> getCouponListByShopIds(String userId, Long shopId) {
        List<CouponOrderDto> couponOrderDtoList = couponMapper.getCouponListByShopIds(userId, shopId);
        for (CouponOrderDto couponOrderDto : couponOrderDtoList) {
            if (Objects.equals(couponOrderDto.getValidTimeType(), 2)) {
                Integer validDays = couponOrderDto.getValidDays();
                if (validDays == null) {
                    validDays = 0;
                }
                Integer afterReceiveDays = couponOrderDto.getAfterReceiveDays();
                if (afterReceiveDays == null) {
                    afterReceiveDays = 0;
                }
                couponOrderDto.setStartTime(DateUtil.offsetDay(couponOrderDto.getReceiveTime(), afterReceiveDays));
                couponOrderDto.setEndTime(DateUtil.offsetDay(couponOrderDto.getStartTime(), validDays));
            }
        }
        return couponOrderDtoList;
    }

    @Override
    @Cacheable(cacheNames = "couponAndCouponProds", key = "#shopId")
    public List<Coupon> listPutonByShopId(Long shopId) {
        return couponMapper.listPutonCouponAndCouponProdsByShopId(shopId);
    }

    @Override
    public List<Long> listCouponIdsByUserId(String userId) {
        return couponMapper.listCouponIdsByUserId(userId);
    }

    @Override
    public IPage<ProductDto> prodListByCoupon(PageParam<ProductDto> page, Coupon coupon) {
        // 优惠券所有商品可用、指定商品可用、指定商品不可用 (0全部商品参与 1指定商品参与 2指定商品不参与)
        IPage<ProductDto> productDtoIPage;
        if (coupon.getSuitableProdType() == 0) {
            productDtoIPage = productMapper.listByShopId(page, coupon.getShopId());
        } else {
            productDtoIPage = productMapper.listBySuitableProdTypeAndCouponIdAndShopId(page, coupon.getShopId(), coupon.getSuitableProdType(), coupon.getCouponId());
        }
        return productDtoIPage;
    }

    @Override
    public Map<String, Long> getCouponCountByStatus(String userId) {
        return couponMapper.getCouponCountByStatus(userId);
    }

    @Override
    public IPage<Coupon> getPlatformPage(PageParam<Coupon> page, Coupon coupon) {
        return couponMapper.getPlatformPage(page, coupon);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(cacheNames = "couponAndCouponProds", key = "#coupon.shopId")
    public void offline(Coupon coupon, String offlineReason, Long sysUserId) {
        // 添加下线处理记录
        Date now = new Date();
        OfflineHandleEvent offlineHandleEvent = new OfflineHandleEvent();
        offlineHandleEvent.setHandleId(coupon.getCouponId());
        offlineHandleEvent.setHandleType(OfflineHandleEventType.COUPON.getValue());
        offlineHandleEvent.setCreateTime(now);
        offlineHandleEvent.setOfflineReason(offlineReason);
        offlineHandleEvent.setHandlerId(sysUserId);
        offlineHandleEvent.setShopId(coupon.getShopId());
        offlineHandleEvent.setStatus(OfflineHandleEventStatus.OFFLINE_BY_PLATFORM.getValue());
        offlineHandleEvent.setUpdateTime(now);
        offlineHandleEventService.save(offlineHandleEvent);

        // 更新活动状态为下线
        couponMapper.updatePutOnStatusByCouponId(coupon.getCouponId(), CouponStatusEnum.OFFLINE.getValue());
        //更新优惠券过期状态为过期
        couponMapper.updateOverdueStatusByCouponId(coupon.getCouponId(), 0);


    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void auditCoupon(OfflineHandleEventAuditParam offlineHandleEventAuditParam, Long sysUserId) {
        // 审核通过
        if (Objects.equals(offlineHandleEventAuditParam.getStatus(), OfflineHandleEventStatus.AGREE_BY_PLATFORM.getValue())) {
            // 更新优惠券为等待投放状态
            couponMapper.updatePutOnStatusByCouponId(offlineHandleEventAuditParam.getHandleId(), CouponStatusEnum.WAIT_PUT_ON.getValue());
            //更新优惠券过期状态为未过期
            couponMapper.updateOverdueStatusByCouponId(offlineHandleEventAuditParam.getHandleId(), 1);
        }
        // 审核不通过
        else if (Objects.equals(offlineHandleEventAuditParam.getStatus(), OfflineHandleEventStatus.DISAGREE_BY_PLATFORM.getValue())) {
            couponMapper.updatePutOnStatusByCouponId(offlineHandleEventAuditParam.getHandleId(), CouponStatusEnum.OFFLINE.getValue());
        }
        offlineHandleEventService.auditOfflineEvent(offlineHandleEventAuditParam, sysUserId);

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void auditApply(Long eventId, Long couponId, String reapplyReason) {
        // 更新活动为待审核状态
        couponMapper.updatePutOnStatusByCouponId(couponId, CouponStatusEnum.WAIT_AUDIT.getValue());

        // 更新事件状态
        offlineHandleEventService.updateToApply(eventId, reapplyReason);
    }

}
