package com.dayou.workflow.config;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.dayou.common.BaseEntity;
import com.dayou.dto.WorkNodeCommit;
import com.dayou.dto.WorkNodeDTO;
import com.dayou.entity.*;
import com.dayou.enums.MainBusinessEnum;
import com.dayou.enums.workflow.CallbackEnum;
import com.dayou.enums.workflow.NodeStateEnum;
import com.dayou.enums.workflow.WorkflowNodeEnum;
import com.dayou.exception.ErrorCode;
import com.dayou.mapper.WorkFlowNodeInstanceMapper;
import com.dayou.service.workflow.IWorkFlowLogService;
import com.dayou.service.workflow.IWorkFlowNodeInstanceService;
import com.dayou.service.workflow.IWorkNodeService;
import com.dayou.service.workflow.IWorkTaskRecordService;
import com.dayou.utils.LoginContext;
import com.dayou.workflow.annotation.WorkflowControl;
import com.dayou.workflow.handler.MajorNodeHandler;
import com.dayou.workflow.handler.PersonalNodeHandler;
import lombok.extern.slf4j.Slf4j;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.lang.reflect.Method;
import java.util.Date;
import java.util.List;

import static com.dayou.common.Constants.MAJOR_HANDLER;
import static com.dayou.common.Constants.PERSONAL_HANDLER;
import static com.dayou.enums.workflow.NodeLogEnum.PASS;


/**
 * 类说明：(后期将删除此类)
 *
 * @author: wucl
 * @since: 2023/10/27
 * created with IntelliJ IDEA.
 */
@Component
@Slf4j
public class WorkflowInterceptor implements MethodInterceptor, ApplicationContextAware {

    private static ApplicationContext applicationContext;

    /**
     * Bean的依赖关系冲突，可能导致拦截器失效。
     *
     */

    @Autowired
    private IWorkFlowLogService workFlowLogService;

    @Autowired
    private IWorkFlowNodeInstanceService workFlowNodeInstanceService;

    @Autowired
    private WorkFlowNodeInstanceMapper workFlowNodeInstanceMapper;

    @Autowired
    private IWorkNodeService workNodeService;

    @Autowired
    private IWorkTaskRecordService workTaskRecordService;


    @Transactional
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        Method method = methodInvocation.getMethod();
        WorkflowControl workflowControl = method.getAnnotation(WorkflowControl.class);
        Object proceed = methodInvocation.proceed();
        if (proceed instanceof Boolean && (Boolean) proceed &&  workflowControl !=null){
            WorkNodeCommit workNodeCommit = (WorkNodeCommit) methodInvocation.getArguments()[0];
            WorkFlowNodeInstance currentInstanceNode = workFlowNodeInstanceMapper.getById(workNodeCommit.getInstanceNodeId());

            if (workNodeCommit.getState().equals(PASS) && workNodeCommit.getIfCheckTask()){
                if (checkCurrentNodeTaskState(currentInstanceNode)){
                    //将当前节点状态变更成已完成
                    workFlowNodeInstanceService.update(new LambdaUpdateWrapper<WorkFlowNodeInstance>().set(WorkFlowNodeInstance::getState,NodeStateEnum.FINISHED)
                            .set(WorkFlowNodeInstance::getComment,workNodeCommit.getComments()).eq(BaseEntity::getId,currentInstanceNode.getId()));
                    addWorkflowLog(workNodeCommit,currentInstanceNode);
                }else {
                    ErrorCode.throwBusinessException(ErrorCode.TASK_NOT_FINISHED);
                }
            }

            //确认当前节点是否需要校验业务数据
            //寻找下一个节点，有可能下一个节点并非流程配置的节点，需根据业务数据变动
            WorkNodeDTO nextWorkNode = workNodeService.getNextWorkNode(currentInstanceNode.getFlowId(), currentInstanceNode.getNodeId());
            String ifVerify = currentInstanceNode.getVerify();
            if (StrUtil.isNotEmpty(ifVerify)){
                CallbackEnum callback = CallbackEnum.valueOf(ifVerify);
//                if (callback !=null){
//                    //根据callback 确定具体的业务数据
//                    //获取产品类型
//                    List<ProductionEnum> production = workNodeCommit.getProduction();
//                    switch (callback){
//                        case CHECK_PRODUCTION_CHECK:
//                           nextWorkNode = workNodeService.getNextWorkNodeByCheckState(currentInstanceNode.getFlowId(),currentInstanceNode.getNodeCode(),
//                                   workNodeCommit.getDoSecondCheck(),workNodeCommit.getDoThirdCheck());
//                            break;
//                        case CHECK_PRODUCTION_TYPE:
//                            //1. 获取业务上出具的产品类型。
//                            //2.根据出具的产品类型 确定需要哪些节点
//                            Map<String,List<WorkflowNodeEnum>> majorNecessaryNodes = CallbackEnum.NecessaryNode.getByProduction(MainBusinessEnum.MAJOR_BUSINESS.name(),
//                                    production);
//                            nextWorkNode = workNodeService.getNextWorkNodeByBusiness(currentInstanceNode.getFlowId(),currentInstanceNode.getNodeCode(), majorNecessaryNodes);
//                            break;
//                        case CHECK_PRODUCTION_FUND:
//                            if (workNodeCommit.getIfProductionFund()){
//                                nextWorkNode = workNodeService.getNextWorkNode(nextWorkNode.getFlowId(), nextWorkNode.getId());
//                            }
//                            break;
//                        case CHECK_PERSONAL_PRODUCTION_TYPE:
//                            //1. 获取业务上出具的产品类型。
//                            //2.根据出具的产品类型 确定需要哪些节点
//                            Map<String,List<WorkflowNodeEnum>> personalNecessaryNodes = CallbackEnum.NecessaryNode.getByProduction(MainBusinessEnum.PERSONAL_BUSINESS.name(),
//                                    production);
//                            nextWorkNode = workNodeService.getNextWorkNodeByBusiness(currentInstanceNode.getFlowId(),currentInstanceNode.getNodeCode(), personalNecessaryNodes);
//                            break;
//                        case CHECK_FEEDBACK:
//                            if (!workNodeCommit.getIfFeedback()){
//                                nextWorkNode = workNodeService.getNextWorkNode(nextWorkNode.getFlowId(), nextWorkNode.getId());
//                            }
//                            break;
//                    }
//                }
            }

            if (nextWorkNode==null ){
                if (currentInstanceNode.getNodeCode().equals(WorkflowNodeEnum.BUSINESS_ARCHIVING.name())){
                    return proceed;
                }else {
                    ErrorCode.throwBusinessException(ErrorCode.NEXT_NODE_NOT_EXISTS);
                }

            }

//            if (CollectionUtil.isNotEmpty(workNodeCommit.getReportNos())){
//
//                //主流程分支
//                String code = workNodeService.getOne(new LambdaQueryWrapper<WorkNode>()
//                        .select(WorkNode::getCode)
//                        .eq(BaseEntity::getId, currentInstanceNode.getNodeId()).eq(BaseEntity::getDeleted, Boolean.FALSE)).getCode();
//                WorkflowNodeEnum workflowNodeEnum = WorkflowNodeEnum.valueOf(code);
//
//                if (workflowNodeEnum.equals(WorkflowNodeEnum.WRITE_STATEMENT)){
//                    for (String businessSubId :workNodeCommit.getReportNos()){
//                        addNextInstanceNode(currentInstanceNode,nextWorkNode, workNodeCommit.getNextHandlerId(),businessSubId,null);
//                    }
//                }
//                else if(workflowNodeEnum.equals(WorkflowNodeEnum.WRITE_REPORT) || workflowNodeEnum.equals(WorkflowNodeEnum.WRITE_LETTER)){
//                    for (String businessMinId :workNodeCommit.getReportNos()){
//                        addNextInstanceNode(currentInstanceNode,nextWorkNode, workNodeCommit.getNextHandlerId(),workNodeCommit.getBusinessSubId(),businessMinId);
//                    }
//                }else {
//                    addNextInstanceNode(currentInstanceNode,nextWorkNode, workNodeCommit.getNextHandlerId(),workNodeCommit.getBusinessSubId(),workNodeCommit.getBusinessMinId());
//                }
//            }
//            else {
//                addNextInstanceNode(currentInstanceNode,nextWorkNode, workNodeCommit.getNextHandlerId(),workNodeCommit.getBusinessSubId(),workNodeCommit.getBusinessMinId());
//            }

        }
        return proceed;
    }

    /**
     * 检查节点任务状态
     * @param currentInstanceNode
     * @return
     */
    private Boolean checkCurrentNodeTaskState(WorkFlowNodeInstance currentInstanceNode){
        List<WorkTaskRecord> notFinish = workTaskRecordService.list(new LambdaQueryWrapper<WorkTaskRecord>().eq(WorkTaskRecord::getInstanceId, currentInstanceNode.getId())
                        .eq(WorkTaskRecord::getFinished,Boolean.FALSE)
                .eq(BaseEntity::getDeleted, Boolean.FALSE));
        return CollectionUtil.isEmpty(notFinish);

    }



    /**
     * 添加流程日志
     * @param workNodeCommit
     */
    private void addWorkflowLog(WorkNodeCommit workNodeCommit,WorkFlowNodeInstance currentInstanceNode){
        WorkFlowLog log = new WorkFlowLog();
                    log.setBusinessId(currentInstanceNode.getBusinessId());
                    log.setBusinessType(currentInstanceNode.getBusinessType());
                    log.setHandingTime(new Date().getTime() - currentInstanceNode.getCreated().getTime());
                    log.setHandlerId(LoginContext.getCurrentUserId());
                    log.setComments(workNodeCommit.getComments());
                    log.setState(workNodeCommit.getState().name());
                    log.setInstanceId(workNodeCommit.getInstanceNodeId());
                    log.setBusinessSubId(workNodeCommit.getBusinessSubId());
                    log.setBusinessMinId(workNodeCommit.getBusinessMinId());
        workFlowLogService.add(log);
    }

    /**
     * 创建运行时的下一个节点实例
     * @param currentInstanceNode
     */
    private void addNextInstanceNode(WorkFlowNodeInstance currentInstanceNode,WorkNodeDTO nextWorkNode,Long handlerId,String businessSubId,String businessMinId){
        if (nextWorkNode!=null){
            List<WorkNodeTask> tasks = nextWorkNode.getTasks();
            //如果该节点下面只有一个任务，且由系统控制。则需要相关条件判断需不需要创建该节点实例
            WorkFlowNodeInstance nodeInstance = buildNodeInstance(nextWorkNode, currentInstanceNode,businessSubId,businessMinId);
            workFlowNodeInstanceService.add(nodeInstance);
            if (CollectionUtil.isNotEmpty(tasks)){
                MainBusinessEnum businessEnum = MainBusinessEnum.valueOf(nodeInstance.getBusinessType());
                switch (businessEnum){
                    case MAJOR_BUSINESS:
                        workTaskRecordService.doCreateNodeTaskRecord((MajorNodeHandler) applicationContext.getBean(MAJOR_HANDLER),tasks,nodeInstance,handlerId);
                        break;
                    case PERSONAL_BUSINESS:
                        workTaskRecordService.doCreateNodeTaskRecord((PersonalNodeHandler) applicationContext.getBean(PERSONAL_HANDLER),tasks,nodeInstance,handlerId);
                        break;
                }


            }
            // 节点下面没有任务，说明是状态节点。状态节点业务上未定义逻辑
        }
    }

    private WorkFlowNodeInstance buildNodeInstance(WorkNodeDTO nextWorkNode,WorkFlowNodeInstance currentInstanceNode,String businessSubId,String businessMinId){
        WorkFlowNodeInstance nodeInstance = new WorkFlowNodeInstance();
        nodeInstance.setNodeId(nextWorkNode.getId());
        nodeInstance.setFlowId(nextWorkNode.getFlowId());
        nodeInstance.setBusinessId(currentInstanceNode.getBusinessId());
        nodeInstance.setBusinessSubId(businessSubId);
        nodeInstance.setBusinessMinId(businessMinId);
        nodeInstance.setBusinessType(currentInstanceNode.getBusinessType());
        nodeInstance.setSequence((currentInstanceNode.getSequence()==null?0:currentInstanceNode.getSequence())+1);
        nodeInstance.setState(NodeStateEnum.PENDING.name());
        return nodeInstance;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}
