package com.pcloud.book.applet.biz.impl;

import com.google.common.collect.Lists;
import com.pcloud.appcenter.app.dto.AppTypeDto;
import com.pcloud.book.applet.contants.AppletConstants;
import com.pcloud.book.applet.dao.AppletRecordDao;
import com.pcloud.book.applet.dto.AppletRecordBookDTO;
import com.pcloud.book.applet.dto.AppletRecordStatisDTO;
import com.pcloud.book.applet.dto.ReadBookDayStatisDTO;
import com.pcloud.book.applet.dto.ReadBookWeekStatisDTO;
import com.pcloud.book.applet.entity.AppletRecordStatis;
import com.pcloud.book.applet.dao.AppletRecordStatisDao;
import com.pcloud.book.applet.biz.AppletRecordStatisBiz;
import com.pcloud.book.consumer.app.AppConsr;
import com.pcloud.book.consumer.resource.ProductConsr;
import com.pcloud.book.util.common.CommonUtils;
import com.pcloud.book.util.common.ThreadPoolUtils;
import com.pcloud.common.core.aspect.ParamLog;
import com.pcloud.common.page.PageBeanNew;
import com.pcloud.common.page.PageParam;
import com.pcloud.common.exceptions.BizException;
import com.pcloud.common.utils.DateUtils;
import com.pcloud.common.utils.ListUtils;
import com.pcloud.common.utils.NumberUtil;
import com.pcloud.common.utils.cache.redis.JedisClusterUtils;
import com.pcloud.common.utils.string.StringUtil;
import com.pcloud.resourcecenter.product.dto.ProductTypeDto;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 小睿小程序点击量统计(AppletRecordStatis)表服务实现类
 *
 * @author makejava
 * @since 2020-12-10 17:58:06
 */
@Service("appletRecordStatisBiz")
public class AppletRecordStatisBizImpl implements AppletRecordStatisBiz {

    private static final Logger LOGGER = LoggerFactory.getLogger(AppletRecordStatisBizImpl.class);

    @Autowired
    private AppletRecordStatisDao appletRecordStatisDao;
    @Autowired
    private AppletRecordDao appletRecordDao;
    @Autowired
    private AppConsr appConsr;
    @Autowired
    private ProductConsr productConsr;

    @Override
    @ParamLog("通过ID查询单条数据")
    public AppletRecordStatis getById(Long id) {
        return appletRecordStatisDao.getById(id);
    }

    @Override
    @ParamLog("查询多条数据")
    public PageBeanNew getList(Integer currentPage, Integer numPerPage) {
        PageBeanNew pageBeanNew = appletRecordStatisDao.listPageNew(new PageParam(currentPage, numPerPage), null, "getList");
        List recordList = pageBeanNew.getRecordList();
        if (ListUtils.isEmpty(recordList)){
            return pageBeanNew;
        }
        // 加载其它数据
        
        return pageBeanNew;
    }

    @Override
    @ParamLog("新增")
    public Long insert(AppletRecordStatis appletRecordStatis) {
        appletRecordStatisDao.insert(appletRecordStatis);
        return appletRecordStatis.getId();
    }

    @Override
    @ParamLog("修改")
    public void update(AppletRecordStatis appletRecordStatis) {
        if(appletRecordStatis == null || !NumberUtil.isNumber(appletRecordStatis.getId())){
            throw BizException.PARAM_IS_NULL;
        }
        appletRecordStatisDao.update(appletRecordStatis);
    }

    @Override
    @ParamLog("删除")
    public void deleteById(Long id) {
        appletRecordStatisDao.deleteById(id);
    }

    @Override
    public ReadBookWeekStatisDTO getReadBookWeekStatis(Long wechatUserId, String mondayDate) {
        if(mondayDate == null){
            throw new BizException(BizException.PARAM_IS_NULL.getCode(),"mondayDate不能为空");
        }
        Date monday = DateUtils.getDateFromString(mondayDate, "yyyy-MM-dd");
        // 校准周一
        monday = CommonUtils.getWeekStart(monday);
        Date sunday = DateUtils.addDay(monday, 6);
        // 计算本周读书本数，已读新书本数
        ReadBookWeekStatisDTO weekStatisDTO = getReadBookWeekStatisDTO(wechatUserId, monday, sunday);
        // 本周每天的读书详情数据图表
        weekStatisDTO.setReadBookDayStatisList(getDayStatisList(wechatUserId, monday, sunday));
        return weekStatisDTO;
    }

    // 计算本周读书本数，已读新书本数
    @Override
    public ReadBookWeekStatisDTO getReadBookWeekStatisDTO(Long wechatUserId, Date monday, Date sunday) {
        ReadBookWeekStatisDTO weekStatisDTO = new ReadBookWeekStatisDTO();
        List<AppletRecordBookDTO> allUserBookRecordList = getAllUserBookRecordList(wechatUserId);

        if(CollectionUtils.isEmpty(allUserBookRecordList)){
            weekStatisDTO.setReadBookCount(0);
            weekStatisDTO.setReadNewBookCount(0);
        }
        // 本周之前的读书数据
        List<AppletRecordBookDTO> beforeUserBookRecordList = allUserBookRecordList.stream().filter(x -> monday.compareTo(x.getCreateDate()) == 1).collect(Collectors.toList());
        // 本周的读书数据
        List<AppletRecordBookDTO> weekUserBookRecordList = allUserBookRecordList.stream().filter(x -> monday.compareTo(x.getCreateDate()) <= 0 && sunday.compareTo(x.getCreateDate()) >= 0).collect(Collectors.toList());
        weekStatisDTO.setReadBookCount(weekUserBookRecordList.size());

        // 用户本周之前所有的bookId
        List<Long> beforeBookIds = beforeUserBookRecordList.stream().map(x -> x.getBookId()).collect(Collectors.toList());
        // 用户本周的bookid
        List<Long> weekBookIds = weekUserBookRecordList.stream().map(x -> x.getBookId()).collect(Collectors.toList());
        weekBookIds.removeAll(beforeBookIds);
        // 新书去重？ weekBookIds.stream().distinct().collect(Collectors.toList()).size()
        weekStatisDTO.setReadNewBookCount(weekBookIds.size());
        weekStatisDTO.setBeginDate(monday);
        weekStatisDTO.setEndDate(sunday);
        return weekStatisDTO;
    }

    // 获取用户所有的读书记录
    private List<AppletRecordBookDTO> getAllUserBookRecordList(Long wechatUserId) {
        String userBooksRedisKey = AppletConstants.APPLET_RECORD_USER_BOOK_LIST + "_" + wechatUserId+"_" + DateUtils.getShortDateStr();
        List<AppletRecordBookDTO> userBooks = JedisClusterUtils.getJsonList(userBooksRedisKey, AppletRecordBookDTO.class);
         if(CollectionUtils.isEmpty(userBooks)){
            userBooks = appletRecordDao.getBooksByWechatUserId(wechatUserId);
            if(CollectionUtils.isEmpty(userBooks)){
                return Lists.newArrayList();
            }
            JedisClusterUtils.setJsonList(userBooksRedisKey, userBooks, 3600 * 24);
         }
        return userBooks;
    }

    // 本周每天的读书量统计
    private List<ReadBookDayStatisDTO> getDayStatisList(Long wechatUserId, Date monday, Date sunday) {
        List<ReadBookDayStatisDTO> resultList = appletRecordDao.getReadBookWeekStatis(wechatUserId, DateUtils.formatDate(monday, "yyyy-MM-dd"), DateUtils.formatDate(sunday, "yyyy-MM-dd"));
        if(CollectionUtils.isEmpty(resultList)){
            resultList = Lists.newArrayList();
        }

        ArrayList<Date> weekDate = Lists.newArrayList(
                monday,
                DateUtils.addDay(monday, 1),
                DateUtils.addDay(monday, 2),
                DateUtils.addDay(monday, 3),
                DateUtils.addDay(monday, 4),
                DateUtils.addDay(monday, 5),
                sunday);
        ArrayList<String> week = Lists.newArrayList("周一", "周二", "周三", "周四", "周五", "周六", "周日");
        Map<Long, ReadBookDayStatisDTO> dayStatisMap = resultList.stream().collect(Collectors.toMap(x -> x.getCreateDate().getTime(), x -> x));
        for (Date date : weekDate) {
            ReadBookDayStatisDTO dto = dayStatisMap.get(date.getTime());
            if(dto == null){
                dto = new ReadBookDayStatisDTO();
                dto.setCreateDate(date);
                dto.setReadBookCount(0);
                resultList.add(dto);
            }
            dto.setDayOfWeek(week.get(weekDate.indexOf(date)));
        }
        resultList.sort(Comparator.comparing(ReadBookDayStatisDTO::getCreateDate));
        return resultList;
    }

    @Override
    public List<AppletRecordStatisDTO> getBookSourceWeekStatis(Long wechatUserId, String mondayDate){
        if(mondayDate == null){
            throw new BizException(BizException.PARAM_IS_NULL.getCode(),"mondayDate不能为空");
        }
        Date monday = DateUtils.getDateFromString(mondayDate, "yyyy-MM-dd");
        // 校准周一
        monday = CommonUtils.getWeekStart(monday);
        List<AppletRecordStatisDTO> resultList = appletRecordStatisDao.getBookSourceWeekStatis(wechatUserId, monday);
        if(CollectionUtils.isEmpty(resultList)){
            return Lists.newArrayList();
        }

        Integer totalClickCount = appletRecordStatisDao.getBookSourceTotalClickCount(wechatUserId, monday);
        if(totalClickCount == 0){
            return resultList;
        }
        // 计算占比
        calculateRatio(resultList, totalClickCount);
        // 填充应用、作品类型名称
        fillAppOrProductName(resultList);
        return resultList;
    }

    private void fillAppOrProductName(List<AppletRecordStatisDTO> resultList) {
        Map<String, AppTypeDto> appTypeDtoMap = appConsr.listAllCode();
        Map<String, ProductTypeDto> productTypeDtoMap = productConsr.getProType();
        for (AppletRecordStatisDTO dto : resultList) {
            if(dto.getRecordType() == null){
                continue;
            }
            if(StringUtil.isEmpty(dto.getSourceType())){
                dto.setSourceTypeName("浏览资源");
                continue;
            }
            if(dto.getRecordType() == 5){
                ProductTypeDto productTypeDto = productTypeDtoMap.get(dto.getSourceType());
                if(productTypeDto == null || StringUtil.isEmpty(productTypeDto.getTypeName())){
                    continue;
                }
                dto.setSourceTypeName("新" + productTypeDto.getTypeName());
            }
            if(dto.getRecordType() == 6){
                dto.setSourceTypeName(Optional.ofNullable(appTypeDtoMap.get(dto.getSourceType())).orElse(new AppTypeDto()).getTypeName());
            }
        }
    }

    private void calculateRatio(List<AppletRecordStatisDTO> resultList, Integer totalClickCount) {
        BigDecimal top5TotalRatio = BigDecimal.ZERO;
        Long totalCount = 0L;
        for (int i = 0; i < resultList.size(); i++) {
            AppletRecordStatisDTO item = resultList.get(i);
            BigDecimal ratio = new BigDecimal(item.getClickCount()).divide(new BigDecimal(totalClickCount), 2, BigDecimal.ROUND_HALF_UP);
            item.setClickRatio(ratio);
            item.setStrClickRatio(ratio.multiply(new BigDecimal(100)).setScale(0)+ "%");
            item.setSourceTypeName(item.getSourceType());
            top5TotalRatio = top5TotalRatio.add(ratio);
            totalCount += item.getClickCount();

            // 如果已经到5个了，并且占比还没到100%，则增加一个其它
            if(i == resultList.size() - 1 && i == 4 && new BigDecimal(1).compareTo(top5TotalRatio) == 1){
                AppletRecordStatisDTO other = new AppletRecordStatisDTO();
                BigDecimal subtract = new BigDecimal(1).subtract(top5TotalRatio);
                other.setClickCount(totalClickCount - totalCount);
                other.setClickRatio(subtract);
                other.setStrClickRatio(subtract.multiply(new BigDecimal(100)).setScale(0) + "%");
                other.setSourceType("其它");
                other.setSourceTypeName("其它");
                resultList.add(other);
                break;
            }
        }
    }

    @Override
    public List<AppletRecordStatisDTO> getBookNewsWeekStatis(Long wechatUserId, String mondayDate) {
        if(mondayDate == null){
            throw new BizException(BizException.PARAM_IS_NULL.getCode(),"mondayDate不能为空");
        }
        Date monday = DateUtils.getDateFromString(mondayDate, "yyyy-MM-dd");
        // 校准周一
        monday = CommonUtils.getWeekStart(monday);
        List<AppletRecordStatisDTO> resultList = appletRecordStatisDao.getBookNewsWeekStatics(wechatUserId, monday);
        if(CollectionUtils.isEmpty(resultList)){
            return Lists.newArrayList();
        }

        Integer totalClickCount = appletRecordStatisDao.getBookNewsTotalClickCount(wechatUserId, monday);
        if(totalClickCount == 0){
            return resultList;
        }
        // 计算占比
        calculateRatio(resultList, totalClickCount);
        return resultList;
    }

    @Override
    public void deleteWeekAppletRecordStatis(String mondayDate){
        appletRecordStatisDao.deleteWeekAppletRecordStatis(mondayDate);
    }

    @Override
    public void addWeekAppletRecordStatis(String mondayDate) {
        // 获取7天前的日期
        Date prevWeekDay = DateUtils.addDay(new Date(), -7);
        if(StringUtil.isNotEmpty(mondayDate)){
            prevWeekDay = DateUtils.getDateByStr(mondayDate);
        }
        // 校准日期为上周一
        Date prevMonday = CommonUtils.getWeekStart(prevWeekDay);
        Date prevSunday = DateUtils.addDay(prevMonday, 6);

        if(AppletConstants.APPLET_RECORD_STATIS_REPORT_START_DATE.compareTo(prevMonday) == 1){
            LOGGER.info("addWeekAppletRecordStatis 统计时间从"+AppletConstants.APPLET_RECORD_STATIS_REPORT_START_DATE+"开始；");
            return;
        }

        Long statisUserCount = appletRecordDao.getStatisUserCount(prevMonday, prevSunday);
        if(statisUserCount == 0){
            LOGGER.info("addWeekAppletRecordStatis 没有查询到用户量，请注意相关功能情况；");
            return;
        }

        LOGGER.info(MessageFormat.format("addWeekAppletRecordStatis 开始更新上周【小程序用户报告】统计数据: 时间：{0}；统计日期的范围：{1}-{2}；", DateUtils.getLongDateStr(), prevMonday, prevSunday));
        // 每次更新1000个用户
        Long limitNumber = 1000L;
        long limitTotalPage = statisUserCount % limitNumber == 0 ? (statisUserCount / limitNumber) : (statisUserCount / limitNumber + 1);
        try {
            for (int i = 0; i < limitTotalPage; i++) {
                long startTime = System.currentTimeMillis();
                LOGGER.info(MessageFormat.format("addWeekAppletRecordStatis-startTime:{0}；i:{1}", startTime, i));
                // 周统计数据（应用、作品）
                appletRecordStatisDao.addWeekAppletRecordStatis4App(prevMonday, prevSunday, i * limitNumber, limitNumber);
                long endTime = System.currentTimeMillis();
                LOGGER.info("addWeekAppletRecordStatis4App-endTime:" + endTime + "；间隔："+ (startTime - endTime));

                startTime = System.currentTimeMillis();
                // 周统计数据（资讯）
                appletRecordStatisDao.addWeekAppletRecordStatis4News(prevMonday, prevSunday, i * limitNumber, limitNumber);
                endTime = System.currentTimeMillis();
                LOGGER.info("addWeekAppletRecordStatis4News-startTime:"+startTime+"；endTime:" + endTime + "；间隔："+ (startTime - endTime));
            }
        } catch (DuplicateKeyException e){
            throw new BizException(BizException.DB_UPDATE_RESULT_0.getCode(), "该周的统计数据已存在，如需重新执行，请先删除相关统计数据");
        }
    }
}
