package com.yonge.cooleshow.biz.dal.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.beust.jcommander.internal.Lists;
import com.microsvc.toolkit.common.webportal.exception.BizException;
import com.yonge.cooleshow.auth.api.entity.SysUser;
import com.yonge.cooleshow.biz.dal.dao.MemberPriceSettingsDao;
import com.yonge.cooleshow.biz.dal.dto.search.VipRecordSearch;
import com.yonge.cooleshow.biz.dal.entity.ActivityReward;
import com.yonge.cooleshow.biz.dal.entity.MemberPriceSettings;
import com.yonge.cooleshow.biz.dal.entity.Student;
import com.yonge.cooleshow.biz.dal.enums.*;
import com.yonge.cooleshow.biz.dal.service.*;
import com.yonge.cooleshow.biz.dal.vo.*;
import com.yonge.cooleshow.biz.dal.wrapper.VipCardRecordWrapper;
import com.yonge.toolset.base.page.PageInfo;
import com.yonge.toolset.mybatis.support.PageUtil;
import com.yonge.toolset.thirdparty.message.MessageSenderPluginContext;
import com.yonge.toolset.utils.date.DateUtil;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.yonge.cooleshow.biz.dal.entity.VipCardRecord;
import com.yonge.cooleshow.biz.dal.dto.search.VipCardRecordSearch;
import com.yonge.cooleshow.biz.dal.dao.VipCardRecordDao;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;


@Service
public class VipCardRecordServiceImpl extends ServiceImpl<VipCardRecordDao, VipCardRecord> implements VipCardRecordService {
    private final static Logger log = LoggerFactory.getLogger(VipCardRecordServiceImpl.class);

    @Autowired
    private SysMessageService sysMessageService;
    @Autowired
    private MemberPriceSettingsDao memberPriceSettingsDao;
    @Autowired
    private StudentService studentService;


    @Autowired
    private TeacherService teacherService;

    @Autowired
    private MemberPriceSettingsService memberPriceSettingsService;
    @Autowired
    private UserOrderDetailService orderDetailService;

    @Autowired
    private SysUserService sysUserService;


    @Override
    public VipCardRecordVo detail(Long orderDetilId) {
        return baseMapper.detail(orderDetilId, null);
    }

    @Override
    public VipCardRecordVo detail(Long orderDetilId, Long userId) {
        VipCardRecordVo detail = baseMapper.detail(orderDetilId, userId);
        if (null == detail && null != userId) {
            detail = new VipCardRecordVo();

            UserOrderDetailVo orderDetailVo = orderDetailService.detail(orderDetilId);
            VipCardRecord vipCardRecord = buildVipCardRecordByOrderDetail(orderDetailVo);
            if (null == vipCardRecord) {
                return detail;
            }
            BeanUtils.copyProperties(vipCardRecord, detail);
            MemberPriceSettings memberPrice = memberPriceSettingsDao.getMemberPriceByOrderDetilId(orderDetilId);
            detail.setOriginalPrice(memberPrice.getOriginalPrice());
            detail.setSalePrice(memberPrice.getSalePrice());
            detail.setPeriod(memberPrice.getPeriod());
        }
        return detail;
    }

    @Override
    public VipCardRecord buildVipCardRecordByOrderDetail(UserOrderDetailVo orderDetailVo) {
        if (null == orderDetailVo) {
            return null;
        }
        MemberPriceSettingsVo detail = memberPriceSettingsService.detail(orderDetailVo.getBizId());
        if (null == detail) {
            return null;
        }
        // 按时间区分 个数
        Integer timeNum = orderDetailVo.getGoodNum();
        String periodType  = null;

        if (PeriodEnum.DAY.equals(detail.getPeriod())) {
            periodType = PeriodEnum.DAY.getCode();
        } else if (PeriodEnum.MONTH.equals(detail.getPeriod())) {
            periodType = PeriodEnum.MONTH.getCode();
        } else if (PeriodEnum.QUARTERLY.equals(detail.getPeriod())) {
            timeNum = timeNum*3;
            periodType = PeriodEnum.MONTH.getCode();
        } else if (PeriodEnum.YEAR_HALF.equals(detail.getPeriod())) {
            timeNum = timeNum*6;
            periodType = PeriodEnum.MONTH.getCode();
        } else if (PeriodEnum.YEAR.equals(detail.getPeriod())) {
            periodType = PeriodEnum.YEAR.getCode();
        } else {
            periodType = PeriodEnum.PERPETUAL.getCode();

        }

        return getVipCardRecord(orderDetailVo.getUserId(),orderDetailVo.getOrderClient(),orderDetailVo.getOrderNo(),
                                orderDetailVo.getSubOrderNo(),periodType,detail.getId(),timeNum,SourceTypeEnum.ORDER,orderDetailVo.getUserId(),"会员购买");
    }

    @Override
    public  VipCardRecord buildVipCardRecordByOrderDetail(Long userId,ClientEnum client, ActivityReward activityReward,Long activityId,String activityName) {

        return getVipCardRecord(userId,client,activityId.toString(),null,activityReward.getUnit().toString(),
                                null,activityReward.getNum(),SourceTypeEnum.ACTIVITY,userId,activityName);
    }

    @Override
    public VipCardRecord getVipCardRecord(Long userId, ClientEnum client, String orderNo, String subOrderNo, String periodType,
                                          Long memberPriceSettingsId, Integer timeNum, SourceTypeEnum sourceType, Long createBy, String reason) {

        VipCardRecord vipCardRecord = new VipCardRecord();
        vipCardRecord.setUserId(userId);
        vipCardRecord.setOrderNo(orderNo);
        vipCardRecord.setSubOrderNo(subOrderNo);
        vipCardRecord.setVipCardId(memberPriceSettingsId);
        vipCardRecord.setMsgStatus(0);
        vipCardRecord.setSourceType(sourceType);
        vipCardRecord.setClientType(client);
        vipCardRecord.setTimes(timeNum);
        vipCardRecord.setType(PeriodEnum.valueOf(periodType));
        vipCardRecord.setCreateBy(createBy);
        vipCardRecord.setReason(reason);

        MemberPriceSettings memberPriceSettings = memberPriceSettingsDao.selectById(memberPriceSettingsId);
        if (memberPriceSettings == null) {
            return null;
        }

        VipCardRecordWrapper.UserVip userVip = userVipInfo(userId, client);
        boolean flag= false;

        if (userVip.getVipType() == EVipType.NOT_VIP) {
            vipCardRecord.setStartTime(new Date());
        } else if (userVip.getVipType() == EVipType.VIP && memberPriceSettings.getVipType() ==EVipType.VIP){
            vipCardRecord.setStartTime(userVip.getVipEndDate());
            flag= true;
        } else if  (userVip.getVipType() == EVipType.VIP && memberPriceSettings.getVipType() ==EVipType.SVIP){
            vipCardRecord.setStartTime(new Date());
        }else if  (userVip.getVipType() == EVipType.SVIP && memberPriceSettings.getVipType() ==EVipType.VIP){
            if (userVip.getVipEndDate() !=null) {
                flag= true;
                vipCardRecord.setStartTime(userVip.getVipEndDate());
            } else {
                flag= true;
                vipCardRecord.setStartTime(userVip.getSvipEndDate());
            }
        }else if  (userVip.getVipType() == EVipType.SVIP && memberPriceSettings.getVipType() ==EVipType.SVIP){
            flag= true;
            vipCardRecord.setStartTime(userVip.getSvipEndDate());
        }
        Calendar cal = Calendar.getInstance();
        cal.setTime(vipCardRecord.getStartTime());

        if (PeriodEnum.DAY.getCode().equals(periodType)) {
            cal.add(Calendar.DAY_OF_MONTH, timeNum);
        } else if (PeriodEnum.MONTH.getCode().equals(periodType)) {
            cal.add(Calendar.MONTH, timeNum);
        } else if (PeriodEnum.YEAR.getCode().equals(periodType)) {
            cal.add(Calendar.YEAR, timeNum);
        }

        vipCardRecord.setEndTime(cal.getTime());
        if (flag) {
            DateTime dateTime = DateTime.parse(DateUtil.format(vipCardRecord.getStartTime(), "yyyy-MM-dd"))
                .plusDays(1)
                .withHourOfDay(0)
                .withMinuteOfHour(0)
                .withSecondOfMinute(0)
                .withMillisOfSecond(0);
            vipCardRecord.setStartTime(dateTime.toDate());
        }

        return vipCardRecord;
    }

    @Override
    public IPage<VipCardRecordVo> selectPage(IPage<VipCardRecordVo> page, VipCardRecordSearch query) {
        return page.setRecords(baseMapper.selectPage(page, query));
    }

    @Override
    public void pollExpireMsg() {
        //提前三天提示
        List<VipCardRecordVo> temporaryRecords = baseMapper.selectTemporaryRecord();
        //标识
        Set<String> sendFlag = new HashSet<>();
        for (VipCardRecordVo record : temporaryRecords) {
            String key = record.getUserId() + "_" + record.getVipType().getCode() + "_" + record.getClientType().getCode();
            if (sendFlag.contains(key)) {
                continue;
            }
            sendFlag.add(key);
            //发送消息
            CompletableFuture.runAsync(() -> {
                temporary3DaysSend(record.getUserId(), record.getPhone(), record.getClientType(), record.getVipType());
            });
            record.setMsgStatus(1);
            record.setUpdateTime(new Date());

            VipCardRecord vipCardRecord = new VipCardRecord();
            BeanUtils.copyProperties(record, vipCardRecord);
            baseMapper.updateById(vipCardRecord);
        }

        //到期提示
        List<VipCardRecordVo> expireRecords = baseMapper.selectExpireRecord();
        //标识
        Set<String> expireSendFlag = new HashSet<>();
        for (VipCardRecordVo record : expireRecords) {
            String key = record.getUserId() + "_" + record.getVipType().getCode() + "_" + record.getClientType().getCode();
            if (expireSendFlag.contains(key)) {
                continue;
            }
            expireSendFlag.add(key);
            //发送消息
            //发送消息
            CompletableFuture.runAsync(() -> {
                expireSend(record.getUserId(),record.getPhone(), record.getClientType(), record.getVipType());
            });
            record.setMsgStatus(2);
            record.setUpdateTime(new Date());

            VipCardRecord vipCardRecord = new VipCardRecord();
            BeanUtils.copyProperties(record, vipCardRecord);
            baseMapper.updateById(vipCardRecord);
        }

    }

    @Override
    public PageInfo<VipRecordVo> vipRecord(VipRecordSearch recordSearch) {
        return PageUtil.pageInfo(baseMapper.selectVipRecord(PageUtil.getPage(recordSearch),recordSearch));


    }

    /**
     * 获取生效中的会员记录
     * @param userId
     * @param clientEnum
     * @return
     */
    @Override
    public List<VipCardRecord> getEfficientVipRecord(List<Long> userId, ClientEnum clientEnum) {
        if (CollectionUtils.isEmpty(userId)) {
            return new ArrayList<>();
        }
        return this.lambdaQuery()
            .in(VipCardRecord::getUserId, userId)
            .eq(VipCardRecord::getEfficientFlag, true)
            .eq(VipCardRecord::getClientType, clientEnum.name())
            .in(VipCardRecord::getVipType, Lists.newArrayList(EVipType.VIP,EVipType.SVIP))
            .and(wrapper -> wrapper
                .gt(VipCardRecord::getEndTime, new Date())
            )
            .list();
    }

    @Override
    public VipCardRecordWrapper.UserVip UserVipInfo(Long userId, ClientEnum clientEnum) {
        return userVipInfo(userId,clientEnum);
    }
    /**
     * 用户会员信息
     *
     * @param userId
     * @param clientEnum
     * @return
     */
    @Override
    public VipCardRecordWrapper.UserVip userVipInfo(Long userId, ClientEnum clientEnum) {
        // 获取生效中的会员记录
        VipCardRecordWrapper.UserVip userVip = new VipCardRecordWrapper.UserVip();
        List<VipCardRecord> vipCardRecords = this.getEfficientVipRecord(Lists.newArrayList(userId),clientEnum);
        if (CollectionUtils.isEmpty(vipCardRecords)) {
            userVip.setVipType(EVipType.NOT_VIP);
            // 判断有没有过期的会员类型
            Integer vipCount = this.lambdaQuery()
                .eq(VipCardRecord::getUserId, userId)
                .eq(VipCardRecord::getEfficientFlag, true)
                .eq(VipCardRecord::getVipType, EVipType.VIP)
                .eq(VipCardRecord::getClientType, clientEnum.name())
                .count();
            Integer svipCount = this.lambdaQuery()
                .eq(VipCardRecord::getUserId, userId)
                .eq(VipCardRecord::getEfficientFlag, true)
                .eq(VipCardRecord::getVipType, EVipType.SVIP)
                .eq(VipCardRecord::getClientType, clientEnum.name())
                .count();
            if (vipCount>0&&svipCount>0) {
                userVip.setExpireVipType(EVipType.ALL_VIP);
            } else if (vipCount>0) {
                userVip.setExpireVipType(EVipType.VIP);
            } else if (svipCount>0) {
                userVip.setExpireVipType(EVipType.SVIP);
            }
        } else {
            // 存在没有结束时间的SVIP记录 永久SVIP
            List<VipCardRecord> svipList = vipCardRecords.stream().filter(o -> o.getVipType() == EVipType.SVIP).collect(Collectors.toList());
            Date svipEndDate = null;
            if (CollectionUtils.isNotEmpty(svipList)) {
                userVip.setVipType(EVipType.SVIP);

                Optional<VipCardRecord> first = svipList.stream().filter(o ->o.getType()==PeriodEnum.PERPETUAL).findFirst();
                if (first.isPresent()) {
                    userVip.setVipType(EVipType.PERMANENT_SVIP);
                }
//                else {
                Optional<VipCardRecord> max = svipList.stream().filter(o -> !PeriodEnum.PERPETUAL.equals(o.getType())).max(Comparator.comparing(VipCardRecord::getEndTime));
                max.ifPresent(vipCardRecord -> userVip.setSvipEndDate(vipCardRecord.getEndTime()));
//                }
                Optional<VipCardRecord> max1 = svipList.stream().max(Comparator.comparing(VipCardRecord::getEndTime));
                if (max1.isPresent()) {
                    svipEndDate = max1.get().getEndTime();
                }
            } else {
                Integer svipCount = this.lambdaQuery()
                    .eq(VipCardRecord::getUserId, userId)
                    .eq(VipCardRecord::getEfficientFlag, true)
                    .eq(VipCardRecord::getVipType, EVipType.SVIP)
                    .eq(VipCardRecord::getClientType, clientEnum.name())
                    .count();
                if (svipCount>0) {
                    userVip.setExpireVipType(EVipType.SVIP);

                }
            }
            // 过滤出VIP数据
            List<VipCardRecord> vipList = vipCardRecords.stream().filter(o -> o.getVipType() == EVipType.VIP).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(vipList)) {
                if (userVip.getVipType() == null) {
                    userVip.setVipType(EVipType.VIP);
                }
                Optional<VipCardRecord> max = vipList.stream().max(Comparator.comparing(VipCardRecord::getEndTime));
                Optional<VipCardRecord> min = vipList.stream().min(Comparator.comparing(VipCardRecord::getStartTime));
                max.ifPresent(vipCardRecord -> userVip.setVipEndDate(vipCardRecord.getEndTime()));
                Date minDate = null;
                if (min.isPresent()) {
                    minDate = min.get().getStartTime();
                }
                // 设置VIP剩余天数
                if (userVip.getVipEndDate() != null) {
                    int num;
                    if (svipEndDate != null && svipEndDate.after(new Date())) {
                        num = DateUtil.daysBetween(svipEndDate, userVip.getVipEndDate());
                    } else if (minDate != null && minDate.after(new Date())){
                        num = DateUtil.daysBetween(minDate, userVip.getVipEndDate());
                    } else {
                        num = DateUtil.daysBetween(new Date(), userVip.getVipEndDate()) +1;
                    }
                    userVip.setVipEndDays(Math.max(num, 0));
                }
            } else {
                Integer vipCount = this.lambdaQuery()
                    .eq(VipCardRecord::getUserId, userId)
                    .eq(VipCardRecord::getEfficientFlag, true)
                    .eq(VipCardRecord::getVipType, EVipType.VIP)
                    .eq(VipCardRecord::getClientType, clientEnum.name())
                    .count();
                if (vipCount>0) {
                    userVip.setExpireVipType(EVipType.VIP);
                }
            }
        }

        if (userVip.getSvipEndDate() != null) {
            int num = DateUtil.daysBetween(new Date(), userVip.getSvipEndDate()) +1;
            userVip.setSvipEndDays(Math.max(num, 0));
        }

        // 设置会员过期时间
        EVipType expireVipType = userVip.getExpireVipType();
        List<VipCardRecordWrapper.UserVipInfo> userVipInfos = this.baseMapper.queryUserVipInfo(Collections.singletonList(userId), clientEnum.getCode());
        if(!userVipInfos.isEmpty()){
            VipCardRecordWrapper.UserVipInfo userVipInfo = userVipInfos.get(0);
            Date date = new Date();
            if (EVipType.VIP.equals(expireVipType) || EVipType.ALL_VIP.equals(expireVipType)) {
                Date vipEndTime = userVipInfo.getVipEndTime();
                if (vipEndTime != null && vipEndTime.before(date)) {
                    double days = Math.ceil((date.getTime() - vipEndTime.getTime()) * 1.0D / (24 * 60 * 60 * 1000));
                    userVip.setVipExpireDays((int) days);
                }
            }
            if (EVipType.SVIP.equals(expireVipType) || EVipType.ALL_VIP.equals(expireVipType)) {
                Date svipEndTime = userVipInfo.getSvipEndTime();
                if (svipEndTime != null && svipEndTime.before(date)) {
                    double days = Math.ceil((date.getTime() - svipEndTime.getTime()) * 1.0D / (24 * 60 * 60 * 1000));
                    userVip.setSvipExpireDays((int) days);
                }
            }
        }
        return userVip;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void add(VipCardRecordWrapper.AddVipCardRecord addVipCardRecord) {
        ClientEnum clientType = addVipCardRecord.getClientType();
        if (!ClientEnum.STUDENT.equals(clientType) && !ClientEnum.TEACHER.equals(clientType)) {
            throw new BizException("暂不支持非老师学生会员的增加或扣减");
        }
        if (EVipRecordStatus.DEDUCTION.equals(addVipCardRecord.getStatus())) {
            VipCardRecord vipCardRecord = deductVip(addVipCardRecord);
            vipCardRecord.setDisplayFlag(true);
            this.save(vipCardRecord);
        } else if (EVipRecordStatus.ADD.equals(addVipCardRecord.getStatus())) {
            VipCardRecord addVip = addVip(addVipCardRecord);
            this.save(addVip);
            Integer vipDays = addVipCardRecord.getVipDays();
            // 存在转换，先扣除，再添加
            if (vipDays != null && vipDays > 0) {
                VipCardRecordWrapper.AddVipCardRecord dedectRecord = JSON.parseObject(JSON.toJSONString(addVipCardRecord), VipCardRecordWrapper.AddVipCardRecord.class);
                dedectRecord.setStatus(EVipRecordStatus.DEDUCTION);
                dedectRecord.setVipType(EVipType.VIP);
                dedectRecord.setType(PeriodEnum.DAY);
                dedectRecord.setTimes(vipDays);
                dedectRecord.setReason("后台转换扣减");
                deductVip(dedectRecord);

                VipCardRecordWrapper.AddVipCardRecord addRecord = JSON.parseObject(JSON.toJSONString(addVipCardRecord), VipCardRecordWrapper.AddVipCardRecord.class);
                addRecord.setStatus(EVipRecordStatus.ADD);
                addRecord.setVipType(EVipType.SVIP);
                addRecord.setType(PeriodEnum.DAY);
                addRecord.setTimes(vipDays);
                addRecord.setReason("购买SVIP升级原VIP");
                VipCardRecord convertVip = addVip(addRecord);

                convertVip.setDisplayFlag(true);
                convertVip.setStatus(EVipRecordStatus.UPDATE);
                convertVip.setSourceType(SourceTypeEnum.FREE_UPGRADE);
                // 添加转换记录
                this.save(convertVip);

            }
        }

        if (Boolean.TRUE.equals(addVipCardRecord.getSendMsg())) {
            sendAddVipMsg(addVipCardRecord);
        }
    }

    @Override
    public Map<Long, EVipType> getVipTypeMapByUserIds(List<Long> studentIds, ClientEnum client) {

        List<VipCardRecord> vipCardRecords = this.getEfficientVipRecord(studentIds,client);

        // 根据用户ID分组
        Map<Long, List<VipCardRecord>> vipCardRecordMap = vipCardRecords.stream().collect(Collectors.groupingBy(VipCardRecord::getUserId));
        Map<Long, EVipType> vipTypeMap = new HashMap<>();
        vipCardRecordMap.forEach((k, v) -> {
            List<VipCardRecord> svipList = v.stream().filter(o -> o.getVipType() == EVipType.SVIP).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(svipList)) {
                vipTypeMap.put(k, EVipType.SVIP);
            } else {
                List<VipCardRecord> vipList = v.stream().filter(o -> o.getVipType() == EVipType.VIP).collect(Collectors.toList());
                if (CollectionUtils.isNotEmpty(vipList)) {
                    vipTypeMap.put(k, EVipType.VIP);
                }
            }
        });
        return vipTypeMap;
    }

    // 会员添加
    @Override
    public VipCardRecord addVip(VipCardRecordWrapper.AddVipCardRecord addVipCardRecord) {
        List<VipCardRecord> vipCardRecordList = this.lambdaQuery()
                .eq(VipCardRecord::getClientType, addVipCardRecord.getClientType())
                .eq(VipCardRecord::getUserId, addVipCardRecord.getUserId())
                .ge(VipCardRecord::getEndTime, new Date())
                .eq(VipCardRecord::getEfficientFlag, true)
                .list()
                .stream()
//                .filter(n -> n.getEndTime() == null || n.getEndTime().after(new Date()))
                .sorted(Comparator.comparing(VipCardRecord::getStartTime))
                .collect(Collectors.toList());


        EVipType addVipType = addVipCardRecord.getVipType();
//        List<VipCardRecord> perpetualRecords = vipCardRecordList.stream()
//                .filter(n -> n.getVipType().equals(addVipType) && PeriodEnum.PERPETUAL.equals(n.getType()))
//                .collect(Collectors.toList());
//        if (!perpetualRecords.isEmpty()) {
//            throw new BizException("已经是永久会员");
//        }

        Date now = new Date();
        long startTimeMills = now.getTime();
        // 没有会员信息
        if (vipCardRecordList.isEmpty()) {
            PeriodEnum period = addVipCardRecord.getType();
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(now);
            calendar.set(Calendar.HOUR_OF_DAY, 23);
            calendar.set(Calendar.MINUTE, 59);
            calendar.set(Calendar.SECOND, 59);
            calendar.set(Calendar.MILLISECOND, 0);
            Date endDate = plusDate(calendar.getTime(), period, Long.valueOf(addVipCardRecord.getTimes()));

            VipCardRecord addRecord = JSON.parseObject(JSON.toJSONString(addVipCardRecord), VipCardRecord.class);
            addRecord.setSourceType(addVipCardRecord.getSourceType() !=null?addVipCardRecord.getSourceType():SourceTypeEnum.BACKEND_GIVE);
            addRecord.setStatus(EVipRecordStatus.ADD);
            addRecord.setDisplayFlag(true);
            addRecord.setEfficientFlag(true);
            addRecord.setStartTime(now);
            addRecord.setEndTime(endDate);
            return addRecord;
        }

        // 找到插入数据的位置
        int index = 0;
        if (addVipType.equals(EVipType.VIP)) { // vip 放到最后
            index = vipCardRecordList.size();
            startTimeMills = vipCardRecordList.stream().map(VipCardRecord::getEndTime).max(Date::compareTo).get().getTime() + 1000L;
        }
        if (addVipType.equals(EVipType.SVIP)) { //放到VIP的前面
            index = (int) vipCardRecordList.stream().filter(n -> EVipType.SVIP.equals(n.getVipType()) && !PeriodEnum.PERPETUAL.equals(n.getType())).count();
            if (index > 0) {
                startTimeMills = vipCardRecordList.stream().filter(n -> EVipType.SVIP.equals(n.getVipType()) && !PeriodEnum.PERPETUAL.equals(n.getType()))
                        .map(VipCardRecord::getEndTime).max(Date::compareTo).get().getTime() + 1000;
            }
        }

        VipCardRecord newRecord = JSON.parseObject(JSON.toJSONString(addVipCardRecord), VipCardRecord.class);
        newRecord.setSourceType(addVipCardRecord.getSourceType() !=null?addVipCardRecord.getSourceType():SourceTypeEnum.BACKEND_GIVE);
        newRecord.setStatus(EVipRecordStatus.ADD);
        newRecord.setStartTime(new Date(startTimeMills));
        if (index == 0) { // 当天剩余时间属于赠送
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(new Date(startTimeMills));
            calendar.set(Calendar.HOUR_OF_DAY, 23);
            calendar.set(Calendar.MINUTE, 59);
            calendar.set(Calendar.SECOND, 59);
            calendar.set(Calendar.MILLISECOND, 0);
            Date endDate = plusDate(calendar.getTime(), addVipCardRecord.getType(), Long.valueOf(addVipCardRecord.getTimes()));
            newRecord.setEndTime(new Date(endDate.getTime()));
        } else {
            Date endDate = plusDate(new Date(startTimeMills - 1000), addVipCardRecord.getType(), Long.valueOf(addVipCardRecord.getTimes()));
            newRecord.setEndTime(endDate);
        }

        newRecord.setDisplayFlag(true);
        newRecord.setEfficientFlag(true);

        // 平移时间
//        long plusMills = newRecord.getEndTime().getTime() - startTimeMills;
        long beforeEndTime = newRecord.getEndTime().getTime();
        for (int i = 0; i < vipCardRecordList.size(); i++) {
            VipCardRecord vipCardRecord = vipCardRecordList.get(i);
            if (i >= index) {
                VipCardRecord addRecord = JSON.parseObject(JSON.toJSONString(vipCardRecord), VipCardRecord.class);
                addRecord.setStatus(EVipRecordStatus.UPDATE);

                addRecord.setStartTime(new Date(beforeEndTime + 1000));

//                int days = (int) Math.ceil(plusMills * 1.0D / (24 * 60 * 60 * 1000));
                Date endTime = plusDate(vipCardRecord.getEndTime(), addVipCardRecord.getType(), addVipCardRecord.getTimes());
                addRecord.setEndTime(endTime);
                addRecord.setDisplayFlag(false);
                addRecord.setEfficientFlag(true);
                save(addRecord);
                Long refId = addRecord.getId();

                VipCardRecord updateRecord = JSON.parseObject(JSON.toJSONString(vipCardRecord), VipCardRecord.class);
                updateRecord.setEfficientFlag(false);
                updateRecord.setRefId(refId);
                updateById(updateRecord);
                beforeEndTime = endTime.getTime();
            }
        }
        return newRecord;
    }

    @Override
    public List<VipCardRecordWrapper.UserVipInfo> queryUserVipInfo(List<Long> userIdList, String clientType) {
        List<VipCardRecordWrapper.UserVipInfo> userVipInfos = baseMapper.queryUserVipInfo(userIdList, clientType);
        Set<Long> userIds = new HashSet<>();
        List<VipCardRecordWrapper.UserVipInfo> newUserVipInfos = new ArrayList<>();
        for (VipCardRecordWrapper.UserVipInfo userVipInfo : userVipInfos) {
            if (userIds.contains(userVipInfo.getUserId())) {
                continue;
            }
            newUserVipInfos.add(userVipInfo);
            userIds.add(userVipInfo.getUserId());
        }
        return newUserVipInfos;
    }


    // 会员扣减
    private VipCardRecord deductVip(VipCardRecordWrapper.AddVipCardRecord addVipCardRecord) {

        // 所有的会员重新生成
        List<VipCardRecord> vipCardRecordList = this.lambdaQuery()
                .eq(VipCardRecord::getClientType, addVipCardRecord.getClientType())
                .eq(VipCardRecord::getUserId, addVipCardRecord.getUserId())
                .ge(VipCardRecord::getEndTime, new Date())
                .eq(VipCardRecord::getEfficientFlag, true)
                .list()
                .stream()
                .sorted(Comparator.comparing(VipCardRecord::getStartTime))
                .collect(Collectors.toList());

        // 当前类型的VIP
        PeriodEnum period = addVipCardRecord.getType();
        // 扣减永久
        if (PeriodEnum.PERPETUAL.equals(period)) {
            addVipCardRecord.setTimes(1);
            return deductedSVipPerpetual(addVipCardRecord, vipCardRecordList);
        }

        // 扣减非永久
        List<VipCardRecord> collect = vipCardRecordList.stream()
                .filter(n -> n.getVipType().equals(addVipCardRecord.getVipType()) && !PeriodEnum.PERPETUAL.equals(n.getType())).collect(Collectors.toList());
        if (collect.isEmpty()) {
            throw new BizException("剩余扣减数量不足");
        }

        Date now = new Date();
        LocalDateTime maxEndTime = collect.stream().map(VipCardRecord::getEndTime).max(Comparator.naturalOrder()).get().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
        // 扣减后的开始时间
        LocalDateTime deductedStartTime;
        switch (period) {
            case DAY:
                deductedStartTime = maxEndTime.minusDays(addVipCardRecord.getTimes());
                break;
            case MONTH:
                deductedStartTime = maxEndTime.minusMonths(addVipCardRecord.getTimes());
                break;
            case QUARTERLY:
                deductedStartTime = maxEndTime.minusMonths(addVipCardRecord.getTimes() * 3L);
                break;
            case YEAR_HALF:
                deductedStartTime = maxEndTime.minusMonths(addVipCardRecord.getTimes() * 6L);
                break;
            case YEAR:
                deductedStartTime = maxEndTime.minusYears(addVipCardRecord.getTimes());
                break;
            default:
                throw new BizException("不支持的扣减类型");
        }
        Date deductedStartDate =  Date.from(deductedStartTime.atZone(ZoneId.systemDefault()).toInstant());;

        Date minStartTime = collect.stream().map(VipCardRecord::getStartTime).min(Comparator.naturalOrder()).get();
        // 如果扣减的数量超过1天，则提示扣减数量不足
        if (deductedStartDate.before(minStartTime)) {
            double day = (minStartTime.getTime() - deductedStartDate.getTime()) * 1.0D / (24 * 60 * 60 * 1000);
            if (day > 1.0D) {
                if (addVipCardRecord.isOrderFlag()) {
                    collect.stream().forEach(o->o.setEfficientFlag(false));

                    this.updateBatchById(collect, 100);
                    return null;
                }

                throw new BizException("剩余扣减数量不足");
            }
//            deductedStartDate = minStartTime;
        }

        // 重新计算会员时间,每一条记录置换成一条新的记录，时间区间重新计算
        List<VipCardRecord> updateRecords = new ArrayList<>();
        Long deductMills = null;
        Long endDeductMills = null;

        EVipType vipType = addVipCardRecord.getVipType();
        boolean giveFlag = false; // 扣减后，剩余时间前移，第一条记录赠送当天的时间
        for (VipCardRecord vipCardRecord : vipCardRecordList) {
            Date startTime = vipCardRecord.getStartTime();
            Date endTime = vipCardRecord.getEndTime();
            if (endTime.before(deductedStartDate) || endTime.equals(deductedStartDate)) {
                continue;
            }
            // 扣减vip,跳过SVIP
            if (EVipType.VIP.equals(vipType) && EVipType.SVIP.equals(vipCardRecord.getVipType())) {
                continue;
            }

            Long addId = null;
            // 扣减到当前时间区间
            if ((deductedStartDate.after(startTime) || deductedStartDate.equals(startTime)) && deductedStartDate.before(endTime)) {
                if (deductedStartDate.after(now)) { // 还有剩余天数,不足一天，补充到当前全天
                    VipCardRecord addRecord = JSON.parseObject(JSON.toJSONString(vipCardRecord), VipCardRecord.class);
                    addRecord.setId(null);
                    addRecord.setDisplayFlag(false);
                    addRecord.setEfficientFlag(true);
                    addRecord.setEndTime(formatEnd(deductedStartDate));
                    addRecord.setStatus(EVipRecordStatus.UPDATE);
                    save(addRecord);
                    addId = addRecord.getId();
                }


                deductMills = endTime.getTime() - deductedStartDate.getTime();
                endDeductMills = endTime.getTime() - formatEnd(deductedStartDate).getTime();
            } else {
                if (vipCardRecord.getVipType().equals(vipType) && !PeriodEnum.PERPETUAL.equals(vipCardRecord.getType())) {
                    deductMills = endTime.getTime() - deductedStartDate.getTime();
                    endDeductMills = endTime.getTime() - formatEnd(deductedStartDate).getTime();
                } else {
                    // 有扣减，整体时间前移
                    if (deductMills != null) {
                        VipCardRecord newRecord = JSON.parseObject(JSON.toJSONString(vipCardRecord), VipCardRecord.class);
                        newRecord.setId(null);
                        newRecord.setDisplayFlag(false);
                        newRecord.setEfficientFlag(true);
                        if (!giveFlag) {
                            newRecord.setStartTime(new Date(newRecord.getStartTime().getTime() - deductMills));
                            giveFlag = true;
                        } else {
                            newRecord.setStartTime(new Date(newRecord.getStartTime().getTime() - endDeductMills));
                        }
                        newRecord.setEndTime(new Date(newRecord.getEndTime().getTime() - endDeductMills));
                        newRecord.setStatus(EVipRecordStatus.UPDATE);
                        save(newRecord);
                        addId = newRecord.getId();
                    }
                }
            }

            VipCardRecord updateRecord = JSON.parseObject(JSON.toJSONString(vipCardRecord), VipCardRecord.class);
            updateRecord.setEfficientFlag(false);
            updateRecord.setRefId(addId);
            updateRecords.add(updateRecord);
        }

        this.updateBatchById(updateRecords, 100);

        VipCardRecord vipCardRecord = JSON.parseObject(JSON.toJSONString(addVipCardRecord), VipCardRecord.class);
        vipCardRecord.setEfficientFlag(false);
        vipCardRecord.setStartTime(deductedStartDate);
        vipCardRecord.setEndTime(Date.from(maxEndTime.atZone(ZoneId.systemDefault()).toInstant()));
        vipCardRecord.setStatus(EVipRecordStatus.DEDUCTION);
        return vipCardRecord;
    }

    // 扣减永久SVIP会员
    private VipCardRecord deductedSVipPerpetual(VipCardRecordWrapper.AddVipCardRecord addVipCardRecord, List<VipCardRecord> vipCardRecordList) {
        List<VipCardRecord> collect = vipCardRecordList.stream()
                .filter(n -> n.getVipType().equals(addVipCardRecord.getVipType()) && n.getType().equals(PeriodEnum.PERPETUAL))
                .collect(Collectors.toList());
        if (collect.isEmpty()) {
            throw new BizException("剩余扣减数量不足");
        }


        // 最后一个非永久SVIP的结束时间
        Date lastUnPereutalDate = new Date();
        Date lastEndUnPereutalDate = formatEnd(new Date());
        Long deductMills = null;
        Long endDeductMills = null;
        boolean giveFlag = false;
        for (VipCardRecord vipCardRecord : vipCardRecordList) {
            if (EVipType.SVIP.equals(vipCardRecord.getVipType())) {
                // 获取后续VIP向前平移的开始时间
                if (!PeriodEnum.PERPETUAL.equals(vipCardRecord.getType())) {
                    if (vipCardRecord.getEndTime().after(lastUnPereutalDate)) {
                        lastUnPereutalDate = vipCardRecord.getEndTime();
                        lastEndUnPereutalDate = vipCardRecord.getEndTime();
                    }
                    continue;
                }
                vipCardRecord.setEfficientFlag(false);
                updateById(vipCardRecord);
            } else {
                VipCardRecord newRecord = JSON.parseObject(JSON.toJSONString(vipCardRecord), VipCardRecord.class);
                newRecord.setId(null);
                newRecord.setDisplayFlag(false);
                newRecord.setEfficientFlag(true);
                if (vipCardRecord.getVipType().equals(addVipCardRecord.getVipType()) && !giveFlag) {
                    deductMills = vipCardRecord.getStartTime().getTime() - lastUnPereutalDate.getTime();
                    endDeductMills = vipCardRecord.getStartTime().getTime() - lastEndUnPereutalDate.getTime();
                    newRecord.setStartTime(new Date(newRecord.getStartTime().getTime() - deductMills));
                    giveFlag = true;
                } else {
                    endDeductMills = vipCardRecord.getStartTime().getTime() - (lastEndUnPereutalDate.getTime() +1000);
                    newRecord.setStartTime(new Date(newRecord.getStartTime().getTime() - endDeductMills));
                }
                newRecord.setEndTime(new Date(newRecord.getEndTime().getTime() - endDeductMills));
                lastEndUnPereutalDate = newRecord.getEndTime();
                save(newRecord);
                Long refId = newRecord.getId();

                VipCardRecord updateRecord = JSON.parseObject(JSON.toJSONString(vipCardRecord), VipCardRecord.class);
                updateRecord.setEfficientFlag(false);
                updateRecord.setRefId(refId);
                updateById(updateRecord);
            }
        }
        VipCardRecord vipCardRecord = JSON.parseObject(JSON.toJSONString(addVipCardRecord), VipCardRecord.class);
        vipCardRecord.setEfficientFlag(false);
        vipCardRecord.setStatus(EVipRecordStatus.DEDUCTION);
        return vipCardRecord;
    }

    private Date plusDate(Date start, PeriodEnum period, long times) {
//        Calendar calendar = Calendar.getInstance();
//        calendar.setTime(start);
//        calendar.set(Calendar.HOUR_OF_DAY, 23);
//        calendar.set(Calendar.MINUTE, 59);
//        calendar.set(Calendar.SECOND, 59);
//        calendar.set(Calendar.MILLISECOND, 0);
        LocalDateTime localDateTimeMaxTime =  LocalDateTime.ofInstant(start.toInstant(),ZoneId.systemDefault());

        LocalDateTime end;
        switch (period) {
            case DAY:
                end = localDateTimeMaxTime.plusDays(times);
                break;
            case MONTH:
                end = localDateTimeMaxTime.plusMonths(times);
                break;
            case QUARTERLY:
                end = localDateTimeMaxTime.plusMonths(times * 3);
                break;
            case YEAR_HALF:
                end = localDateTimeMaxTime.plusMonths(times * 6);
                break;
            case YEAR:
                end = localDateTimeMaxTime.plusYears(times);
                break;
            case PERPETUAL:
                // 永久默认给100年
                end = localDateTimeMaxTime.plusYears(100);
                break;
            default:
                throw new BizException("不支持的扣减类型");
        }
        return Date.from(end.atZone(ZoneId.systemDefault()).toInstant());
    }

    // 发送会员到期3天消息推送
    private void temporary3DaysSend(Long userId, String phone, ClientEnum clientType, EVipType vipType) {
        Map<Long, String> receivers = new HashMap<>();
        receivers.put(userId, phone);
        // 判断是否是机构学生 机构学生推送走另一个
        if (clientType.equals(ClientEnum.STUDENT)) {
            Student student = studentService.getById(userId);
            if (student == null || (student.getTenantId() != null && student.getTenantId() > 0 )) {
                return;
            }
        }
        try {
            sysMessageService.batchSendMessage(MessageSenderPluginContext.MessageSender.JIGUANG,
                    EVipType.VIP.equals(vipType) ? MessageTypeEnum.VIP_EXPIRE_THIRTY_DAY : MessageTypeEnum.SVIP_EXPIRE_THIRTY_DAY
                    , receivers, null, 0, null, clientType.getCode());
        } catch (Exception e) {
            log.error("会员到期3天极光消息推送异常,userId={}", userId);
        }

        try {
            sysMessageService.batchSendMessage(MessageSenderPluginContext.MessageSender.AWSMS,
                    EVipType.VIP.equals(vipType) ? MessageTypeEnum.SMS_VIP_EXPIRE_THIRTY_DAY : MessageTypeEnum.SMS_SVIP_EXPIRE_THIRTY_DAY
                    , receivers, null, 0, null, clientType.getCode());
        } catch (Exception e) {
            log.error("会员到期3天短信消息推送异常,userId={}", userId);
        }
    }

    // 发送会员到期消息推送
    private void expireSend(Long userId,String phone,ClientEnum userType, EVipType vipType) {
        Map<Long, String> receivers = new HashMap<>();
        receivers.put(userId, phone);
        Student student = studentService.getById(userId);
        if (userType.equals(ClientEnum.STUDENT) && student != null  && student.getTenantId() != null && student.getTenantId() >0) {
//            try {
//                sysMessageService.batchSendMessage(MessageSenderPluginContext.MessageSender.JIGUANG, MessageTypeEnum.TENANT_VIP_EXPIRE,
//                        receivers, null, 0, null, ClientEnum.TENANT_STUDENT.getCode());
//            } catch (Exception e) {
//                log.error("会员到期极光消息推送异常,userId={}", userId);
//            }
        } else {
            try {
                sysMessageService.batchSendMessage(MessageSenderPluginContext.MessageSender.JIGUANG,
                        EVipType.VIP.equals(vipType) ? MessageTypeEnum.VIP_EXPIRE : MessageTypeEnum.SVIP_EXPIRE
                        ,receivers, null, 0, null, userType.getCode());
            } catch (Exception e) {
                log.error("会员到期3天极光消息推送异常,userId={}", userId);
            }

            try {
                sysMessageService.batchSendMessage(MessageSenderPluginContext.MessageSender.AWSMS,
                        EVipType.VIP.equals(vipType) ? MessageTypeEnum.SMS_VIP_EXPIRE : MessageTypeEnum.SMS_SVIP_EXPIRE,
                        receivers, null, 0, null, userType.getCode());
            } catch (Exception e) {
                log.error("会员到期3天短信消息推送异常,userId={}", userId);
            }
        }
    }

    // 添加、扣除会员发送推送
    private void sendAddVipMsg(VipCardRecordWrapper.AddVipCardRecord addVipCardRecord) {
        SysUser sysUser = sysUserService.getByUserId(addVipCardRecord.getUserId());
        if (sysUser == null) {
            return;
        }
        MessageTypeEnum messageTypeEnum;
        EVipRecordStatus status = addVipCardRecord.getStatus();
        EVipType vipType = addVipCardRecord.getVipType();
        if (EVipRecordStatus.ADD.equals(status)) {
            if (EVipType.VIP.equals(vipType)) {
                messageTypeEnum = MessageTypeEnum.PLATFORM_ADD_VIP;
            }else {
                PeriodEnum period = addVipCardRecord.getType();
                if (PeriodEnum.PERPETUAL.equals(period)) {
                    messageTypeEnum = MessageTypeEnum.PLATFORM_ADD_PER_SVIP;
                } else {
                    messageTypeEnum = MessageTypeEnum.PLATFORM_ADD_SVIP;
                }
            }
        } else if (EVipRecordStatus.DEDUCTION.equals(status)) {
            if (EVipType.VIP.equals(vipType)) {
                messageTypeEnum = MessageTypeEnum.PLATFORM_ADD_DUDECT_VIP;
            }else {
                PeriodEnum period = addVipCardRecord.getType();
                if (PeriodEnum.PERPETUAL.equals(period)) {
                    messageTypeEnum = MessageTypeEnum.PLATFORM_ADD_DUDECT_PER_SVIP;
                } else {
                    messageTypeEnum = MessageTypeEnum.PLATFORM_ADD_DUDECT_SVIP;
                }
            }
        } else {
            throw new BizException("不支持类型");
        }

        Map<Long, String> receivers = new HashMap<>();
        receivers.put(addVipCardRecord.getUserId(), sysUser.getPhone());

        try {
            sysMessageService.batchSendMessage(MessageSenderPluginContext.MessageSender.JIGUANG, messageTypeEnum,
                    receivers, null, 0, null, addVipCardRecord.getClientType().getCode(), String.valueOf(addVipCardRecord.getTimes()), addVipCardRecord.getType().getMsg(),
                    addVipCardRecord.getReason());
        } catch (Exception e) {
            log.error("会员添加消息发送失败 ： {}", e.getMessage());
        }
    }

    private Date formatEnd(Date date) {
        Calendar c1 = Calendar.getInstance();
        c1.setTime(date);
        c1.set(Calendar.HOUR_OF_DAY, 23);
        c1.set(Calendar.MINUTE, 59);
        c1.set(Calendar.SECOND, 59);
        return c1.getTime();
    }

    private Date formatStart(Date date) {
        Calendar c1 = Calendar.getInstance();
        c1.setTime(date);
        c1.set(Calendar.HOUR_OF_DAY, 0);
        c1.set(Calendar.MINUTE, 0);
        c1.set(Calendar.SECOND, 0);
        c1.set(Calendar.MILLISECOND, 0);
        return c1.getTime();
    }
}