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

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.pcloud.appcenter.app.dto.AppDto;
import com.pcloud.book.applet.biz.AppletBannerBiz;
import com.pcloud.book.applet.biz.AppletNewsBiz;
import com.pcloud.book.applet.dao.AppletBannerDao;
import com.pcloud.book.applet.dao.AppletUserBookcaseDao;
import com.pcloud.book.applet.dto.AppletBannerDTO;
import com.pcloud.book.applet.dto.AppletBannerStats;
import com.pcloud.book.applet.dto.AppletBannerTrendDTO;
import com.pcloud.book.applet.dto.AppletNewsDTO;
import com.pcloud.book.applet.dto.ServeItemInfoDTO;
import com.pcloud.book.applet.entity.AppletBanner;
import com.pcloud.book.applet.entity.AppletBannerRecord;
import com.pcloud.book.applet.entity.AppletUserBookcase;
import com.pcloud.book.applet.enums.AppletRecordTypeEnum;
import com.pcloud.book.applet.mapper.AppletBannerRecordMapper;
import com.pcloud.book.base.exception.BookBizException;
import com.pcloud.book.consumer.app.AppConsr;
import com.pcloud.book.consumer.channel.QrcodeSceneConsr;
import com.pcloud.book.consumer.resource.ProductConsr;
import com.pcloud.book.group.tools.SendWeixinRequestTools;
import com.pcloud.book.rightsSetting.biz.RightsSettingBiz;
import com.pcloud.book.util.common.YesOrNoEnums;
import com.pcloud.channelcenter.wechat.dto.AccountSettingDto;
import com.pcloud.common.page.PageBeanNew;
import com.pcloud.common.page.PageParam;
import com.pcloud.common.utils.DateUtils;
import com.pcloud.common.utils.ListUtils;
import com.pcloud.common.utils.ParamChecker;
import com.pcloud.common.utils.ServeLinkUtils;
import com.pcloud.facade.quartz.entity.CallBackParam;
import com.pcloud.facade.quartz.entity.ScheduleJob;
import com.pcloud.facade.quartz.service.ScheduleService;
import com.pcloud.resourcecenter.product.dto.ProductDto;
import com.pcloud.wechatgroup.base.exception.WechatGroupBizException;
import lombok.extern.slf4j.Slf4j;
import org.codehaus.jackson.JsonParseException;
import org.quartz.SchedulerException;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;

import static com.pcloud.book.book.constant.BookConstant.JOB_NAME_BANNER_PUT_OFF;
import static com.pcloud.book.book.constant.BookConstant.JOB_NAME_BANNER_PUT_ON;

/**
 * 小程序banner
 */
@Component
@Slf4j
public class AppletBannerBizImpl implements AppletBannerBiz {

    @Autowired
    private AppletBannerDao appletBannerDao;
    @Autowired
    private AppletNewsBiz appletNewsBiz;
    @Autowired
    private AppletBannerRecordMapper appletBannerRecordMapper;
    @Autowired
    private ProductConsr productConsr;
    @Autowired
    private AppConsr appConsr;
    @Autowired
    private AppletUserBookcaseDao appletUserBookcaseDao;
    @Autowired
    private RightsSettingBiz rightsSettingBiz;
    @Autowired
    private QrcodeSceneConsr qrcodeSceneConsr;
    @Autowired
    private ScheduleService scheduleService;


    @Override
    public Long addBanner(AppletBanner appletBanner) {
        if (!ListUtil.toList(3,4).contains(appletBanner.getJumpType())) {
            ParamChecker.checkStringIsEmpty(appletBanner.getBannerPic(), "Banner图缺失！");
        }
        if (null == appletBanner.getAgentId()){
            appletBanner.setAgentId(0L);
        }
        Integer seq = appletBannerDao.getMaxSeq(appletBanner.getAgentId());
        appletBanner.setSeq(seq+1);
        appletBanner.setShowState(false);
        appletBannerDao.insert(appletBanner);
        Long id = appletBanner.getId();
        if (null != appletBanner.getStartTime()) {
            addSimpleJob4Banner(id, JOB_NAME_BANNER_PUT_ON + id,appletBanner.getStartTime(), YesOrNoEnums.YES.getValue());
        }
        if (null != appletBanner.getEndTime()) {
            addSimpleJob4Banner(id, JOB_NAME_BANNER_PUT_OFF + id,appletBanner.getEndTime(), YesOrNoEnums.NO.getValue());

        }
        return id;
    }

    private void addSimpleJob4Banner(Long id, String jobName, Date startTime, Integer showState) {
        Long second = (startTime.getTime() - System.currentTimeMillis()) / 1000;
        ScheduleJob scheduleJob = new ScheduleJob();
        scheduleJob.setJobGroup("book");
        scheduleJob.setStartTimeFormat("ss");
        scheduleJob.setStartTime(second.intValue());
        scheduleJob.setRepeatCount(0);
        scheduleJob.setIntervalTime(0);
        scheduleJob.setIntervalTimeFormat("dd");
        scheduleJob.setJobName(jobName);
        CallBackParam callBackParam = new CallBackParam();
        Map<String, Object> paramMap = new HashMap<>();
        //定时任务调用时用到的参数
        paramMap.put("id", id);
        paramMap.put("showState", showState);
        callBackParam.setParamMap(paramMap);
        callBackParam.setMethodName("autoChageBannerState");
        callBackParam.setBeanName("appletService");
        Map<String, Object> scheduleMap = new HashMap<>();
        scheduleMap.put("scheduleJob", scheduleJob);
        scheduleMap.put("callBackParam", callBackParam);
        try {
            scheduleService.addSimpleJob(scheduleMap);
        } catch (Exception e) {
            log.error("设置定时任务失败" + e.getMessage(), e);
            throw new BookBizException(BookBizException.ERROR, "定时任务设置失败");
        }
    }

    @Override
    public void updateBanner(AppletBanner appletBanner) {
        if (null==appletBanner.getId()){
            throw new BookBizException(BookBizException.PARAM_IS_NULL,"缺少id");
        }
        if (Objects.nonNull(appletBanner.getJumpType()) && !ListUtil.toList(3, 4).contains(appletBanner.getJumpType())) {
            ParamChecker.checkStringIsEmpty(appletBanner.getBannerPic(), "Banner图缺失！");
        }
        appletBannerDao.update(appletBanner);
        Long id = appletBanner.getId();
        if (YesOrNoEnums.YES.getValue().equals(appletBanner.getIsTime())) {
            try {
                scheduleService.deleteJob(JOB_NAME_BANNER_PUT_ON + id, "book");
                scheduleService.deleteJob(JOB_NAME_BANNER_PUT_OFF + id, "book");
            } catch (SchedulerException e) {
                log.warn("删除banner定时任务：{}失败", appletBanner.getId());
            } catch (JsonParseException e) {
                log.warn("删除banner定时任务：{}失败", appletBanner.getId());
            }
            if (null != appletBanner.getStartTime() && DateUtil.compare(new Date(), appletBanner.getStartTime()) < 0) {
                addSimpleJob4Banner(id, JOB_NAME_BANNER_PUT_ON + id,appletBanner.getStartTime(), YesOrNoEnums.YES.getValue());
            }
            if (null != appletBanner.getEndTime() && DateUtil.compare(new Date(), appletBanner.getEndTime()) < 0) {
                addSimpleJob4Banner(id, JOB_NAME_BANNER_PUT_OFF + id,appletBanner.getEndTime(), YesOrNoEnums.NO.getValue());

            }
        }
    }

    @Override
    public void deleteBanner(Long id) {
        try {
            scheduleService.deleteJob(JOB_NAME_BANNER_PUT_ON + id, "book");
            scheduleService.deleteJob(JOB_NAME_BANNER_PUT_OFF + id, "book");
        } catch (SchedulerException e) {
            log.warn("删除banner定时任务：{}失败", id);
        } catch (JsonParseException e) {
            log.warn("删除banner定时任务：{}失败", id);
        }
        appletBannerDao.deleteById(id);
    }

    @Override
    public AppletBannerDTO getBannerById(Long id) {
        AppletBanner appletBanner = appletBannerDao.getById(id);
        AppletBannerDTO appletBannerDTO = new AppletBannerDTO();
        if (null != appletBanner){
            BeanUtils.copyProperties(appletBanner,appletBannerDTO);
            fillServeInfo(ListUtil.toList(appletBannerDTO));
            List<AppletBannerStats> bannerPVUV = appletBannerRecordMapper.getBannerPVUV(ListUtil.toList(appletBanner.getId()));
            if (CollUtil.isNotEmpty(bannerPVUV) && Objects.nonNull(bannerPVUV.get(0))){
                AppletBannerStats appletBannerStats = bannerPVUV.get(0);
                String pv = Objects.nonNull(appletBannerStats.getPv()) ? String.valueOf(appletBannerStats.getPv()) : "0";
                String uv = Objects.nonNull(appletBannerStats.getUv()) ? String.valueOf(appletBannerStats.getUv()) : "0";
                appletBannerDTO.setClickPVUV(pv + "/" + uv);
            }
        }
        return appletBannerDTO;
    }

    @Override
    public PageBeanNew<AppletBannerDTO> listBanner(Integer currentPage, Integer numPerPage, Boolean showState, Long agentId) {
        Map<String, Object> map = new HashMap<>();
        map.put("showState", showState);
        map.put("agentId", agentId);
        PageBeanNew<AppletBannerDTO> banner = appletBannerDao.listPageNew(new PageParam(currentPage, numPerPage), map, "listBanner");
        if (Objects.isNull(banner) || CollUtil.isEmpty(banner.getRecordList())) {
            return banner;
        }
        // 填充 资讯 | 作品 | 应用 信息
        fillServeInfo(banner.getRecordList());

        List<Long> bannerIds = banner.getRecordList().stream().filter(Objects::nonNull).map(AppletBannerDTO::getId).collect(Collectors.toList());
        if (CollUtil.isEmpty(bannerIds)) {
            return banner;
        }
        // 根据 bannerId 统计 PV/UV
        List<AppletBannerStats> bannerPVUV = appletBannerRecordMapper.getBannerPVUV(bannerIds);
        if (CollUtil.isEmpty(bannerPVUV)) {
            return banner;
        }
        Map<Long, AppletBannerStats> bannerStatsMap = bannerPVUV.stream()
                .collect(Collectors.toMap(AppletBannerStats::getBannerId, AppletBannerStats -> AppletBannerStats));
        banner.getRecordList().forEach(item -> {
            if (Objects.nonNull(item) && Objects.nonNull(item.getId()) && CollUtil.isNotEmpty(bannerStatsMap) && Objects.nonNull(bannerStatsMap.get(item.getId()))) {
                AppletBannerStats bannerStats = bannerStatsMap.get(item.getId());
                String pv = Objects.nonNull(bannerStats.getPv()) ? String.valueOf(bannerStats.getPv()) : "0";
                String uv = Objects.nonNull(bannerStats.getUv()) ? String.valueOf(bannerStats.getUv()) : "0";
                item.setClickPVUV(pv + "/" + uv);
            }
        });
        return banner;
    }

    private void fillServeInfo(List<AppletBannerDTO> recordList) {

        // 获取资讯信息
        Map<Long, AppletNewsDTO> newsDTOMap = new HashMap<>();
        CompletableFuture<List<Long>> newsBannerFuture = CompletableFuture.supplyAsync(() -> recordList.stream()
                .filter(x -> Objects.nonNull(x) && Objects.equals(x.getJumpType(), 3))
                .map(AppletBannerDTO::getServeId).filter(Objects::nonNull).distinct()
                .collect(Collectors.toList())).whenComplete((list, throwable) -> {
            if (Objects.nonNull(throwable)) {
                log.error("[fillServeInfo] banner 图填充资讯信息失败，err：{}", throwable.getMessage(), throwable);
            } else {
                if (CollUtil.isEmpty(list)){
                    return;
                }
                Map<Long, AppletNewsDTO> dtoMap = appletNewsBiz.getByIds(list);
                newsDTOMap.putAll(Objects.nonNull(dtoMap) ? dtoMap : new HashMap<>());
            }
        });

        // 获取应用信息
        Map<Long, AppDto> appDtoMap = new HashMap<>();
        CompletableFuture<List<Long>> appBannerFuture = CompletableFuture.supplyAsync(() -> recordList.stream()
                .filter(x -> Objects.nonNull(x) && Objects.equals(x.getServeType(), AppletRecordTypeEnum.APP.value))
                .map(AppletBannerDTO::getServeId).collect(Collectors.toList())).whenComplete((list, throwable) -> {
            if (Objects.nonNull(throwable)) {
                log.error("[fillServeInfo] banner 图填充应用信息失败，err：{}", throwable.getMessage(), throwable);
            } else {
                Map<Long, AppDto> dtoMap = !ListUtils.isEmpty(list) ? appConsr.mapByIds4AuditPass(list) : new HashMap<>();
                appDtoMap.putAll(dtoMap);
            }
        });

        // 获取作品信息
        Map<Long, ProductDto> productDtoMap = new HashMap<>();
        CompletableFuture<List<Long>> productBannerFuture = CompletableFuture.supplyAsync(() -> recordList.stream()
                .filter(x -> Objects.nonNull(x) && Objects.equals(x.getServeType(), AppletRecordTypeEnum.PRODUCT.value))
                .map(AppletBannerDTO::getServeId).collect(Collectors.toList())).whenComplete((list, throwable) -> {
            if (Objects.nonNull(throwable)) {
                log.error("[fillServeInfo] banner 图填充作品信息失败，err：{}", throwable.getMessage(), throwable);
            } else {
                Map<Long, ProductDto> dtoMap = !ListUtils.isEmpty(list) ? productConsr.getProBasesByIds(list) : new HashMap<>();
                productDtoMap.putAll(dtoMap);
            }
        });

        try {
            CompletableFuture.allOf(newsBannerFuture, appBannerFuture, productBannerFuture).get();
        } catch (InterruptedException | ExecutionException e) {
            log.error("[fillServeInfo] banner 填充信息失败，err：{}", e.getMessage(), e);
        }

        // 填充信息
        for (AppletBannerDTO banner : recordList) {
            if (Objects.nonNull(banner) && Objects.nonNull(banner.getServeId()) && Objects.nonNull(banner.getServeType())) {
                Integer serveType = banner.getServeType();
                if (Objects.equals(banner.getJumpType(), 3) && Objects.nonNull(newsDTOMap.get(banner.getServeId()))) {
                    banner.setNewsItem(newsDTOMap.get(banner.getServeId()));
                } else if (Objects.equals(serveType, AppletRecordTypeEnum.APP.value) && Objects.nonNull(appDtoMap.get(banner.getServeId()))) {
                    AppDto appDto = appDtoMap.get(banner.getServeId());
                    String resultLinkUrl = null;
                    if (Objects.nonNull(appDto.getChannelId())) {
                        AccountSettingDto accountSettingDto = qrcodeSceneConsr.getWechatInfo(appDto.getChannelId());
                        // 根据 APP 拼接 linkUrl
                        String linkUrl = ServeLinkUtils.getServeLink("APP", appDto.getTypeCode(), appDto.getAppId().toString(),
                                appDto.getSceneCode(), appDto.getChannelId().toString(), appDto.getCreatedUser().toString(), null);
                        if (Objects.nonNull(accountSettingDto)) {
                            resultLinkUrl = SendWeixinRequestTools.splitUrl(accountSettingDto, linkUrl);
                        }
                    }
                    ServeItemInfoDTO build = ServeItemInfoDTO.builder().serveId(appDto.getAppId()).serveType(serveType)
                            .serveName(appDto.getTitle()).servePic(appDto.getSquareImg()).transverseImg(appDto.getTransverseImg())
                            .resultUrl(resultLinkUrl).hasThirdLink(StrUtil.isNotBlank(appDto.getTurnUrl()))
                            .serveTypeName(appDto.getTypeName()).build();
                    banner.setAppItem(build);
                } else if (Objects.equals(serveType, AppletRecordTypeEnum.PRODUCT.value) && Objects.nonNull(productDtoMap.get(banner.getServeId()))) {
                    ProductDto productDto = productDtoMap.get(banner.getServeId());
                    String resultLinkUrl = null;
                    if (Objects.nonNull(productDto.getChannelId())) {
                        AccountSettingDto accountSettingDto = qrcodeSceneConsr.getWechatInfo(productDto.getChannelId());
                        if (null != accountSettingDto) {
                            resultLinkUrl = SendWeixinRequestTools.splitUrl(accountSettingDto, productDto.getTurnUrl());
                        }
                    }
                    ServeItemInfoDTO build = ServeItemInfoDTO.builder().serveId(productDto.getProductId()).serveType(serveType)
                            .serveName(productDto.getProductName()).servePic(productDto.getCoverImg()).transverseImg(productDto.getPicture1())
                            .resultUrl(resultLinkUrl).hasThirdLink(StrUtil.isNotBlank(productDto.getSkipUrl()))
                            .serveTypeName(productDto.getProductTypeName()).build();
                    banner.setAppItem(build);
                }
            }
        }
    }

    @Override
    public PageBeanNew<AppletBannerDTO> listBanner4Wechat(Integer currentPage, Integer numPerPage, Boolean showState, Long officialAccountsId, Long wechatUserId) {
        Long agentId = appletNewsBiz.getAgentIdByAccountId(officialAccountsId);
        PageBeanNew<AppletBannerDTO> pageBeanNew = listBanner(currentPage, numPerPage, showState, agentId);
        // 依据当前用户正在读的书，所属分类获取最新上架的资讯
        processFillType(pageBeanNew.getRecordList(), wechatUserId);
        return pageBeanNew;
    }

    private void processFillType(List<AppletBannerDTO> recordList, Long wechatUserId) {
        if (CollUtil.isEmpty(recordList)) {
            return;
        }
        List<AppletBannerDTO> fillBanner = recordList.stream().filter(x -> Objects.nonNull(x) && Objects.equals(x.getJumpType(), 3)
                && Objects.equals(x.getFillType(), 2)).collect(Collectors.toList());
        if (CollUtil.isEmpty(fillBanner)) {
            return;
        }

        AppletUserBookcase userReadBook = appletUserBookcaseDao.getUserReadBook(wechatUserId);
        if (Objects.isNull(userReadBook)) {
            recordList.removeAll(fillBanner);
            return;
        }
        AppletNewsDTO newsDTO = rightsSettingBiz.getAppletNewsDTO4UserReadBook(userReadBook);
        if (Objects.isNull(newsDTO) || Objects.isNull(newsDTO.getId())) {
            recordList.removeAll(fillBanner);
            return;
        }
        recordList.stream().filter(x -> Objects.nonNull(x) && Objects.equals(x.getJumpType(), 3)
                && Objects.equals(x.getFillType(), 2)).forEach(item -> item.setNewsItem(newsDTO));
    }

    @Override
    public void bannerClickRecord(Long wechatUserId, Long bannerId) {
        AppletBanner banner = appletBannerDao.getById(bannerId);

        if (Objects.isNull(banner)) {
            appletBannerRecordMapper.insert(AppletBannerRecord.builder()
                    .bannerId(bannerId).createTime(new Date()).wechatUserId(wechatUserId).createHour(DateUtil.thisHour(Boolean.TRUE)).createDate(DateUtil.today()).build());
        } else {
            AppletBannerRecord record = AppletBannerRecord.builder()
                    .createTime(new Date()).wechatUserId(wechatUserId)
                    .bannerId(bannerId).jumpType(banner.getJumpType())
                    .serveId(banner.getServeId()).serveType(banner.getServeType()).
                            createHour(DateUtil.thisHour(Boolean.TRUE)).
                            createDate(DateUtil.today()).build();
            appletBannerRecordMapper.insert(record);
        }
    }

    @Override
    public List<AppletBannerTrendDTO> getBannerRecordTrend(String startDate, String endDate, Long agentId, Boolean inHour, Long id) throws ParseException {
        List<AppletBannerTrendDTO> trendDTOList = new ArrayList<>();
        if (inHour) {
            //每小时发送趋势
            Integer startHour = DateUtil.hour(new Date(), Boolean.TRUE);
            String startTime = DateUtil.yesterday().toStringDefaultTimeZone() + " " + startHour + ":00:00";
            String endTime = DateUtil.today() + " " + startHour + ":00:00";
            trendDTOList = appletBannerRecordMapper.getBannerRecordTrendByHour(startTime,endTime, id);
            List<Integer> hours = trendDTOList.stream().filter(s -> s.getHour() != null).map(AppletBannerTrendDTO::getHour).collect(Collectors.toList());
            List<Integer> hourAll = new ArrayList<>();
            for (int i = 0; i <= 23; i++) {
                hourAll.add(i);
            }
            for (Integer hour : hourAll) {
                if (!hours.contains(hour)) {
                    AppletBannerTrendDTO appletBannerTrendDTO = new AppletBannerTrendDTO();
                    appletBannerTrendDTO.setHour(hour);
                    appletBannerTrendDTO.setPV(0);
                    appletBannerTrendDTO.setUV(0);
                    trendDTOList.add(appletBannerTrendDTO);
                }
            }
            trendDTOList.sort(Comparator.comparing(AppletBannerTrendDTO::getHour));
            List<AppletBannerTrendDTO> list4YesterDay = trendDTOList.stream().filter(e -> e.getHour() >= startHour).collect(Collectors.toList());
            List<AppletBannerTrendDTO> list4toDay = trendDTOList.stream().filter(e -> e.getHour() < startHour).collect(Collectors.toList());
            list4YesterDay.addAll(list4toDay);
            trendDTOList = list4YesterDay;
        } else {
            //日发送趋势
            trendDTOList = appletBannerRecordMapper.getBannerRecordTrendByDay(startDate, endDate, id);
            List<String> dates = trendDTOList.stream().filter(s -> s.getDate() != null).map(AppletBannerTrendDTO::getDate).collect(Collectors.toList());
            SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
            //填充数据为0的日期
            long start = formatter.parse(startDate).getTime();
            long end = formatter.parse(endDate).getTime();
            long days = ((end - start) / (1000 * 3600 * 24));
            if (days < 0) {
                throw new WechatGroupBizException(WechatGroupBizException.ERROR, "选取时间错误");
            }
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(formatter.parse(startDate));
            List<String> daysAll = new ArrayList<>();
            for (int i = 0; i <= days; i++) {
                daysAll.add(formatter.format(calendar.getTime()));
                calendar.add(Calendar.DAY_OF_MONTH, 1);
            }
            for (String day : daysAll) {
                if (!dates.contains(day)) {
                    AppletBannerTrendDTO appletBannerTrendDTO = new AppletBannerTrendDTO();
                    appletBannerTrendDTO.setDate(day);
                    appletBannerTrendDTO.setPV(0);
                    appletBannerTrendDTO.setUV(0);
                    trendDTOList.add(appletBannerTrendDTO);
                }
            }
            trendDTOList.sort(Comparator.comparing(AppletBannerTrendDTO::getDate));
        }
        return trendDTOList;
    }

    @Override
    public void autoChageBannerState(Long id, Integer showState) {
        if (null == id || null == showState) {
            throw new BookBizException(BookBizException.PARAM_IS_NULL, "book定时任务更新banner上下架状态autoChageBannerState:id或showState为空");
        }
        AppletBanner appletBanner = new AppletBanner();
        appletBanner.setId(id);
        appletBanner.setShowState(YesOrNoEnums.YES.getValue().equals(showState) ? Boolean.TRUE : Boolean.FALSE);
        appletBannerDao.update(appletBanner);

    }
}
