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

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Assert;
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.exception.BizException;
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.basic.util.StrPool;
import com.hz.pm.api.common.model.constant.BizConst;
import com.hz.pm.api.common.enumeration.CommonEnum;
import com.hz.pm.api.common.enumeration.ProjectProcessStageEnum;
import com.hz.pm.api.common.helper.RegionCacheHelper;
import com.hz.pm.api.common.helper.UserInfoHelper;
import com.hz.pm.api.common.util.ExcelDownUtil;
import com.hz.pm.api.common.util.ExcelExportStyle;
import com.hz.pm.api.gov.manage.GovProjectCollectionManage;
import com.hz.pm.api.irs.manage.ProjectIrsManage;
import com.hz.pm.api.projectdeclared.contants.DeclaredProjectConst;
import com.hz.pm.api.projectdeclared.model.dto.*;
import com.hz.pm.api.projectdeclared.model.entity.ProjectDraft;
import com.hz.pm.api.projectdeclared.model.vo.ProjectDraftVO;
import com.hz.pm.api.projectdeclared.service.IProjectDraftService;
import com.hz.pm.api.projectlib.model.enumeration.ProjectStatusEnum;
import com.hz.pm.api.projectlib.model.enumeration.ProjectTypeNewEnum;
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.ProjectApplication;
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.IProjectService;
import com.hz.pm.api.staging.enums.MsgTypeEnum;
import com.hz.pm.api.sys.manage.NoticeManage;
import com.hz.pm.api.todocenter.constant.WorkNoticeConstant;
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.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.beans.factory.annotation.Value;
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.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * @Classname DeclaredProjectManage
 * @Description
 * @Date 2023/2/1 14:48
 * @Author PoffyZhang
 */
@Component
@Slf4j
@RequiredArgsConstructor
public class DeclaredProjectManage {

    private final IProjectService projectService;

    private final ProjectLibManage projectLibManage;

    private final IProjectDraftService projectDraftService;

    private final ProcessInstanceService processService;

    private final ProcessModelService processModelService;

    private final DefaultDeclaredProjectManage defaultDeclaredProjectManage;

    private final NoticeManage noticeManage;

    private final UserInfoHelper userInfoHelper;

    private final RegionCacheHelper regionCacheHelper;

    private final ProjectIrsManage projectIrsManage;

    private final GovProjectCollectionManage collectionManage;

    @Value("${spring.profiles.active}")
    private String active;

    /**
     * 新项目 启动实例
     *
     * @param dto
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public String startTheProcess(DefaultDeclaredDTO dto) {
        UserInfoDetails user = LoginUserUtil.loginUserDetail();
        ProjectDTO projectInfo = dto.getProjectInfo();

        projectInfo.setBuildOrgCode(user.getMhUnitIdStr());
        projectInfo.setBuildOrgName(user.getMhUnitName());

        //项目名称去重
        defaultDeclaredProjectManage.checkDuplication(projectInfo);
        //判断申报金额 是否等于总的 判断年度支付金额 是否等于总金额
        defaultDeclaredProjectManage.checkAmount(projectInfo);

        //写死 是否有主管单位
        projectInfo.setIsHigherSuperOrg(CommonEnum.YES.getCode());
        projectInfo.setIsSuperOrg(CommonEnum.YES.getCode());

        //如果主管单位没有 那么主管单位就是自己
        if (CommonEnum.NO.getCode().equals(projectInfo.getIsSuperOrg())) {
            projectInfo.setSuperOrgCode(user.getMhUnitIdStr());
            projectInfo.setSuperOrg(user.getMhUnitName());
        }

        //如果是重新提交的话 判断下 项目是否存在
        if (Objects.nonNull(projectInfo.getId())) {
            //新申报的项目不允许带项目id
            projectInfo.setId(null);
        }

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

        if (Objects.isNull(model)) {
            throw BizException.wrap("找不到单位流程配置");
        }

        ProcessStartParamsVo params = new ProcessStartParamsVo();
        params.setUser(defaultDeclaredProjectManage.buildUser(user.getUserId()));
        params.setProcessUsers(Collections.emptyMap());
        //放入条件判断的项目字段
        ProjectConditionDTO conditionDto = new ProjectConditionDTO();
        BeanUtils.copyProperties(dto.getProjectInfo(), conditionDto);
        dto.getFormData().putAll(
                JSON.parseObject(JSON.toJSONString(conditionDto), new TypeReference<Map<String, Object>>() {
                })
        );
        params.setFormData(dto.getFormData());
        //开始申报
        // 获取发起单位、发起单位主管单位、发起单位上级主管条线单位信息
        Project project = new Project();
        BeanUtils.copyProperties(projectInfo, project);
        Map<String, OrgInfoDTO> orgModelMap = defaultDeclaredProjectManage.buildOrgModelMap(user.getUserId(), project);
        String instanceId = processService.startProcessLs(model, params, orgModelMap);
        log.info("申报项目成功 【{}】", instanceId);

        //如果是重新提交的话 判断下 项目是否存在
        //保存项目相关
        Project buildProject = projectLibManage.saveProjectInDeclared(projectInfo, instanceId, user.getUserId());
        if (Objects.nonNull(projectInfo.getDraftId())) {
            //如果是草稿箱提交 删除对应的草稿箱
            projectDraftService.removeById(projectInfo.getDraftId());
        }

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

        return instanceId;
    }


    /**
     * 重新提交 启动实例
     *
     * @param dto
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public String reStartTheProcess(DefaultDeclaredDTO dto) {
        UserFullInfoDTO user = userInfoHelper.getUserFullInfo(LoginUserUtil.getUserId());
        ProjectDTO projectDto = dto.getProjectInfo();
        projectDto.setBuildOrgCode(user.getMhUnitIdStr());
        projectDto.setBuildOrgName(user.getMhUnitName());

        VUtils.isTrue(Objects.isNull(projectDto.getId())).throwMessage("提交失败 缺少项目ID!");
        Project projectInfo = projectService.getById(projectDto.getId());
        VUtils.isTrue(Objects.isNull(projectInfo)).throwMessage("提交失败 此项目不存在!");

        //项目名称去重
        if (StringUtils.isNotBlank(projectDto.getProjectName()) &&
                !projectDto.getProjectName().equals(projectInfo.getProjectName())) {
            projectDto.setProjectCode(projectInfo.getProjectCode());
            defaultDeclaredProjectManage.checkDuplication(projectDto);
        }

        //写死 是否有主管单位
        projectInfo.setIsHigherSuperOrg(CommonEnum.YES.getCode());
        projectInfo.setIsSuperOrg(CommonEnum.YES.getCode());

        //如果主管单位没有 那么主管单位就是自己
        if (CommonEnum.NO.getCode().equals(projectDto.getIsSuperOrg())) {
            projectInfo.setSuperOrgCode(user.getMhUnitIdStr());
            projectInfo.setSuperOrg(user.getMhUnitName());
            projectDto.setSuperOrgCode(user.getMhUnitIdStr());
            projectDto.setSuperOrg(user.getMhUnitName());
        }

        //判断申报金额 是否等于总的 判断年度支付金额 是否等于总金额
        defaultDeclaredProjectManage.checkAmount(projectDto);

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

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

        //首先要判断 项目当前状态 是不是 单位内部拒绝
        VUtils.isTrue(!ProjectStatusEnum.UNDER_INTERNAL_AUDIT_NOT_PASS.getCode().equals(projectInfo.getStatus()) ||
                        !ProjectStatusEnum.NOT_APPROVED.getCode().equals(projectInfo.getStage()))
                .throwMessage("提交失败 该项目不是 单位内部拒绝审核状态或者未立项阶段");

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

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

        // 获取发起单位、发起单位主管单位、发起单位上级主管条线单位信息
        Project project = new Project();
        BeanUtils.copyProperties(projectDto, project);
        Map<String, OrgInfoDTO> orgModelMap = defaultDeclaredProjectManage.buildOrgModelMap(user.getUserId(), project);
        String instanceId = processService.startProcessLs(model, params, orgModelMap);
        log.info("重新申报项目成功 【{}】", instanceId);

        //保存项目相关
        Project buildProject = projectLibManage.saveProjectInDeclared(projectDto, instanceId, user.getUserId());

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

        return instanceId;
    }

    public PageVo<ProjectDraftVO> pageDraft(DeclaredProjectListParamDTO params) {
        Long userId = LoginUserUtil.getUserId();
        Page<ProjectDraft> page = params.page();
        LambdaQueryWrapper<ProjectDraft> wrapper = Wrappers.lambdaQuery(ProjectDraft.class)
                .eq(ProjectDraft::getUserId, userId)
                .ge(Objects.nonNull(params.getStartTime()), ProjectDraft::getCreateOn, params.getStartTime())
                .le(Objects.nonNull(params.getEndTime()), ProjectDraft::getCreateOn, params.getEndTime())
                .eq(Objects.nonNull(params.getProjectType()), ProjectDraft::getProjectType, params.getProjectType())
                .eq(Objects.nonNull(params.getProjectYear()), ProjectDraft::getProjectYear, params.getProjectYear())
                .eq(Objects.nonNull(params.getProjectStage()), ProjectDraft::getStage, params.getProjectStage())
                .eq(Objects.nonNull(params.getProjectStatus()), ProjectDraft::getStatus, params.getProjectStatus())
                .like(StringUtils.isNotBlank(params.getProjectName()), ProjectDraft::getProjectName, params.getProjectName())
                .orderByDesc(ProjectDraft::getUpdateOn);
        projectDraftService.page(page, wrapper);
        if (CollUtil.isEmpty(page.getRecords())) {
            return PageVo.empty();
        }
        List<ProjectDraftVO> res = page.getRecords().stream().map(record -> {
            ProjectDraftVO vo = new ProjectDraftVO();
            BeanUtils.copyProperties(record, vo);
            vo.setBizDomain(Objects.nonNull(record.getBizDomain()) ? String.valueOf(record.getBizDomain()) : StringUtils.EMPTY);
            return vo;
        }).collect(Collectors.toList());
        return PageVo.of(res, page.getTotal());
    }

    public ProjectDraftVO draftDetail(Long id) {
        ProjectDraft draft = projectDraftService.getById(id);
        ProjectDraftVO vo = new ProjectDraftVO();
        BeanUtils.copyProperties(draft, vo);
        vo.setBizDomain(Objects.nonNull(draft.getBizDomain()) ? String.valueOf(draft.getBizDomain()) : StringUtils.EMPTY);
        if (StringUtils.isNotBlank(draft.getProjectApplicationList())) {
            vo.setApplicationList(JSON.parseArray(draft.getProjectApplicationList(), ProjectApplication.class));
        }
        if (StringUtils.isNotBlank(draft.getDynamicForm())) {
            vo.setDynamicForm(JSON.parseObject(draft.getDynamicForm(), Map.class));
        }
        return vo;
    }

    /**
     * 保存至草稿箱
     *
     * @param dto
     * @return
     */
    public Long saveToDraft(ProjectDraftSaveDTO dto) {
        Long userId = LoginUserUtil.getUserId();
        UserFullInfoDTO user = userInfoHelper.getUserFullInfo(LoginUserUtil.getUserId());

        ProjectDTO projectInfo = dto.getProjectInfo();
        ProjectDraft draft = new ProjectDraft();
        BeanUtils.copyProperties(projectInfo, draft);
        //写死 是否有主管单位
        draft.setIsHigherSuperOrg(CommonEnum.YES.getCode());
        draft.setIsSuperOrg(CommonEnum.YES.getCode());
        draft.setUserId(String.valueOf(userId));
        draft.setBuildOrgCode(user.getMhUnitIdStr());
        draft.setBuildOrgName(user.getMhUnitName());

        if (CollUtil.isNotEmpty(projectInfo.getDynamicForm())) {
            draft.setDynamicForm(JSON.toJSONString(projectInfo.getDynamicForm()));
        }
        if (CollUtil.isNotEmpty(projectInfo.getApplicationList())) {
            draft.setProjectApplicationList(JSON.toJSONString(projectInfo.getApplicationList()));
        }
        if (Objects.isNull(draft.getId())) {
            draft.setCreateOn(LocalDateTime.now());
        } else {
            ProjectDraft old = projectDraftService.getById(draft.getId());
            if (Objects.isNull(old)) {
                draft.setCreateOn(LocalDateTime.now());
            }
        }
        draft.setUpdateOn(LocalDateTime.now());
        draft.setUserId(String.valueOf(userId));
        projectDraftService.saveOrUpdate(draft);
        return draft.getId();
    }

    /**
     * 项目列表
     *
     * @param req
     * @return
     */
    public PageVo<ProjectLibListItemVO> projectLibList(ProjectListReq req) {
        UserFullInfoDTO user = userInfoHelper.getUserFullInfo(LoginUserUtil.getUserId());
        LambdaQueryWrapper<Project> query = ProjectHelper.projectQuery(req);
        // 当非预审申报的时候 是自己单位 当是预审申报的时候 要主管单位
        preQuery(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 -> BeanUtil.copyProperties(w, ProjectLibListItemVO.class));
        return PageVo.of(records, total);
    }

    //当非预审申报的时候 是自己单位 当是预审申报的时候 要主管单位 并且他是单位管理员
    private void preQuery(LambdaQueryWrapper<Project> query, UserFullInfoDTO user) {
        //如果当前登录是单位管理员
        String mhUnitId = user.getMhUnitIdStr();
        if (user.getIsOrgAdmin()) {
            query.and(s1 -> s1.eq(Project::getStatus, ProjectStatusEnum.PREQUALIFICATION_FAILED.getCode())
                    .eq(Project::getSuperOrgCode, mhUnitId)
                    .or(q2 -> q2.eq(Project::getBuildOrgCode, mhUnitId)));
        } else {
            //否则 只能看到 非预审 并且
            query.and(q2 -> q2.eq(Project::getBuildOrgCode, mhUnitId));
        }
    }

    public void exportList(HttpServletResponse response, ProjectListReq param) {
        UserFullInfoDTO user = userInfoHelper.getUserFullInfo(LoginUserUtil.getUserId());
        LambdaQueryWrapper<Project> query = ProjectHelper.projectQuery(param);
        //当非预审申报的时候 是自己单位 当是预审申报的时候 要主管单位
        preQuery(query, user);
        query.eq(Project::getNewest, Boolean.TRUE);
        List<Project> records = projectService.list(query);

        AtomicInteger serialNumber = new AtomicInteger(0);
        List<DeclaredProjectExportDTO> collect = records.stream().map(r -> {
            DeclaredProjectExportDTO exportDTO = new DeclaredProjectExportDTO();
            BeanUtils.copyProperties(r, exportDTO);
            exportDTO.setProjectTypeName(ProjectTypeNewEnum.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());
            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 BizException(e);
        }
    }

    /**
     * 查询 核心业务
     *
     * @param businessName \
     * @return \
     */
    public JSONObject bizList(String businessName, Integer limit, Integer page, String orgCode) {
        JSONObject jsonObject = new JSONObject();
        UserInfoDetails user = LoginUserUtil.loginUserDetail();
        if (BizConst.DEV.equals(active)) {
            jsonObject = JSONObject.parseObject(DeclaredProjectConst.Biz.CORE_BIZ);
            if (CollUtil.isEmpty(jsonObject)) {
                return jsonObject;
            }
            if (StringUtils.isNotBlank(businessName)) {
                JSONArray dataArray = jsonObject.getJSONArray(BizConst.RESPONSE_KEY_DATA);
                if (CollUtil.isEmpty(dataArray)) {
                    return new JSONObject();
                }
                Iterator<Object> iter = dataArray.stream().iterator();
                JSONArray dataArrayRes = new JSONArray();
                while (iter.hasNext()) {
                    JSONObject value = (JSONObject) iter.next();
                    String businessValue = value.getString("matterName");
                    if (StringUtils.isNotBlank(businessValue) && businessValue.contains(businessName)) {
                        dataArrayRes.add(value);
                    }
                }
                jsonObject.put("data", dataArrayRes);
                jsonObject.put("total", dataArrayRes.size());
            }
        } else if (BizConst.PRE.equals(active) ||
                BizConst.PROD.equals(active)) {
            if (StringUtils.isNotBlank(orgCode)) {
                String[] orgCodes = orgCode.split(StrPool.COMMA);
                JSONArray finalArray = new JSONArray();
                for (String og : orgCodes) {
                    JSONObject res = projectIrsManage.searchCoreBiz(businessName, og, limit, page);
                    if (Objects.nonNull(res)) {
                        JSONArray dataArray = res.getJSONArray(BizConst.RESPONSE_KEY_DATA);
                        if (CollUtil.isNotEmpty(dataArray)) {
                            finalArray.addAll(dataArray);
                        }
                    }
                }
                jsonObject.put("data", finalArray);
                jsonObject.put("total", finalArray.size());
            } else {
                jsonObject = projectIrsManage.searchCoreBiz(businessName, user.getMhUnitIdStr(), limit, page);
            }
        }
        return jsonObject;
    }

    /**
     * 预审失败的时候 可以删除项目
     *
     * @param projectCode \
     */
    @Transactional
    public String removeProject(String projectCode) {
        Project project = projectService.getProjectByCode(projectCode);
        Assert.notNull(project, "该项目不存在");
        VUtils.isTrue(Objects.isNull(project.getStatus()) ||
                        !ProjectStatusEnum.PREQUALIFICATION_FAILED.getCode().equals(project.getStatus()))
                .throwMessage("不是预审不通过 状态 不能删除！");

        return collectionManage.removeProject(Lists.newArrayList(projectCode));
    }
}
