package com.ningdatech.pmapi.projectdeclared.manage;

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.DefaultDeclaredDTO;
import com.ningdatech.pmapi.projectdeclared.model.vo.FinalAcceptanceVO;
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.dto.ProjectDTO;
import com.ningdatech.pmapi.projectlib.model.entity.Project;
import com.ningdatech.pmapi.projectlib.model.req.ProjectListReq;
import com.ningdatech.pmapi.projectlib.model.vo.ProjectLibListItemVO;
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.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 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 FinalAcceptanceManage
 * @Description 终验
 * @Date 2023/6/2 14:48
 * @Author PoffyZhang
 */
@Component
@Slf4j
@RequiredArgsConstructor
public class FinalAcceptanceManage {

    private final IProjectService projectService;
    private final UserInfoHelper userInfoHelper;

    private final ProcessModelService processModelService;

    private final ProcessInstanceService processInstanceService;

    private final DefaultDeclaredProjectManage declaredProjectManage;

    private final NoticeManage noticeManage;

    private final ProjectLibManage projectLibManage;

    /**
     * 待采购的-项目列表
     * @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.ge(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());
            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);
        }
    }


    public FinalAcceptanceVO detailByProjectId(Long projectId) {
        FinalAcceptanceVO vo = new FinalAcceptanceVO();

        Project project = projectService.getById(projectId);
        vo.setProjectId(projectId);
        if(Objects.nonNull(project)){
            vo.setFinalAcceptanceMaterials(project.getFinalAcceptanceMaterials());
            // TODO IRS接口获取 应用信息
            vo.setApps(Collections.emptyList());
        }

        return vo;
    }

    /**
     * 开启流程
     * @param dto
     * @return
     */
    public String startProcess(DefaultDeclaredDTO dto) {
        UserFullInfoDTO user = userInfoHelper.getUserFullInfo(LoginUserUtil.getUserId());
        String employeeCode = user.getEmployeeCode();
        VUtils.isTrue(Objects.isNull(user) || Objects.isNull(employeeCode))
                .throwMessage("获取登录用户失败!");

        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(StringUtils.isBlank(projectDto.getFinalAcceptanceMaterials())).throwMessage("提交失败 请提交终验材料!");

        String regionCode = projectInfo.getAreaCode();

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

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

        //首先要判断 项目当前状态 是不是 方案待申报
        VUtils.isTrue((!ProjectStatusEnum.TO_BE_FINALLY_INSPECTED.getCode().equals(projectInfo.getStatus()) &&
                        !ProjectStatusEnum.FINAL_ACCEPTANCE_REVIEW_FAILED.getCode().equals(projectInfo.getStatus())) ||
                        !ProjectStatusEnum.PROJECT_APPROVED.getCode().equals(projectInfo.getStage()))
                .throwMessage("提交失败 该项目不是 待终验状态|不通过或者已立项阶段");

        ProcessStartParamsVo params = new ProcessStartParamsVo();
        params.setUser(declaredProjectManage.buildUser(employeeCode));
        params.setProcessUsers(Collections.emptyMap());
        //放入条件判断的项目字段
        //把条件值给放入工作流
        declaredProjectManage.buildCondition(params, dto);
        // 获取发起单位、发起单位主管单位、发起单位上级条线主管单位信息
        Map<String, OrgInfoDTO> orgModelMap = declaredProjectManage.buildOrgModelMap(employeeCode, projectInfo);
        String instanceId = processInstanceService.startProcessLs(model, params, orgModelMap);
        log.info("终验方案项目申报成功 【{}】", instanceId);

        //保存终验项目
        //生成新版本 并且进入下一状态
        projectInfo = projectLibManage.saveProjectWithVersionAndPass(projectInfo,instanceId,projectDto,
                InstTypeEnum.PROJECT_FINAL_INSPECTION.getCode(),Boolean.FALSE);

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

        return instanceId;
    }

    /**
     * 重新申报
     * @param dto
     * @return
     */
    public String restartProcess(DefaultDeclaredDTO dto) {
        UserFullInfoDTO user = userInfoHelper.getUserFullInfo(LoginUserUtil.getUserId());
        String employeeCode = user.getEmployeeCode();
        VUtils.isTrue(Objects.isNull(user) || Objects.isNull(employeeCode))
                .throwMessage("获取登录用户失败!");

        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(StringUtils.isBlank(projectDto.getFinalAcceptanceMaterials())).throwMessage("提交失败 请提交终验材料!");

        String regionCode = projectInfo.getAreaCode();

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

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

        //首先要判断 项目当前状态 是不是 方案待申报
        VUtils.isTrue((!ProjectStatusEnum.TO_BE_FINALLY_INSPECTED.getCode().equals(projectInfo.getStatus()) &&
                        !ProjectStatusEnum.FINAL_ACCEPTANCE_REVIEW_FAILED.getCode().equals(projectInfo.getStatus())) ||
                        !ProjectStatusEnum.PROJECT_APPROVED.getCode().equals(projectInfo.getStage()))
                .throwMessage("提交失败 该项目不是 待终验状态|不通过或者已立项阶段");

        ProcessStartParamsVo params = new ProcessStartParamsVo();
        params.setUser(declaredProjectManage.buildUser(employeeCode));
        params.setProcessUsers(Collections.emptyMap());
        //放入条件判断的项目字段
        //把条件值给放入工作流
        declaredProjectManage.buildCondition(params, dto);
        // 获取发起单位、发起单位主管单位、发起单位上级条线主管单位信息
        Map<String, OrgInfoDTO> orgModelMap = declaredProjectManage.buildOrgModelMap(employeeCode, projectInfo);
        String instanceId = processInstanceService.startProcessLs(model, params, orgModelMap);
        log.info("终验方案项目重新申报成功 【{}】", instanceId);

        //保存终验项目
        //生成新版本 并且进入下一状态
        projectInfo = projectLibManage.saveProjectWithVersionAndPass(projectInfo,instanceId,projectDto,
                InstTypeEnum.PROJECT_FINAL_INSPECTION.getCode(),Boolean.TRUE);

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

        return instanceId;
    }
}
