package com.ningdatech.pmapi.projectdeclared.manage;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
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.ningdatech.basic.function.VUtils;
import com.ningdatech.basic.model.PageVo;
import com.ningdatech.basic.util.CollUtils;
import com.ningdatech.basic.util.NdDateUtils;
import com.ningdatech.pmapi.common.constant.BizConst;
import com.ningdatech.pmapi.common.enumeration.ProjectProcessStageEnum;
import com.ningdatech.pmapi.common.helper.UserInfoHelper;
import com.ningdatech.pmapi.common.util.ExcelDownUtil;
import com.ningdatech.pmapi.common.util.ExcelExportStyle;
import com.ningdatech.pmapi.projectdeclared.model.dto.DeclaredProjectExportDTO;
import com.ningdatech.pmapi.projectdeclared.model.dto.DelayedApplyDTO;
import com.ningdatech.pmapi.projectdeclared.model.vo.DelayedApplyVO;
import com.ningdatech.pmapi.projectlib.enumeration.InstTypeEnum;
import com.ningdatech.pmapi.projectlib.enumeration.ProjectStatusEnum;
import com.ningdatech.pmapi.projectlib.enumeration.ProjectTypeEnum;
import com.ningdatech.pmapi.projectlib.helper.ProjectHelper;
import com.ningdatech.pmapi.projectlib.manage.ProjectLibManage;
import com.ningdatech.pmapi.projectlib.model.entity.Project;
import com.ningdatech.pmapi.projectlib.model.entity.ProjectDelayApply;
import com.ningdatech.pmapi.projectlib.model.entity.ProjectInst;
import com.ningdatech.pmapi.projectlib.model.req.ProjectListReq;
import com.ningdatech.pmapi.projectlib.model.vo.ProjectLibListItemVO;
import com.ningdatech.pmapi.projectlib.service.INdProjectDelayApplyService;
import com.ningdatech.pmapi.projectlib.service.IProjectInstService;
import com.ningdatech.pmapi.projectlib.service.IProjectService;
import com.ningdatech.pmapi.staging.enums.MsgTypeEnum;
import com.ningdatech.pmapi.sys.manage.NoticeManage;
import com.ningdatech.pmapi.todocenter.constant.WorkNoticeConstant;
import com.ningdatech.pmapi.user.security.auth.model.UserFullInfoDTO;
import com.ningdatech.pmapi.user.util.LoginUserUtil;
import com.wflow.bean.entity.WflowModels;
import com.wflow.contants.HisProInsEndActId;
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.flowable.engine.HistoryService;
import org.flowable.engine.history.HistoricProcessInstance;
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.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * @Classname DelayedManage
 * @Description
 * @Date 2023/5/29 14:48
 * @Author PoffyZhang
 */
@Component
@Slf4j
@RequiredArgsConstructor
public class DelayedApplyManage {

    private final IProjectService projectService;
    private final UserInfoHelper userInfoHelper;
    private final ProcessModelService processModelService;
    private final DefaultDeclaredProjectManage defaultDeclaredProjectManage;
    private final ProcessInstanceService processInstanceService;
    private final ProjectLibManage projectLibManage;
    private final NoticeManage noticeManage;
    private final IProjectInstService projectInstService;
    private final HistoryService historyService;

    private final INdProjectDelayApplyService projectDelayApplyService;

    /**
     * 延期的-项目列表
     * @param req
     * @return
     */
    public PageVo<ProjectLibListItemVO> projectLibList(ProjectListReq req) {
        UserFullInfoDTO user = userInfoHelper.getUserFullInfo(LoginUserUtil.getUserId());
        VUtils.isTrue(Objects.isNull(user)).throwMessage("获取登录用户失败!");
        LambdaQueryWrapper<Project> query = ProjectHelper.projectQuery(req);
        //待终验 并且已经过期
        query.eq(Project::getStatus,ProjectStatusEnum.TO_BE_FINALLY_INSPECTED.getCode());
        query.eq(Project::getStage,ProjectStatusEnum.PROJECT_APPROVED.getCode());
        query.lt(Project::getPlanAcceptanceTime, LocalDateTime.now());
        query.eq(Project::getNewest,Boolean.TRUE);
        query.isNotNull(Project::getApprovalDate);
        query.isNotNull(Project::getBuildCycle);
        query.orderByAsc(Project::getApprovalDate);
        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());
            item.setApprovedAmount(w.getApprovalAmount());
            item.setApprovalDate(w.getApprovalDate());
            item.setBuildCycle(StringUtils.isNotBlank(w.getBuildCycle()) ?
                    Integer.valueOf(w.getBuildCycle()) : null);
            item.setPlanAcceptanceTime(w.getPlanAcceptanceTime());
            // 判断当前项目是否已经开启了延期申请并且没有审批完成
            ProjectDelayApply delayApply = projectDelayApplyService.getOne(Wrappers.lambdaQuery(ProjectDelayApply.class)
                    .eq(ProjectDelayApply::getProjectId, w.getId())
                    .orderByDesc(ProjectDelayApply::getCreateOn)
                    .last("limit 1"));
            if (Objects.nonNull(delayApply)) {
                String instCode = delayApply.getInstanceId();
                // 获取流程实例
                HistoricProcessInstance newInstance = historyService.createHistoricProcessInstanceQuery()
                        .processInstanceId(instCode)
                        .singleResult();
                ProjectInst projectInst = projectInstService.getOne(Wrappers.lambdaQuery(ProjectInst.class)
                        .eq(ProjectInst::getInstCode, instCode));
                Integer instType = projectInst.getInstType();
                // 延期申请流程还未审核结束
                if (InstTypeEnum.APPLY_DELAY.getCode().equals(instType) &&
                        Objects.isNull(newInstance.getEndActivityId())) {
                    item.setCanDelayApply(Boolean.FALSE);
                }
                // 如果是延期申请审核被驳回,设置项目可以申请延期申报
                else if (InstTypeEnum.APPLY_DELAY.getCode().equals(instType) &&
                        HisProInsEndActId.REJECT.equals(newInstance.getEndActivityId())) {
                    item.setCanDelayApply(Boolean.TRUE);
                }
            }
            return item;
        });
        return PageVo.of(records, total);
    }

    public void exportList(HttpServletResponse response, ProjectListReq param) {
        UserFullInfoDTO user = userInfoHelper.getUserFullInfo(LoginUserUtil.getUserId());
        VUtils.isTrue(Objects.isNull(user)).throwMessage("获取登录用户失败!");
        LambdaQueryWrapper<Project> query = ProjectHelper.projectQuery(param);
        //待终验
        query.eq(Project::getStatus,ProjectStatusEnum.TO_BE_FINALLY_INSPECTED.getCode());
        query.eq(Project::getStage,ProjectStatusEnum.PROJECT_APPROVED.getCode());
        query.eq(Project::getNewest,Boolean.TRUE);
        query.isNotNull(Project::getApprovalDate);
        query.isNotNull(Project::getBuildCycle);
        query.orderByAsc(Project::getApprovalDate);
        List<Project> records = projectService.list(query);

        AtomicInteger serialNumber = new AtomicInteger(0);
        List<DeclaredProjectExportDTO> collect = Lists.newArrayList();
        if(CollUtil.isNotEmpty(records)){
            collect = records.stream().map(r -> {
                DeclaredProjectExportDTO exportDTO = new DeclaredProjectExportDTO();
                BeanUtils.copyProperties(r, exportDTO);
                exportDTO.setProjectTypeName(ProjectTypeEnum.getDesc(r.getProjectType()));
                exportDTO.setStatusName(ProjectStatusEnum.getDesc(r.getStatus()));
                String createOnStr = NdDateUtils.format(r.getCreateOn(), "yyyy-MM-dd HH:mm");
                exportDTO.setCreateOn(createOnStr);
                exportDTO.setSerialNumber(serialNumber.incrementAndGet());
                exportDTO.setPlanAcceptanceTime(Objects.nonNull(r.getPlanAcceptanceTime()) ?
                        NdDateUtils.format(r.getPlanAcceptanceTime(), "yyyy-MM-dd") : StringUtils.EMPTY);
                return exportDTO;
            }).collect(Collectors.toList());
        }

        String fileName = "待终验申请项目列表";
        ExcelDownUtil.setFileName(fileName,response);
        //数据导出处理函数
        try {
            EasyExcel.write(response.getOutputStream(), DeclaredProjectExportDTO.class)
                    .autoCloseStream(false)
                    .registerWriteHandler(ExcelExportStyle.formalStyle())
                    .sheet(fileName)
                    .doWrite(collect);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 延期申请
     * @param dto
     * @return
     */
    public String delayedApply(DelayedApplyDTO dto) {
        UserFullInfoDTO user = userInfoHelper.getUserFullInfo(LoginUserUtil.getUserId());
        String employeeCode = user.getEmployeeCode();
        VUtils.isTrue(StringUtils.isBlank(employeeCode)).throwMessage("获取登录用户 员工号 失败!");

        Long projectId = dto.getProjectId();
        Project project = projectService.getNewProject(projectId);
        VUtils.isTrue(Objects.isNull(project)).throwMessage("提交失败 此项目不存在!");

        //首先要判断 项目当前状态 是不是 以终验
        VUtils.isTrue(!ProjectStatusEnum.TO_BE_FINALLY_INSPECTED.getCode().equals(project.getStatus()) ||
                        !ProjectStatusEnum.PROJECT_APPROVED.getCode().equals(project.getStage()))
                .throwMessage("提交失败 该项目不是 已立项|待终验");

        VUtils.isTrue(Objects.isNull(project.getPlanAcceptanceTime())
                        || project.getPlanAcceptanceTime().compareTo(LocalDateTime.now()) <= 0)
                .throwMessage("当前项目还未过期验收");

        return "申请发起成功";
    }

    /**
     * 开启延期申请审批流程
     * @param dto
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public String startDelayedApplyProcess(DelayedApplyDTO dto) {
        UserFullInfoDTO user = userInfoHelper.getUserFullInfo(LoginUserUtil.getUserId());
        String employeeCode = user.getEmployeeCode();
        VUtils.isTrue(StringUtils.isBlank(employeeCode)).throwMessage("获取登录用户 员工号 失败!");

        Long projectId = dto.getProjectId();
        Project project = projectService.getNewProject(projectId);
        VUtils.isTrue(Objects.isNull(project)).throwMessage("提交失败 此项目不存在!");

        //首先要判断 项目当前状态 是不是 以终验
        VUtils.isTrue(!ProjectStatusEnum.TO_BE_FINALLY_INSPECTED.getCode().equals(project.getStatus()) ||
                        !ProjectStatusEnum.PROJECT_APPROVED.getCode().equals(project.getStage()))
                .throwMessage("提交失败 该项目不是 已立项|待终验");

        VUtils.isTrue(Objects.isNull(project.getPlanAcceptanceTime())
                        || project.getPlanAcceptanceTime().compareTo(LocalDateTime.now()) > 0)
                .throwMessage("当前项目还未过期验收");

        String regionCode = project.getAreaCode();

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

        if (Objects.isNull(model)) {
            log.error("此 【{}】区域找不到延期申请流程配置", regionCode);
            throw new BusinessException(String.format("此 【%s】区域找不到延期申请流程配置", regionCode));
        }

        ProcessStartParamsVo params = new ProcessStartParamsVo();
        params.setUser(defaultDeclaredProjectManage.buildUser(employeeCode));
        params.setProcessUsers(Collections.emptyMap());
        //放入条件判断的项目字段
        //把条件值给放入工作流
        defaultDeclaredProjectManage.buildCondition(params, project);
        // 获取发起单位、发起单位主管单位、发起单位上级条线主管单位信息
        Map<String, OrgInfoDTO> orgModelMap = defaultDeclaredProjectManage.buildOrgModelMapUserOrgCode(project);
        String instanceId = processInstanceService.newStartProcess(model.getProcessDefId(), model.getFormId(), params, orgModelMap);
        log.info("延期申请申报成功 【{}】", instanceId);

        // 保存项目延期实例相关
        projectLibManage.saveProjectByApplyDelay(dto, project, instanceId);

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

        return instanceId;
    }

    /**
     * 查询延期 详情
     * @param projectId
     * @return
     */
    public DelayedApplyVO detailByProjectId(Long projectId) {
        ProjectDelayApply delayApply = projectDelayApplyService.getOne(Wrappers.lambdaQuery(ProjectDelayApply.class)
                .eq(ProjectDelayApply::getProjectId, projectId)
                .orderByDesc(ProjectDelayApply::getCreateOn)
                .last(BizConst.LIMIT_1));
        if(Objects.isNull(delayApply)){
            return null;
        }
        return BeanUtil.copyProperties(delayApply,DelayedApplyVO.class);
    }

    /**
     * 查看某个项目的 延期列表
     * @param projectId
     * @return
     */
    public List<DelayedApplyVO> delayList(Long projectId) {
        List<Long> allVersionProjectId = projectService.getAllVersionProjectId(projectId);
        List<ProjectDelayApply> delays = projectDelayApplyService.list(Wrappers.lambdaQuery(ProjectDelayApply.class)
                .in(ProjectDelayApply::getProjectId, allVersionProjectId)
                .eq(ProjectDelayApply::getSuccess, Boolean.TRUE));

        if(CollUtil.isEmpty(delays)){
            return Collections.emptyList();
        }

        return delays.stream().map(d -> BeanUtil.copyProperties(d,DelayedApplyVO.class))
                .collect(Collectors.toList());
    }
}
