package com.horizon.wf.action;


import com.horizon.wf.action.base.BaseAction;
import com.horizon.wf.core.node.INextNode;
import com.horizon.wf.core.node.ITaskNode;
import com.horizon.wf.core.runtime.RunningData;
import com.horizon.wf.core.work.ICustomAction;
import com.horizon.wf.definition.pub.IFlowNode;
import com.horizon.wf.definition.pub.node.INodeControl;
import com.horizon.wf.definition.tools.NodeTypeEnum;
import com.horizon.wf.entity.db.DBTodo;
import com.horizon.wf.entity.db.DBTrack;
import com.horizon.wf.entity.db.DBWorkAuth;
import com.horizon.wf.global.CollectionUtil;
import com.horizon.wf.global.StaticVarExtend;
import com.horizon.wf.global.StringUtilExtend;
import com.horizon.wf.tools.AccessUtil;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * 正常拿回操作实现类
 * **不支持节点内部拿回
 * 此类为同一路径下的节点间拿回,从分支和合并节点拿回时,系统在获取操作时,
 * 自动对操作实现类进行重置为ActionGetBackForBranch和ActionGetBackForMerger
 * @author liys
 * @version 1.0
 * @since v7.2.2
 */
public class ActionGetBack extends BaseAction implements ICustomAction {
    private List<INextNode> nextNodes =null;
    /**
     * 操作调用的方法
     */
    public boolean execute(ITaskNode curTaskNode) {

        runningdata.setFunname(StaticVarExtend.OPERATOR_FUNNAME_GETBACK);
        setActionname("GetBack_Msg0001","拿回");
        if(isGetBack()){
            msg = pubInfo.getInfo("GetBack_Msg0002", "拿回成功");
            getCurTrack().setFlowstatus(StaticVarExtend.FlowStatus_GetBack);
            //将办理人设置的交互对象中,供节点进入事件使用
            selectedUsers = new LinkedHashMap<String, String>();

            //将当前人员放到人员列表中
            selectedUsers.put(StaticVarExtend.AUTH_AUTHOR,  getCurUser().getFullName());
            /**
             * 20160711 liys
             * 拿回分为以下几种情况:
             * 1.同一路径内流转时的拿回,例如:(N1->N2->N3),N2的办理人在流转到N3节点时执行拿回
             * 2.路径出现分支时,各分支未处理之前,例如:(N1->N2,N1->N3),N1的办理人在N2或者N3上执行拿回,
             *   每次只能拿回一个分支,拿回后的N1其实是在分支路径上,而不是在分支之前的路径上
             */
            //将本来的主办人都设置为读者
            List<DBWorkAuth> curAuth = runningdata
                    .getListAuthLikeAuthorByTrackid(getCurTrack().getId());
//			curAuth.addAll(getXAuthList(StaticVarExtend.AUTH_SECOND_AUTHOR));
            for(int i=0,n=curAuth.size();i<n;i++){
                DBWorkAuth a  = curAuth.get(i);
                //更改其余办理人权限
                a.setStatus(StaticVarExtend.AUTH_CREADER);
            }

            //将处理好的人员列表放回底层
            runningdata.setCurAuthToMap(curAuth);
            curTaskNode.backToPrevNode(nextNodeList, selectedUsers);
            INextNode curNode = curTaskNode.getCurNode();
            result = curNode.getInitResult();
            msg = curNode.getBackMsg();
            if(result == StaticVarExtend.F_STATUS_Success){
                nextNodes = curTaskNode.getGNextNodes();
                getCurTrack().setEnd(true);//结束当前路径
                ac.dowithTimer("cancel", getCurNodeType(), runningdata, getIdentifier());//如果当前节点包含定时任务则撤销定时任务

                //2024-2-19 触发进入事件，如果同时想触发进入事件和离开事件，参数传递true
                doWithNextNodeResult(false);

                //执行操作完成事件
                if(executeEventSave()){
//					selectedUsers = ac.makeAgentUser(selectedUsers, runningdata);
                    runningdata.setUpdateTask(true);
                    msg = pubInfo.getInfo("GetBack_Msg0002", "拿回成功");
                    memoBuffer.append(msg);
                }
            }
        }
        getCurTrack().setFlowstatus(StaticVarExtend.FlowStatus_GetBack);
        runningdata.setBackMsg(msg);
        runningdata.setResultCode(result);
        return true;
    }

    /**
     * 判断是否允许拿回
     * @return
     */
    protected boolean isGetBack(){
        if(!isNodeGetBack()){//节点不在可拿回节点的类型范围之内
            result = StaticVarExtend.F_STATUS_OtherError;
            msg = pubInfo.getInfo("GetBack_Msg0003","当前节点不在可拿回的节点类型范围之内");
            return false;
        }else if(isDone()){//当前流程已经办理,无法拿回
            result = StaticVarExtend.F_STATUS_OtherError;
            msg = pubInfo.getInfo("GetBack_Msg0004","当前流程已经办理,无法拿回");
            return false;
        }else if(isSave()){//当前流程主办人已经暂存，无法拿回
            result = StaticVarExtend.F_STATUS_OtherError;
            msg = pubInfo.getInfo("GetBack_Msg0005","当前流程主办人已暂存,无法拿回");
            return false;
        }else if(getCurTrack().getFlowstatus() != StaticVarExtend.FlowStatus_Normal){
            result = StaticVarExtend.F_STATUS_OtherError;
            msg = pubInfo.getInfo("GetBack_Msg0006","当前流程状态不是正常状态,不允许拿回");
            return false;
        }

        return true;
    }


    /**
     * 判断当前节点类型是否允许拿回
     * @return true:允许拿回
     */
    protected boolean isNodeGetBack(){
        int nodetype = getCurNodeType();
        if(nodetype == NodeTypeEnum.Node_Single
                || nodetype == NodeTypeEnum.Node_Multi_Single
                || nodetype == NodeTypeEnum.Node_Multi
                || nodetype == NodeTypeEnum.Node_Order
                || nodetype == NodeTypeEnum.Node_Progressively){
            return true;
        }
        return false;
    }

    /**
     * 判断当前节点是否已经暂存
     * @return
     */
    protected boolean isSave(){

        String sql="SELECT L.ACTION FROM TW_HZ_LOG L WHERE L.WORKID=? AND L.TRACKID=? ORDER BY L.ACTIONTIME DESC";
        List<Object> con= new ArrayList<Object>();
        con.add(getWorkid());
        con.add(getCurTrackid());
        List<List<Object>> workLogs= AccessUtil.getInstance().getMultiList(sql,con,getIdentifier());
        if(null==workLogs || workLogs.isEmpty())return false;
        if("Save".equals(workLogs.get(0).get(0).toString())){
            return true;
        }
        return false;
    }

    /**
     * 判断当前节点是否已经办理
     * @return
     */
    protected boolean isDone(){
        //获取会签人员和已办人员
        List<DBWorkAuth> curAuth = getXAuthList(StaticVarExtend.AUTH_DONE);
        curAuth.addAll(getXAuthList(StaticVarExtend.AUTH_HUIQIAN_AUTHOR));
        curAuth.addAll(getXAuthList(StaticVarExtend.AUTH_START_HUIQIAN_AUTHOR));

        return CollectionUtil.isListNotEmpty(curAuth);
    }

    /**
     * 判断当前操作人是否为主办
     * @return true:当前人是主办
     */
    private boolean isCurUserAuthor(){
        List<DBWorkAuth> authors =  getXAuthList(StaticVarExtend.AUTH_AUTHOR);
        for(int i=0,n=authors.size();i<n;i++){
            DBWorkAuth a = authors.get(i);
            if(a.equalsRunUser(getCurUser())){
                return true;
            }
        }
        return false;
    }

    /**
     * 给task表添加数据
     */
    public Map<String,List<Object>>  actionForTask() {
        Map<String, List<Object>> saveMap = new LinkedHashMap<String, List<Object>>();
        actionForTask(saveMap);
        //下一节点任务创建
        for(int i=0,n=runningdata.getNextTrackList().size();i<n;i++){
            DBTrack tTrack = runningdata.getNextTrackList().get(i);
            createTaskObj(null,tTrack,saveMap);
        }
        return saveMap;
    }
    /**
     * 取消分支时调用
     */
    public void cancelTrackAction(String arg0, List<DBWorkAuth> arg1) {

    }

    /**
     * 当取消某个路径的激活状态时,对应的权限变化规则设定<br>
     * (比如把待办的状态改为只读,或者是删除,或者忽略)<br>
     * 如果不指定,则忽略权限变更(比如Reader不需要处理)<br>
     * 主要用于合并节点提交时,如果有其他分支未到达时,对未到达的分支的参与者进行处理<br>
     * @return key=修改前的权限名称(比如:办理人Author),value=修改后的权限名称(比如:只读权限CReader)
     */
    public Map<String,String> getCancelTrackAuthRuleMap(){
        return null;
    }
    /**
     * 保存之后的接口方法.例如:消息数据的更新
     */
    public void actionAfterSaveToDB() {
        if(isNotSaveToDB){
            return;
        }
        actionStartTodoThread();
    }
    /**
     * 保存数据之前的接口方法.例如:消息数据的处理
     * key=sql,value=parameter
     */
    public Map<String,List<Object>> actionBeforeSaveToDB() {
        Map<String, List<Object>> saveMap = new LinkedHashMap<String, List<Object>>();
        sendTodo();
        actionBefore(saveMap);
        return saveMap ;
    }
    /**
     * 当前节点权限处理(由操作再次干预参与者的接口)<br>
     * auths列表中为系统节点默认处理完毕后的当前节点的参与者列表
     * 对应的消息是否需要处理,可以在这个方法中写
     */
    public boolean chgCurNodeAuth(List<DBWorkAuth> auths) {
        if(CollectionUtil.isListNotEmpty(auths)){
            makeSendInfoByCurAuthList(auths);
        }

        return true;
    }
    /**
     * 重写父类待办发送方法
     * 给当前办理人发送待阅
     */
    protected void sendTodo(){
        String getbackNodeId = getGetBackNode(runningdata);
        if(StringUtilExtend.isNotNull(getbackNodeId)){
            if(isTrackByNodeId(getbackNodeId, StaticVarExtend.FlowStatus_Reject, getWorkid(), getIdentifier())){
                runningdata.putMsgToConsole("[msg]this node have track.");
                return;
            }
        }

        String todoUser = selectedUsers.get(StaticVarExtend.AUTH_AUTHOR);
        DBTodo todo = makeDefaultDBTodo();
        String nodeid="";
        if(nextNodes != null){
            nodeid = nextNodes.get(0).getNodeid();
        }
        IFlowNode node = runningdata.getInstanceDefinition().getFlowinfo().getNodeById(nodeid);
        DBTrack track = getTrackIdByNodeId(nodeid);
        makeSendInfoForNewAuth(todo,todoUser,StaticVarExtend.AUTH_AUTHOR,StaticVarExtend.Send_Todo,
                null,track,node);
    }
    /**
     * 在指定节点是否有指定状态的track
     * @param nodeid
     * @param status
     * @param workid
     * @param identifier
     */
    private boolean isTrackByNodeId(String nodeid, int status, String workid, String identifier){
        String sql = "SELECT id FROM " + StaticVarExtend.Table_Track +" WHERE WORKID=? AND NODEID=? AND FLOWSTATUS=?";
        List<Object> conditionList = new ArrayList<Object>();
        conditionList.add(workid);
        conditionList.add(nodeid);
        conditionList.add(status);
        int count = AccessUtil.getInstance().getRsCount(sql, conditionList, identifier);
        if(count == 0){
            return false;
        }
        return true;
    }
    /**
     * 当前节点是否为上一节点,拿回操作调用
     * @return
     */
    private String getGetBackNode(RunningData rd){
        IFlowNode node = rd.getCurFlowNode();
        INodeControl control = node.getParticipantsControl(StaticVarExtend.AUTH_DONE+"_DO");
        String allNodes = "";
        if(null != control){
            allNodes = control.getAllowGetBackNode();
        }
        //获取上一节点
        return allNodes;
    }

}
