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

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.pcloud.book.book.constant.BookConstant;
import com.pcloud.book.consumer.reader.ReaderConsr;
import com.pcloud.book.consumer.trade.TradeConsr;
import com.pcloud.book.consumer.user.AdviserConsr;
import com.pcloud.book.consumer.user.AgentConsr;
import com.pcloud.book.consumer.wechat.WechatConsr;
import com.pcloud.book.es.biz.ESBookGroupQrcodeBiz;
import com.pcloud.book.es.dto.BookAdviserInfo;
import com.pcloud.book.es.dto.BookTagDTO;
import com.pcloud.book.es.dto.ESBookGroupQrcodeDTO;
import com.pcloud.book.es.dto.GroupQrcodeSearchDTO;
import com.pcloud.book.es.entity.ESBookGroupQrcode;
import com.pcloud.book.es.repository.BookGroupQrcodeRepository;
import com.pcloud.book.group.dao.BookGroupClassifyDao;
import com.pcloud.book.group.dao.GroupQrcodeDao;
import com.pcloud.book.group.entity.GroupQrcode;
import com.pcloud.common.page.PageBeanNew;
import com.pcloud.common.utils.cache.redis.JedisClusterUtils;
import com.pcloud.facade.tradecenter.dto.OrderGroupFormDto;
import com.pcloud.usercenter.party.agent.dto.AgentCity;
import com.pcloud.wechatgroup.group.dto.ESGroupBookQrcodeDTO;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.admin.indices.analyze.AnalyzeAction;
import org.elasticsearch.action.admin.indices.analyze.AnalyzeRequestBuilder;
import org.elasticsearch.action.admin.indices.analyze.AnalyzeResponse;
import org.elasticsearch.index.query.MatchQueryBuilder;
import org.elasticsearch.index.query.MultiMatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import static com.pcloud.book.book.constant.BookConstant.BOOK_GROUP_QRCODE_ES_INIT;

/**
 * @author guiq
 * @version 1.0
 * @since 2020年7月10日
 */
@Component("esBookGroupQrcodeBiz")
@Slf4j
public class ESBookGroupQrcodeBizImpl implements ESBookGroupQrcodeBiz {

    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;
    @Autowired
    private BookGroupQrcodeRepository bookGroupQrcodeRepository;
    @Autowired
    private GroupQrcodeDao groupQrcodeDao;
    @Autowired
    private BookGroupClassifyDao bookGroupClassifyDao;
    @Autowired
    private ReaderConsr readerConsr;
    @Autowired
    private WechatConsr wechatConsr;
    @Autowired
    private TradeConsr tradeConsr;
    @Autowired
    private AdviserConsr adviserConsr;
    @Autowired
    private AgentConsr agentConsr;

    @Override
    public void initData() {
        long total = groupQrcodeDao.countAll();
        if (JedisClusterUtils.setnx(BOOK_GROUP_QRCODE_ES_INIT, String.valueOf(total))) {
            // 每次处理 1000 条数据
            final long pageSize = 1000;
            // 计算分页数据
            long maxPage = (total + pageSize - 1) / pageSize;
            long pageNum = 0;
            while (pageNum < maxPage) {
                // 每 1000 条处理时间大致 60s
                JedisClusterUtils.expire(BOOK_GROUP_QRCODE_ES_INIT, 60);
                importData(pageNum * pageSize, pageSize);
                pageNum++;
            }
            // 处理完删除
            JedisClusterUtils.del(BOOK_GROUP_QRCODE_ES_INIT);
        }
    }

    private void importData(long start, long offset) {
        List<ESBookGroupQrcode> qrcodes = groupQrcodeDao.selectPage(start, offset);
        if (CollUtil.isEmpty(qrcodes)) {
            return;
        }

        // 将群二维码根据群分类进行分组，获取群对应的书名信息
        Map<Long, List<ESBookGroupQrcode>> classifyMap = qrcodes.stream().collect(Collectors.groupingBy(ESBookGroupQrcode::getClassifyId));
        Set<Long> classifyIds = classifyMap.keySet();
        if (CollUtil.isEmpty(classifyIds)) {
            return;
        }
        List<BookAdviserInfo> infos = bookGroupClassifyDao.getBookAndAdviserByClassifyIds(classifyIds);
        Map<Long, BookAdviserInfo> classifyBookMap = new HashMap<>();
        if (!CollUtil.isEmpty(infos)) {
            classifyBookMap = infos.stream().collect(Collectors.toMap(BookAdviserInfo::getClassifyId, BookAdviserInfo -> BookAdviserInfo));
        }

        // 获取编辑id集合
        List<Long> advisers = qrcodes.stream().map(ESBookGroupQrcode::getCreateUser).distinct().filter(Objects::nonNull).collect(Collectors.toList());
        Map<Long, AgentCity> agentCityMap = CollUtil.newHashMap();
        if (!CollUtil.isEmpty(advisers)){
            agentCityMap = readerConsr.getAgentPositionByAdviserIds(advisers);
        }

        // 获取群ID集合,查询城市标签及性别比例
        List<String> groupIds = qrcodes.stream().map(ESBookGroupQrcode::getWeixinGroupId).distinct().filter(x->!StrUtil.isEmpty(x)).collect(Collectors.toList());
        Map<String, ESGroupBookQrcodeDTO> qrcodeDTOMap = wechatConsr.aggGroupMemberUser(groupIds);

        // 交易中心查询群内订单数据
        List<Long> collect = qrcodes.stream().map(ESBookGroupQrcode::getId).distinct().filter(Objects::nonNull).collect(Collectors.toList());
        Map<Long, OrderGroupFormDto> orderGroupForm4ES = tradeConsr.getOrderGroupForm4ES(collect);

        // 查询编辑姓名列表
        Map<Long, String> adviserNames = CollUtil.newHashMap(0);
        if (CollUtil.isNotEmpty(advisers)){
            adviserNames = adviserConsr.getNames(advisers);

        }

        // 查询出版社名称列表
        Map<Long, String> agentNames = CollUtil.newHashMap(0);
        if (CollUtil.isNotEmpty(agentCityMap)){
            List<Long> agentIds = agentCityMap.values().stream().filter(Objects::nonNull).map(AgentCity::getAgentId).distinct().collect(Collectors.toList());
            if (CollUtil.isNotEmpty(agentIds)){
                agentNames = agentConsr.getNames(agentIds);
            }
        }

        for (ESBookGroupQrcode qrcode : qrcodes) {
            if (qrcode == null) {
                continue;
            }
            // 根据群名称分词获取群标签
            List<String> groupNameTags = qrcode.getGroupNameTags() == null ? new ArrayList<>() : qrcode.getGroupNameTags();
            groupNameTags.addAll(analyzer(qrcode.getGroupName()));
            if (!CollUtil.isEmpty(groupNameTags)) {
                // 去除重复标签
                qrcode.setGroupNameTags(groupNameTags.stream().distinct().collect(Collectors.toList()));
            }

            if (qrcode.getClassifyId() != null) {
                // 根据书名获取分词标签
                List<String> bookNameTags = qrcode.getBookNameTags() == null ? new ArrayList<>() : qrcode.getBookNameTags();
                List<String> newBookNameTags = new ArrayList<>();
                if (!CollUtil.isEmpty(classifyBookMap) && classifyBookMap.get(qrcode.getClassifyId()) != null){
                    qrcode.setIsbn(classifyBookMap.get(qrcode.getClassifyId()).getIsbn());
                }
                if (JedisClusterUtils.hexists(BookConstant.BOOK_NAME_TAGS_CACHE, String.valueOf(qrcode.getClassifyId()))) {
                    BookTagDTO bookTagDTO = JedisClusterUtils.hgetJson2Class(BookConstant.BOOK_NAME_TAGS_CACHE, String.valueOf(qrcode.getClassifyId()), BookTagDTO.class);
                    newBookNameTags = bookTagDTO.getBookTags();
                    qrcode.setQrcodeBookName(bookTagDTO.getBookName());
                } else if (!CollUtil.isEmpty(classifyBookMap) && classifyBookMap.get(qrcode.getClassifyId()) != null){
                    String bookName = classifyBookMap.get(qrcode.getClassifyId()).getBookName();
                     if( !StrUtil.isEmpty(bookName)) {
                        newBookNameTags = analyzer(bookName);
                        qrcode.setQrcodeBookName(bookName);
                        BookTagDTO bookTagDTO = new BookTagDTO(bookName, newBookNameTags);
                        JedisClusterUtils.hset(BookConstant.BOOK_NAME_TAGS_CACHE, String.valueOf(qrcode.getClassifyId()), JSONObject.toJSONString(bookTagDTO));
                    }
                }
                bookNameTags.addAll(newBookNameTags);
                qrcode.setBookNameTags(bookNameTags.stream().distinct().collect(Collectors.toList()));
            }

            // 获取书对应的出版社，并将出版社的地理位置作为群的地理标签,并补充出版社和编辑名称
            if (qrcode.getCreateUser() != null) {
                qrcode.setAdviserName(adviserNames.get(qrcode.getCreateUser()));
                AgentCity agentCity = agentCityMap.get(qrcode.getCreateUser());
                if (agentCity != null) {
                    qrcode.setAgentName(agentNames.get(agentCity.getAgentId()));
                    List<String> strings = CollUtil.toList(agentCity.getCity());
                    strings = strings == null ? new ArrayList<>() : strings;
                    qrcode.setPressTags(CollUtil.removeEmpty(strings));
                }
            }


            // 补充城市及性别信息
            if (!CollUtil.isEmpty(qrcodeDTOMap)) {
                ESGroupBookQrcodeDTO qrcodeDTO = qrcodeDTOMap.get(qrcode.getWeixinGroupId());
                if (qrcodeDTO != null) {
                    qrcode.setCityTags(qrcodeDTO.getCityTags());
                    int male = qrcodeDTO.getMaleCount() == null ? 0 : qrcodeDTO.getMaleCount();
                    int female = qrcodeDTO.getFemaleCount() == null ? 0 : qrcodeDTO.getFemaleCount();
                    int unknown = qrcodeDTO.getUnknownCount() == null ? 0 : qrcodeDTO.getUnknownCount();
                    qrcode.setMaleCount(male);
                    qrcode.setFemaleCount(female);
                    qrcode.setUnknownCount(unknown);
                    qrcode.setGroupMemberCount(male + female + unknown);
                }
            }

            // 补充交易信息
            if (!CollUtil.isEmpty(orderGroupForm4ES) && qrcode.getId() != null) {
                OrderGroupFormDto dto = orderGroupForm4ES.get(qrcode.getId());
                if (dto != null) {
                    qrcode.setAmountActuallyPaid(dto.getAmountActuallyPaid());
                    qrcode.setPurchaseVolume(dto.getPurchaseVolume());
                    qrcode.setPurchaseAmount(dto.getPurchaseAmount().toPlainString());
                }
            }
        }
        bookGroupQrcodeRepository.save(qrcodes);
    }

    /**
     * 分词方法
     *
     * @param content 待分词内同
     * @return 分词结果
     */
    private List<String> analyzer(String content) {
        AnalyzeRequestBuilder ikRequest = new AnalyzeRequestBuilder(elasticsearchTemplate.getClient(), AnalyzeAction.INSTANCE, "book", content);
        ikRequest.setTokenizer("ik_smart");
        final ArrayList<String> list = CollUtil.newArrayList();
        final List<AnalyzeResponse.AnalyzeToken> tokens = ikRequest.execute().actionGet().getTokens();
        tokens.forEach(x -> list.add(x.getTerm()));
        return list;
    }

    @Override
    public List<ESBookGroupQrcodeDTO> searchByWxGroupIds(List<String> wxGroupIds) {
        if (CollUtil.isEmpty(wxGroupIds)) {
            return new ArrayList<>();
        }
        List<ESBookGroupQrcodeDTO> result = new ArrayList<>();
        Iterable<ESBookGroupQrcode> weixinGroupIds = bookGroupQrcodeRepository.search(QueryBuilders.termsQuery("weixinGroupId", wxGroupIds));
        for (ESBookGroupQrcode weixinGroupId : weixinGroupIds) {
            if (weixinGroupId != null) {
                result.add(BeanUtil.copyProperties(weixinGroupId, ESBookGroupQrcodeDTO.class));
            }
        }
        return result;
    }

    @Override
    public void save(List<ESBookGroupQrcodeDTO> dtos) {
        if (!CollUtil.isEmpty(dtos)) {
            List<ESBookGroupQrcode> qrcodes = new ArrayList<>();
            for (ESBookGroupQrcodeDTO dto : dtos) {
                qrcodes.add(BeanUtil.copyProperties(dto, ESBookGroupQrcode.class));
            }
            bookGroupQrcodeRepository.save(qrcodes);
        }
    }

    @Override
    public PageBeanNew<GroupQrcodeSearchDTO> search(String text, String methodType, Boolean isOrder, Integer pageSize, Integer pageNum) {
        QueryBuilder queryBuilder;
        PageRequest pageRequest;

        if (Objects.nonNull(isOrder)&&isOrder){
            pageRequest = new PageRequest(pageNum,pageSize,new Sort(Sort.Direction.DESC,"groupMemberCount","userNumber"));
        }else {
            pageRequest = new PageRequest(pageNum, pageSize);
        }

        if (StrUtil.isEmpty(text)) {
            queryBuilder = QueryBuilders.boolQuery();
        } else {
            if ("AND".equals(methodType)) {
                queryBuilder = QueryBuilders.multiMatchQuery(text, "groupName", "groupNameTags", "qrcodeBookName", "bookNameTags", "cityTags", "pressTags", "pressTags", "adviserName").type(MultiMatchQueryBuilder.Type.CROSS_FIELDS).operator(MatchQueryBuilder.Operator.AND);
            } else {
                queryBuilder = QueryBuilders.multiMatchQuery(text, "groupName", "groupNameTags", "qrcodeBookName", "bookNameTags", "cityTags", "pressTags", "pressTags", "adviserName");
            }
        }
        Page<ESBookGroupQrcode> page = bookGroupQrcodeRepository.search(queryBuilder, pageRequest);

        List<GroupQrcodeSearchDTO> list = convert(page.getContent());
        if (CollUtil.isEmpty(list)) {
            return new PageBeanNew<>(pageNum, pageSize, null);
        }
        return new PageBeanNew<>(pageNum, pageSize, (int) page.getTotalElements(), list);
    }

    private List<GroupQrcodeSearchDTO> convert(List<ESBookGroupQrcode> content) {
        if (CollUtil.isEmpty(content)){
            return new ArrayList<>();
        }

        List<GroupQrcodeSearchDTO> list = new ArrayList<>(content.size());
        for (ESBookGroupQrcode esBookGroupQrcode : content) {
            GroupQrcodeSearchDTO groupQrcodeSearchDTO = new GroupQrcodeSearchDTO();
            if (CollUtil.isNotEmpty(content)){
                BeanUtil.copyProperties(esBookGroupQrcode,groupQrcodeSearchDTO);
                groupQrcodeSearchDTO.setPayMemberCount(esBookGroupQrcode.getAmountActuallyPaid() == null ? 0 : Math.toIntExact(esBookGroupQrcode.getAmountActuallyPaid()));
                if (StrUtil.isEmpty(esBookGroupQrcode.getPurchaseAmount())){
                    groupQrcodeSearchDTO.setPurchaseAmount(BigDecimal.ZERO);
                }else {
                    groupQrcodeSearchDTO.setPurchaseAmount(new BigDecimal(esBookGroupQrcode.getPurchaseAmount()));
                }
                GroupQrcode qrcode = groupQrcodeDao.getById(esBookGroupQrcode.getId());
                if (qrcode!=null&&qrcode.getQrcodeUrl()!=null){
                    groupQrcodeSearchDTO.setQrcodeUrl(qrcode.getQrcodeUrl());
                }
            }
            list.add(groupQrcodeSearchDTO);
        }
        return list;
    }
}
