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

import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
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.statemachine.util.StateMachineUtil;
import com.hz.pm.api.common.util.ExcelDownUtil;
import com.hz.pm.api.common.util.ExcelExportStyle;
import com.hz.pm.api.projectdeclared.model.dto.DefaultDeclaredDTO;
import com.hz.pm.api.projectdeclared.model.dto.PretrialDeclaredExportDTO;
import com.hz.pm.api.projectdeclared.model.req.PrequalificationDeclaredListReq;
import com.hz.pm.api.projectlib.helper.ProjectHelper;
import com.hz.pm.api.projectlib.manage.ProjectLibManage;
import com.hz.pm.api.projectlib.model.dto.ProjectDTO;
import com.hz.pm.api.projectlib.model.entity.Project;
import com.hz.pm.api.projectlib.model.entity.ProjectInst;
import com.hz.pm.api.projectlib.model.enumeration.ProjectStatusEnum;
import com.hz.pm.api.projectlib.model.enumeration.ProjectTypeNewEnum;
import com.hz.pm.api.projectlib.model.req.ProjectListReq;
import com.hz.pm.api.projectlib.model.vo.ProjectLibListItemVO;
import com.hz.pm.api.projectlib.service.IProjectInstService;
import com.hz.pm.api.projectlib.service.IProjectService;
import com.hz.pm.api.staging.enums.MsgTypeEnum;
import com.hz.pm.api.staging.service.IProjectStagingService;
import com.hz.pm.api.sys.manage.NoticeManage;
import com.hz.pm.api.todocenter.constant.TodoCenterConst;
import com.hz.pm.api.todocenter.constant.WorkNoticeConstant;
import com.hz.pm.api.user.security.model.UserFullInfoDTO;
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.basic.util.NdDateUtils;
import com.wflow.bean.entity.WflowModels;
import com.wflow.exception.BusinessException;
import com.wflow.workflow.bean.dto.OrgInfoDTO;
import com.wflow.workflow.bean.vo.ProcessStartParamsVo;
import com.wflow.workflow.service.ProcessInstanceService;
import com.wflow.workflow.service.ProcessModelService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @Classname ProqualificationDeclaredProjectManage
 * @Description
 * @Date 2023/2/13 14:48
 * @Author PoffyZhang
 */
@Component
@Slf4j
@RequiredArgsConstructor
public class PrequalificationDeclaredProjectManage {

    private final IProjectService projectService;

    private final ProjectLibManage projectLibManage;

    private final StateMachineUtil stateMachineUtil;

    private final IProjectStagingService projectStagingService;

    private final UserInfoHelper userInfoHelper;

    private final ProcessModelService processModelService;

    private final DefaultDeclaredProjectManage defaultDeclaredProjectManage;

    private final ProcessInstanceService processInstanceService;

    private final IProjectInstService projectInstService;

    private final NoticeManage noticeManage;

    /**
     * 提交预审
     *
     * @param dto
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public String startTheProcess(DefaultDeclaredDTO dto) {
        UserFullInfoDTO user = userInfoHelper.getUserFullInfo(LoginUserUtil.getUserId());
        ProjectDTO projectDto = dto.getProjectInfo();
        VUtils.isTrue(Objects.isNull(projectDto.getId())).throwMessage("提交失败 缺少项目ID!");
        Project projectInfo = projectService.getNewProject(projectDto.getId());
        VUtils.isTrue(Objects.isNull(projectInfo)).throwMessage("提交失败 此项目不存在!");

        //要判断 当前操作人 是不是项目主管单位的人
        VUtils.isTrue(Objects.isNull(user.getMhUnitId()))
                .throwMessage(String.format("当前登录人没有单位 【%s】", user.getRealName()));
        VUtils.isTrue(!user.getMhUnitIdStr().equals(projectInfo.getSuperOrgCode()))
                .throwMessage(String.format("只有主管单位 【%s】的人 才能够提交", projectInfo.getSuperOrg()));

        //首先要判断 项目当前状态 是不是 待预审
        VUtils.isTrue((!ProjectStatusEnum.PENDING_PREQUALIFICATION.getCode().equals(projectInfo.getStatus()) &&
                        !ProjectStatusEnum.JOINT_REVIEW_BY_PROVINCIAL_DEPARTMENTS_SUCCESS.getCode().equals(projectInfo.getStatus())) ||
                        !ProjectStatusEnum.NOT_APPROVED.getCode().equals(projectInfo.getStage()))
                .throwMessage("提交失败 该项目不是 待预审状态(省级部门联审成功)或者未立项阶段");

        //使用状态机 进入下一步 看看需不需要走省级审批 放入文件
        if (StringUtils.isNotBlank(projectDto.getHigherLineSuperOrgReviewComments())) {
            projectInfo.setHigherLineSuperOrgReviewComments(projectDto.getHigherLineSuperOrgReviewComments());
        }
        stateMachineUtil.pass(projectInfo);
        String instanceId = null;
        //如果是省级部门 需要联审的(申报金额大于1000万 并且是市级项目)
        if (ProjectStatusEnum.JOINT_REVIEW_BY_PROVINCIAL_DEPARTMENTS
                .getCode().equals(projectInfo.getStatus())) {
            //入库暂存表 后续处理 对接外部接口
            projectInfo.setUpdateOn(LocalDateTime.now());
            //保存一下  当前的主管单位发起人
            projectInfo.setPreStartUserId(user.getUserIdStr());
            //当前实例置为空
            projectInfo.setInstCode(TodoCenterConst.Declared.NULL_INST_CODE);
            if (Boolean.TRUE.equals(projectStagingService.addByProject(projectInfo, "省级部门联审"))
                    && projectService.updateById(projectInfo)) {
                return "提交省级部门联审成功";
            }
            return "提交省级部门联审失败";
        } else if (ProjectStatusEnum.PRE_APPLYING
                .getCode().equals(projectInfo.getStatus())) {
            //如果是非省级联审的项目 直接提交 预审
            instanceId = directStartProcess(projectInfo, user.getUserId(), dto.getRestart());
        } else {
            throw new BusinessException("项目状态 错误 project :" + projectInfo.getId() + "," + projectInfo.getStatus());
        }

        return "提交预审成功【" + instanceId + "】";
    }

    /**
     * 重新提交预审
     *
     * @param dto
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public String restartTheProcess(DefaultDeclaredDTO dto) {
        ProjectDTO projectDto = dto.getProjectInfo();
        VUtils.isTrue(Objects.isNull(projectDto.getId())).throwMessage("提交失败 缺少项目ID!");
        Project projectInfo = projectService.getById(projectDto.getId());
        VUtils.isTrue(Objects.isNull(projectInfo)).throwMessage("提交失败 此项目不存在!");
        //重新提交 生成新版本号 回到 待预审状态
        Project project = projectLibManage.saveProjectWithVersionAndStatus(projectDto, null,
                ProjectStatusEnum.PENDING_PREQUALIFICATION.getCode(), Boolean.FALSE);
        dto.getProjectInfo().setId(project.getId());
        dto.setRestart(Boolean.TRUE);
        return startTheProcess(dto);
    }

    //直接提交预审方法 提取 在省级联审通过的时候 也可以用
    public String directStartProcess(Project projectInfo, Long userId, Boolean restart) {
        VUtils.isTrue(Objects.isNull(userId))
                .throwMessage("发起人 员工code 不能为空!");

        ProcessStartParamsVo params = new ProcessStartParamsVo();
        params.setUser(defaultDeclaredProjectManage.buildUser(userId));
        params.setProcessUsers(Collections.emptyMap());

        //把条件值给放入工作流
        defaultDeclaredProjectManage.buildCondition(params, projectInfo);

        WflowModels model = processModelService.getOne(Wrappers.lambdaQuery(WflowModels.class)
                .eq(WflowModels::getProcessType, ProjectProcessStageEnum.PROJECT_PREQUALIFICATION_APPROVAL_PROCESS.getCode())
                .last(BizConst.LIMIT_1));

        if (Objects.isNull(model)) {
            throw new BusinessException("找不到预审流程配置");
        }

        // 获取发起单位、发起单位主管单位、发起单位上级主管条线单位信息
        Map<String, OrgInfoDTO> orgModelMap = defaultDeclaredProjectManage.buildOrgModelMap(userId, projectInfo);
        String instanceId = processInstanceService.startProcessLs(model, params, orgModelMap);
        log.info("提交预审项目成功 【{}】", instanceId);

        log.info("restart 【{}】", restart);
        //保存预审项目
        if (Boolean.TRUE.equals(restart)) {
            //如果是重新提交 不用生成新版本 前面已经生成过了
            preModifyProject(projectInfo, instanceId);
        } else {
            projectInfo = projectLibManage.saveProjectWithVersion(projectInfo, instanceId,
                    ProjectProcessStageEnum.PROJECT_PREQUALIFICATION_APPROVAL_PROCESS.getCode());
        }

        //发送给第一个审批人消息
        noticeManage.sendFirstUser(projectInfo, model.getFormName(), instanceId,
                WorkNoticeConstant.PASS_MSG_TEMPLATE, MsgTypeEnum.PROJECT_REVIEW);

        return instanceId;
    }

    /**
     * 提交预审项目 时 更新信息
     *
     * @param project
     * @param instanceId
     */
    private void preModifyProject(Project project, String instanceId) {
        //流程启动之后 入库项目 重要业务信息 用于列表查询 展示
        try {
            project.setUpdateOn(LocalDateTime.now());
            project.setInstCode(instanceId);
            projectService.updateById(project);

            //保存项目和实例的关系
            ProjectInst projectInst = new ProjectInst();
            projectInst.setProjectId(project.getId());
            projectInst.setInstCode(instanceId);
            projectInst.setCreatOn(LocalDateTime.now());
            projectInst.setUpdateOn(LocalDateTime.now());
            projectInst.setInstType(ProjectProcessStageEnum.PROJECT_PREQUALIFICATION_APPROVAL_PROCESS.getCode());
            projectInstService.save(projectInst);
        } catch (Exception e) {
            log.error("提交预审 项目信息修改 错误 ", e);
            throw new BusinessException("提交预审 项目信息修改 错误 :" + e.getMessage());
        }
    }

    /**
     * 查询项目库
     *
     * @param preReq
     * @return
     */
    public PageVo<ProjectLibListItemVO> pageInfo(PrequalificationDeclaredListReq preReq) {
        UserFullInfoDTO user = userInfoHelper.getUserFullInfo(LoginUserUtil.getUserId());
        VUtils.isTrue(Objects.isNull(user)).throwMessage("获取登录用户失败!");
        //限定参数 复制bean
        ProjectListReq req = new ProjectListReq();
        BeanUtils.copyProperties(preReq, req);
        //项目阶段 状态 已定 待预审和省级部门联审成功
        req.setStage(ProjectStatusEnum.NOT_APPROVED.getCode());
        req.setStatusList(Lists.newArrayList(ProjectStatusEnum.PENDING_PREQUALIFICATION.getCode(),
                ProjectStatusEnum.JOINT_REVIEW_BY_PROVINCIAL_DEPARTMENTS_SUCCESS.getCode()));
        LambdaQueryWrapper<Project> query = ProjectHelper.projectQuery(req);
        buildPermission(query, user);
        query.eq(Project::getNewest, Boolean.TRUE);
        Page<Project> page = projectService.page(req.page(), query);
        long total;
        if ((total = page.getTotal()) == 0) {
            return PageVo.empty();
        }
        List<ProjectLibListItemVO> records = CollUtils.convert(page.getRecords(), w -> {
            ProjectLibListItemVO item = new ProjectLibListItemVO();
            item.setId(w.getId());
            item.setProjectName(w.getProjectName());
            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.getCode().equals(item.getStatus()) ||
                            ProjectStatusEnum.JOINT_REVIEW_BY_PROVINCIAL_DEPARTMENTS_SUCCESS.getCode().equals(item.getStatus()))
                    && StringUtils.isNotBlank(w.getSuperOrgCode())
                    && w.getSuperOrgCode().equals(user.getMhUnitIdStr())) {
                item.setCanPreDeclared(Boolean.TRUE);
            }
            return item;
        });
        return PageVo.of(records, total);
    }

    /**
     * build 项目的角色权限到req
     *
     * @param query
     */
    public void buildPermission(LambdaQueryWrapper<Project> query, UserFullInfoDTO user) {
        if (user.getIsOrgAdmin()) {
            //单位管理员 看到自己单位去申报的 + 待预审的主管单位是自己单位的项目
            query.and(q1 -> q1.eq(Project::getBuildOrgCode, user.getMhUnitIdStr())
                    .or(q2 -> q2.eq(Project::getStage, ProjectStatusEnum.NOT_APPROVED.getCode())
                            .and(s1 -> s1.eq(Project::getStatus, ProjectStatusEnum.PENDING_PREQUALIFICATION.getCode())
                                    .or(s2 -> s2.eq(Project::getStatus, ProjectStatusEnum.JOINT_REVIEW_BY_PROVINCIAL_DEPARTMENTS_SUCCESS.getCode())))
                            .eq(Project::getSuperOrgCode, user.getMhUnitIdStr())));
        } else {
            query.eq(Project::getBuildOrgCode, user.getMhUnitIdStr());
        }
    }

    public void exportList(HttpServletResponse response, PrequalificationDeclaredListReq preReq) {
        ProjectListReq req = new ProjectListReq();
        BeanUtils.copyProperties(preReq, req);
        //项目阶段 状态 已定 待预审
        req.setStage(ProjectStatusEnum.NOT_APPROVED.getCode());
        req.setStatusList(Lists.newArrayList(ProjectStatusEnum.PENDING_PREQUALIFICATION.getCode(),
                ProjectStatusEnum.JOINT_REVIEW_BY_PROVINCIAL_DEPARTMENTS_SUCCESS.getCode()));
        UserFullInfoDTO user = userInfoHelper.getUserFullInfo(LoginUserUtil.getUserId());
        LambdaQueryWrapper<Project> query = ProjectHelper.projectQuery(req);
        //角色权限
        buildPermission(query, user);
        query.eq(Project::getNewest, Boolean.TRUE);
        List<Project> records = projectService.list(query);

        List<PretrialDeclaredExportDTO> collect = records.stream().map(r -> {
            PretrialDeclaredExportDTO exportDTO = new PretrialDeclaredExportDTO();
            BeanUtils.copyProperties(r, exportDTO);
            exportDTO.setProjectTypeName(ProjectTypeNewEnum.getDesc(r.getProjectType()));
            String createOnStr = NdDateUtils.format(r.getCreateOn(), "yyyy-MM-dd HH:mm");
            exportDTO.setCreateOn(createOnStr);
            return exportDTO;
        }).collect(Collectors.toList());
        for (int i = 0; i < collect.size(); i++) {
            collect.get(i).setSerialNumber(i + 1);
        }
        String fileName = "预审申报项目列表";
        ExcelDownUtil.setFileName(fileName, response);
        //数据导出处理函数
        try {
            EasyExcel.write(response.getOutputStream(), PretrialDeclaredExportDTO.class)
                    .autoCloseStream(false)
                    .registerWriteHandler(ExcelExportStyle.formalStyle())
                    .sheet(fileName)
                    .doWrite(collect);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
