package com.yunyan.business.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSONObject;
import com.yunyan.business.constant.FinalDataStatus;
import com.yunyan.business.constant.TemplateEpType;
import com.yunyan.business.document.y22.ReadFileNoFIllDeptUtils;
import com.yunyan.business.document.y22.UpdateReportFinalData;
import com.yunyan.business.document.y22.pojo.finalExcelY22.FinalP1Vo;
import com.yunyan.business.document.y22.pojo.finalExcelY22.FinalP3T8NoFillDeptVo;
import com.yunyan.business.document.y23.budget.ReadBudgetExcelY23Utils;
import com.yunyan.business.document.y23.budget.ReadBudgetFileJsonDataUtils;
import com.yunyan.business.document.y23.budget.UpdateReportBudgetData;
import com.yunyan.business.document.y23.budget.pojo.budgetY23.*;
import com.yunyan.business.document.y23.budget.pojo.budgetY23.purchase.BudgetP3p10p2ItemVo;
import com.yunyan.business.domain.CourseCode;
import com.yunyan.business.domain.FinalData;
import com.yunyan.business.domain.ReportExplain;
import com.yunyan.business.mapper.FinalDataMapper;
import com.yunyan.business.pojo.admin.dto.FinalImportExcelDto;
import com.yunyan.business.service.*;
import com.yunyan.common.config.YunYanConfig;
import com.yunyan.common.constant.Constants;
import com.yunyan.common.core.domain.AjaxResultVo;
import com.yunyan.common.core.domain.entity.SysDept;
import com.yunyan.common.utils.StringUtils;
import com.yunyan.common.utils.spring.SpringUtils;
import com.yunyan.system.service.ISysDeptService;
import com.yunyan.system.service.ISysOperLogService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;

@Slf4j
@Service
public class BudgetImportServiceImpl implements IBudgetImportService {

    @Autowired
    private ISysOperLogService sysOperLogService;

    @Autowired
    private ISysDeptService sysDeptService;

    @Autowired
    private IFinalDataService finalDataService;

    @Autowired
    private IReportExplainService reportExplainService;

    @Autowired
    private FinalDataMapper finalDataMapper;

    @Autowired
    private ICourseCodeService courseCodeService;

    @Autowired
    private IPurchaseDataService purchaseDataService;

    @Override
    public AjaxResultVo budgetImportExcel(FinalImportExcelDto dto) {
        // 预算  导入 budget
        if(dto.getDataType().equals(0)) {
            budgetImportExcelNewYear(dto);
        } else if(dto.getDataType().equals(1)) {
            budgetImportExcelLastYear(dto);
        }

        return null;
    }

    @Override
    public void budgetImportExcelNewYear(FinalImportExcelDto dto) {
        //查询编码 政府收支分类科目
        List<CourseCode> codeList = courseCodeService.getCodeListByType(3);

        try
        {
            //日志中 记录 导入的记录
            JSONObject jsonObject = new JSONObject();
            jsonObject.fluentPut("date", DateUtil.formatDateTime(new Date()));
            List<String> noInDeptList = new ArrayList<>();
            List<String> noInBudgetDataList = new ArrayList<>();
            List<String> exceptionList = new ArrayList<>();
            jsonObject.putArray("noInBudgetDataList");
            jsonObject.putArray("noInDeptList");
            jsonObject.putArray("exceptionList");
            jsonObject.getJSONArray("noInBudgetDataList").fluentAddAll(noInBudgetDataList);
            jsonObject.getJSONArray("noInDeptList").fluentAddAll(noInDeptList);
            jsonObject.getJSONArray("exceptionList").fluentAddAll(exceptionList);


            // 本机地址的文件路径
            String localPath = YunYanConfig.getProfile();
            ArrayList< String> fileNamesList = dto.getFileNamesList();
            ArrayList< String> originalFilenamesList = dto.getOriginalFilenamesList();

            //p3p8部分 不用填报的单位
            List<FinalP3T8NoFillDeptVo> p3T8NoFillDeptVoList = ReadFileNoFIllDeptUtils.readData();

            List<BudgetP3p10p2ItemVo> purchaseDataList = purchaseDataService.getPurchaseDataList(dto.getArea(), dto.getYear());

            //多线程 处理
            //每个线程至少同步30条记录，计算线程数，不超过10个线程
            int threadSize = Math.min((int) Math.ceil(fileNamesList.size() / 30D), 10);
//            threadSize = 2;
            //计算平均每个任务数
            int taskNum = (int) Math.ceil(fileNamesList.size() / (float) threadSize);

            final Integer[] successNum = new Integer[]{0};

            List<Future> taskList = new ArrayList<>();
            ScheduledExecutorService executor = SpringUtils.getBean("scheduledExecutorService");
            for (int j = 0, size = threadSize - 1; j <= size; j++) {

                List<String> subFileNamesList= fileNamesList.subList(j * taskNum, j < size ? (j + 1) * taskNum : fileNamesList.size());
                List<String> subOriginalFilenamesList= originalFilenamesList.subList(j * taskNum, j < size ? (j + 1) * taskNum : originalFilenamesList.size());
                taskList.add(executor.submit(() -> {
                    //保存 excel的 预算的本年数据
                    saveExcelBudgetNewData(subFileNamesList, localPath, subOriginalFilenamesList, codeList, dto, noInDeptList, noInBudgetDataList, exceptionList, successNum, p3T8NoFillDeptVoList, purchaseDataList);
                }));
            }
            for (Future f : taskList) {
                while (true) {
                    if (f.isDone() && !f.isCancelled()) {
                        //log.debug("导入的文件线程 结束");
                        break;
                    }
                }
            }

            //成功
            jsonObject.fluentPut("successNum", String.format("插入的数量: %s", successNum[0]));

            //记录日志 如果是 没有异常的, 返回 0
            Integer operStatus = noInBudgetDataList.isEmpty() && noInDeptList.isEmpty() && exceptionList.isEmpty() ? 0 : 1;
            sysOperLogService.insertOperlog(sysOperLogService.createSysOperLog("预算采集-本年", 1, "com.yunyan.web.controller.business.FinalDataController.finalImportExcel()",
                    "POST", operStatus, jsonObject.toJSONString()));
        }
        catch (Exception e)
        {
            log.error("决算导入出现异常: [{}] - [{}]", e.getMessage(), e);
        }
    }

    @Override
    public void budgetImportExcelLastYear(FinalImportExcelDto dto) {
        try
        {
            JSONObject jsonObject = new JSONObject();
            jsonObject.fluentPut("date", DateUtil.formatDateTime(new Date()));
            List<String> noInYearList = new ArrayList<>();
            List<String> exceptionList = new ArrayList<>();

            // 本机地址的文件路径
            String localPath = YunYanConfig.getProfile();
            ArrayList< String> fileNamesList = dto.getFileNamesList();
            ArrayList< String> originalFilenamesList = dto.getOriginalFilenamesList();

            //成功的数量
            final Integer[] successNum = new Integer[]{0};

            //多线程 处理
            //每个线程至少同步30条记录，计算线程数，不超过10个线程
            int threadSize = Math.min((int) Math.ceil(fileNamesList.size() / 30D), 10);
//            threadSize = 2;
            //计算平均每个任务数
            int taskNum = (int) Math.ceil(fileNamesList.size() / (float) threadSize);

            List<Future> taskList = new ArrayList<>();
            ScheduledExecutorService executor = SpringUtils.getBean("scheduledExecutorService");
            for (int j = 0, size = threadSize - 1; j <= size; j++) {

                List<String> subFileNamesList= fileNamesList.subList(j * taskNum, j < size ? (j + 1) * taskNum : fileNamesList.size());
                List<String> subOriginalFilenamesList= originalFilenamesList.subList(j * taskNum, j < size ? (j + 1) * taskNum : originalFilenamesList.size());
                taskList.add(executor.submit(() -> {

                    //保存 excel的 预算的去年数据
                    saveExcelBudgetLastData(subFileNamesList, localPath,subOriginalFilenamesList, dto, noInYearList, exceptionList, successNum);
                }));
            }
            for (Future f : taskList) {
                while (true) {
                    if (f.isDone() && !f.isCancelled()) {
                        //log.debug("导入的文件线程 结束");
                        break;
                    }
                }
            }

            jsonObject.putArray("noInYearList");
            jsonObject.putArray("exceptionList");
            jsonObject.getJSONArray("noInYearList").fluentAddAll(noInYearList);
            jsonObject.getJSONArray("exceptionList").fluentAddAll(exceptionList);

            //成功
            jsonObject.fluentPut("successNum", String.format("插入的数量: %s", successNum[0]));

            //记录日志 如果是 没有异常的, 返回 0
            Integer operStatus =  noInYearList.isEmpty() && exceptionList.isEmpty() ? 0 : 1;
            sysOperLogService.insertOperlog(sysOperLogService.createSysOperLog("预算采集-去年", 2, "com.yunyan.web.controller.business.FinalDataController.finalImportExcel()",
                    "PUST", operStatus, jsonObject.toJSONString()));

        }
        catch (Exception e)
        {
            log.debug("导入决算的去年数据异常: [{}] [{}]", e.getMessage(), e);
//            return AjaxResult.error(e.getMessage());
        }
    }

    /**
     * 保存 excel的 决算的本年数据
     * @param subFileNamesList 文件名
     * @param localPath 本地位置
     * @param subOriginalFilenamesList 源文件名
     * @param codeList 编码列表
     * @param dto
     * @param noInDeptList 不在部门中的异常
     * @param noInBudgetDataList 没有预算数据的异常
     * @param exceptionList 导入异常的list
     * @param successNum
     * @param p3T8NoFillDeptVoList p3t8部分未填报的部门
     */
    private void saveExcelBudgetNewData(List<String> subFileNamesList, String localPath, List<String> subOriginalFilenamesList, List<CourseCode> codeList,
                                       FinalImportExcelDto dto, List<String> noInDeptList, List<String> noInBudgetDataList, List<String> exceptionList, Integer[] successNum,
                                        List<FinalP3T8NoFillDeptVo> p3T8NoFillDeptVoList, List<BudgetP3p10p2ItemVo> purchaseDataList) {
        //读取 本地的本年的 预算
        List<BudgetP3p5SubDeptVo> p2p5SubDeptVoList = null;
        //上次的区域
        String area = dto.getArea();
        int i = 0;
        //年份
        String year = dto.getYear();
        Integer deptNameBy = dto.getDeptNameBy();

        SysDept sysDeptArea = sysDeptService.getfinalExportTypeByArea(area);
        if(null == sysDeptArea) {
            exceptionList.add("区域还未设置模板类型~");
            return;
        }

        //预算表8 的 主要用于更新p3p4部分的项
        List<BudgetP3p8DeptVo> p2p8DeptList = null;

        for (String fileName : subFileNamesList) {
            String absolutePathName = localPath + StringUtils.substringAfter(fileName, Constants.RESOURCE_PREFIX);
            String originalFilename = subOriginalFilenamesList.size() - 1 >= i ? subOriginalFilenamesList.get(i++) : "";
            try {
                FinalData finalData = new FinalData();
                //类型
                finalData.setType(2);

                //读取 excel
                BudgetDataVo excelDataVo = ReadBudgetExcelY23Utils.getSheetInfos(null, absolutePathName, 0, year, sysDeptArea.getBudgetExportType());
                //初始化 去年 数据
                UpdateReportBudgetData.initBudgetDeptInfo(excelDataVo);
                UpdateReportBudgetData.initValue(excelDataVo.getExplainVo());
                UpdateReportBudgetData.setLastValue(excelDataVo.getExplainVo(), null);

                //读取 excel中的编制单位名称
                String deptName = excelDataVo.getDeptName();
                if(null != deptNameBy && 2 == deptNameBy) {
                    //田东 部分部门的取 文件名的 编制单位来匹配
                    //deptName = originalFilename.substring(0, originalFilename.lastIndexOf(".XLS"));
                    deptName = originalFilename.substring(0, originalFilename.indexOf("."));
                }
                excelDataVo.setDeptName(deptName);
                excelDataVo.setYear(year);

                //设置表6,p8,p9的编码 设置 p3p3部分的款级
                UpdateReportBudgetData.updateCourseCode(excelDataVo, codeList);

                //保存的excel数据
                finalData.setExcelTitle(originalFilename);
                finalData.setExcelUrl(fileName);
                finalData.setExcelDeptName(deptName);
                //关联部门id
                SysDept sysDept=  sysDeptService.getDeptByName(deptName, area);
                FinalP1Vo finalP1Vo = new FinalP1Vo();
                if(null != sysDept) {
                    finalData.setDeptId(sysDept.getDeptId());
                    finalData.setParentDeptId(sysDept.getParentId());

                    excelDataVo.setDeptCode(sysDept.getDeptCode());
                    excelDataVo.setDeptId(sysDept.getDeptId());

                    //设置 决算的导出模板
                    if(null != sysDeptArea && StringUtils.isNotBlank(sysDeptArea.getBudgetExportType())) {
                        finalData.setTemplateExportType(sysDeptArea.getBudgetExportType());
                    }

                    //区域 来查询  预算数据的文件
                    if(StringUtils.isBlank(area) || !area.equals(sysDept.getArea()) ) {
                        area = sysDept.getArea();
                    }

                    //依据单位的级别来显示说明
                    BudgetDeptInfoVo deptInfoVo = new BudgetDeptInfoVo();
                    deptInfoVo.setDeptLevel(sysDept.getDeptLevel());
                    deptInfoVo.setShowTitle( 9 == sysDept.getDeptLevel() ? "单位" : "部门" );
                    excelDataVo.setDeptInfoVo(deptInfoVo);

                    //设置 单位属性
                    excelDataVo.getExplainVo().getP3p10Vo().getP1Vo().setDeptAttr(sysDept.getDeptAttr());

                    //设置p3p10p2采购的信息
                    UpdateReportBudgetData.setPurchaseValue(excelDataVo.getExplainVo(), purchaseDataList, sysDept.getDeptCode());

                    //博白 预算模板2 查询下级是否有行政和事业单位, 模板一也要
                    List<SysDept> tb5ChildDeptList = sysDeptService.checkChildDeptAttrAndSumTotal(UpdateReportBudgetData.subDeptCodeListTb5(excelDataVo.getExplainVo().getP3p5Vo().getSubDeptList()), area);
                    UpdateReportBudgetData.setBudgetP3p10p1VoByDeptAttr(excelDataVo.getExplainVo().getP3p5Vo().getSubDeptList(), excelDataVo.getExplainVo().getP3p10Vo().getP1Vo(), tb5ChildDeptList);
                    //比对 p3p10去年的数据 初始化
                    UpdateReportBudgetData.setP3p10P1Last(excelDataVo.getExplainVo().getP3p10Vo().getP1Vo(), null, true);

                    //判断 10部分的 是否开展单位的自评
                    boolean checkNoInDeptP3p10 = ReadBudgetFileJsonDataUtils.readNoInDeptP3p10BudgetData(year, area, sysDept.getDeptCode());
                    if(checkNoInDeptP3p10) {
                        excelDataVo.getExplainVo().getP3p10Vo().getP4Vo().setNoProjectFlag(1);
                    } else {
                        excelDataVo.getExplainVo().getP3p10Vo().getP4Vo().setNoProjectFlag(0);
                    }
                } else {
                    finalData.setDeptId(0L);
                    finalData.setParentDeptId(0L);
                    finalP1Vo.setDuty("");
                    finalP1Vo.setSettings("");
                    finalP1Vo.setSettings2("");
                    finalP1Vo.setBudgetInfo("");
                    finalP1Vo.setBudgetInfo2("");
                    excelDataVo.setDeptCode("");

                    excelDataVo.setP1Vo(finalP1Vo);
                    excelDataVo.getExplainVo().getP3p10Vo().getP1Vo().setDeptAttr(1);
                    excelDataVo.getExplainVo().getP3p10Vo().getP1Vo().setDeptAttrChild(0);

                    noInDeptList.add(String.format("[%s] 决算单位没有在单位管理中", deptName));

                }
                finalData.setUserId(0L);

                //状态
                finalData.setStatus(FinalDataStatus.YEAR_DATA.getCode());
                //取传值的年份
                finalData.setYear(year);

                //保存 excel读取数据
                String jsonTableContent = JSONUtil.toJsonStr(excelDataVo);
                boolean saveRes = finalDataService.saveFinalDataAndReportExplain(finalData, jsonTableContent, null);

                if(saveRes) {
                    successNum[0]++;
                }
            } catch (Exception e) {
                log.error("读取 预算以及保存的过程异常: [{}],  [{}]", e.getMessage(), e);
                exceptionList.add(String.format("[%s] 单位导入的出现异常：[%s]", originalFilename, e.getMessage()));

            }

        }
    }

    /**
     * 保存 预算的 excel数据 前一年的
     * @param subFileNamesList
     * @param localPath
     * @param subOriginalFilenamesList
     * @param dto
     * @param noInYearList
     * @param exceptionList
     */
    private void saveExcelBudgetLastData(List<String> subFileNamesList, String localPath, List<String> subOriginalFilenamesList,
                                        FinalImportExcelDto dto, List<String> noInYearList, List<String> exceptionList, Integer[] successNum) {

        //年份
        String year = dto.getYear();
        Integer deptNameBy = dto.getDeptNameBy();
        String area = dto.getArea();

        int i = 0;
        for (String fileName : subFileNamesList ) {
            String absolutePathName = localPath + StringUtils.substringAfter(fileName, Constants.RESOURCE_PREFIX);
            String originalFilename = subOriginalFilenamesList.size() - 1 >= i ? subOriginalFilenamesList.get(i++) : "";
            try {

                //读取 excel中文件名的编制单位名称
                String deptName = "";
                if(null != deptNameBy && 2 == deptNameBy) {
                    //田东 部分部门的取 文件名的 编制单位来匹配
                    deptName = originalFilename.substring(0, originalFilename.indexOf("."));
                }

                List<FinalData> finalDataList = finalDataService.lambdaQuery().eq(FinalData::getExcelDeptName, deptName).eq(FinalData::getType, 2)
                        .eq(FinalData::getYear, year).eq(FinalData::getStatus, FinalDataStatus.YEAR_DATA.getCode())
                        .select(FinalData::getId, FinalData::getDeptId, FinalData::getTemplateExportType).orderByDesc(FinalData::getId).list();

                FinalData finalData = null;
                if(!finalDataList.isEmpty()) {
                    finalData = finalDataList.get(0);
                }
                if(null == finalData) {
                    log.error("[{}] 未查到已导入的决算年份的数据~, 请先导入决算年份的数据， 再导入前一年的数据", deptName);

                    noInYearList.add(String.format("[%s] 决算单位没有在已导入的本年数据中", deptName));
                    continue;
                }

                //读取 excel
                BudgetDataVo lastDataVo = ReadBudgetExcelY23Utils.getSheetInfos(null, absolutePathName, 1, year, finalData.getTemplateExportType());

                //查询 报告数据json
                ReportExplain reportExplain = reportExplainService.getReportExplainByReportId(finalData.getId());
                if(null == reportExplain) {
                    log.error("[{}] 未查到已导入的决算年份的报告数据~, 请先导入决算年份的数据， 再导入前一年的数据", deptName);

                    noInYearList.add(String.format("[%s] 决算单位没有在已导入的本年数据中", deptName));
                    continue;
                }

                BudgetDataVo dataVo = JSONUtil.toBean(reportExplain.getJsonContent(), BudgetDataVo.class);
                //设置 去年数据 对比
                UpdateReportBudgetData.initValue(lastDataVo.getExplainVo());
                UpdateReportBudgetData.setLastValue(dataVo.getExplainVo(), lastDataVo.getExplainVo());

                //博白 预算模板2 查询下级是否有行政和事业单位
                List<SysDept> tb5ChildDeptList = sysDeptService.checkChildDeptAttrAndSumTotal(UpdateReportBudgetData.subDeptCodeListTb5(lastDataVo.getExplainVo().getP3p5Vo().getSubDeptList()), area);
                UpdateReportBudgetData.setBudgetP3p10p1VoByDeptAttr(lastDataVo.getExplainVo().getP3p5Vo().getSubDeptList(), lastDataVo.getExplainVo().getP3p10Vo().getP1Vo(), tb5ChildDeptList);
                //比对 p3p10去年的数据
                UpdateReportBudgetData.setP3p10P1Last(dataVo.getExplainVo().getP3p10Vo().getP1Vo(), lastDataVo.getExplainVo().getP3p10Vo().getP1Vo(), false);

                //json 转对象
                String jsonTableContent = JSONUtil.toJsonStr(dataVo);

                //更新 保存 去年的 excel数据
                FinalData updateEntity = new FinalData();
                updateEntity.setId(finalData.getId());
                updateEntity.setStatus(FinalDataStatus.LAST_DATA.getCode());
                //保存 去年的 数据的 excel
                updateEntity.setExcelUrlLast(fileName);

                //保存 excel读取数据
                boolean updateRes = finalDataService.saveFinalDataAndReportExplain(updateEntity, jsonTableContent, reportExplain.getId());

                if(updateRes) {
                    successNum[0]++;
                }
            } catch (Exception e) {
                log.error("读取 预决算以及保存的过程异常: [{}],  [{}]", e.getMessage(), e);
                exceptionList.add(String.format("[%s] 决算单位导入的出现异常：[%s]", originalFilename, e.getMessage()));
            }
        }
    }

}
