package com.hz.pm.api.projectlib.manage;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.NumberUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.hz.pm.api.common.compare.CompareUtils;
import com.hz.pm.api.common.enumeration.CommonEnum;
import com.hz.pm.api.common.enumeration.ProjectProcessStageEnum;
import com.hz.pm.api.common.helper.UserInfoHelper;
import com.hz.pm.api.common.model.constant.BizConst;
import com.hz.pm.api.common.model.constant.CommonConst;
import com.hz.pm.api.common.model.entity.ExcelExportWriter;
import com.hz.pm.api.common.statemachine.util.StateMachineUtil;
import com.hz.pm.api.common.util.BizUtils;
import com.hz.pm.api.common.util.ExcelDownUtil;
import com.hz.pm.api.dashboard.handle.ApplicationHandler;
import com.hz.pm.api.datascope.model.DataScopeDTO;
import com.hz.pm.api.datascope.utils.DataScopeUtil;
import com.hz.pm.api.expert.model.entity.ExpertReview;
import com.hz.pm.api.expert.model.enumeration.ReviewTemplateTypeEnum;
import com.hz.pm.api.expert.service.IExpertReviewService;
import com.hz.pm.api.filemanage.model.entity.ProjectApplyBorrow;
import com.hz.pm.api.filemanage.service.INdProjectApplyBorrowService;
import com.hz.pm.api.gov.model.vo.GovBizProjectDetailVO;
import com.hz.pm.api.gov.utils.ProjectConvertUtil;
import com.hz.pm.api.performance.model.dto.ProjectCoreBusinessDTO;
import com.hz.pm.api.performance.model.entity.ProjectCoreBusinessIndicators;
import com.hz.pm.api.performance.service.IProjectCoreBusinessIndicatorsService;
import com.hz.pm.api.portrait.model.entity.ProjectTag;
import com.hz.pm.api.portrait.model.entity.Tag;
import com.hz.pm.api.portrait.model.vo.TagVO;
import com.hz.pm.api.portrait.service.IProjectTagService;
import com.hz.pm.api.portrait.service.ITagService;
import com.hz.pm.api.projectdeclared.model.dto.DelayedApplyDTO;
import com.hz.pm.api.projectdeclared.model.entity.*;
import com.hz.pm.api.projectdeclared.model.vo.*;
import com.hz.pm.api.projectdeclared.service.*;
import com.hz.pm.api.projectdeclared.utils.GenerateProjectCodeUtil;
import com.hz.pm.api.projectlib.handle.ProcessExecuteChainHandle;
import com.hz.pm.api.projectlib.helper.ProjectHelper;
import com.hz.pm.api.projectlib.model.constant.ProjectConstant;
import com.hz.pm.api.projectlib.model.dto.ProjectDTO;
import com.hz.pm.api.projectlib.model.entity.*;
import com.hz.pm.api.projectlib.model.enumeration.InstTypeEnum;
import com.hz.pm.api.projectlib.model.enumeration.ProjectRenewalApprovalStatusEnum;
import com.hz.pm.api.projectlib.model.enumeration.ProjectStatusEnum;
import com.hz.pm.api.projectlib.model.po.ProjectPO;
import com.hz.pm.api.projectlib.model.req.ProjectApplicationListReq;
import com.hz.pm.api.projectlib.model.req.ProjectListReq;
import com.hz.pm.api.projectlib.model.vo.AnnualAmountVO;
import com.hz.pm.api.projectlib.model.vo.ProjectApplicationVO;
import com.hz.pm.api.projectlib.model.vo.ProjectDetailVO;
import com.hz.pm.api.projectlib.model.vo.ProjectLibListItemVO;
import com.hz.pm.api.projectlib.service.*;
import com.hz.pm.api.safety.model.entity.PersonSafetyInfo;
import com.hz.pm.api.safety.model.entity.SupplierSafetyQualification;
import com.hz.pm.api.safety.model.vo.PersonSafetyInfoVO;
import com.hz.pm.api.safety.model.vo.SafetyMonitorVO;
import com.hz.pm.api.safety.model.vo.SupplierSafetyQualificationVO;
import com.hz.pm.api.safety.service.IPersonSafetyInfoService;
import com.hz.pm.api.safety.service.ISupplierSafetyQualificationService;
import com.hz.pm.api.todocenter.model.req.ProcessDetailReq;
import com.hz.pm.api.todocenter.service.ITodoService;
import com.hz.pm.api.user.security.model.UserFullInfoDTO;
import com.hz.pm.api.user.security.model.UserInfoDetails;
import com.hz.pm.api.user.util.LoginUserUtil;
import com.ningdatech.basic.function.VUtils;
import com.ningdatech.basic.model.PageVo;
import com.ningdatech.basic.util.CollUtils;
import com.ningdatech.file.entity.File;
import com.ningdatech.file.service.FileService;
import com.wflow.exception.BusinessException;
import com.wflow.workflow.bean.vo.ProcessDetailVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.flowable.engine.TaskService;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskInfo;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * ProjectLibManage
 * </p>
 *
 * @author WendyYang
 * @since 14:19 2023/2/1
 */
@Component
@RequiredArgsConstructor
@Slf4j
public class ProjectLibManage {

    private final IProjectService projectService;
    private final IProjectApplicationService applicationService;
    private final IProjectRenewalFundDeclarationService renewalFundDeclarationService;
    private final ProcessExecuteChainHandle processExecuteHandle;
    private final FileService fileService;
    private final IProjectApplicationService projectApplicationService;
    private final GenerateProjectCodeUtil generateProjectCodeUtil;
    private final IProjectInstService projectInstService;
    private final StateMachineUtil stateMachineUtil;
    private final INdProjectDelayApplyService projectDelayApplyService;
    private final INdProjectApplyBorrowService projectApplyBorrowService;
    private final IContractService contractService;
    private final IPurchaseService purchaseService;
    private final IPreInsAcceptancePersonService acceptancePersonService;
    private final IPaymentPlanService paymentPlanService;
    private final ITodoService todoService;
    private final IProjectCoreBusinessIndicatorsService projectCoreBusinessIndicatorsService;

    private final IOperationService operationService;

    private final IProjectTagService projectTagService;
    private final ITagService tagService;

    private final ISupplierSafetyQualificationService supplierSafetyQualificationService;

    private final IPersonSafetyInfoService personSafetyInfoService;

    private final ApplicationHandler applicationHandler;

    private final TaskService taskService;

    private final UserInfoHelper userInfoHelper;

    private final IExpertReviewService expertReviewService;

    public PageVo<ProjectLibListItemVO> projectLibList(ProjectListReq req) {
        LambdaQueryWrapper<Project> query = ProjectHelper.projectQuery(req);
        query.eq(Project::getNewest, Boolean.TRUE);
        Page<Project> page = projectService.page(req.page(), query);

        if (CollUtil.isEmpty(page.getRecords())) {
            return PageVo.empty();
        }

        List<String> projectCodes = page.getRecords().stream()
                .map(Project::getProjectCode).collect(Collectors.toList());
        Map<Long, List<ProjectRenewalFundDeclaration>> renewalMap = listRenewDeclarations(projectCodes);

        List<ProjectLibListItemVO> records = CollUtils.convert(page.getRecords(), w -> {
            ProjectLibListItemVO item = new ProjectLibListItemVO();
            item.setId(w.getId());
            item.setProjectName(w.getProjectName());
            item.setProjectCode(w.getProjectCode());
            item.setArea(w.getArea());
            item.setAreaCode(w.getAreaCode());
            item.setCreateOn(w.getCreateOn());
            item.setDeclaredAmount(w.getDeclareAmount());
            item.setStage(w.getStage());
            item.setStatus(w.getStatus());
            item.setProjectType(w.getProjectType());
            item.setProjectYear(w.getProjectYear());
            item.setBuildOrg(w.getBuildOrgName());
            item.setBizDomain(w.getBizDomain());
            item.setProcessStatus(w.getProcessStatus());
            item.setInstCode(w.getInstCode());
            item.setIsHigherSuperOrg(w.getIsHigherSuperOrg());
            List<ProjectRenewalFundDeclaration> fundDeclarations = renewalMap.get(w.getId());
            if (fundDeclarations != null) {
                item.setAnnualAccumulateAmount(computeAmount(w.getAnnualPlanAmount(), fundDeclarations));
                item.setAnnualAccumulateAmountList(convertAccmulate(w, fundDeclarations));
            } else {
                item.setAnnualAccumulateAmount(w.getAnnualPlanAmount());
                item.setAnnualAccumulateAmountList(convertAccmulate(w, Collections.emptyList()));
            }
            return item;
        });
        return PageVo.of(records, page.getTotal());
    }

    public PageVo<ProjectLibListItemVO> projectLibListWithPermission(ProjectListReq req, UserFullInfoDTO user) {
        LambdaQueryWrapper<Project> query = ProjectHelper.projectQuery(req);
        user = buildProjectLibPermission(query, user);
        //项目查最新
        query.eq(Project::getNewest, Boolean.TRUE);
        Page<Project> page = projectService.page(req.page(), query);

        if (CollUtil.isEmpty(page.getRecords())) {
            return PageVo.empty();
        }
        UserFullInfoDTO finalUser = user;

        Set<String> projectCodes = page.getRecords().stream()
                .map(Project::getProjectCode).collect(Collectors.toSet());
        Map<String, List<TagVO>> projectTagMap = getProjectTags(projectCodes);

        Map<Long, List<ProjectRenewalFundDeclaration>> renewalMap = listRenewDeclarations(projectCodes);
        List<ProjectLibListItemVO> records = CollUtils.convert(page.getRecords(), w -> {
            ProjectLibListItemVO item = new ProjectLibListItemVO();
            item.setId(w.getId());
            item.setProjectName(w.getProjectName());
            item.setProjectCode(w.getProjectCode());
            item.setArea(w.getArea());
            item.setAreaCode(w.getAreaCode());
            item.setCreateOn(w.getCreateOn());
            item.setDeclaredAmount(w.getDeclareAmount());
            item.setStage(w.getStage());
            item.setStatus(w.getStatus());
            item.setProjectType(w.getProjectType());
            item.setProjectYear(w.getProjectYear());
            item.setBuildOrg(w.getBuildOrgName());
            item.setBizDomain(w.getBizDomain());
            item.setProcessStatus(w.getProcessStatus());
            item.setInstCode(w.getInstCode());
            item.setIsHigherSuperOrg(w.getIsHigherSuperOrg());
            if (finalUser.getIsOrgAdmin() && ProjectStatusEnum.PENDING_PREQUALIFICATION.eq(item.getStatus())
                    && StringUtils.isNotBlank(w.getSuperOrgCode())
                    && w.getSuperOrgCode().equals(finalUser.getMhUnitIdStr())) {
                item.setCanPreDeclared(Boolean.TRUE);
            }
            item.setApprovedAmount(w.getApprovalAmount());
            item.setAnnualPlanAmount(w.getAnnualPlanAmount());
            item.setPrePlanProjectId(w.getPrePlanProjectId());
            item.setTags(projectTagMap.get(w.getProjectCode()));
            List<ProjectRenewalFundDeclaration> fundDeclarations = renewalMap.get(w.getId());
            if (fundDeclarations != null) {
                item.setAnnualAccumulateAmount(computeAmount(w.getAnnualPlanAmount(), fundDeclarations));
                item.setAnnualAccumulateAmountList(convertAccmulate(w, fundDeclarations));
            } else {
                item.setAnnualAccumulateAmount(w.getAnnualPlanAmount());
                item.setAnnualAccumulateAmountList(convertAccmulate(w, Collections.emptyList()));
            }
            return item;
        });
        return PageVo.of(records, page.getTotal());
    }

    /**
     * 项目列表带当前审批人(工作流的)
     *
     * @param req
     * @param user
     * @return
     */
    public PageVo<ProjectLibListItemVO> projectLibListApprove(ProjectListReq req, UserFullInfoDTO user) {
        LambdaQueryWrapper<Project> query = ProjectHelper.projectQuery(req);
        user = buildProjectLibPermission(query, user);
        //项目查最新
        query.eq(Project::getNewest, Boolean.TRUE);
        Page<Project> page = projectService.page(req.page(), query);
        if (CollUtil.isEmpty(page.getRecords())) {
            return PageVo.empty();
        }
        UserFullInfoDTO finalUser = user;

        Set<String> instCodes = page.getRecords().stream()
                .map(Project::getInstCode)
                .filter(StringUtils::isNotBlank).collect(Collectors.toSet());
        List<Task> tasks = taskService.createTaskQuery()
                .processInstanceIdIn(instCodes)
                .orderByTaskCreateTime()
                .asc()
                .list();
        Map<String, List<Task>> map = Maps.newHashMap();
        Map<Long, UserFullInfoDTO> userMap = Maps.newHashMap();
        if (CollUtil.isNotEmpty(tasks)) {
            map = tasks.stream().collect(Collectors.groupingBy(Task::getProcessInstanceId));
            userMap = searchUser(tasks, userInfoHelper);
        }

        List<ProjectLibListItemVO> records = Lists.newArrayList();
        for (Project w : page.getRecords()) {
            ProjectLibListItemVO item = new ProjectLibListItemVO();
            item.setId(w.getId());
            item.setProjectName(w.getProjectName());
            item.setProjectCode(w.getProjectCode());
            item.setArea(w.getArea());
            item.setAreaCode(w.getAreaCode());
            item.setCreateOn(w.getCreateOn());
            item.setDeclaredAmount(w.getDeclareAmount());
            item.setStage(w.getStage());
            item.setStatus(w.getStatus());
            item.setProjectType(w.getProjectType());
            item.setProjectYear(w.getProjectYear());
            item.setBuildOrg(w.getBuildOrgName());
            item.setBizDomain(w.getBizDomain());
            item.setProcessStatus(w.getProcessStatus());
            item.setInstCode(w.getInstCode());
            item.setIsHigherSuperOrg(w.getIsHigherSuperOrg());
            item.setApproveUsers(buildApproveUsers(w.getInstCode(), map, userMap));
            if (finalUser.getIsOrgAdmin() && ProjectStatusEnum.PENDING_PREQUALIFICATION.eq(item.getStatus())
                    && StringUtils.isNotBlank(w.getSuperOrgCode())
                    && w.getSuperOrgCode().equals(finalUser.getMhUnitIdStr())) {
                item.setCanPreDeclared(Boolean.TRUE);
            }
            item.setApprovedAmount(w.getApprovalAmount());
            item.setAnnualPlanAmount(w.getAnnualPlanAmount());
            item.setPrePlanProjectId(w.getPrePlanProjectId());
            records.add(item);
        }
        return PageVo.of(records, page.getTotal());
    }

    private List<UserFullInfoDTO> buildApproveUsers(String instCode, Map<String, List<Task>> map,
                                                    Map<Long, UserFullInfoDTO> userMap) {
        if (map.containsKey(instCode)) {
            List<Task> tasks = map.get(instCode);
            return tasks.stream().map(TaskInfo::getAssignee)
                    .filter(NumberUtil::isNumber)
                    .map(w -> userMap.get(Long.parseLong(w)))
                    .filter(Objects::nonNull)
                    .collect(Collectors.toList());
        }
        return Collections.emptyList();
    }

    private Map<Long, UserFullInfoDTO> searchUser(List<Task> tasks, UserInfoHelper userInfoHelper) {
        if (CollUtil.isEmpty(tasks)) {
            return Collections.emptyMap();
        }
        Set<Long> userIds = tasks.stream().filter(w -> NumberUtil.isNumber(w.getAssignee()))
                .map(w -> Long.parseLong(w.getAssignee()))
                .collect(Collectors.toSet());
        if (userIds.isEmpty()) {
            return Collections.emptyMap();
        }
        List<UserFullInfoDTO> users = userInfoHelper.listUserFullInfoByUserIds(userIds);
        return users.stream().collect(Collectors.toMap(UserFullInfoDTO::getUserId, u -> u));
    }

    private Map<String, List<TagVO>> getProjectTags(Collection<String> projectCodes) {
        Map<String, List<ProjectTag>> projectTagMap = projectTagService.listByProjectCodes(projectCodes);
        if (CollUtil.isNotEmpty(projectTagMap)) {
            Set<Long> tagIds = projectTagMap.values().stream()
                    .flatMap(Collection::stream)
                    .map(ProjectTag::getTagId)
                    .collect(Collectors.toSet());
            List<Tag> tags = tagService.listByIds(tagIds);
            Map<Long, Tag> tagMap = CollUtils.listToMap(tags, Tag::getId);
            return projectTagMap.entrySet().stream()
                    .collect(Collectors.toMap(Map.Entry::getKey, w -> w.getValue().stream().map(tg -> {
                        Tag tag = tagMap.get(tg.getTagId());
                        if (tag == null) {
                            return null;
                        }
                        TagVO tagVO = new TagVO();
                        tagVO.setId(tag.getId());
                        tagVO.setName(tag.getName());
                        return tagVO;
                    }).filter(Objects::nonNull).collect(Collectors.toList())));
        }
        return Collections.emptyMap();
    }

    /**
     * 项目申报和项目归集的并集
     *
     * @param req
     * @param user
     * @return
     */
    public PageVo<ProjectLibListItemVO> projectAllListWithPermission(ProjectListReq req, UserFullInfoDTO user) {
        if (!buildProjectLibPermission(req, user)) {
            return PageVo.empty();
        }
        Page<ProjectPO> page = projectService.pageAllWithPermission(req.page(), req);
        if (CollUtil.isEmpty(page.getRecords())) {
            return PageVo.empty();
        }
        Set<String> projectCodes = CollUtils.fieldSet(page.getRecords(), ProjectPO::getProjectCode);
        Map<String, List<TagVO>> projectTagsMap = getProjectTags(projectCodes);
        Map<Long, List<ProjectRenewalFundDeclaration>> renewalMap = listRenewDeclarations(projectCodes);

        List<ProjectLibListItemVO> records = CollUtils.convert(page.getRecords(), w -> {
            ProjectLibListItemVO item = new ProjectLibListItemVO();
            item.setId(w.getId());
            item.setFromType(w.getFromType());
            item.setProjectName(w.getProjectName());
            item.setProjectCode(w.getProjectCode());
            item.setArea(w.getArea());
            item.setAreaCode(w.getAreaCode());
            item.setCreateOn(w.getCreateOn());
            item.setDeclaredAmount(w.getDeclareAmount());
            item.setStage(w.getStage());
            item.setStatus(w.getStatus());
            item.setProjectType(w.getProjectType());
            item.setProjectYear(w.getProjectYear());
            item.setBuildOrg(w.getBuildOrgName());
            item.setBizDomain(w.getBizDomain());
            item.setProcessStatus(w.getProcessStatus());
            item.setInstCode(w.getInstCode());
            item.setIsHigherSuperOrg(w.getIsHigherSuperOrg());
            if (user.getIsOrgAdmin() && ProjectStatusEnum.PENDING_PREQUALIFICATION.eq(item.getStatus())
                    && StringUtils.isNotBlank(w.getSuperOrgCode())
                    && w.getSuperOrgCode().equals(user.getMhUnitIdStr())) {
                item.setCanPreDeclared(Boolean.TRUE);
            }
            item.setApprovedAmount(w.getApprovalAmount());
            item.setAnnualPlanAmount(w.getAnnualPlanAmount());
            item.setPrePlanProjectId(w.getPrePlanProjectId());
            item.setTags(projectTagsMap.get(w.getProjectCode()));
            List<ProjectRenewalFundDeclaration> currRenewDeclarations = renewalMap.get(item.getId());
            if (currRenewDeclarations != null) {
                item.setAnnualAccumulateAmount(computeAmount(w.getAnnualPlanAmount(), currRenewDeclarations));
                item.setAnnualAccumulateAmountList(convertAccumulate(w, currRenewDeclarations));
            } else {
                item.setAnnualAccumulateAmount(w.getAnnualPlanAmount());
                item.setAnnualAccumulateAmountList(convertAccumulate(w, Collections.emptyList()));
            }
            return item;
        });
        return PageVo.of(records, page.getTotal());
    }

    public PageVo<ProjectLibListItemVO> libListWithPermission(ProjectListReq req, UserFullInfoDTO user) {
        if (!buildProjectLibPermission(req, user)) {
            return PageVo.empty();
        }
        Page<ProjectPO> page = projectService.pageLibWithPermission(req.page(), req);

        if (CollUtil.isEmpty(page.getRecords())) {
            return PageVo.empty();
        }
        Set<String> projectCodes = page.getRecords().stream()
                .map(ProjectPO::getProjectCode).collect(Collectors.toSet());
        Map<String, List<TagVO>> projectTagsMap = getProjectTags(projectCodes);
        Map<Long, List<ProjectRenewalFundDeclaration>> renewalMap = listRenewDeclarations(projectCodes);

        List<ProjectLibListItemVO> records = CollUtils.convert(page.getRecords(), w -> {
            ProjectLibListItemVO item = new ProjectLibListItemVO();
            item.setId(w.getId());
            item.setFromType(w.getFromType());
            item.setProjectName(w.getProjectName());
            item.setProjectCode(w.getProjectCode());
            item.setArea(w.getArea());
            item.setAreaCode(w.getAreaCode());
            item.setCreateOn(w.getCreateOn());
            item.setDeclaredAmount(w.getDeclareAmount());
            item.setStage(w.getStage());
            item.setStatus(w.getStatus());
            item.setProjectType(w.getProjectType());
            item.setProjectYear(w.getProjectYear());
            item.setBuildOrg(w.getBuildOrgName());
            item.setBizDomain(w.getBizDomain());
            item.setProcessStatus(w.getProcessStatus());
            item.setInstCode(w.getInstCode());
            item.setIsHigherSuperOrg(w.getIsHigherSuperOrg());
            if (user.getIsOrgAdmin() && ProjectStatusEnum.PENDING_PREQUALIFICATION.eq(item.getStatus())
                    && StringUtils.isNotBlank(w.getSuperOrgCode())
                    && w.getSuperOrgCode().equals(user.getMhUnitIdStr())) {
                item.setCanPreDeclared(Boolean.TRUE);
            }
            item.setApprovedAmount(w.getApprovalAmount());
            item.setAnnualPlanAmount(w.getAnnualPlanAmount());
            item.setPrePlanProjectId(w.getPrePlanProjectId());
            item.setTags(projectTagsMap.get(w.getProjectCode()));
            List<ProjectRenewalFundDeclaration> currRenewDeclarations = renewalMap.get(item.getId());
            if (currRenewDeclarations != null) {
                item.setAnnualAccumulateAmount(computeAmount(w.getAnnualPlanAmount(), currRenewDeclarations));
                item.setAnnualAccumulateAmountList(convertAccumulate(w, currRenewDeclarations));
            } else {
                item.setAnnualAccumulateAmount(w.getAnnualPlanAmount());
                item.setAnnualAccumulateAmountList(convertAccumulate(w, Collections.emptyList()));
            }
            return item;
        });
        return PageVo.of(records, page.getTotal());
    }

    private Map<Long, List<ProjectRenewalFundDeclaration>> listRenewDeclarations(Collection<String> projectCodes) {
        Map<Long, List<ProjectRenewalFundDeclaration>> renewalMap;
        if (CollUtil.isNotEmpty(projectCodes)) {
            List<ProjectRenewalFundDeclaration> renewalDeclarations = renewalFundDeclarationService.list(Wrappers.lambdaQuery(ProjectRenewalFundDeclaration.class)
                    .in(ProjectRenewalFundDeclaration::getProjectCode, projectCodes)
                    .eq(ProjectRenewalFundDeclaration::getApprovalStatus, ProjectRenewalApprovalStatusEnum.PASS.name())
                    .eq(ProjectRenewalFundDeclaration::getDeleted, Boolean.FALSE)
                    .orderByAsc(ProjectRenewalFundDeclaration::getProjectYear));
            renewalMap = CollUtils.group(renewalDeclarations, ProjectRenewalFundDeclaration::getProjectId);
        } else {
            renewalMap = Collections.emptyMap();
        }
        return renewalMap;
    }

    public PageVo<ProjectLibListItemVO> listWithPermissionWorkBench(ProjectListReq req,
                                                                    UserFullInfoDTO user) {
        if (!buildProjectLibPermission(req, user)) {
            return PageVo.empty();
        }
        Page<ProjectPO> page = projectService.pageLibWithPermission(req.page(), req);

        if (CollUtil.isEmpty(page.getRecords())) {
            return PageVo.empty();
        }

        Set<String> instCodes = page.getRecords().stream()
                .map(ProjectPO::getInstCode)
                .filter(StringUtils::isNotBlank)
                .collect(Collectors.toSet());
        List<Task> tasks = taskService.createTaskQuery()
                .processInstanceIdIn(instCodes)
                .orderByTaskCreateTime()
                .asc()
                .list();
        Map<String, List<Task>> map = Maps.newHashMap();
        Map<Long, UserFullInfoDTO> userMap = Maps.newHashMap();
        if (CollUtil.isNotEmpty(tasks)) {
            map = tasks.stream()
                    .collect(Collectors.groupingBy(Task::getProcessInstanceId));
            userMap = searchUser(tasks, userInfoHelper);
        }

        List<ProjectLibListItemVO> records = Lists.newArrayList();
        for (ProjectPO w : page.getRecords()) {
            ProjectLibListItemVO item = new ProjectLibListItemVO();
            item.setId(w.getId());
            item.setProjectName(w.getProjectName());
            item.setProjectCode(w.getProjectCode());
            item.setArea(w.getArea());
            item.setAreaCode(w.getAreaCode());
            item.setCreateOn(w.getCreateOn());
            item.setDeclaredAmount(w.getDeclareAmount());
            item.setStage(w.getStage());
            item.setStatus(w.getStatus());
            item.setProjectType(w.getProjectType());
            item.setProjectYear(w.getProjectYear());
            item.setBuildOrg(w.getBuildOrgName());
            item.setBizDomain(w.getBizDomain());
            item.setProcessStatus(w.getProcessStatus());
            item.setInstCode(w.getInstCode());
            item.setIsHigherSuperOrg(w.getIsHigherSuperOrg());
            item.setApproveUsers(buildApproveUsers(w.getInstCode(), map, userMap));
            if (user.getIsOrgAdmin() && ProjectStatusEnum.PENDING_PREQUALIFICATION.eq(item.getStatus())
                    && StringUtils.isNotBlank(w.getSuperOrgCode())
                    && w.getSuperOrgCode().equals(user.getMhUserId())) {
                item.setCanPreDeclared(Boolean.TRUE);
            }
            item.setApprovedAmount(w.getApprovalAmount());
            item.setAnnualPlanAmount(w.getAnnualPlanAmount());
            item.setPrePlanProjectId(w.getPrePlanProjectId());
            records.add(item);
        }
        return PageVo.of(records, page.getTotal());
    }

    public static BigDecimal computeAmount(BigDecimal currentAmount, List<ProjectRenewalFundDeclaration> prfs) {
        BigDecimal res = Objects.isNull(currentAmount) ? BigDecimal.ZERO : currentAmount;
        for (ProjectRenewalFundDeclaration prf : prfs) {
            res = res.add(prf.getAnnualPaymentAmount());
        }
        return res;
    }

    public static List<AnnualAmountVO> convertAccumulate(ProjectPO project, List<ProjectRenewalFundDeclaration> prfs) {
        List<AnnualAmountVO> annualAmounts = Lists.newArrayList();
        annualAmounts.add(AnnualAmountVO.builder()
                .projectId(project.getId())
                .projectYear(project.getProjectYear())
                .annualAmount(project.getAnnualPlanAmount())
                .build());
        for (ProjectRenewalFundDeclaration prf : prfs) {
            annualAmounts.add(AnnualAmountVO.builder()
                    .projectId(prf.getProjectId())
                    .projectYear(prf.getProjectYear())
                    .annualAmount(prf.getAnnualPaymentAmount())
                    .build());
        }
        return annualAmounts;
    }

    public static List<AnnualAmountVO> convertAccmulate(Project project, List<ProjectRenewalFundDeclaration> prfs) {
        List<AnnualAmountVO> annualAmounts = Lists.newArrayList();
        annualAmounts.add(AnnualAmountVO.builder()
                .projectId(project.getId())
                .projectYear(project.getProjectYear())
                .annualAmount(project.getAnnualPlanAmount())
                .build());
        for (ProjectRenewalFundDeclaration prf : prfs) {
            annualAmounts.add(AnnualAmountVO.builder()
                    .projectId(prf.getProjectId())
                    .projectYear(prf.getProjectYear())
                    .annualAmount(prf.getAnnualPaymentAmount())
                    .build());
        }
        return annualAmounts;
    }

    public List<Project> projectList(ProjectListReq req) {
        LambdaQueryWrapper<Project> query = ProjectHelper.projectQuery(req);
        return projectService.list(query);
    }

    /**
     * @param projectId 项目详情
     * @return com.hz.pm.api.projectlib.model.entity.vo.ProjectDetailVO
     * @author ZPF
     * @since 2023/02/11 11:15
     */
    public ProjectDetailVO getProjectDetail(Long projectId) {
        // 查询最新的项目申报信息
        Project projectInfo = projectService.getNewProject(projectId);
        if (Objects.isNull(projectInfo)) {
            return null;
        }
        return getProjectDetailLogic(projectInfo);
    }

    public ProjectDetailVO detailProjectCode(String projectCode) {
        // 查询最新的项目申报信息
        Project projectInfo = projectService.getProjectByCode(projectCode);
        if (Objects.isNull(projectInfo)) {
            return null;
        }
        return getProjectDetailLogic(projectInfo);
    }

    /**
     * @param projectId 项目详情 不查询最新版本
     * @return com.hz.pm.api.projectlib.model.entity.vo.ProjectDetailVO
     * @author ZPF
     * @since 2023/02/11 11:15
     */
    public ProjectDetailVO getProjectDetailThisVersion(Long projectId) {
        // 查询最新的项目申报信息
        Project projectInfo = projectService.getById(projectId);
        if (Objects.isNull(projectInfo)) {
            return null;
        }
        return getProjectDetailLogic(projectInfo);
    }

    private ProjectDetailVO getProjectDetailLogic(Project projectInfo) {
        ProjectDetailVO vo = new ProjectDetailVO();
        BeanUtils.copyProperties(projectInfo, vo);
        vo.buildDynamicForm(projectInfo.getDynamicForm());
        // 查询应用
        List<ProjectApplication> applications = applicationService.list(Wrappers.lambdaQuery(ProjectApplication.class)
                .eq(ProjectApplication::getProjectCode, vo.getProjectCode())
                .eq(ProjectApplication::getIsConstruct, projectInfo.getIsConstruct())
                .eq(ProjectApplication::getProjectVersion, projectInfo.getVersion()));

        List<Long> applicationIds = applications.stream().map(ProjectApplication::getId).collect(Collectors.toList());

        //核心业务
        if (CollUtil.isNotEmpty(applicationIds)) {
            List<ProjectCoreBusinessIndicators> coreBizs = projectCoreBusinessIndicatorsService.list(Wrappers.lambdaQuery(ProjectCoreBusinessIndicators.class)
                    .in(ProjectCoreBusinessIndicators::getApplicationId, applicationIds));
            Map<Long, List<ProjectCoreBusinessIndicators>> coreBizMap = coreBizs.stream().collect(Collectors.groupingBy(ProjectCoreBusinessIndicators::getApplicationId));

            Optional.of(applications).ifPresent(apps ->
                    vo.setProjectApplications(CollUtils.convert(apps,
                            app -> ProjectHelper.convertVOWithMap(app, coreBizMap))
                    ));
        }

        //审批信息
        ProcessDetailReq req = new ProcessDetailReq();
        req.setInstanceId(projectInfo.getInstCode());
        req.setProjectId(projectInfo.getId());
        vo.setProcess(todoService.getProcessDetail(req));

        List<Long> allVersionProjectId = projectService.getAllVersionProjectId(projectInfo);

        //查询采购备案
        List<Purchase> purchases = purchaseService.listByProjectIds(allVersionProjectId);
        vo.setPurchases(BeanUtil.copyToList(purchases, PurchaseVO.class));

        //查询合同备案
        Contract contract = contractService.getOne(Wrappers.lambdaQuery(Contract.class)
                .eq(Contract::getProjectCode, projectInfo.getProjectCode())
                .last(BizConst.LIMIT_1));
        ContractVO contractVO = BeanUtil.copyProperties(contract, ContractVO.class);
        vo.setContract(contractVO);
        List<PaymentPlan> payments = paymentPlanService.list(Wrappers.lambdaQuery(PaymentPlan.class)
                .eq(PaymentPlan::getProjectCode, projectInfo.getProjectCode())
                .orderByAsc(PaymentPlan::getPaymentTime));
        if (Objects.nonNull(contractVO)) {
            contractVO.setPayments(convertPayments(payments,
                    contractVO.getTotalAmount()));
        }

        //查询实施信息
        Operation operation = operationService.getOne(Wrappers.lambdaQuery(Operation.class)
                .in(Operation::getProjectId, allVersionProjectId)
                .orderByAsc(Operation::getCreateOn)
                .last(BizConst.LIMIT_1));
        vo.setOperation(BeanUtil.copyProperties(operation, OperationVO.class));

        SafetyMonitorVO safetyVo = new SafetyMonitorVO();
        //查询风险监测
        List<SupplierSafetyQualification> ssq = supplierSafetyQualificationService.list(Wrappers.lambdaQuery(SupplierSafetyQualification.class)
                .eq(SupplierSafetyQualification::getProjectCode, projectInfo.getProjectCode()));
        if (CollUtil.isNotEmpty(ssq)) {
            safetyVo.setSupplierSafetyQualification(ssq.stream()
                    .map(s -> BeanUtil.copyProperties(s, SupplierSafetyQualificationVO.class))
                    .collect(Collectors.toList()));
        }
        vo.setSafetyInfo(safetyVo);

        List<PersonSafetyInfo> psi = personSafetyInfoService.list(Wrappers.lambdaQuery(PersonSafetyInfo.class)
                .eq(PersonSafetyInfo::getProjectCode, projectInfo.getProjectCode()));
        if (CollUtil.isNotEmpty(psi)) {
            safetyVo.setPersonSafetyInfo(psi.stream()
                    .map(p -> BeanUtil.copyProperties(p, PersonSafetyInfoVO.class))
                    .collect(Collectors.toList()));
        }

        //查询初验信息
        List<PreInsAcceptancePerson> acceptancePersons = acceptancePersonService.list(Wrappers.lambdaQuery(PreInsAcceptancePerson.class)
                .in(PreInsAcceptancePerson::getProjectId, allVersionProjectId)
                .orderByAsc(PreInsAcceptancePerson::getCreateOn));
        vo.setAcceptancePersons(convertPersons(acceptancePersons));

        //查询终验信息
        vo.setFinalIrsApps(applicationHandler.generateIrsApp(projectInfo.getProjectCode()));

        //查询年度投资金额 要是已验收的项目
        if (ProjectStatusEnum.ACCEPTED.getCode().equals(projectInfo.getStatus())) {
            List<AnnualAmountVO> annualAmounts = Lists.newArrayList();
            annualAmounts.add(AnnualAmountVO.builder()
                    .projectId(projectInfo.getId())
                    .projectYear(projectInfo.getProjectYear())
                    .annualAmount(projectInfo.getAnnualPlanAmount())
                    .build());
            List<ProjectRenewalFundDeclaration> renewalDeclarations = renewalFundDeclarationService.list(Wrappers.lambdaQuery(ProjectRenewalFundDeclaration.class)
                    .eq(ProjectRenewalFundDeclaration::getProjectId, vo.getId())
                    .eq(ProjectRenewalFundDeclaration::getApprovalStatus, ProjectRenewalApprovalStatusEnum.PASS.name())
                    .eq(ProjectRenewalFundDeclaration::getDeleted, Boolean.FALSE)
                    .orderByAsc(ProjectRenewalFundDeclaration::getProjectYear));
            Optional.ofNullable(renewalDeclarations).ifPresent(declarations ->
                    declarations.forEach(declaration -> annualAmounts.add(AnnualAmountVO.builder()
                            .projectId(projectInfo.getId())
                            .projectYear(declaration.getProjectYear())
                            .annualAmount(declaration.getAnnualPaymentAmount())
                            .build()))
            );
            vo.setAnnualAccumulateAmountList(annualAmounts);
        }
        // 处理文件名
        List<Long> fileIdList = new ArrayList<>();
        BizUtils.notBlank(vo.getApprovedFile(), w -> fileIdList.add(Long.parseLong(w)));
        BizUtils.notBlank(vo.getApprovedConstructionPlanFile(), w -> fileIdList.add(Long.parseLong(w)));
        if (!fileIdList.isEmpty()) {
            List<File> files = fileService.listByIds(fileIdList);
            Map<String, String> fileMap = CollUtils.listToMap(files, w -> w.getId().toString(), File::getOriginalFileName);
            vo.setApprovedFileName(fileMap.get(vo.getApprovedFile()));
            vo.setApprovedConstructionPlanFileName(fileMap.get(vo.getApprovedConstructionPlanFile()));
        }
        //专家评审 其它建议 -> 建设方案的会议
        ExpertReview finalReview = expertReviewService.getFinalReview(projectInfo.getProjectCode(),
                ReviewTemplateTypeEnum.CONSTRUCTION_SCHEME_REVIEW.getCode());

        if (Objects.nonNull(finalReview)) {
            vo.setProposeAttach(finalReview.getOtherAdvice());
            vo.setBaseReviewResults(finalReview.getReviewResult());
        }
        return vo;
    }


    /**
     * 申报新项目时 保存项目信息和其它相关联的信息
     *
     * @param projectDto
     * @param instanceId
     * @return
     */
    public Project saveProjectInDeclared(ProjectDTO projectDto, String instanceId,
                                         Long userId) {
        Project project = saveProjectNewVersion(projectDto, instanceId, userId, Boolean.FALSE);
        //保存项目和实例的关系
        ProjectInst projectInst = new ProjectInst();
        projectInst.setProjectId(project.getId());
        projectInst.setInstCode(instanceId);
        projectInst.setCreatOn(LocalDateTime.now());
        projectInst.setUpdateOn(LocalDateTime.now());
        projectInst.setInstType(ProjectProcessStageEnum.ORG_INTERNAL_APPROVAL_PROCESS.getCode());
        projectInstService.save(projectInst);
        return project;
    }


    /**
     * 建设方案申报项目时 保存项目信息和其它相关联的信息
     *
     * @param projectDto \
     * @param instanceId \
     * @param userId     \
     * @param oldProject \
     * @return \
     */
    public Project saveConstructProjectInDeclared(ProjectDTO projectDto, String instanceId,
                                                  Long userId, Project oldProject) {

        Project project = saveConstructProjectNewVersion(projectDto, instanceId, userId, oldProject);
        // 将旧的项目状态、阶段置为null,防止项目还会出现在待申报列表
        LambdaUpdateWrapper<Project> updateWrapper = Wrappers.lambdaUpdate(Project.class);
        updateWrapper.set(Project::getStage, null)
                .set(Project::getStatus, null)
                .eq(Project::getId, oldProject.getId());
        projectService.update(updateWrapper);
        //保存项目和实例的关系
        ProjectInst projectInst = new ProjectInst();
        projectInst.setProjectId(project.getId());
        projectInst.setInstCode(instanceId);
        projectInst.setCreatOn(LocalDateTime.now());
        projectInst.setUpdateOn(LocalDateTime.now());
        projectInst.setInstType(ProjectProcessStageEnum.CONSTRUCTION_PROJECT_APPROVAL_PROCESS.getCode());
        projectInstService.save(projectInst);
        return project;
    }

    /**
     * 申报新项目时 保存项目信息和其它相关联的信息
     *
     * @param projectDto
     * @param instanceId
     * @return
     */
    public Project saveProjectNewVersion(ProjectDTO projectDto, String instanceId,
                                         Long userId, Boolean isContruct) {
        //流程启动之后 入库项目 重要业务信息 用于列表查询 展示
        try {
            Project project = new Project();
            //为空 代表是新申报的
            if (Objects.isNull(projectDto.getId())) {
                BeanUtils.copyProperties(projectDto, project);
                project.setCreateOn(LocalDateTime.now());
                project.setUpdateOn(LocalDateTime.now());
                project.setStage(ProjectStatusEnum.NOT_APPROVED.getCode());
                project.setStatus(ProjectStatusEnum.UNDER_INTERNAL_AUDIT.getCode());
                project.setInstCode(instanceId);
                project.setSponsor(String.valueOf(userId));
                String projectCode = generateProjectCodeUtil.generateProjectCode(projectDto);
                project.setProjectCode(projectCode);
                if (projectService.save(project)) {
                    saveApplication(projectDto, project, null);
                }
            } else {
                //否则是重新提交的 新生成一个新版本的项目
                project = newProjectWithVersion(projectDto, isContruct);
                if (Objects.nonNull(project)) {
                    project.setInstCode(instanceId);
                    project.setSponsor(String.valueOf(userId));
                    projectService.updateById(project);
                }
            }
            return project;
        } catch (Exception e) {
            log.error("项目信息入库错误 " + e);
            throw new BusinessException("项目信息入库错误 :" + e);
        }
    }

    /**
     * 建设方案申报项目时 保存项目信息和其它相关联的信息
     *
     * @param projectDto \
     * @param instanceId \
     * @param userId     \
     * @param oldProject \
     * @return \
     */
    public Project saveConstructProjectNewVersion(ProjectDTO projectDto, String instanceId,
                                                  Long userId, Project oldProject) {
        //流程启动之后 入库项目 重要业务信息 用于列表查询 展示
        try {
            Project project = new Project();
            //为空 代表是新申报的
            if (Objects.isNull(projectDto.getId())) {
                BeanUtils.copyProperties(projectDto, project);
                // 被撤回重新申报的项目,项目ID要置空
                project.setId(null);
                project.setCreateOn(LocalDateTime.now());
                project.setUpdateOn(LocalDateTime.now());
                project.setStage(ProjectStatusEnum.NOT_APPROVED.getCode());
                project.setStatus(ProjectStatusEnum.SCHEME_UNDER_REVIEW.getCode());
                project.setInstCode(instanceId);
                project.setSponsor(String.valueOf(userId));
                // 项目编号不变，版本号加1
                project.setProjectCode(oldProject.getProjectCode());
                project.setVersion(oldProject.getVersion() + 1);
                // 标记为建设方案申报
                project.setIsConstruct(Boolean.TRUE);
                project.setIsBackReject(Boolean.FALSE);
                // 保存初步方案项目ID
                if (Boolean.TRUE.equals(oldProject.getIsConstruct())) {
                    // 重新提交
                    project.setPrePlanProjectId(oldProject.getPrePlanProjectId());
                } else {
                    project.setPrePlanProjectId(oldProject.getId());
                }
                if (projectService.save(project)) {
                    saveApplication(projectDto, project, Boolean.TRUE);
                    // 将旧的项目版本置为不是最新
                    projectService.reverseNewest(project.getProjectCode(), project.getId());
                }

            } else {
                //否则是被驳回,重新提交的 新生成一个新版本的项目
                project = newProjectWithVersion(projectDto, Boolean.TRUE);
                if (Objects.nonNull(project)) {
                    project.setInstCode(instanceId);
                    project.setSponsor(String.valueOf(userId));
                    projectService.updateById(project);
                }
            }
            return project;
        } catch (Exception e) {
            log.error("项目信息入库错误 " + e);
            throw new BusinessException("项目信息入库错误 :" + e);
        }
    }


    /**
     * 在其它项目阶段 保存项目信息和其它相关联的信息
     *
     * @param projectDto
     * @return
     */
    public Project reSaveProjectNewVersion(ProjectDTO projectDto, Boolean isContruct) {
        //流程启动之后 入库项目 重要业务信息 用于列表查询 展示
        try {
            return newProjectWithVersion(projectDto, isContruct);
        } catch (Exception e) {
            log.error("项目信息入库错误 " + e);
            throw new BusinessException("项目信息入库错误 :" + e);
        }
    }

    /**
     * 重新提交工作流时 舍弃在原有项目修改
     * 新增一个新的项目 新的版本号
     */
    public Project newProjectWithVersion(ProjectDTO projecDto, Boolean isContruct) {
        Project oldProject = projectService.getById(projecDto.getId());
        Project project = new Project();
        VUtils.isTrue(Objects.isNull(oldProject))
                .throwMessage("项目不存在!");
        BeanUtil.copyProperties(oldProject, project, CopyOptions.create()
                .setIgnoreError(Boolean.TRUE).setIgnoreNullValue(Boolean.TRUE));
        BeanUtil.copyProperties(projecDto, project, CopyOptions.create()
                .setIgnoreError(Boolean.TRUE).setIgnoreNullValue(Boolean.TRUE));
        project.setVersion(oldProject.getVersion() + 1);
        project.setId(null);
        project.setCreateOn(LocalDateTime.now());
        project.setUpdateOn(LocalDateTime.now());
        project.setIsBackReject(Boolean.FALSE);
        stateMachineUtil.pass(project);
        projectService.save(project);

        oldProject.setIsBackReject(Boolean.TRUE);
        projectService.updateById(oldProject);
        projectService.reverseNewest(project.getProjectCode(), project.getId());
        saveApplication(projecDto, project, isContruct);

        return project;
    }

    public Project saveProjectWithVersionAndStatus(ProjectDTO projectDto, Integer stageCode, Integer statusCode, Boolean isConstruct) {
        Project oldProject = projectService.getById(projectDto.getId());
        Project project = new Project();
        Assert.notNull(oldProject, "项目不存在");
        BeanUtil.copyProperties(oldProject, project, CopyOptions.create()
                .setIgnoreError(Boolean.TRUE).setIgnoreNullValue(Boolean.TRUE));
        BeanUtil.copyProperties(projectDto, project, CopyOptions.create()
                .setIgnoreError(Boolean.TRUE).setIgnoreNullValue(Boolean.TRUE));
        project.setVersion(oldProject.getVersion() + 1);
        project.setId(null);
        project.setCreateOn(LocalDateTime.now());
        project.setUpdateOn(LocalDateTime.now());
        //都是在驳回 重新提交时用的
        project.setIsBackReject(Boolean.FALSE);
        if (Objects.nonNull(stageCode)) {
            project.setStage(stageCode);
        }
        if (Objects.nonNull(statusCode)) {
            project.setStatus(statusCode);
        }

        oldProject.setIsBackReject(Boolean.TRUE);
        projectService.updateById(oldProject);

        if (projectService.save(project)) {
            projectService.reverseNewest(project.getProjectCode(), project.getId());
            saveApplication(projectDto, project, isConstruct);
        }

        return project;
    }

    public Project saveProjectWithVersion(ProjectDTO projectDto, String instanceId, Integer instType, Boolean isConstruct) {
        Project oldProject = projectService.getById(projectDto.getId());
        Project project = new Project();
        VUtils.isTrue(Objects.isNull(oldProject))
                .throwMessage("项目不存在!");
        BeanUtil.copyProperties(oldProject, project, CopyOptions.create()
                .setIgnoreError(Boolean.TRUE).setIgnoreNullValue(Boolean.TRUE));
        BeanUtil.copyProperties(projectDto, project, CopyOptions.create()
                .setIgnoreError(Boolean.TRUE).setIgnoreNullValue(Boolean.TRUE));
        project.setVersion(oldProject.getVersion() + 1);
        project.setId(null);
        project.setInstCode(instanceId);
        project.setCreateOn(LocalDateTime.now());
        project.setUpdateOn(LocalDateTime.now());

        if (projectService.save(project)) {
            //保存项目和实例的关系
            ProjectInst projectInst = new ProjectInst();
            projectInst.setProjectId(project.getId());
            projectInst.setInstCode(instanceId);
            projectInst.setCreatOn(LocalDateTime.now());
            projectInst.setUpdateOn(LocalDateTime.now());
            projectInst.setInstType(instType);
            projectInstService.save(projectInst);

            projectService.reverseNewest(project.getProjectCode(), project.getId());

            //app
            saveApplication(projectDto, project, isConstruct);
        }

        return project;
    }

    public Project saveProjectWithVersion(Project oldProject, String instanceId, Integer instType) {
        Project project = new Project();
        Assert.notNull(oldProject, "项目不存在");
        BeanUtil.copyProperties(oldProject, project, CopyOptions.create()
                .setIgnoreError(Boolean.TRUE).setIgnoreNullValue(Boolean.TRUE));
        project.setVersion(oldProject.getVersion() + 1);
        project.setId(null);
        project.setInstCode(instanceId);
        project.setCreateOn(LocalDateTime.now());
        project.setUpdateOn(LocalDateTime.now());

        if (projectService.save(project)) {
            //保存项目和实例的关系
            ProjectInst projectInst = new ProjectInst();
            projectInst.setProjectId(project.getId());
            projectInst.setInstCode(instanceId);
            projectInst.setCreatOn(LocalDateTime.now());
            projectInst.setUpdateOn(LocalDateTime.now());
            projectInst.setInstType(instType);
            projectInstService.save(projectInst);

            projectService.reverseNewest(project.getProjectCode(), project.getId());

            saveApplication(project, oldProject, null);
        }

        return project;
    }

    public Project saveProjectWithVersionAndPass(Project oldProject, String instanceId, ProjectDTO dto, Integer instType, Boolean isBackReject) {
        Project project = new Project();
        VUtils.isTrue(Objects.isNull(oldProject))
                .throwMessage("项目不存在!");
        BeanUtil.copyProperties(oldProject, project, CopyOptions.create()
                .setIgnoreError(Boolean.TRUE).setIgnoreNullValue(Boolean.TRUE));
        project.setVersion(oldProject.getVersion() + 1);
        project.setId(null);
        project.setInstCode(instanceId);
        project.setCreateOn(LocalDateTime.now());
        project.setUpdateOn(LocalDateTime.now());
        // 标识保存的项目信息是否为建设方案申报项目
        project.setIsConstruct(Boolean.TRUE);
        project.setIsBackReject(isBackReject);
        //终验材料
        project.setFinalAcceptanceMaterials(dto.getFinalAcceptanceMaterials());
        stateMachineUtil.pass(project);

        oldProject.setIsBackReject(isBackReject);
        projectService.updateById(oldProject);

        if (projectService.save(project)) {
            //保存项目和实例的关系
            ProjectInst projectInst = new ProjectInst();
            projectInst.setProjectId(project.getId());
            projectInst.setInstCode(instanceId);
            projectInst.setCreatOn(LocalDateTime.now());
            projectInst.setUpdateOn(LocalDateTime.now());
            projectInst.setInstType(instType);
            projectInstService.save(projectInst);

            projectService.reverseNewest(project.getProjectCode(), project.getId());

            //APP
            saveApplication(project, oldProject, Boolean.TRUE);
        }

        return project;
    }

    public Project saveConstructProjectWithVersionAndPass(Project oldProject, String instanceId, Integer instType) {
        Project project = new Project();
        VUtils.isTrue(Objects.isNull(oldProject))
                .throwMessage("项目不存在!");
        BeanUtil.copyProperties(oldProject, project, CopyOptions.create()
                .setIgnoreError(Boolean.TRUE).setIgnoreNullValue(Boolean.TRUE));
        project.setVersion(oldProject.getVersion() + 1);
        project.setId(null);
        project.setInstCode(instanceId);
        project.setCreateOn(LocalDateTime.now());
        project.setUpdateOn(LocalDateTime.now());
        // 标识保存的项目信息是否为建设方案申报项目
        project.setIsConstruct(Boolean.FALSE);
        stateMachineUtil.pass(project);

        oldProject.setIsBackReject(Boolean.TRUE);
        projectService.updateById(oldProject);

        if (projectService.save(project)) {
            //保存项目和实例的关系
            ProjectInst projectInst = new ProjectInst();
            projectInst.setProjectId(project.getId());
            projectInst.setInstCode(instanceId);
            projectInst.setCreatOn(LocalDateTime.now());
            projectInst.setUpdateOn(LocalDateTime.now());
            projectInst.setInstType(instType);
            projectInstService.save(projectInst);

            projectService.reverseNewest(project.getProjectCode(), project.getId());

            saveApplication(project, oldProject, Boolean.TRUE);
        }
        return project;
    }

    /**
     * 获取项目初步方案详情
     *
     * @param projectId \
     * @return com.hz.pm.api.projectlib.model.vo.ProjectDetailVO
     * @author CMM
     * @since 2023/07/12 11:43
     */
    public ProjectDetailVO getPrePlanProjectDetail(Long projectId) {
        // 查询建设方案申报前最新的项目申报信息
        Project projectInfo = projectService.getById(projectId);
        if (Objects.isNull(projectInfo)) {
            return null;
        }
        ProjectDetailVO vo = new ProjectDetailVO();
        BeanUtils.copyProperties(projectInfo, vo);
        vo.buildDynamicForm(projectInfo.getDynamicForm());
        // 查询应用
        List<ProjectApplication> applications = applicationService.list(Wrappers.lambdaQuery(ProjectApplication.class)
                .eq(ProjectApplication::getProjectCode, vo.getProjectCode())
                .eq(ProjectApplication::getIsConstruct, Boolean.FALSE)
                .eq(ProjectApplication::getProjectVersion, projectInfo.getVersion()));

        Optional.ofNullable(applications).ifPresent(apps ->
                vo.setProjectApplications(CollUtils.convert(apps,
                        ProjectHelper::convertVO)
                ));

        return vo;
    }

    public List<ProcessDetailVO> processScheduleDetail(Long projectId) {
        List<ProcessDetailVO> processDetailVOS = new ArrayList<>();
        return processExecuteHandle.handle(projectId, processDetailVOS);
    }

    private boolean checkCanRead(Set<String> subOrgSet, Project project) {
        // 从申请借阅信息表中查出本单位及下属单位审批通过的项目
        LambdaQueryWrapper<ProjectApplyBorrow> query = Wrappers.lambdaQuery(ProjectApplyBorrow.class)
                .select(ProjectApplyBorrow::getProjectId)
                .in(ProjectApplyBorrow::getApplyBorrowOrgCode, subOrgSet)
                .eq(ProjectApplyBorrow::getIsSuccess, Boolean.TRUE);
        List<Long> applyBorrowProjectIdList = CollUtils.fieldList(projectApplyBorrowService.list(query), ProjectApplyBorrow::getProjectId);
        return applyBorrowProjectIdList.contains(project.getId());
    }

    public void exportList(ProjectListReq param, HttpServletResponse response) {
        param.setPageNumber(CommonConst.EXPORT_PAGE_NUMBER);
        param.setPageSize(CommonConst.EXPORT_PAGE_SIZE);
        LambdaQueryWrapper<Project> query = ProjectHelper.projectQuery(param);
        query.eq(Project::getNewest, Boolean.TRUE);
        List<Project> projects = projectService.list(query);

        ExcelExportWriter excelExportWriter = new ExcelExportWriter();

        String fileName = "项目库列表";
        excelExportWriter.setFileName(fileName);

        List<String> sheetsNames = new ArrayList<>();
        sheetsNames.add(fileName);
        // 表体行数据集合
        List<List<String>> exportDatas = ExcelDownUtil.getProjectExportDatas(param.getExportOptionList(), projects);
        // sheet列表集合
        List<List<List<String>>> sheets = new ArrayList<>();
        sheets.add(exportDatas);
        excelExportWriter.setHeads(sheets);
        excelExportWriter.setDatas(exportDatas);
        excelExportWriter.setSheets(sheetsNames);
        excelExportWriter.setHeadPropertyClass(List.class);
        excelExportWriter.setHeadPropertyFlag(Boolean.FALSE);
        ExcelDownUtil.excelExportFile(response, excelExportWriter);
    }

    /**
     * build 项目的角色权限到req
     *
     * @param query
     */
    public UserFullInfoDTO buildProjectLibPermission(LambdaQueryWrapper<Project> query, UserFullInfoDTO user) {
        Optional<DataScopeDTO> currentUserDataScope = DataScopeUtil.getCurrentUserDataScopeHasUserId(user);
        if (!currentUserDataScope.isPresent()) {
            log.warn("没有取到权限信息 当前查询 没有权限条件");
            return user;
        }

        switch (currentUserDataScope.get().getRole()) {
            case NORMAL_MEMBER:
                //普通用户 只能看到自己单位去申报的
                query.eq(Project::getBuildOrgCode, user.getMhUnitId());
                break;
            case COMPANY_MANAGER:
                //单位管理员 看到自己单位去申报的 + 待预审的主管单位是自己单位的项目
                query.and(q1 -> q1.eq(Project::getBuildOrgCode, user.getMhUnitId()).or(q2 ->
                        q2.eq(Project::getStage, ProjectStatusEnum.NOT_APPROVED.getCode())
                                .eq(Project::getStatus, ProjectStatusEnum.PENDING_PREQUALIFICATION.getCode())
                                .eq(Project::getSuperOrgCode, user.getMhUnitId())));
                break;
            case SUPER_ADMIN:
                //超级管理员 看到丽水全市的 并且也要判断他 同时是不是单位管理员
                break;
            case VISITOR:
                //访客可以看全市的
                break;
            case DASHBOARD:
                break;
            default:
                //没有权限的话 就让它查不到
                query.eq(Project::getId, 0L);
                break;
        }
        return user;
    }

    public boolean buildProjectLibPermission(ProjectListReq req, UserFullInfoDTO user) {
        boolean queryState = true;
        Optional<DataScopeDTO> dataScope = DataScopeUtil.getCurrentUserDataScopeHasUserId(user);
        if (!dataScope.isPresent()) {
            log.warn("没有取到权限信息 当前查询 没有权限条件");
            queryState = Boolean.FALSE;
        } else {
            switch (dataScope.get().getRole()) {
                case NORMAL_MEMBER:
                    //普通用户 只能看到自己单位去申报的
                    req.setUserType("normal");
                    req.setUserValue(user.getMhUnitIdStr());
                    break;
                case COMPANY_MANAGER:
                    //单位管理员 看到自己单位去申报的 + 待预审的主管单位是自己单位的项目
                    req.setUserType("org");
                    req.setUserValue(user.getMhUnitIdStr());
                    break;
                case SUPER_ADMIN:
                    //超级管理员 看到丽水全市的 并且也要判断他 同时是不是单位管理员
                    break;
                case VISITOR:
                    //访客可以看全市的
                    break;
                case DASHBOARD:
                    break;
                default:
                    //没有权限的话 就让它查不到
                    queryState = Boolean.FALSE;
                    break;
            }
        }
        return queryState;
    }

    public void saveProjectByApplyDelay(DelayedApplyDTO dto, Project project, String instanceId) {

        // 更新项目流程实例ID
        project.setInstCode(instanceId);
        project.setCreateOn(LocalDateTime.now());
        project.setUpdateOn(LocalDateTime.now());
        projectService.updateById(project);

        // 保存延期申请信息和申请延期前项目建设周期和验收时间
        ProjectDelayApply projectDelayApply = new ProjectDelayApply();
        projectDelayApply.setDelayTime(dto.getDelayedMonth());
        projectDelayApply.setDelayApplyFile(dto.getSupportingMaterials());
        projectDelayApply.setDelayApplyReason(dto.getDelayedReason());
        projectDelayApply.setProjectId(project.getId());
        projectDelayApply.setInstanceId(instanceId);
        projectDelayApply.setCreateBy(LoginUserUtil.getUserId());
        projectDelayApply.setCreateOn(LocalDateTime.now());
        projectDelayApply.setUpdateBy(LoginUserUtil.getUserId());
        projectDelayApply.setUpdateOn(LocalDateTime.now());
        projectDelayApplyService.save(projectDelayApply);

        //保存项目和实例的关系
        ProjectInst projectInst = new ProjectInst();
        projectInst.setProjectId(project.getId());
        projectInst.setInstCode(instanceId);
        projectInst.setInstType(InstTypeEnum.APPLY_DELAY.getCode());
        projectInst.setCreatOn(LocalDateTime.now());
        projectInst.setUpdateOn(LocalDateTime.now());
        projectInstService.save(projectInst);
    }

    public void saveProjectByApplyBorrow(Project project, UserFullInfoDTO user, String instanceId) {
        // 更新项目流程实例ID
        project.setInstCode(instanceId);
        project.setCreateOn(LocalDateTime.now());
        project.setUpdateOn(LocalDateTime.now());
        projectService.updateById(project);

        // 保存项目借阅信息
        ProjectApplyBorrow projectApplyBorrow = new ProjectApplyBorrow();
        projectApplyBorrow.setProjectId(project.getId());
        projectApplyBorrow.setApplyBorrowUserId(user.getUserIdStr());
        projectApplyBorrow.setApplyBorrowOrgCode(user.getMhUnitIdStr());
        projectApplyBorrow.setInstanceId(instanceId);
        projectApplyBorrow.setCreateOn(LocalDateTime.now());
        projectApplyBorrow.setCreateBy(LoginUserUtil.getUserId());
        projectApplyBorrow.setUpdateOn(LocalDateTime.now());
        projectApplyBorrow.setUpdateBy(LoginUserUtil.getUserId());
        projectApplyBorrow.setIsSuccess(Boolean.FALSE);
        projectApplyBorrowService.save(projectApplyBorrow);

        //保存项目和实例的关系
        ProjectInst projectInst = new ProjectInst();
        projectInst.setProjectId(project.getId());
        projectInst.setInstCode(instanceId);
        projectInst.setInstType(InstTypeEnum.APPLY_BORROW.getCode());
        projectInst.setCreatOn(LocalDateTime.now());
        projectInst.setUpdateOn(LocalDateTime.now());
        projectInstService.save(projectInst);
    }

    /**
     * 应用列表
     *
     * @param req
     * @return
     */
    public PageVo<ProjectApplicationVO> applicationList(ProjectApplicationListReq req) {
        Page<ProjectApplication> page = req.page();
        LambdaQueryWrapper<ProjectApplication> wrapper = Wrappers.lambdaQuery(ProjectApplication.class)
                .eq(StringUtils.isNotBlank(req.getBuildOrgName()), ProjectApplication::getBuildOrgName, req.getBuildOrgName());

        if (StringUtils.isNotBlank(req.getApplicationName())) {
            wrapper.and(q1 -> q1.like(ProjectApplication::getApplicationName, req.getApplicationName())
                    .or(q2 -> q2.like(ProjectApplication::getRelatedExistsApplication, req.getApplicationName())));
        }
        wrapper.orderByDesc(ProjectApplication::getUpdateOn);
        projectApplicationService.page(page, wrapper);

        if (CollUtil.isEmpty(page.getRecords())) {
            return PageVo.empty();
        }

        List<ProjectApplicationVO> res = page.getRecords().stream()
                .map(app -> BeanUtil.copyProperties(app, ProjectApplicationVO.class))
                .collect(Collectors.toList());

        return PageVo.of(res, page.getTotal());
    }

    private List<PreInsAcceptancePersonVO> convertPersons(List<PreInsAcceptancePerson> acceptancePersons) {
        if (CollUtil.isEmpty(acceptancePersons)) {
            return Collections.emptyList();
        }

        return acceptancePersons.stream().map(u -> BeanUtil.copyProperties(u, PreInsAcceptancePersonVO.class)).collect(Collectors.toList());
    }

    private List<PaymentPlanVO> convertPayments(List<PaymentPlan> payments, BigDecimal totalAmount) {
        if (CollUtil.isEmpty(payments)) {
            return Collections.emptyList();
        }
        return payments.stream().map(p -> {
                    PaymentPlanVO vo = BeanUtil.copyProperties(p, PaymentPlanVO.class);
                    vo.setRatio((Objects.isNull(totalAmount) || totalAmount.compareTo(BigDecimal.ZERO) == 0) ? "0%"
                            : p.getPaymentAmount().multiply(BigDecimal.valueOf(100))
                            .divide(totalAmount, BigDecimal.ROUND_CEILING, RoundingMode.CEILING) + "%");
                    return vo;
                })
                .collect(Collectors.toList());
    }

    /**
     * 历史版本 项目列表
     *
     * @param id
     * @return
     */
    public JSONObject historyProjects(Long id) {
        JSONObject res = new JSONObject();
        Project project = projectService.getById(id);

        if (Objects.isNull(project)) {
            return res;
        }

        //只查 当前版本 和历史被驳回|退回的版本
        List<Project> historyProjects = projectService.list(Wrappers.lambdaQuery(Project.class)
                .eq(Project::getProjectCode, project.getProjectCode())
                .and(q1 -> q1.eq(Project::getNewest, Boolean.TRUE)
                        .or(q2 -> q2.eq(Project::getIsBackReject, Boolean.TRUE)))
                .orderByDesc(Project::getCreateOn));

        if (CollUtil.isEmpty(historyProjects)) {
            return res;
        }

        List<ProjectDetailVO> basicProjects = historyProjects.stream()
                .map(p -> this.getProjectDetailThisVersion(p.getId()))
                .filter(p -> Objects.isNull(p.getIsConstruct()) || !p.getIsConstruct())
                .collect(Collectors.toList());
        List<ProjectDetailVO> constructProjects = historyProjects.stream()
                .map(p -> this.getProjectDetailThisVersion(p.getId()))
                .filter(p -> Objects.nonNull(p.getIsConstruct()) && p.getIsConstruct())
                .collect(Collectors.toList());
        res.put(ProjectConstant.ProjectLib.PROJECT_BASIC, basicProjects);
        res.put(ProjectConstant.ProjectLib.PROJECT_CONSTRUCT, constructProjects);
        return res;
    }

    /**
     * 查看 项目的 变更记录(驳回 重新发起的 和上个版本的变更字段)
     *
     * @param projectId
     * @return
     */
    public JSONObject changeRecord(Long projectId) {
        JSONObject res = new JSONObject();

        //1.先判断下 项目存不存在
        Project project = projectService.getById(projectId);
        VUtils.isTrue(Objects.isNull(project)).throwMessage("项目不存在");


        //2.要判断 项目在当前状态 有没有被驳回和退回过
        //当前项目状态流程的 实例 看看是不是有2个以上 有2个说明 有退回 驳回
        boolean isChangeRecord = todoService.isChangeRecord(projectId);
        if (!isChangeRecord) {
            return res;
        }

        //3. 去对比 当前版本和上个版本 的字段对比
        String projectCode = project.getProjectCode();
        List<Project> twoVersions = projectService.list(Wrappers.lambdaQuery(Project.class)
                .eq(Project::getProjectCode, projectCode)
                .orderByDesc(Project::getVersion)
                .last("limit 2"));
        if (CollUtil.isEmpty(twoVersions) || twoVersions.size() < 2) {
            return res;
        }

        Project lastProject = twoVersions.get(1);
        Project thisProject = twoVersions.get(0);
        res.put(ProjectConstant.ProjectLib.CHANGES_KEY,
                new CompareUtils<Project>().compareToJson(thisProject, lastProject, null));
        ProcessDetailReq req = new ProcessDetailReq();
        req.setProjectId(lastProject.getId());
        req.setInstanceId(lastProject.getInstCode());
        res.put(ProjectConstant.ProjectLib.PROCESS_KEY, todoService.getProcessDetail(req));
        return res;
    }

    public void saveApplication(Project project, Project oldProject, Boolean isConstruct) {
        List<ProjectApplication> apps = projectApplicationService.list(Wrappers.lambdaQuery(ProjectApplication.class)
                .eq(ProjectApplication::getProjectCode, oldProject.getProjectCode())
                .eq(ProjectApplication::getProjectVersion, oldProject.getVersion()));
        if (CollUtil.isNotEmpty(apps)) {
            apps.forEach(app -> {
                Long oldAppId = app.getId();
                app.setProjectVersion(project.getVersion());
                app.setProjectId(project.getId());
                app.setId(null);
                if (Objects.nonNull(isConstruct)) {
                    app.setIsConstruct(isConstruct);
                }
                projectApplicationService.save(app);

                //核心业务
                List<ProjectCoreBusinessIndicators> cores = projectCoreBusinessIndicatorsService.list(Wrappers.lambdaQuery(ProjectCoreBusinessIndicators.class)
                        .eq(ProjectCoreBusinessIndicators::getApplicationId, oldAppId));
                if (CollUtil.isNotEmpty(cores)) {
                    cores.forEach(c -> {
                        c.setId(null);
                        c.setApplicationId(app.getId());
                        projectCoreBusinessIndicatorsService.save(c);
                    });
                }
            });
        }
    }

    private void saveApplication(ProjectDTO projectDto, Project project, Boolean isConstruct) {
        UserInfoDetails user = LoginUserUtil.loginUserDetail();
        //保存项目应用
        boolean isApp = Objects.nonNull(projectDto.getIncludeApplication()) && CommonEnum.YES.getCode().equals(projectDto.getIncludeApplication())
                ? Boolean.TRUE : Boolean.FALSE;
        //采取批量删除 批量添加的方式
        Integer version = Objects.nonNull(project.getVersion()) ? project.getVersion() : 1;
        //删除当前版本的app 一般情况是没有 保险起见
        List<ProjectApplication> applications = projectApplicationService.list(Wrappers.lambdaQuery(ProjectApplication.class)
                .eq(ProjectApplication::getProjectCode, project.getProjectCode())
                .eq(ProjectApplication::getProjectVersion, version));
        if (CollUtil.isNotEmpty(applications)) {
            projectApplicationService.removeBatchByIds(applications);
        }

        if (isApp && CollUtil.isNotEmpty(projectDto.getApplicationList())) {
            Project finalProject = project;
            projectDto.getApplicationList().forEach(application -> {
                ProjectApplication projectApplication = new ProjectApplication();
                BeanUtils.copyProperties(application, projectApplication);
                projectApplication.setId(null);
                projectApplication.setProjectId(finalProject.getId());
                projectApplication.setProjectCode(finalProject.getProjectCode());
                projectApplication.setBuildOrgCode(finalProject.getBuildOrgCode());
                projectApplication.setBuildOrgName(finalProject.getBuildOrgName());
                projectApplication.setProjectVersion(version);
                projectApplication.setIsConstruct(isConstruct);
                if (StringUtils.isNotBlank(application.getRelatedExistsApplication())) {
                    projectApplication.setApplicationName(null);
                }

                boolean result = projectApplicationService.save(projectApplication);
                // 保存应用关联的核心业务
                List<ProjectCoreBusinessDTO> coreBusinessList = application.getCoreBusinessList();
                Boolean hasCoreBusiness = CollUtil.isNotEmpty(coreBusinessList) ? Boolean.TRUE : Boolean.FALSE;
                if (Boolean.TRUE.equals(result) && Boolean.TRUE.equals(hasCoreBusiness)) {
                    for (ProjectCoreBusinessDTO coreBusiness : coreBusinessList) {
                        ProjectCoreBusinessIndicators projectCoreBusinessIndicators = new ProjectCoreBusinessIndicators();
                        BeanUtils.copyProperties(coreBusiness, projectCoreBusinessIndicators);
                        projectCoreBusinessIndicators.setId(null);
                        projectCoreBusinessIndicators.setApplicationId(projectApplication.getId());
                        projectCoreBusinessIndicators.setCreateOn(LocalDateTime.now());
                        projectCoreBusinessIndicators.setCreateBy(user.getUsername());
                        projectCoreBusinessIndicatorsService.save(projectCoreBusinessIndicators);
                    }
                }
            });
        }
    }

    /**
     * 申报项目转化为 项目归集
     *
     * @param projectCode
     * @return
     */
    public GovBizProjectDetailVO convertToCollection(String projectCode) {
        ProjectDetailVO projectDetailVO = this.detailProjectCode(projectCode);
        return ProjectConvertUtil.declaredToCollection(projectDetailVO, fileService);
    }
}
