package com.ningdatech.pmapi.projectdeclared.manage;

import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.TypeReference;
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.ningdatech.basic.model.PageVo;
import com.ningdatech.pmapi.common.enumeration.ProjectProessStageEnum;
import com.ningdatech.pmapi.projectdeclared.entity.ProjectDraft;
import com.ningdatech.pmapi.projectdeclared.entity.dto.DeclaredProjectDto;
import com.ningdatech.pmapi.projectdeclared.entity.dto.DeclaredProjectListParamDto;
import com.ningdatech.pmapi.projectdeclared.entity.dto.ProjectConditionDto;
import com.ningdatech.pmapi.projectdeclared.entity.dto.ProjectDraftSaveDto;
import com.ningdatech.pmapi.projectdeclared.entity.vo.ProjectDeclaredDetailVO;
import com.ningdatech.pmapi.projectdeclared.entity.vo.ProjectDraftVo;
import com.ningdatech.pmapi.projectdeclared.service.IProjectDraftService;
import com.ningdatech.pmapi.projectlib.enumeration.ProjectStatusEnum;
import com.ningdatech.pmapi.projectlib.model.dto.ProjectDTO;
import com.ningdatech.pmapi.projectlib.model.entity.Project;
import com.ningdatech.pmapi.projectlib.model.entity.ProjectApplication;
import com.ningdatech.pmapi.projectlib.model.vo.ProjectLibListItemVO;
import com.ningdatech.pmapi.projectlib.service.IProjectApplicationService;
import com.ningdatech.pmapi.projectlib.service.IProjectService;
import com.wflow.bean.entity.WflowModels;
import com.wflow.exception.BusinessException;
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 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 DeclaredProjectManage
 * @Description
 * @Date 2023/2/1 14:48
 * @Author PoffyZhang
 */
@Component
@Slf4j
@RequiredArgsConstructor
public class DeclaredProjectManage {

    private final IProjectService projectService;

    private final IProjectApplicationService projectApplicationService;

    private final IProjectDraftService projectDraftService;

    private final ProcessInstanceService processService;

    private final ProcessModelService processModelService;

    public PageVo<ProjectLibListItemVO> page(DeclaredProjectListParamDto params) {
        Page<Project> page = params.page();
        LambdaQueryWrapper<Project> wrapper = Wrappers.lambdaQuery(Project.class)
                .ge(Objects.nonNull(params.getStartTime()), Project::getCreateOn, params.getStartTime())
                .le(Objects.nonNull(params.getEndTime()), Project::getCreateOn, params.getEndTime())
                .eq(Objects.nonNull(params.getProjectType()), Project::getProjectType, params.getProjectType())
                .eq(Objects.nonNull(params.getProjectYear()), Project::getProjectYear, params.getProjectYear())
                .eq(Objects.nonNull(params.getProjectStage()), Project::getStage, params.getProjectStage())
                .eq(Objects.nonNull(params.getProjectStatus()), Project::getStatus, params.getProjectStatus())
                .like(StringUtils.isNotBlank(params.getProjectName()), Project::getProjectName, params.getProjectName())
                .orderByDesc(Project::getUpdateOn);
        projectService.page(page, wrapper);
        if (0L == page.getTotal()) {
            return PageVo.empty();
        }
        List<ProjectLibListItemVO> res = page.getRecords().stream().map(record -> {
            ProjectLibListItemVO vo = new ProjectLibListItemVO();
            BeanUtils.copyProperties(record, vo);
            return vo;
        }).collect(Collectors.toList());
        return PageVo.of(res, page.getTotal());
    }

    public PageVo<ProjectDraftVo> pageDraft(DeclaredProjectListParamDto params) {
        Page<ProjectDraft> page = params.page();
        LambdaQueryWrapper<ProjectDraft> wrapper = Wrappers.lambdaQuery(ProjectDraft.class)
                .eq(ProjectDraft::getUserId,params.getUserId())
                .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 (0L == page.getTotal()) {
            return PageVo.empty();
        }
        List<ProjectDraftVo> res = page.getRecords().stream().map(record -> {
            ProjectDraftVo vo = new ProjectDraftVo();
            BeanUtils.copyProperties(record, vo);
            return vo;
        }).collect(Collectors.toList());
        return PageVo.of(res, page.getTotal());
    }

    public ProjectDraftVo draftDatail(Long id) {
        ProjectDraft draft = projectDraftService.getById(id);
        ProjectDraftVo vo = new ProjectDraftVo();
        BeanUtils.copyProperties(draft, vo);
        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 String startTheProcess(DeclaredProjectDto dto) {
        ProjectDTO projectInfo = dto.getProjectInfo();
        String regionCode = projectInfo.getAreaCode();

        WflowModels model = processModelService.getOne(Wrappers.lambdaQuery(WflowModels.class)
                .eq(WflowModels::getRegionCode, regionCode)
                .eq(WflowModels::getFormName, ProjectProessStageEnum.ORG_INTERNAL_APPROVAL_PROCESS.getDesc())
                .last("limit 1"));

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

        ProcessStartParamsVo params = new ProcessStartParamsVo();
        params.setUser(dto.getUser());
        params.setProcessUsers(Collections.emptyMap());
        //放入条件判断的项目字段
        ProjectConditionDto conditionDto = new ProjectConditionDto();
        BeanUtils.copyProperties(dto.getProjectInfo(), conditionDto);
        if (Objects.nonNull(conditionDto)) {
            dto.getFormData().putAll(
                    JSON.parseObject(JSON.toJSONString(conditionDto), new TypeReference<Map<String, Object>>() {
                    })
            );
        }
        params.setFormData(dto.getFormData());
        String instanceId = processService.startProcess(model.getProcessDefId(), params);
        log.info("申报项目成功 【{}】", instanceId);

        //保存项目
        saveProject(dto.getProjectInfo(), instanceId, regionCode);

        return instanceId;
    }

    /**
     * 申报项目 时 新增项目到项目库
     *
     * @param projectDto
     * @param instanceId
     */
    private void saveProject(ProjectDTO projectDto, String instanceId, String regionCode) {
        //流程启动之后 入库项目 重要业务信息 用于列表查询 展示
        try {
            //保存项目表信息
            Project project = new Project();
            BeanUtils.copyProperties(projectDto, project);
            project.setCreateOn(LocalDateTime.now());
            project.setUpdateOn(LocalDateTime.now());
            project.setAreaCode(regionCode);
            project.setStage(ProjectStatusEnum.NOT_APPROVED.getCode());
            project.setStatus(ProjectStatusEnum.UNDER_INTERNAL_AUDIT.getCode());
            project.setInstCode(instanceId);
            projectService.save(project);
            //保存项目应用
            if (CollUtil.isNotEmpty(projectDto.getApplicationList())) {
                List<ProjectApplication> applications = projectDto.getApplicationList().stream().map(application -> {
                    ProjectApplication projectApplication = new ProjectApplication();
                    BeanUtils.copyProperties(application, projectApplication);
                    projectApplication.setProjectId(project.getId());
                    return projectApplication;
                }).collect(Collectors.toList());
                projectApplicationService.saveBatch(applications);
            }
        } catch (Exception e) {
            log.error("项目信息入库错误 ", e);
            throw new BusinessException("项目信息入库错误 :" + e.getMessage());
        }
    }

    /**
     * 保存至草稿箱
     *
     * @param dto
     * @return
     */
    public Long saveToDraft(ProjectDraftSaveDto dto) {
        ProjectDTO projectInfo = dto.getProjectInfo();
        ProjectDraft draft = new ProjectDraft();
        BeanUtils.copyProperties(projectInfo, draft);
        draft.setUserId(dto.getUser().getUserId());
        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());
        }
        draft.setUpdateOn(LocalDateTime.now());
        projectDraftService.saveOrUpdate(draft);
        return draft.getId();
    }
}
