package com.flossom.miniProgram.service.impl;

import com.flossom.common.core.domain.entity.*;
import com.flossom.common.core.domain.req.WxClockLogReq;
import com.flossom.common.core.domain.ret.WxClockLogRet;
import com.flossom.common.core.enums.IntegralChangeTypeEnum;
import com.flossom.common.core.enums.Status;
import com.flossom.common.core.mapper.*;
import com.flossom.common.core.utils.DateUtils;
import com.flossom.common.security.utils.SecurityUtils;
import com.flossom.miniProgram.service.IWxClockLogService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
@RefreshScope
public class WxClockLogServiceImpl implements IWxClockLogService {

    @Autowired
    private WxClockLogMapper wxClockLogMapper;

    @Autowired
    private WxClockImgMapper wxClockImgMapper;

    @Autowired
    private WxClockInstrumentLogMapper wxClockInstrumentLogMapper;

    @Autowired
    private WxInstrumentMapper wxInstrumentMapper;

    @Autowired
    private WxClockStatisticsMapper wxClockStatisticsMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private WxUserMemberMapper wxUserMemberMapper;

    @Autowired
    private WxUserIntegralLogMapper wxUserIntegralLogMapper;

    @Autowired
    private IntegralClockMapper integralClockMapper;

    @Value("${rank.clock.redisKey}")
    private String CLOCK_RANK_REDIS_KEY;


    @Override
    @Transactional
    public void insertWxClockLog(WxClockLogReq wxClockLogReq) {
        WxUserMember wxUserMember = SecurityUtils.getLoginUser().getWxUserMember();
        LocalDateTime now = LocalDateTime.now();

        /* 查询当天是否有打卡 */
        WxClockLog queryClockLog = new WxClockLog();
        queryClockLog.setUserId(SecurityUtils.getLoginUser().getWxUserMember().getId());
        queryClockLog.getParams().put("beginTime", LocalDateTime.of(now.toLocalDate(), LocalTime.MIN));
        queryClockLog.getParams().put("endTime", LocalDateTime.of(now.toLocalDate(), LocalTime.MAX));
        List<WxClockLog> wxClockLogList = wxClockLogMapper.selectWxClockLogList(queryClockLog);
        WxClockLog wxClockLog = new WxClockLog();
        if (wxClockLogList != null && wxClockLogList.size() == 1) {
            // 打卡记录一天只有一条
            wxClockLog.setId(wxClockLogList.get(0).getId());
        } else {
            IntegralClock integralClock = integralClockMapper.selectIntegralClockById(1L);
            // 日常奖励加分
            Long credit = integralClock.getDailyClockCredit();
            if (credit != null && 1 == integralClock.getIsExtraClock() && integralClock.getStartTime() != null
                    && integralClock.getEndTime() != null && integralClock.getExtraClockCredit() != null) {
                LocalDateTime nowTime = LocalDateTime.now();
                if (nowTime.isAfter(integralClock.getStartTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime())
                        && nowTime.isBefore(integralClock.getEndTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime())) {
                    credit += integralClock.getExtraClockCredit();
                }
            }
            if (credit != null && credit != 0) {
                // 增加积分
                WxUserMember query = wxUserMemberMapper.selectWxUserMemberById(wxUserMember.getId());
                WxUserMember updateEntity = new WxUserMember();
                updateEntity.setId(query.getId());
                updateEntity.setCredit(query.getCredit() + credit.intValue());
                updateEntity.setUpdateBy(wxUserMember.getNickname());
                updateEntity.setUpdateTime(DateUtils.getNowDate());
                wxUserMemberMapper.updateWxUserMember(updateEntity);

                // 保存积分操作记录
                WxUserIntegralLog wxUserIntegralLog = new WxUserIntegralLog();
                wxUserIntegralLog.setUserId(wxUserMember.getId());
                wxUserIntegralLog.setUserName(wxUserMember.getNickname());
                wxUserIntegralLog.setUserPhone(wxUserMember.getMobile());
                wxUserIntegralLog.setSource(IntegralChangeTypeEnum.INCREASE.getCode());
                wxUserIntegralLog.setFloatScore(credit);
                wxUserIntegralLog.setRemarkContent("打卡积分奖励");
                wxUserIntegralLog.setCreateBy(SecurityUtils.getLoginUser().getWxUserMember().getNickname());
                wxUserIntegralLog.setCreateTime(DateUtils.getNowDate());
                wxUserIntegralLogMapper.insertWxUserIntegralLog(wxUserIntegralLog);

                /**
                 * TODO：当天首次打卡奖励积分，同步数云
                 */

            }
        }
        wxClockLog.setClockContent(wxClockLogReq.getClockContent());
        wxClockLog.setUserId(wxUserMember.getId());
        wxClockLog.setCreateBy(wxUserMember.getNickname());
        wxClockLog.setCreateTime(DateUtils.getNowDate());
        // 当天使用过的仪器
        WxClockInstrumentLog queryInstrumentLog = new WxClockInstrumentLog();
        queryInstrumentLog.setUserId(wxUserMember.getId());
        wxClockLog.getParams().put("beginTime", LocalDateTime.of(now.toLocalDate(), LocalTime.MIN));
        wxClockLog.getParams().put("endTime", LocalDateTime.of(now.toLocalDate(), LocalTime.MAX));
        queryInstrumentLog.setStatus(Status.OK.getCode().longValue());
        List<WxClockInstrumentLog> wxClockInstrumentLogList = wxClockInstrumentLogMapper.selectWxClockInstrumentLogList(queryInstrumentLog);
        if (wxClockInstrumentLogList != null && wxClockInstrumentLogList.size() > 0) {
            String instrumentIdList = wxClockInstrumentLogList.stream()
                    .map(WxClockInstrumentLog::getInstrumentId)
                    .distinct()
                    .map(Objects::toString)
                    .collect(Collectors.joining(","));
            wxClockLog.setInstrumentId(instrumentIdList);
            String instrumentNameList = wxClockInstrumentLogList.stream()
                    .map(WxClockInstrumentLog::getInstrumentName)
                    .distinct()
                    .map(Objects::toString)
                    .collect(Collectors.joining(","));
            wxClockLog.setInstrumentName(instrumentNameList);
        }

        if (wxClockLog.getId() != null) {
            wxClockLog.setUpdateBy(SecurityUtils.getLoginUser().getWxUserMember().getNickname());
            wxClockLog.setUpdateTime(DateUtils.getNowDate());
            wxClockLogMapper.updateWxClockLog(wxClockLog);
        } else {
            // 当天第一次打卡
            wxClockLogMapper.insertWxClockLog(wxClockLog);

            /* 保存redis构建排名 */
            String redisKey = CLOCK_RANK_REDIS_KEY + LocalDate.now().getYear() + LocalDate.now().getMonthValue();
            redisTemplate.opsForZSet().incrementScore(redisKey, wxUserMember.getId(), 1);
            redisTemplate.expire(redisKey, LocalDate.now().getDayOfMonth() * 2, TimeUnit.DAYS);

            // 保存打卡记录，不计算比例
            List<Long> userIdList = Arrays.asList(wxUserMember.getId());
            List<WxClockStatistics> wxClockStatisticsList = wxClockStatisticsMapper.selectByUserIdAndYearMonth(userIdList, LocalDate.now().getYear(), LocalDate.now().getMonthValue());
            if (wxClockStatisticsList != null && wxClockStatisticsList.size() == 0) {
                // 数据库不存在则新增，也就是当月第一次打卡
                WxClockStatistics save = new WxClockStatistics();
                save.setYear(LocalDate.now().getYear());
                save.setMonth(LocalDate.now().getMonthValue());
                save.setUserId(wxUserMember.getId());
                save.setClockNum(1);
                save.setPercentage(new BigDecimal(0));
                save.setStatus(Status.OK.getCode().longValue());
                save.setCreateBy(wxUserMember.getNickname());
                save.setCreateTime(DateUtils.getNowDate());
                save.setUpdateBy(wxUserMember.getNickname());
                save.setUpdateTime(DateUtils.getNowDate());
                wxClockStatisticsMapper.insertWxClockStatistics(save);
            } else {
                // 数据库存在则更新打卡次数，也就是当月第二次以上打卡
                WxClockStatistics wxClockStatistics = wxClockStatisticsList.get(0);
                WxClockStatistics update = new WxClockStatistics();
                update.setId(wxClockStatistics.getId());
                update.setClockNum(wxClockStatistics.getClockNum() + 1);
                update.setUpdateBy(wxUserMember.getNickname());
                update.setUpdateTime(DateUtils.getNowDate());
                wxClockStatisticsMapper.updateWxClockStatistics(update);
            }
        }

        /* 先删除当天的打卡图片，保存打卡图片 */
        if (wxClockLogReq.getClockImageList() != null && wxClockLogReq.getClockImageList().size() > 0) {
            wxClockImgMapper.deleteByUserClockId(wxClockLog.getId());
            for (String image : wxClockLogReq.getClockImageList()) {
                WxClockImg wxClockImg = new WxClockImg();
                wxClockImg.setUserClockId(wxClockLog.getId());
                wxClockImg.setClockImg(image);
                wxClockImg.setCreateBy(wxUserMember.getNickname());
                wxClockImg.setCreateTime(DateUtils.getNowDate());
                wxClockImgMapper.insertWxClockImg(wxClockImg);
            }
        }
    }

    @Override
    @Transactional
    public void addClockInstrument(Long instrumentId) {
        // 添加仪器使用记录
        WxClockInstrumentLog wxClockInstrumentLog = new WxClockInstrumentLog();
        wxClockInstrumentLog.setUserId(SecurityUtils.getLoginUser().getWxUserMember().getId());
        wxClockInstrumentLog.setInstrumentId(instrumentId);
        WxInstrument wxInstrument = wxInstrumentMapper.selectWxInstrumentById(instrumentId);
        if (wxInstrument != null) {
            wxClockInstrumentLog.setInstrumentName(wxInstrument.getName());
        }
        wxClockInstrumentLog.setStatus(Status.OK.getCode().longValue());
        wxClockInstrumentLog.setCreateBy(SecurityUtils.getLoginUser().getWxUserMember().getNickname());
        wxClockInstrumentLog.setCreateTime(DateUtils.getNowDate());
        wxClockInstrumentLogMapper.insertWxClockInstrumentLog(wxClockInstrumentLog);

        // 查询今天是否有打卡，存在打卡，则将仪器添加到最近一条打卡记录上
        WxClockLog queryClockLog = new WxClockLog();
        queryClockLog.setUserId(SecurityUtils.getLoginUser().getWxUserMember().getId());
        LocalDateTime now = LocalDateTime.now();
        queryClockLog.getParams().put("beginTime", LocalDateTime.of(now.toLocalDate(), LocalTime.MIN));
        queryClockLog.getParams().put("endTime", LocalDateTime.of(now.toLocalDate(), LocalTime.MAX));
        List<WxClockLog> wxClockLogList = wxClockLogMapper.selectWxClockLogList(queryClockLog);
        if (wxClockLogList != null && wxClockLogList.size() > 0) {
            // 直接获取最新的打卡记录
            WxClockLog updateClockLog = new WxClockLog();
            WxClockLog newWxClockLog = wxClockLogList.get(0);
            // 获取打卡记录的使用过的仪器ID
            List<Long> usedList = Arrays.asList(newWxClockLog.getInstrumentId().split(",")).stream().map(Long::valueOf).collect(Collectors.toList());
            if (!usedList.contains(instrumentId)) {
                updateClockLog.setInstrumentId(newWxClockLog.getInstrumentId() + "," + instrumentId);
                WxInstrument instrument = wxInstrumentMapper.selectWxInstrumentById(instrumentId);
                if (instrument != null) {
                    updateClockLog.setInstrumentName(newWxClockLog.getInstrumentName() + "," + instrument.getName());
                }
                updateClockLog.setId(newWxClockLog.getId());
                updateClockLog.setUpdateBy(SecurityUtils.getLoginUser().getWxUserMember().getNickname());
                updateClockLog.setUpdateTime(DateUtils.getNowDate());
                wxClockLogMapper.updateWxClockLog(updateClockLog);
            }
        }
    }

    @Override
    public WxClockLogRet latestClockRecord() {
        WxClockLog queryClockLog = new WxClockLog();
        queryClockLog.setUserId(SecurityUtils.getLoginUser().getWxUserMember().getId());
        LocalDateTime now = LocalDateTime.now();
        queryClockLog.getParams().put("beginTime", LocalDateTime.of(now.toLocalDate(), LocalTime.MIN));
        queryClockLog.getParams().put("endTime", LocalDateTime.of(now.toLocalDate(), LocalTime.MAX));
        List<WxClockLog> wxClockLogList = wxClockLogMapper.selectWxClockLogList(queryClockLog);
        if (wxClockLogList == null || wxClockLogList.size() == 0) {
            return null;
        }
        WxClockLogRet wxClockLogRet = new WxClockLogRet();
        BeanUtils.copyProperties(wxClockLogList.get(0), wxClockLogRet);

        WxClockImg queryClockImg = new WxClockImg();
        queryClockImg.setUserClockId(wxClockLogRet.getId());
        List<WxClockImg> wxClockImgList = wxClockImgMapper.selectWxClockImgList(queryClockImg);
        if (wxClockImgList != null && wxClockImgList.size() > 0) {
            wxClockImgList.forEach(wxClockImg -> {
                wxClockLogRet.getClockImg().add(wxClockImg.getClockImg());
            });
        }
        return wxClockLogRet;
    }

    @Override
    public List<WxClockLogRet> selectWxClockLogList(WxClockLog queryClockLog) {
        List<WxClockLogRet> list = wxClockLogMapper.selectWxClockImgRetList(queryClockLog);
        if (list != null && list.size() > 0) {
            for (WxClockLogRet wxClockLogRet : list) {
                WxClockImg wxClockImg = new WxClockImg();
                wxClockImg.setUserClockId(wxClockLogRet.getId());
                List<WxClockImg> wxClockImgs = wxClockImgMapper.selectWxClockImgList(wxClockImg);
                if (wxClockImgs != null && wxClockImgs.size() > 0) {
                    List<String> collect = wxClockImgs.stream().map(WxClockImg::getClockImg).collect(Collectors.toList());
                    wxClockLogRet.setClockImg(collect);
                }
            }
        }
        return list;
    }

    @Override
    public List<WxClockStatistics> clockStatistics(Integer year) {
        WxClockStatistics query = new WxClockStatistics();
        query.setYear(year);
        query.setUserId(SecurityUtils.getLoginUser().getWxUserMember().getId());
        // 获取历史统计信息，数据在数据库
        List<WxClockStatistics> list = wxClockStatisticsMapper.selectWxClockStatisticsList(query);
        if (list != null && list.size() > 0 && year == LocalDate.now().getYear()) {
            /* 当前月，则需要从redis中获取排名，计算比例 */
            WxClockStatistics isCurrent = list.get(0);
            //   获取排名
            String redisKey = CLOCK_RANK_REDIS_KEY + LocalDate.now().getYear() + LocalDate.now().getMonthValue();
            Long rank = redisTemplate.opsForZSet().reverseRank(redisKey, SecurityUtils.getLoginUser().getWxUserMember().getId());
            if (rank != null) {
                // 计算超越百分比
                Integer wxUserTotal = wxUserMemberMapper.selectWxUserTotal();
                BigDecimal percentage = new BigDecimal(wxUserTotal - (rank + 1))
                        .divide(new BigDecimal(wxUserTotal), 3, RoundingMode.HALF_UP);
                isCurrent.setPercentage(percentage);
            }
        }
        return list;
    }

    @Override
    public void clockStatisticsTimedTask(List<Long> userIdList, Integer year, Integer month) {
        if (year == null || month == null) {
            LocalDate localDate = LocalDate.now().minusMonths(1);
            year = localDate.getYear();
            month = localDate.getMonthValue();
        }
        String redisKey = CLOCK_RANK_REDIS_KEY + year + month;
        // 清理旧数据
        redisTemplate.delete(redisKey);
        // 添加假数据，来创建 redisKey，并设置有效期
        redisTemplate.opsForZSet().incrementScore(redisKey, 0, 1);
        redisTemplate.expire(redisKey, LocalDate.now().getDayOfMonth() * 2, TimeUnit.DAYS);
        // 重置新数据
        List<WxClockStatistics> wxClockStatisticsList = wxClockStatisticsMapper.selectByUserIdAndYearMonth(userIdList, year, month);
        if (wxClockStatisticsList != null && wxClockStatisticsList.size() > 0) {
            for (WxClockStatistics wxClockStatistics : wxClockStatisticsList) {
                redisTemplate.opsForZSet().add(redisKey, wxClockStatistics.getUserId(), wxClockStatistics.getClockNum());
            }
        }
        if (wxClockStatisticsList != null && wxClockStatisticsList.size() > 0) {
            WxClockStatistics update;
            for (WxClockStatistics wxClockStatistics : wxClockStatisticsList) {
                // 获取排名
                Long rank = redisTemplate.opsForZSet().reverseRank(redisKey, wxClockStatistics.getUserId());
                // 计算超越百分比
                Integer wxUserTotal = wxUserMemberMapper.selectWxUserTotal();
                BigDecimal percentage = new BigDecimal(wxUserTotal - (rank + 1))
                        .divide(new BigDecimal(wxUserTotal), 3, RoundingMode.HALF_UP);
                update = new WxClockStatistics();
                update.setPercentage(percentage);
                update.setId(wxClockStatistics.getId());
                update.setUpdateTime(DateUtils.getNowDate());
                wxClockStatisticsMapper.updateWxClockStatistics(update);
            }
        }
    }


}
