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

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.BigExcelWriter;
import cn.hutool.poi.excel.ExcelUtil;
import com.pcloud.book.applet.biz.AppletBookScoreBiz;
import com.pcloud.book.applet.dto.AppletBookScoreSubTagDTO;
import com.pcloud.book.applet.dto.AppletBookScoreTagDTO;
import com.pcloud.book.applet.dto.AppletBookScoreTagInfoDTO;
import com.pcloud.book.applet.dto.AppletBookTagUserExportDTO;
import com.pcloud.book.applet.dto.AppletBookUserScoreDTO;
import com.pcloud.book.applet.dto.AppletBookUserScoreExportDTO;
import com.pcloud.book.applet.dto.AppletFeedbackDTO;
import com.pcloud.book.applet.dto.AppletUserFeedbackExportDTO;
import com.pcloud.book.applet.dto.AppletUserScoreHeadDTO;
import com.pcloud.book.applet.entity.AppletBookFeedback;
import com.pcloud.book.applet.entity.AppletBookUserScore;
import com.pcloud.book.applet.mapper.AppletBookFeedbackMapper;
import com.pcloud.book.applet.mapper.AppletBookScoreMapper;
import com.pcloud.book.applet.mapper.AppletBookUserScoreMapper;
import com.pcloud.book.base.exception.BookBizException;
import com.pcloud.book.book.dao.BookDao;
import com.pcloud.book.book.dto.BookDto;
import com.pcloud.book.consumer.reader.ReaderConsr;
import com.pcloud.book.consumer.user.AdviserConsr;
import com.pcloud.book.util.common.ThreadPoolUtils;
import com.pcloud.common.entity.UploadResultInfo;
import com.pcloud.common.utils.aliyun.OssUtils;
import com.pcloud.common.utils.robot.WeWorkWebHookRobotUtils;
import com.pcloud.readercenter.wechat.entity.WechatUser;
import com.pcloud.usercenter.party.adviser.dto.AdviserBaseInfoDto;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

@Component
@Slf4j
public class AppletBookScoreBizImpl implements AppletBookScoreBiz {

    @Autowired
    private AppletBookUserScoreMapper appletBookUserScoreMapper;
    @Autowired
    private AppletBookScoreMapper appletBookScoreMapper;
    @Autowired
    private AppletBookFeedbackMapper appletBookFeedbackMapper;
    @Autowired
    private ReaderConsr readerConsr;
    @Autowired
    private AdviserConsr adviserConsr;
    @Autowired
    private BookDao bookDao;

    @Override
    public Long score(Long wechatUserId, Long bookId, Long channelId, Long adviserId, Long scoreTagId, Long subTagId) {
        AppletBookUserScore appletBookUserScore = new AppletBookUserScore();
        appletBookUserScore.setWechatUserId(wechatUserId);
        appletBookUserScore.setBookId(bookId);
        appletBookUserScore.setChannelId(channelId);
        appletBookUserScore.setAdviserId(adviserId);
        appletBookUserScore.setScoreTagId(scoreTagId);
        appletBookUserScore.setSubTagId(subTagId);

        // 不存在则新增
        Long primaryKey = appletBookUserScoreMapper.selectPrimaryKey(appletBookUserScore);
        if (Objects.isNull(primaryKey)) {
            // 删除旧的选项
            appletBookUserScoreMapper.deleteChosen(appletBookUserScore);
            // 没有二级标签时 标签只能单选
            if (Objects.isNull(subTagId)) {
                appletBookUserScoreMapper.deleteChosenTag(appletBookUserScore);
            }
            appletBookUserScoreMapper.insert(appletBookUserScore);
            return appletBookUserScore.getId();
        }
        // 存在则更新
        appletBookUserScore.setId(primaryKey);
        appletBookUserScoreMapper.updateByPrimaryKey(appletBookUserScore);
        return primaryKey;
    }

    @Override
    public List<AppletBookUserScoreDTO> get(Long wechatUserId, Long bookId, Long channelId, Long adviserId) {

        List<AppletBookScoreTagInfoDTO> dtos = appletBookScoreMapper.listTags(wechatUserId, bookId, channelId, adviserId);
        // 获取选择的二级标签与以及标签
        List<AppletBookScoreTagInfoDTO> chosenSubTag = appletBookUserScoreMapper.getSubTagChosen(wechatUserId, bookId, channelId, adviserId);
        Map<Long, Long> tagRelateMap = Optional.ofNullable(chosenSubTag).map(
                map -> map.stream().filter(x -> Objects.nonNull(x.getTagId()) && Objects.nonNull(x.getSubTagId())).collect(Collectors.toMap(AppletBookScoreTagInfoDTO::getTagId, AppletBookScoreTagInfoDTO::getSubTagId)))
                .orElse(new HashMap<>());

        List<Long> chosenScore = new ArrayList<>();
        Map<Long, List<AppletBookScoreSubTagDTO>> subTagsMap = dtos.stream().filter(map -> Objects.nonNull(map.getTagId())).collect(
                Collectors.groupingBy(AppletBookScoreTagInfoDTO::getTagId, Collectors.mapping(item -> {
                    if (Objects.isNull(item.getSubTagId())) return null;
                    AppletBookScoreSubTagDTO tag = new AppletBookScoreSubTagDTO();
                    tag.setTagId(item.getTagId());
                    tag.setSubTagName(item.getSubTagName());
                    tag.setSubTagId(item.getSubTagId());
                    tag.setChooseTime(item.getChosenTime());
                    tag.setChosen(Objects.equals(tagRelateMap.get(item.getTagId()), item.getSubTagId()));
                    return tag;
                }, Collectors.toList())));

        Map<Long, List<AppletBookScoreTagDTO>> tagsMap = dtos.stream().filter(distinctByKey1(AppletBookScoreTagInfoDTO::getTagId)).collect(
                Collectors.groupingBy(AppletBookScoreTagInfoDTO::getScoreId, Collectors.mapping(item -> {
                    AppletBookScoreTagDTO tag = new AppletBookScoreTagDTO();
                    tag.setTagId(item.getTagId());
                    tag.setTagName(item.getTagName());
                    if (item.getChosen() == 1) {
                        chosenScore.add(item.getScoreId());
                        tag.setChosen(true);
                    }
                    tag.setChooseTime(item.getChosenTime());
                    List<AppletBookScoreSubTagDTO> subTags = Optional.ofNullable(subTagsMap).map(map -> map.get(item.getTagId())).orElse(new ArrayList<>());
                    subTags.removeAll(Collections.singleton(null));
                    tag.setSubTags(subTags);
                    return tag;
                }, Collectors.toList())));


        ArrayList<AppletBookUserScoreDTO> result = new ArrayList<>();
        if (CollUtil.isEmpty(tagsMap)) return result;

        dtos.stream().collect(Collectors.toMap(AppletBookScoreTagInfoDTO::getScoreId, AppletBookScoreTagInfoDTO::getScoreName, (key1, key2) -> key1)).forEach((scoreId, scoreName) -> {
            AppletBookUserScoreDTO appletBookUserScoreDTO = new AppletBookUserScoreDTO();
            appletBookUserScoreDTO.setScoreId(scoreId);
            appletBookUserScoreDTO.setScoreName(scoreName);
            appletBookUserScoreDTO.setWechatUserId(wechatUserId);
            if (chosenScore.contains(scoreId)) appletBookUserScoreDTO.setChosen(true);
            appletBookUserScoreDTO.setTags(Optional.of(tagsMap).map(map -> map.get(scoreId)).orElse(new ArrayList<>()));
            result.add(appletBookUserScoreDTO);
        });
        return result;
    }

    @Override
    public Void feedback(Long wechatUserId, AppletFeedbackDTO feedbackDTO) {
        AppletBookFeedback appletBookFeedback = new AppletBookFeedback();
        BeanUtil.copyProperties(feedbackDTO, appletBookFeedback);
        appletBookFeedback.setCreateTime(new Date());
        appletBookFeedback.setWechatUserId(wechatUserId);
        appletBookFeedbackMapper.insert(appletBookFeedback);
        ThreadPoolUtils.OTHER_THREAD_POOL.execute(() -> {
            WeWorkWebHookRobotUtils.sendMarkdownMsg("8e58db23-c42d-414a-92d2-f344a1cd8407", getMarkdown(wechatUserId, feedbackDTO));
        });
        return null;
    }

    @Override
    public List<String> uploadFile(MultipartFile[] file) {
        List<String> urls = new ArrayList<>(file.length);
        for (MultipartFile multipartFile : file) {
            try {
                UploadResultInfo uploadResultInfo = OssUtils.uploadFileStream(multipartFile.getInputStream(), UUID.randomUUID().toString(), FilenameUtils.getExtension(multipartFile.getOriginalFilename()));
                urls.add(uploadResultInfo.getUrl());
            } catch (IOException e) {
                log.error("[upload4ScanCodeInfoByWxScanResult] 文件上传失败！err:{}", e.getMessage(), e);
                throw new BookBizException(BookBizException.ERROR, "文件上传失败！");
            }
        }
        return urls;
    }

    @Override
    public byte[] export() {
        // 每页sheet最大包含行数（不算标题栏）
        File file = FileUtil.file("本书服务评价数据导出" + System.currentTimeMillis() + ".xlsx");
        BigExcelWriter writer = ExcelUtil.getBigWriter(file);
        // 1. 本书服务用户评价明细表
        exportAppletBookUserScore(writer);
        // 2. 各评价用户人数统计表
        exportTagUserCount(writer);
        // 3. 用户反馈明细表
        exportUserFeedback(writer);
        writer.close();
        byte[] bytes = FileUtil.readBytes(file);
        file.delete();
        return bytes;
    }

    /**
     * 导出用户反馈明细表
     */
    private void exportUserFeedback(BigExcelWriter writer) {
        List<AppletBookFeedback> appletBookFeedbacks = appletBookFeedbackMapper.queryAll(null);
        List<AppletUserFeedbackExportDTO> feedbackExportDTOS = fillAppletBookFeedbackExportDTOs(appletBookFeedbacks);
        List<List<AppletUserFeedbackExportDTO>> feedbacks = CollUtil.split(feedbackExportDTOS, 1048575);
        for (int i = 0; i < feedbacks.size(); i++) {
            if (i == 0) {
                writer.setSheet("本书服务用户反馈明细表");
            } else {
                writer.setSheet("本书服务用户反馈明细表(续" + i + ")");
            }
            writer.addHeaderAlias("wechatUserId", "用户ID");
            writer.addHeaderAlias("nickname", "用户昵称");
            writer.addHeaderAlias("mobile", "用户手机");
            writer.addHeaderAlias("bookId", "图书ID");
            writer.addHeaderAlias("bookName", "书名");
            writer.addHeaderAlias("adviserId", "编辑ID");
            writer.addHeaderAlias("adviserName", "编辑名称");
            writer.addHeaderAlias("channelId", "渠道ID");
            writer.addHeaderAlias("dateStr", "评价日期");
            writer.addHeaderAlias("content", "反馈内容");
            writer.addHeaderAlias("pic1", "反馈图片1");
            writer.addHeaderAlias("pic2", "反馈图片2");
            writer.addHeaderAlias("pic3", "反馈图片3");
            writer.addHeaderAlias("pic4", "反馈图片4");
            writer.write(feedbacks.get(i), true);
        }
    }

    /**
     * 填充用户反馈信息
     */
    private List<AppletUserFeedbackExportDTO> fillAppletBookFeedbackExportDTOs(List<AppletBookFeedback> appletBookFeedbacks) {
        if (CollUtil.isEmpty(appletBookFeedbacks)) return new ArrayList<>();

        List<AppletUserFeedbackExportDTO> dtos = new ArrayList<>(appletBookFeedbacks.size());
        // 编辑ID列表
        List<Long> adviserIds = appletBookFeedbacks.stream().filter(x -> Objects.nonNull(x) && Objects.nonNull(x.getAdviserId()))
                .map(AppletBookFeedback::getAdviserId).distinct().collect(Collectors.toList());
        // 读者ID列表
        List<Long> userIds = appletBookFeedbacks.stream().filter(x -> Objects.nonNull(x) && Objects.nonNull(x.getWechatUserId()))
                .map(AppletBookFeedback::getWechatUserId).distinct().collect(Collectors.toList());
        // 图书ID列表
        List<Long> bookIds = appletBookFeedbacks.stream().filter(x -> Objects.nonNull(x) && Objects.nonNull(x.getBookId()))
                .map(AppletBookFeedback::getBookId).distinct().collect(Collectors.toList());

        // 查询编辑信息
        CompletableFuture<Map<Long, AdviserBaseInfoDto>> adviserFuture = CompletableFuture.supplyAsync(() -> adviserConsr.getAdviserId2AdviserInfoDtoMap(adviserIds));
        // 查询图书信息
        CompletableFuture<Map<Long, BookDto>> bookFuture = CompletableFuture.supplyAsync(() -> bookDao.getMapByIds(bookIds));
        // 查询用户信息
        CompletableFuture<Map<Long, WechatUser>> userFuture = CompletableFuture.supplyAsync(() -> readerConsr.getUserList(userIds));

        try {
            CompletableFuture.allOf(userFuture, adviserFuture, bookFuture).get();
            Map<Long, WechatUser> userMap = Optional.of(userFuture.get()).orElse(new HashMap<>());
            Map<Long, AdviserBaseInfoDto> adviserMap = Optional.of(adviserFuture.get()).orElse(new HashMap<>());
            Map<Long, BookDto> bookMap = Optional.of(bookFuture.get()).orElse(new HashMap<>());
            appletBookFeedbacks.stream().filter(Objects::nonNull).forEach(dto -> {
                AppletUserFeedbackExportDTO feedbackExportDTO = new AppletUserFeedbackExportDTO();
                BeanUtil.copyProperties(dto,feedbackExportDTO);
                feedbackExportDTO.setAdviserName(Optional.ofNullable(dto.getAdviserId()).map(adviserMap::get).map(AdviserBaseInfoDto::getPartyName).orElse(""));
                feedbackExportDTO.setNickname(Optional.ofNullable(dto.getWechatUserId()).map(userMap::get).map(WechatUser::getWechatUserNickname).orElse(""));
                feedbackExportDTO.setMobile(Optional.ofNullable(dto.getWechatUserId()).map(userMap::get).map(WechatUser::getPhoneNumber).orElse(""));
                feedbackExportDTO.setBookName(Optional.ofNullable(dto.getBookId()).map(bookMap::get).map(BookDto::getBookName).orElse(""));
                feedbackExportDTO.setDateStr(Optional.ofNullable(dto.getCreateTime()).map(x -> DateUtil.format(x, DatePattern.NORM_DATETIME_FORMAT)).orElse(""));
                dtos.add(feedbackExportDTO);
            });
        } catch (InterruptedException | ExecutionException e) {
            log.warn("[fillAppletBookUserScoreExportDTOs] 填充信息失败", e);
        }
        return dtos;
    }

    /**
     * 导出标签人数统计表
     */
    private void exportTagUserCount(BigExcelWriter writer) {
        List<AppletBookTagUserExportDTO> tagUser = appletBookUserScoreMapper.listTagUser();
        writer.setSheet("本书服务各评价用户人数统计表");
        writer.addHeaderAlias("scoreName", "表情");
        writer.addHeaderAlias("tagName", "标签");
        writer.addHeaderAlias("subTagName", "子标签");
        writer.addHeaderAlias("userCount", "人数");
        writer.write(tagUser, true);
    }

    /**
     * 导出本书服务用户评价明细表
     */
    private void exportAppletBookUserScore(BigExcelWriter writer) {
        List<AppletBookUserScoreExportDTO> exportDTOS = appletBookUserScoreMapper.listBookUserScore();
        // 填充读者信息 图书信息和编辑信息
        List<AppletUserScoreHeadDTO> headDTOS = fillAppletBookUserScoreExportDTOs(exportDTOS);
        // 写入表格 - 如果数据量过大需要分页
        List<List<AppletUserScoreHeadDTO>> bookUserScoreList = CollUtil.split(headDTOS, 1048575);
        for (int i = 0; i < bookUserScoreList.size(); i++) {
            if (i == 0) {
                writer.renameSheet("本书服务用户评价明细表");
            } else {
                writer.setSheet("本书服务用户评价明细表(续" + i + ")");
            }
            writer.addHeaderAlias("wechatUserId", "用户ID");
            writer.addHeaderAlias("nickname", "用户昵称");
            writer.addHeaderAlias("mobile", "用户手机");
            writer.addHeaderAlias("scoreName", "表情");
            writer.addHeaderAlias("tagName", "标签");
            writer.addHeaderAlias("subTagName", "子标签");
            writer.addHeaderAlias("bookId", "图书ID");
            writer.addHeaderAlias("bookName", "书名");
            writer.addHeaderAlias("adviserId", "编辑ID");
            writer.addHeaderAlias("adviserName", "编辑名称");
            writer.addHeaderAlias("channelId", "渠道ID");
            writer.addHeaderAlias("dateStr", "评价日期");
            writer.write(bookUserScoreList.get(i), true);
        }
    }

    /**
     * 补充导出数据
     */
    private List<AppletUserScoreHeadDTO> fillAppletBookUserScoreExportDTOs(List<AppletBookUserScoreExportDTO> exportDTOS) {
        if (CollUtil.isEmpty(exportDTOS)) return new ArrayList<>();

        List<AppletUserScoreHeadDTO> dtos = new ArrayList<>(exportDTOS.size());
        // 读者ID列表
        List<Long> userIds = exportDTOS.stream().filter(x -> Objects.nonNull(x) && Objects.nonNull(x.getWechatUserId()))
                .map(AppletBookUserScoreExportDTO::getWechatUserId).distinct().collect(Collectors.toList());
        // 编辑ID列表
        List<Long> adviserIds = exportDTOS.stream().filter(x -> Objects.nonNull(x) && Objects.nonNull(x.getAdviserId()))
                .map(AppletBookUserScoreExportDTO::getAdviserId).distinct().collect(Collectors.toList());
        // 图书ID列表
        List<Long> bookIds = exportDTOS.stream().filter(x -> Objects.nonNull(x) && Objects.nonNull(x.getBookId()))
                .map(AppletBookUserScoreExportDTO::getBookId).distinct().collect(Collectors.toList());
        // 查询用户信息
        CompletableFuture<Map<Long, WechatUser>> userFuture = CompletableFuture.supplyAsync(() -> readerConsr.getUserList(userIds));
        // 查询编辑信息
        CompletableFuture<Map<Long, AdviserBaseInfoDto>> adviserFuture = CompletableFuture.supplyAsync(() -> adviserConsr.getAdviserId2AdviserInfoDtoMap(adviserIds));
        // 查询图书信息
        CompletableFuture<Map<Long, BookDto>> bookFuture = CompletableFuture.supplyAsync(() -> bookDao.getMapByIds(bookIds));

        try {
            CompletableFuture.allOf(userFuture, adviserFuture, bookFuture).get();
            Map<Long, WechatUser> userMap = Optional.of(userFuture.get()).orElse(new HashMap<>());
            Map<Long, AdviserBaseInfoDto> adviserMap = Optional.of(adviserFuture.get()).orElse(new HashMap<>());
            Map<Long, BookDto> bookMap = Optional.of(bookFuture.get()).orElse(new HashMap<>());
            exportDTOS.stream().filter(Objects::nonNull).forEach(dto -> {
                dto.setNickname(Optional.ofNullable(dto.getWechatUserId()).map(userMap::get).map(WechatUser::getWechatUserNickname).orElse(""));
                dto.setMobile(Optional.ofNullable(dto.getWechatUserId()).map(userMap::get).map(WechatUser::getPhoneNumber).orElse(""));
                dto.setAdviserName(Optional.ofNullable(dto.getAdviserId()).map(adviserMap::get).map(AdviserBaseInfoDto::getPartyName).orElse(""));
                dto.setBookName(Optional.ofNullable(dto.getBookId()).map(bookMap::get).map(BookDto::getBookName).orElse(""));
                dto.setDateStr(Optional.ofNullable(dto.getUpdateTime()).map(x -> DateUtil.format(x, DatePattern.NORM_DATETIME_FORMAT)).orElse(""));
                AppletUserScoreHeadDTO appletUserScoreHeadDTO = new AppletUserScoreHeadDTO();
                BeanUtil.copyProperties(dto,appletUserScoreHeadDTO);
                dtos.add(appletUserScoreHeadDTO);
            });
        } catch (InterruptedException | ExecutionException e) {
            log.warn("[fillAppletBookUserScoreExportDTOs] 填充信息失败", e);
        }

        return dtos;
    }


    private String getMarkdown(Long wechatUserId, AppletFeedbackDTO feedbackDTO) {
        WechatUser wechatUser = Optional.ofNullable(readerConsr.getWechatUser(wechatUserId)).orElse(new WechatUser());
        return "# 小睿小程序用户反馈" + DateUtil.format(new Date(), DatePattern.NORM_DATETIME_FORMAT) + "\n"
                + "> 用户ID：" + wechatUserId + "\n"
                + (StrUtil.isBlank(wechatUser.getWechatUserNickname()) ? "" : ("> 用户昵称：" + wechatUser.getWechatUserNickname() + "\n"))
                + (StrUtil.isBlank(wechatUser.getPhoneNumber()) ? "" : ("> 用户手机号：" + wechatUser.getPhoneNumber() + "\n"))
                + "> 图书ID：" + feedbackDTO.getBookId() + "\n"
                + "> 渠道ID：" + feedbackDTO.getChannelId() + "\n"
                + "> 编辑ID：" + feedbackDTO.getAdviserId() + "\n"
                + (StrUtil.isBlank(feedbackDTO.getContent()) ? "" : ("> 反馈内容：" + feedbackDTO.getContent() + "\n"))
                + (StrUtil.isAllBlank(feedbackDTO.getPic1(), feedbackDTO.getPic2(), feedbackDTO.getPic3(), feedbackDTO.getPic4()) ? "" : ("> 反馈图片列表："
                + ((StrUtil.isBlank(feedbackDTO.getPic1()) ? "" : String.format("[反馈图片|](%s)", feedbackDTO.getPic1())))
                + ((StrUtil.isBlank(feedbackDTO.getPic2()) ? "" : String.format("[反馈图片|](%s)", feedbackDTO.getPic2())))
                + ((StrUtil.isBlank(feedbackDTO.getPic3()) ? "" : String.format("[反馈图片|](%s)", feedbackDTO.getPic3())))
                + ((StrUtil.isBlank(feedbackDTO.getPic4()) ? "" : String.format("[反馈图片|](%s)", feedbackDTO.getPic4())))
        ));
    }


    // 用于方法去重
    static <T> Predicate<T> distinctByKey1(Function<? super T, ?> keyExtractor) {
        Map<Object, Boolean> seen = new ConcurrentHashMap<>();
        return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
    }
}


