package com.ningdatech.pmapi.projectdeclared.manage;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.Maps;
import com.ningdatech.basic.function.VUtils;
import com.ningdatech.pmapi.common.constant.BizConst;
import com.ningdatech.pmapi.common.constant.RegionConst;
import com.ningdatech.pmapi.common.enumeration.CommonEnum;
import com.ningdatech.pmapi.common.enumeration.ProjectProcessStageEnum;
import com.ningdatech.pmapi.common.helper.UserInfoHelper;
import com.ningdatech.pmapi.projectdeclared.model.dto.DefaultDeclaredDTO;
import com.ningdatech.pmapi.projectdeclared.model.dto.ProjectConditionDTO;
import com.ningdatech.pmapi.projectdeclared.model.po.DeclaredProjectStatisticsPO;
import com.ningdatech.pmapi.projectdeclared.service.IDeclaredStatisticsService;
import com.ningdatech.pmapi.projectlib.model.dto.ProjectDTO;
import com.ningdatech.pmapi.projectlib.model.entity.Project;
import com.ningdatech.pmapi.projectlib.service.IProjectService;
import com.ningdatech.pmapi.user.security.auth.model.UserFullInfoDTO;
import com.ningdatech.pmapi.user.security.auth.model.UserInfoDetails;
import com.ningdatech.pmapi.user.util.LoginUserUtil;
import com.wflow.bean.entity.WflowModels;
import com.wflow.bean.entity.WflowOrgModels;
import com.wflow.enums.OrgTypeEnum;
import com.wflow.enums.ProcessDefTypeEnum;
import com.wflow.exception.BusinessException;
import com.wflow.service.OrgProcdefService;
import com.wflow.workflow.bean.dto.OrgInfoDTO;
import com.wflow.workflow.bean.dto.ProcessInstanceUserDto;
import com.wflow.workflow.bean.vo.ProcessStartParamsVo;
import com.wflow.workflow.service.ProcessModelService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Classname DefaultDeclaredProjectManage
 *
 * @Description
 * @Date 2023/2/14 17:12
 * @Author PoffyZhang
 */
@Component
@Slf4j
@RequiredArgsConstructor
public class DefaultDeclaredProjectManage {

    private final IProjectService projectService;

    private final UserInfoHelper userInfoHelper;

    private final ProcessModelService processModelService;
    private final OrgProcdefService orgProcdefService;

    private final IDeclaredStatisticsService statisticsService;

    /**
     * 公共的发起流程方法
     */
    public void startProcess(ProjectDTO projectDTO,UserFullInfoDTO user,Integer processType){
        WflowModels model = processModelService.getOne(Wrappers.lambdaQuery(WflowModels.class)
                .eq(WflowModels::getRegionCode, user.getRegionCode())
                .eq(WflowModels::getProcessType, processType)
                .last(BizConst.LIMIT_1));

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

        ProcessStartParamsVo params = new ProcessStartParamsVo();
        params.setUser(buildUser(user.getEmployeeCode()));
        params.setProcessUsers(Collections.emptyMap());
        //放入条件判断的项目字段
        ProjectConditionDTO conditionDto = new ProjectConditionDTO();
        BeanUtils.copyProperties(projectDTO, conditionDto);
        Map<String,Object> formData = Maps.newHashMap();
        formData.putAll(
                JSON.parseObject(JSON.toJSONString(conditionDto), new TypeReference<Map<String, Object>>() {
                })
        );
        params.setFormData(formData);

    }

    //项目名称去重
    public void checkDuplication(ProjectDTO project){
        VUtils.isTrue(projectService.count(Wrappers.lambdaQuery(Project.class)
                        .eq(Project::getProjectName,project.getProjectName())
                        .eq(Project::getNewest,Boolean.TRUE)
                        .ne(Objects.nonNull(project.getProjectCode()),Project::getProjectCode,project.getProjectCode())) > 0)
                .throwMessage(String.format("修改失败 此项目名 【%s】 已存在!",project.getProjectName()));
    }

    public void checkAmount(ProjectDTO projectInfo) {
        if(Objects.nonNull(projectInfo.getDeclareAmount())){
            BigDecimal declareAmount = projectInfo.getDeclareAmount();
            BigDecimal govSuperFinanceAmount = projectInfo.getDeclareGovSuperiorFinanceAmount();
            BigDecimal govOwnFinanceAmount = projectInfo.getDeclareGovOwnFinanceAmount();
            BigDecimal bankLendingAmount = projectInfo.getDeclareBankLendingAmount();
            BigDecimal haveAmount = projectInfo.getDeclareHaveAmount();
            BigDecimal otherAmount = projectInfo.getDeclareOtherAmount();
            BigDecimal totalAmount = govSuperFinanceAmount
                    .add(govOwnFinanceAmount).add(bankLendingAmount).add(haveAmount).add(otherAmount);
            VUtils.isTrue(declareAmount.compareTo(totalAmount) != 0)
                    .throwMessage(String.format("申报失败! 申报总金额【%s】 不等于其它申报金额 【%s】",declareAmount,totalAmount));

            //判断 年度计划金额 如果开着的话
            if(Objects.nonNull(projectInfo.getAnnualPlanAmount())){
                BigDecimal annualPlanAmount = projectInfo.getAnnualPlanAmount();

                VUtils.isTrue(annualPlanAmount.compareTo(BigDecimal.ZERO) <= 0)
                        .throwMessage(String.format("申报失败! 年度支付总金额【%s】 不能小于等于0",annualPlanAmount));
                VUtils.isTrue(annualPlanAmount.compareTo(declareAmount) > 0)
                        .throwMessage(String.format("申报失败! 年度支付总金额【%s】 不能大于申报金额 【%s】",annualPlanAmount,declareAmount));

                BigDecimal annualGovSuperAmount = projectInfo.getAnnualPlanGovSuperiorFinanceAmount();
                BigDecimal annualOwnSuperAmount = projectInfo.getAnnualPlanGovOwnFinanceAmount();
                BigDecimal annualBankLendingAmount = projectInfo.getAnnualPlanBankLendingAmount();
                BigDecimal annualHaveAmount = projectInfo.getAnnualPlanHaveAmount();
                BigDecimal anualOtherAmount = projectInfo.getAnnualPlanOtherAmount();
                BigDecimal totalAnnual = annualGovSuperAmount
                        .add(annualOwnSuperAmount).add(annualBankLendingAmount).add(annualHaveAmount).add(anualOtherAmount);
                VUtils.isTrue(annualPlanAmount.compareTo(totalAnnual) != 0)
                        .throwMessage(String.format("申报失败! 年度支付总金额【%s】 不等于其它年度支付金额 【%s】",annualPlanAmount,totalAnnual));
            }
        }
    }

    public ProcessInstanceUserDto buildUser(String employeeCode){
        UserFullInfoDTO userInfoDetail = userInfoHelper.getUserFullInfoByEmployeeCode(employeeCode);

        VUtils.isTrue(Objects.isNull(userInfoDetail))
                .throwMessage(String.format("【%s】获取不到用户!",employeeCode));

        return ProcessInstanceUserDto.builder()
                .userId(String.valueOf(userInfoDetail.getEmployeeCode()))
                .userName(userInfoDetail.getRealName())
                .orgCode(userInfoDetail.getEmpPosUnitCode())
                .orgName(userInfoDetail.getEmpPosUnitName())
                .build();
    }

    public Map<String, OrgInfoDTO> buildOrgModelMap(String employeeCode,Project project) {
        Map<String, OrgInfoDTO> orgMap = new HashMap<>();

        // 查出所有的单位流程配置
        List<WflowOrgModels> orgModelsList = orgProcdefService.list();

        UserFullInfoDTO userFullInfo = userInfoHelper.getUserFullInfoByEmployeeCode(employeeCode);
        if(Objects.isNull(userFullInfo)){
            log.warn("取不到员工 返回空map");
            return Collections.emptyMap();
        }

        String startOrgCode = userFullInfo.getEmpPosUnitCode();
        String startOrgName = userFullInfo.getEmpPosUnitName();
        String startOrgParentCode;
        String startOrgParentName;
        // 如果申报时 上级主管单位,由该单位自己审核
        if (CommonEnum.NO.getCode().equals(project.getIsSuperOrg())){
            startOrgParentCode = startOrgCode;
            startOrgParentName = startOrgName;
        }else {
            startOrgParentCode = project.getSuperOrgCode();
            startOrgParentName = project.getSuperOrg();
        }
        //查询 当前发起人及主管单位所在区域的 单位流程配置
        OrgInfoDTO startOrgInfoDto = new OrgInfoDTO();
        startOrgInfoDto.setOrganizationCode(startOrgCode);
        startOrgInfoDto.setOrganizationName(startOrgName);

        startOrgInfoDto.setOrgModelMap(orgModelsList.stream()
                .filter(v -> v.getOrgCode().equals(startOrgCode)
                        && Boolean.FALSE.equals(v.getIsDelete())
                        && (ProcessDefTypeEnum.SEAL.name().equals(v.getType())||
                        ProcessDefTypeEnum.JOINT_REVIEW.name().equals(v.getType()) ||
                        ProcessDefTypeEnum.DEFAULT.name().equals(v.getType())))
                .collect(Collectors.toMap(WflowOrgModels::getType, v -> v)));

        OrgInfoDTO parentOrgInfoDto = new OrgInfoDTO();
        parentOrgInfoDto.setOrganizationCode(startOrgParentCode);
        parentOrgInfoDto.setOrganizationName(startOrgParentName);
        parentOrgInfoDto.setOrgModelMap(orgModelsList.stream()
                .filter(v -> v.getOrgCode().equals(startOrgParentCode)
                        && Boolean.FALSE.equals(v.getIsDelete())
                        && (ProcessDefTypeEnum.SEAL.name().equals(v.getType()) ||
                        ProcessDefTypeEnum.JOINT_REVIEW.name().equals(v.getType()) ||
                        ProcessDefTypeEnum.DEFAULT.name().equals(v.getType())))
                .collect(Collectors.toMap(WflowOrgModels::getType, v -> v)));

        orgMap.put(OrgTypeEnum.TARGET_OWNER.name(),startOrgInfoDto);
        orgMap.put(OrgTypeEnum.TARGET_MANAGEMENT.name(),parentOrgInfoDto);

        // 如果是指定单位，直接根据流程定义ID放入map
        OrgInfoDTO orgInfoDTO = new OrgInfoDTO();
        orgInfoDTO.setOrgModelMap(orgModelsList.stream()
                .collect(Collectors.toMap(WflowOrgModels::getProcessDefId, v -> v)));
        orgMap.put(OrgTypeEnum.TARGET_LABEL.name(),orgInfoDTO);

        //如果有上级条线主管单位 并且 不是市本级
        if(!RegionConst.RC_LS.equals(project.getAreaCode()) &&
                CommonEnum.YES.getCode().equals(project.getIsHigherSuperOrg())){
            OrgInfoDTO parentLineOrgInfoDto = new OrgInfoDTO();
            parentLineOrgInfoDto.setOrganizationCode(project.getHigherSuperOrgCode());
            parentLineOrgInfoDto.setOrganizationName(project.getHigherSuperOrg());
            parentLineOrgInfoDto.setOrgModelMap(orgModelsList.stream()
                    .filter(v -> v.getOrgCode().equals(project.getHigherSuperOrgCode())
                            && Boolean.FALSE.equals(v.getIsDelete())
                            && (ProcessDefTypeEnum.SEAL.name().equals(v.getType()) ||
                            ProcessDefTypeEnum.JOINT_REVIEW.name().equals(v.getType()) ||
                            ProcessDefTypeEnum.DEFAULT.name().equals(v.getType())))
                    .collect(Collectors.toMap(WflowOrgModels::getType, v -> v)));

            orgMap.put(OrgTypeEnum.TARGET_LINE_MANAGEMENT.name(),parentLineOrgInfoDto);
        }
        return orgMap;
    }

    public Map<String, OrgInfoDTO> buildOrgModelMapUserOrgCode(Project project) {
        Map<String, OrgInfoDTO> orgMap = new HashMap<>();

        // 查出所有的单位流程配置
        List<WflowOrgModels> orgModelsList = orgProcdefService.list();

        String startOrgCode = project.getBuildOrgCode();
        String startOrgName = project.getBuildOrgName();
        String startOrgParentCode;
        String startOrgParentName;
        // 如果申报时 上级主管单位,由该单位自己审核
        if (CommonEnum.NO.getCode().equals(project.getIsSuperOrg())){
            startOrgParentCode = startOrgCode;
            startOrgParentName = startOrgName;
        }else {
            startOrgParentCode = project.getSuperOrgCode();
            startOrgParentName = project.getSuperOrg();
        }
        //查询 当前发起人及主管单位所在区域的 单位流程配置
        OrgInfoDTO startOrgInfoDto = new OrgInfoDTO();
        startOrgInfoDto.setOrganizationCode(startOrgCode);
        startOrgInfoDto.setOrganizationName(startOrgName);

        startOrgInfoDto.setOrgModelMap(orgModelsList.stream()
                .filter(v -> v.getOrgCode().equals(startOrgCode)
                        && Boolean.FALSE.equals(v.getIsDelete())
                        && (ProcessDefTypeEnum.SEAL.name().equals(v.getType()) ||
                        ProcessDefTypeEnum.JOINT_REVIEW.name().equals(v.getType()) ||
                        ProcessDefTypeEnum.DEFAULT.name().equals(v.getType())))
                .collect(Collectors.toMap(WflowOrgModels::getType, v -> v)));

        OrgInfoDTO parentOrgInfoDto = new OrgInfoDTO();
        parentOrgInfoDto.setOrganizationCode(startOrgParentCode);
        parentOrgInfoDto.setOrganizationName(startOrgParentName);
        parentOrgInfoDto.setOrgModelMap(orgModelsList.stream()
                .filter(v -> v.getOrgCode().equals(startOrgParentCode)
                        && Boolean.FALSE.equals(v.getIsDelete())
                        && (ProcessDefTypeEnum.SEAL.name().equals(v.getType()) ||
                        ProcessDefTypeEnum.JOINT_REVIEW.name().equals(v.getType()) ||
                        ProcessDefTypeEnum.DEFAULT.name().equals(v.getType())))
                .collect(Collectors.toMap(WflowOrgModels::getType, v -> v)));

        orgMap.put(OrgTypeEnum.TARGET_OWNER.name(),startOrgInfoDto);
        orgMap.put(OrgTypeEnum.TARGET_MANAGEMENT.name(),parentOrgInfoDto);

        // 如果是指定单位，直接根据流程定义ID放入map
        OrgInfoDTO orgInfoDTO = new OrgInfoDTO();
        orgInfoDTO.setOrgModelMap(orgModelsList.stream()
                .collect(Collectors.toMap(WflowOrgModels::getProcessDefId, v -> v)));
        orgMap.put(OrgTypeEnum.TARGET_LABEL.name(),orgInfoDTO);

        //如果有上级条线主管单位 并且 不是市本级
        if(!RegionConst.RC_LS.equals(project.getAreaCode()) &&
                CommonEnum.YES.getCode().equals(project.getIsHigherSuperOrg())){
            OrgInfoDTO parentLineOrgInfoDto = new OrgInfoDTO();
            parentLineOrgInfoDto.setOrganizationCode(project.getHigherSuperOrgCode());
            parentLineOrgInfoDto.setOrganizationName(project.getHigherSuperOrg());
            parentLineOrgInfoDto.setOrgModelMap(orgModelsList.stream()
                    .filter(v -> v.getOrgCode().equals(project.getHigherSuperOrgCode())
                            && Boolean.FALSE.equals(v.getIsDelete())
                            && (ProcessDefTypeEnum.SEAL.name().equals(v.getType()) ||
                            ProcessDefTypeEnum.JOINT_REVIEW.name().equals(v.getType()) ||
                            ProcessDefTypeEnum.DEFAULT.name().equals(v.getType())))
                    .collect(Collectors.toMap(WflowOrgModels::getType, v -> v)));

            orgMap.put(OrgTypeEnum.TARGET_LINE_MANAGEMENT.name(),parentLineOrgInfoDto);
        }
        return orgMap;
    }

    //根据提交者的单位
    public DeclaredProjectStatisticsPO declaredProjectOrgStatistics(Integer year, UserFullInfoDTO user){
        //查此人建设单位的项目
        return statisticsService.getOrgStatistics(user.getEmpPosUnitCode(),year);
    }

    //根据提交者的区域 他是区管或者超管
    public DeclaredProjectStatisticsPO declaredProjectRegionStatistics(Integer year, UserFullInfoDTO user){
        //查此人建设单位的项目
        return statisticsService.getRegionStatistics(user.getRegionCode(),year);
    }

    /**
     * 把条件值放入工作流 用于判断条件 根据dto
     * @param params
     * @param dto
     */
    public void buildCondition(ProcessStartParamsVo params, DefaultDeclaredDTO dto) {
        //放入条件判断的项目字段
        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());
    }

    /**
     * 把条件值放入工作流 用于判断条件 根据d项目实体
     * @param params
     * @param projectInfo
     */
    public void buildCondition(ProcessStartParamsVo params, Project projectInfo) {
        //放入条件判断的项目字段
        ProjectConditionDTO conditionDto = new ProjectConditionDTO();
        BeanUtils.copyProperties(projectInfo, conditionDto);
        params.setFormData(JSON.parseObject(JSON.toJSONString(conditionDto), new TypeReference<Map<String, Object>>() {
        }));
    }
}
