package com.artfess.cqxy.processManagermant.manager.impl;

import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import com.artfess.base.context.BaseContext;
import com.artfess.base.feign.SystemConfigFeignService;
import com.artfess.base.manager.impl.BaseManagerImpl;
import com.artfess.base.query.PageBean;
import com.artfess.base.query.PageList;
import com.artfess.base.query.QueryFilter;
import com.artfess.base.util.BeanUtils;
import com.artfess.cqxy.processManagermant.dao.ProgressManageReportDao;
import com.artfess.cqxy.processManagermant.manager.ProgressManageReportManager;
import com.artfess.cqxy.processManagermant.model.ProgressManage;
import com.artfess.cqxy.processManagermant.model.ProgressManageReport;
import com.artfess.cqxy.projectManagement.enums.ProjectStatusEnum;
import com.artfess.cqxy.projectManagement.manager.ProjectManagementManager;
import com.artfess.cqxy.projectManagement.model.ProjectManagement;
import com.artfess.cqxy.search.enums.FunctionEnum;
import com.artfess.cqxy.search.manager.GlobalRetrievalManager;
import com.artfess.cqxy.search.model.GlobalRetrieval;
import com.artfess.cqxy.statistics.vo.StatisticsVo;
import com.artfess.cqxy.universal.manager.AccessoryManager;
import com.artfess.cqxy.universal.model.Accessory;
import com.artfess.cqxy.utils.BizUtils;
import com.artfess.cqxy.utils.ThreadUtil;
import com.artfess.poi.util.ExcelUtil;
import com.artfess.sysConfig.persistence.manager.SysDictionaryManager;
import com.artfess.sysConfig.persistence.param.DictModel;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Workbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;

/**
 * 过程管理 - 进度管理 - 工程月报(BizProgressManageReport)表服务实现类
 *
 * @author 黎沐华
 * @since 2022-03-11 17:46:51
 */
@Service
public class ProgressManageReportManagerImpl extends BaseManagerImpl<ProgressManageReportDao, ProgressManageReport> implements ProgressManageReportManager {
    private static Logger log = LoggerFactory.getLogger(ProgressManageReportManagerImpl.class);
    @Autowired
    private AccessoryManager accessoryManager;

    @Autowired
    private SystemConfigFeignService scfs;

    @Autowired
    private SysDictionaryManager sdm;

    @Autowired
    private ProjectManagementManager pmm;

    @Autowired
    private BaseContext baseContext;

    @Autowired
    private GlobalRetrievalManager grm;

    @Override
    public boolean saveOrUpdate(ProgressManageReport entity) {
        boolean save = StringUtils.isEmpty(entity.getId());
        boolean savedMain = super.saveOrUpdate(entity);
        // 处理附件信息
        List<Accessory> flag = entity.getAccessoryInfo();//处理空指针
        List<Accessory> accessoryList = null == flag ? new ArrayList<>() : flag;
        for (Accessory ele : accessoryList) {
            ele.setSourceId(entity.getId());
            ele.setProjectId(entity.getProjectId());
            ele.setDirectory(ProjectStatusEnum.one.getCode());
            ele.setGroup("ProgressManageReport");
            ele.setNode(ProjectStatusEnum.one.getCode());
            ele.setCreateBy(baseContext.getCurrentUserId());
            ele.setCreateName(baseContext.getCurrentUserName());
            ele.setCreateTime(LocalDateTime.now());
        }
        // 先清空该ID下的所有附件信息再添加
        accessoryManager.removeBySourceId(entity.getId());
        // 如果没有附件则返回true，不进入添加
        boolean saveAcc = accessoryList.size() == 0 || accessoryManager.saveAccess(accessoryList);
        //更新项目状态
        pmm.updateStatusById(entity.getProjectId(), Integer.valueOf(ProjectStatusEnum.one.getCode()));
        // 修改项目累计投资额度、项目当前进度
        ProjectManagement project = pmm.getById(entity.getProjectId());
        if(null != project){
            ProgressManageReport projectOvervieByProjectId = getProjectOvervieByProjectId(entity.getProjectId());
            // 形象进度 weeklyMouthlyWork
            project.setConstructionProgress(projectOvervieByProjectId.getWeeklyMouthlyWork());
            // 累计总投资 projectOvervie
            project.setConstructionProgress(projectOvervieByProjectId.getProjectOvervie().stripTrailingZeros().toPlainString());
            pmm.updateById(project);
        }
        // 同步到检索表
        GlobalRetrieval globalRetrieval= grm.getByBizId(entity.getId());
        handleRetrieval(save || BeanUtils.isEmpty(globalRetrieval) ?new GlobalRetrieval():globalRetrieval,entity);

        return savedMain && saveAcc;
    }

    private void handleRetrieval(GlobalRetrieval globalRetrieval, ProgressManageReport entity){
        ProjectManagement projectManagement = pmm.getById(entity.getProjectId());
        globalRetrieval.setProjectId(entity.getProjectId());
        globalRetrieval.setProjectName(projectManagement.getProjectName());
        globalRetrieval.setPersonCharge(projectManagement.getProjectManager());
        globalRetrieval.setArchivesType(1);
        globalRetrieval.setBizDataId(entity.getId());
        globalRetrieval.setFunctionCode(FunctionEnum.twenty.getCode());
        globalRetrieval.setFunctionName(FunctionEnum.twenty.getName());
        globalRetrieval.setBizTableName(FunctionEnum.twenty.getTableName());
        globalRetrieval.setDetailsRoteUrl(FunctionEnum.twenty.getTableRoteUrl());
        globalRetrieval.setTableRoteUrl(FunctionEnum.twenty.getTableRoteUrl());
        globalRetrieval.setTableApiUrl(FunctionEnum.twenty.getTableApiUrl());
        globalRetrieval.setDetailsApiUrl(FunctionEnum.twenty.getDetailsApiUrl());
        globalRetrieval.setFunctionPath(FunctionEnum.twenty.getFunctionPath());
        globalRetrieval.setSearchTitle(entity.getPortUserName()+"_"+entity.getProjectOvervie()+"_"+entity.getProgressDescription()+"_"+
                entity.getWeeklyMouthlyWork()+"_"+entity.getEngineeringChange()+"_"+entity.getNextStepPlan()+"_"+entity.getRemarks());
        grm.saveOrUpdate(globalRetrieval);
    }

    @Override
    public boolean deleteByIds(List<String> ids) {
        // 同步删除检索表中的信息
        for(String ele:ids){
            grm.remove(1,ele);
        }
        return removeByIds(ids);
    }

    @Override
    public ProgressManageReport getById(String id) {
        ProgressManageReport result = baseMapper.getById(id);
        result.setAccessoryInfo(accessoryManager.getAccessoryBySourceId(id));
        return result;
    }

    @Override
    public PageList<ProgressManageReport> queryAllByPage(QueryFilter<ProgressManageReport> queryFilter) {
        BizUtils.handleFilter(queryFilter, "bpmr", "pm");
        IPage<ProgressManageReport> result =
                baseMapper.queryAllByPage(
                        convert2IPage(queryFilter.getPageBean()),
                        convert2Wrapper(queryFilter, currentModelClass()));
        // 附件信息处理
        List<ProgressManageReport> records = result.getRecords();
        for (ProgressManageReport ele : records) {
            ele.setAccessoryInfo(accessoryManager.getAccessoryBySourceId(ele.getId()));
        }

        return new PageList<>(result);
    }

    @Override
    public BigDecimal getInvestmentTotal(String projectId, String portDate) {
        if(StringUtils.isBlank(portDate)){
            portDate = LocalDate.now().toString();
        }
        BigDecimal total = this.baseMapper.getInvestmentTotal(projectId, portDate);
        if (total == null){
            total = new BigDecimal(0);
        }
        return total;
    }

    @Override
    public void importExcelData(MultipartFile file, String projectId, String contractId/**, Integer portType, String portDate, Integer portUnit, String portUserName**/) {
        Assert.notNull(projectId,"项目ID不能为空");
        Assert.notNull(contractId, "合同ID不能为空");
        try(InputStream inputStream = file.getInputStream();) {
            // 获取数据
            List<ProgressManageReport> data = ExcelImportUtil.importExcel(inputStream,ProgressManageReport.class,new ImportParams());
            for(ProgressManageReport ele:data){
                ele.setProjectId(projectId);
                ele.setContractId(contractId);
                // 保存数据 & 同步到检索表
                save(ele);
                handleRetrieval(new GlobalRetrieval(),ele);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void exportDatatoExcel(QueryFilter<ProgressManageReport> queryFilter, HttpServletResponse response) throws IOException {
        String fileName = "过程管理-进度管理-工程月报-导出结果";

        // 获取字典
        List<DictModel> portType = sdm.queryDictListItemsByCode("gcyb-tblx");
        List<DictModel> portUnit = sdm.queryDictListItemsByCode("gcyb-tbf");

        // 获取数据
        BizUtils.handleFilter(queryFilter, "bpmr", "pm");
        List<ProgressManageReport> data = baseMapper.queryAllByPage(
                        convert2IPage(new PageBean(0, -1, false)),
                        convert2Wrapper(queryFilter, currentModelClass())).
                getRecords();
        if(null==data||data.size()==0){
            throw new RuntimeException("没有要导出的的数据！");
        }

        // 翻译字典
        for(ProgressManageReport ele : data){
            ele.setPortType(BizUtils.getDicValueByCode(portType, ele.getPortType()));
            ele.setPortUnit(BizUtils.getDicValueByCode(portUnit, ele.getPortUnit()));
        }

        // EasyPoi 导出参数、样式、表格格式设置
        ExportParams exportParams = BizUtils.getExportParams(fileName);
        fileName += ".xlsx";

        // 导出下载excel文件
        Workbook workbook = ExcelExportUtil.exportExcel(exportParams, ProgressManageReport.class, data);
        ExcelUtil.downloadExcel(workbook, fileName, response);
    }

    @Override
    public void updateProjectIdByProiectId(String oldProjectIds, String newProjectId) {
        UpdateWrapper<ProgressManageReport> wrapper = new UpdateWrapper<ProgressManageReport>()
                .set(StringUtils.isNotBlank(newProjectId), "PROJECT_ID_", newProjectId)
                .in("PROJECT_ID_", Arrays.asList(oldProjectIds.split(",")));
        this.update(wrapper);
    }

    @Override
    public List<Map<String, Object>> queryInvestment(StatisticsVo statisticsVo) {

        return this.baseMapper.queryInvestment(statisticsVo);
    }

    @Override
    public ProgressManageReport getByProjectId(String projectId){
        return this.baseMapper.getByProjectId(projectId);
    }

    @Override
    public ProgressManageReport getProjectOvervieByProjectId(String projectId){
        ProgressManageReport projectOvervieByProjectId = this.baseMapper
                .getProjectOvervieByProjectId(projectId, LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM")));
        // 如果本月为空则查询上一月份的
        if(BeanUtils.isEmpty(projectOvervieByProjectId)){
            projectOvervieByProjectId = this.baseMapper
                    .getProjectOvervieByProjectId(projectId, LocalDate.now().minusMonths(1).format(DateTimeFormatter.ofPattern("yyyy-MM")));
        }
        // 如果本月及上月都为空，则显示最新的便于以后调整，所以分开查询
        if(BeanUtils.isEmpty(projectOvervieByProjectId)){
            projectOvervieByProjectId = this.baseMapper
                    .getProjectOvervieByProjectId(projectId, null);
        }

        return projectOvervieByProjectId;
    }


    /**
     * 多线程重算工程月报
     * @param projectId
     */
    @Override
    public void recalManageReport(String projectId) {
        /**
         查支付表，查出有多少个项目，
         项目分线程。线程里查出项目的所有支付明细。合同分组（忽略合同为空的数据），查出已拨款额（查询sum）修改
         */
        if(null !=projectId && !"".equals(projectId)){
            //手动传值不使用线程处理
            List<String> projectIds= Arrays.asList(projectId);
            recalManageReportLogic(projectIds);
        } else {
            List<String> projectIds= baseMapper.selectProjectIds();
            //线程处理
            if(null != projectIds && projectIds.size() > 0){
                log.info("工程月报重算开始，项目共"+projectIds.size()+"个");
                //分线程
                int projectNum = projectIds.size();
                //暂定4线程
                int threadNum = 4;
                //每个线程任务数量
                int threadTaskNum = ThreadUtil.getThreadTaskNum(threadNum, projectNum);
                //开启多线程
                log.info("工程月报重算-开启多线程, 任务数[{}], 线程数[{}], 每个线程任务数[{}]", projectNum, threadNum, threadTaskNum);
                for (int i = 0; i < threadNum; i++) {
                    List<String> subList = projectIds.subList(i * threadTaskNum, i == threadNum - 1 ? projectNum : (i + 1) * threadTaskNum);
                    Future<Object[]> future = ThreadUtil.publicPool.submit(new ProgressManageReportManagerImpl.recalManageReportThread(subList));
                }
            }
        }
        log.info("工程月报重算全部完成");
    }


    /**
     * 多线程开启
     */
    class recalManageReportThread implements Callable<Object[]> {
        private List<String> projectIds;
        public recalManageReportThread(List<String> projectIds) {
            this.projectIds = projectIds;
        }

        @Override
        @SuppressWarnings("unchecked")
        public Object[] call() {
            recalManageReportLogic(projectIds);
            return new Object[] {};
        }
    }


    /**
     * 工程月报重算 实际执行逻辑
     * @param projectIds
     */
    private void recalManageReportLogic(List<String> projectIds) {

        for (String projectId : projectIds) {
            log.info("工程月报重算开始-projectId:"+projectId);
            //查询该项目的月报
            List<ProgressManageReport> progressManageReports= baseMapper.selectByProjectId(projectId);

            if(null != progressManageReports && progressManageReports.size() > 0){
                for (ProgressManageReport report : progressManageReports) {

                    Date portDate = report.getPortDate();
                    String id = report.getId();
                    BigDecimal progressDescriptionTotal = baseMapper.selectProgressDescriptionSum(projectId,portDate,id);
                    report.setProjectOvervie(progressDescriptionTotal);
                }
                this.saveOrUpdateBatch(progressManageReports);
            }
            log.info("工程月报重算完成-projectId:"+projectId);
        }
    }

}

