package com.fjhx.common.service.documentary.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fjhx.common.constant.SourceConstant;
import com.fjhx.common.entity.documentary.bo.DocumentaryData;
import com.fjhx.common.entity.documentary.dto.DocumentaryRecordDto;
import com.fjhx.common.entity.documentary.dto.DocumentaryRecordSelectDto;
import com.fjhx.common.entity.documentary.eums.DocumentaryTypeEnum;
import com.fjhx.common.entity.documentary.po.Documentary;
import com.fjhx.common.entity.documentary.po.DocumentaryRecord;
import com.fjhx.common.entity.documentary.vo.DocumentaryRecordInfoVo;
import com.fjhx.common.entity.documentary.vo.DocumentaryRecordVo;
import com.fjhx.common.entity.documentary.vo.DocumentaryVo;
import com.fjhx.common.mapper.documentary.DocumentaryRecordMapper;
import com.fjhx.common.service.documentary.DocumentaryRecordService;
import com.fjhx.common.service.documentary.DocumentaryService;
import com.fjhx.common.service.documentary.GetDocumentaryBusinessTemplate;
import com.fjhx.file.entity.FileInfoVo;
import com.fjhx.file.utils.ObsFileUtil;
import com.ruoyi.common.core.domain.BaseIdPo;
import com.ruoyi.common.exception.ServiceException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;


/**
 * <p>
 * 跟单记录 服务实现类
 * </p>
 *
 * @author
 * @since 2023-04-20
 */
@Service
public class DocumentaryRecordServiceImpl extends ServiceImpl<DocumentaryRecordMapper, DocumentaryRecord> implements DocumentaryRecordService {

    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
            4,
            20,
            60,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(20),
            new ThreadPoolExecutor.CallerRunsPolicy()
    );

    @Autowired
    private DocumentaryService documentaryService;

    @Override
    public DocumentaryRecordInfoVo getInfo(DocumentaryRecordSelectDto dto) {

        // 获取跟单类型
        DocumentaryTypeEnum typeEnum = DocumentaryTypeEnum.getEnum(dto.getType());
        GetDocumentaryBusinessTemplate bean = typeEnum.getBusinessBean();

        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        SecurityContext context = SecurityContextHolder.getContext();

        CompletableFuture<? extends Page<? extends DocumentaryData>> pageCompletableFuture =
                CompletableFuture.supplyAsync(() -> {
                    // 设置子线程共享
                    RequestContextHolder.setRequestAttributes(servletRequestAttributes, true);
                    SecurityContextHolder.setContext(context);
                    DynamicDataSourceContextHolder.push(SourceConstant.COMMON);

                    List<Long> excludeBusinessId = null;
                    Long noRecordDocumentaryId = dto.getNoRecordDocumentaryId();
                    if (noRecordDocumentaryId != null) {
                        // 排除业务id
                        excludeBusinessId = listObject(DocumentaryRecord::getBusinessId,
                                q -> q.eq(DocumentaryRecord::getDocumentaryId, noRecordDocumentaryId)
                                        .isNotNull(DocumentaryRecord::getBusinessId)
                                        .isNotNull(DocumentaryRecord::getDocumentaryId)
                        );
                    }

                    Page<? extends DocumentaryData> page = bean.getDocumentaryPage(dto.getCondition(), dto, excludeBusinessId);
                    setDocumentaryRecord(page);

                    DynamicDataSourceContextHolder.poll();
                    return page;
                }, threadPoolExecutor).exceptionally(e -> {
                    if (e instanceof ServiceException) {
                        throw (ServiceException) e;
                    }
                    throw new RuntimeException(e);
                });

        CompletableFuture<List<DocumentaryVo>> getDocumentaryListFuture =
                CompletableFuture.supplyAsync(() -> {
                    // 设置子线程共享
                    RequestContextHolder.setRequestAttributes(servletRequestAttributes, true);
                    SecurityContextHolder.setContext(context);
                    DynamicDataSourceContextHolder.push(SourceConstant.COMMON);

                    List<DocumentaryVo> documentaryList = getDocumentaryList(typeEnum.getKey());
                    if (documentaryList.size() > 0) {
                        setDocumentaryRecordCount(documentaryList);
                    }

                    DynamicDataSourceContextHolder.poll();
                    return documentaryList;
                }, threadPoolExecutor).exceptionally(e -> {
                    if (e instanceof ServiceException) {
                        throw (ServiceException) e;
                    }
                    throw new RuntimeException(e);
                });


        Page<? extends DocumentaryData> documentaryDataPage = pageCompletableFuture.join();
        List<DocumentaryVo> documentaryList = getDocumentaryListFuture.join();

        DocumentaryRecordInfoVo vo = new DocumentaryRecordInfoVo();
        vo.setDocumentaryList(documentaryList);
        vo.setPage(documentaryDataPage);
        return vo;
    }

    @DSTransactional
    @Override
    public void add(DocumentaryRecordDto dto) {
        save(dto);
        ObsFileUtil.saveFile(dto.getFileList(), dto.getId());
    }

    @DSTransactional
    @Override
    public void edit(DocumentaryRecordDto dto) {
        if (dto.getId() == null) {
            throw new ServiceException("跟单记录id不能为空");
        }

        updateById(dto);
        ObsFileUtil.editFile(dto.getFileList(), dto.getId());
    }

    @DSTransactional
    @Override
    public void delete(Long id) {
        removeById(id);
        ObsFileUtil.removeFile(id);
    }

    /**
     * 获取跟单配置
     *
     * @param type 跟单类型
     * @return 跟单列表
     */
    private List<DocumentaryVo> getDocumentaryList(Integer type) {
        List<Documentary> list = documentaryService.list(q -> q
                .eq(Documentary::getType, type)
                .orderByAsc(Documentary::getSort)
        );
        return BeanUtil.copyToList(list, DocumentaryVo.class);
    }

    /**
     * 设置跟单记录总条数
     *
     * @param documentaryList 跟单配置
     */
    private void setDocumentaryRecordCount(List<DocumentaryVo> documentaryList) {
        List<Long> documentaryIdList = documentaryList.stream().map(BaseIdPo::getId).collect(Collectors.toList());

        Map<Object, Long> countMap = listMaps(Wrappers.<DocumentaryRecord>query()
                .select("documentary_id as documentaryId", "count(0) as count")
                .lambda()
                .in(DocumentaryRecord::getDocumentaryId, documentaryIdList)
                .groupBy(DocumentaryRecord::getDocumentaryId)
        ).stream().collect(Collectors.toMap(
                item -> item.get("documentaryId"),
                item -> (Long) item.get("count")));

        for (DocumentaryVo documentaryVo : documentaryList) {
            documentaryVo.setCount(countMap.getOrDefault(documentaryVo.getId(), 0L));
        }

    }

    /**
     * 赋值跟单记录明细
     */
    private void setDocumentaryRecord(Page<? extends DocumentaryData> page) {
        List<? extends DocumentaryData> records = page.getRecords();

        if (records.size() == 0) {
            return;
        }

        // 业务id
        List<Long> businessIdList = records.stream().map(DocumentaryData::getId).collect(Collectors.toList());

        // 根据业务id查询跟单记录
        List<DocumentaryRecord> list = list(q -> q.in(DocumentaryRecord::getBusinessId, businessIdList)
                .isNotNull(DocumentaryRecord::getDocumentaryId)
        );

        // 查询跟单记录文件
        List<Long> documentaryRecordList = list.stream().map(BaseIdPo::getId).collect(Collectors.toList());
        Map<Long, List<FileInfoVo>> fileMap = ObsFileUtil.getFileMap(documentaryRecordList);

        List<DocumentaryRecordVo> documentaryRecordVoList = BeanUtil.copyToList(list, DocumentaryRecordVo.class);

        Map<Long, Map<Long, List<DocumentaryRecordVo>>> map = documentaryRecordVoList.stream()
                .peek(item -> item.setFileList(fileMap.getOrDefault(item.getId(), new ArrayList<>())))
                .collect(Collectors.groupingBy(DocumentaryRecord::getBusinessId,
                        Collectors.groupingBy(DocumentaryRecord::getDocumentaryId)));

        // 赋值
        for (DocumentaryData record : records) {
            record.setDocumentaryRecord(map.getOrDefault(record.getId(), new HashMap<>()));
        }

    }


}
