package com.alibaba.smart.framework.engine.test.process;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.alibaba.smart.framework.engine.configuration.LockStrategy;
import com.alibaba.smart.framework.engine.model.assembly.ProcessDefinition;
import com.alibaba.smart.framework.engine.model.instance.ExecutionInstance;
import com.alibaba.smart.framework.engine.model.instance.InstanceStatus;
import com.alibaba.smart.framework.engine.model.instance.ProcessInstance;
import com.alibaba.smart.framework.engine.service.command.RepositoryCommandService;
import com.alibaba.smart.framework.engine.service.param.query.ProcessInstanceQueryParam;
import com.alibaba.smart.framework.engine.test.DatabaseBaseTestCase;
import com.alibaba.smart.framework.engine.test.process.helper.DoNothingLockStrategy;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

/**
 * Created by 高海军 帝奇 74394 on 2017 May  16:31.
 *
 */
@ContextConfiguration("/spring/application-test.xml")
@RunWith(SpringJUnit4ClassRunner.class)
@Transactional
public class ParalleyGatewayAndCallActivityTest extends DatabaseBaseTestCase {


    protected void initProcessConfiguration() {
        super.initProcessConfiguration();
        LockStrategy doNothingLockStrategy = new DoNothingLockStrategy();
        processEngineConfiguration.setLockStrategy(doNothingLockStrategy);
    }

    @Test
    public void testBasicCallActivityWhenChildProcessIsPaused()  {

        //=======1.0 进入到子流程 =========
        ProduceProcessInstanceIds produceProcessInstanceIds = new ProduceProcessInstanceIds().invoke();

        String parentProcessInstanceId = produceProcessInstanceIds.getParentProcessInstanceId();
        String childProcessInstanceId = produceProcessInstanceIds.getChildProcessInstanceId();

        //
        //ExecutionInstance childDebitExecutionInstance = assertAndGetExecutionInstance(parentProcessInstanceId,
        //    childProcessInstanceId);
        //
        ////=======1.0 END =========
        //
        ////=======2.0 START ========= 这里需要驱动子流程运作。
        //
        ////完成下单确认,将流程驱动到等待资金到账环节。
        //executionCommandService.signal(childDebitExecutionInstance.getInstanceId());
        //
        //// 父流程还是在callActivity节点（其实没啥必要再断言了）
        //List<ExecutionInstance> parentCallActivityExecutionInstanceList = executionQueryService.findActiveExecutionList(parentProcessInstanceId);
        //assertEquals(1, parentCallActivityExecutionInstanceList.size());
        //ExecutionInstance parentCallActivityExecutionInstance = parentCallActivityExecutionInstanceList.get(0);
        //assertTrue("callActivity".equals(parentCallActivityExecutionInstance.getProcessDefinitionActivityId()));
        //
        //// 子流程在checkDebitResult节点
        //List<ExecutionInstance> childExecutionInstanceList = executionQueryService.findActiveExecutionList(childProcessInstanceId);
        //assertEquals(1, childExecutionInstanceList.size());
        //ExecutionInstance childDebitChildExecutionInstance = childExecutionInstanceList.get(0);
        //assertTrue("checkDebitResult".equals(childDebitChildExecutionInstance.getProcessDefinitionActivityId()));
        //
        //
        ////=======2.0 END ========= 这里需要驱动子流程运作。
        //
        //
        ////=======3.0 START ========= 完成驱动子流程，此时流程需要回到父流程。
        //
        ////完成资金到账,将流程驱动到资金交割处理环节。
        //executionCommandService.signal(childDebitChildExecutionInstance.getInstanceId());
        //
        ////当子流程结束后，父主流程将从callActivity回到下一个节点。在本例中，callActivity的下一个节点是在end_order节点
        //List<ExecutionInstance> endOrderExecutionInstanceList = executionQueryService.findActiveExecutionList(parentProcessInstanceId);
        //assertEquals(1, endOrderExecutionInstanceList.size());
        //ExecutionInstance endOrderExecutionInstance = endOrderExecutionInstanceList.get(0);
        //assertTrue("end_order".equals(endOrderExecutionInstance.getProcessDefinitionActivityId()));
        //
        ////断言子流程结束
        //ProcessInstance subProcessInstance = processQueryService.findById(childProcessInstanceId);
        //Assert.assertEquals(InstanceStatus.completed, subProcessInstance.getStatus());
        //List<ExecutionInstance> childExecutionInstanceList4 = executionQueryService.findActiveExecutionList(childProcessInstanceId);
        //assertEquals(0, childExecutionInstanceList4.size());
        //
        ////完成资金交割处理,将流程驱动到ACK确认环节。
        //ProcessInstance   processInstance4 = executionCommandService.signal(endOrderExecutionInstance.getInstanceId());
        //
        ////测试下是否符合预期
        //List<ExecutionInstance> parentExecutionInstanceList4 = executionQueryService.findActiveExecutionList(processInstance4.getInstanceId());
        //assertEquals(0, parentExecutionInstanceList4.size());
        //
        //assertEquals(InstanceStatus.completed, processInstance4.getStatus());


    }

    private ExecutionInstance assertAndGetExecutionInstance(String parentProcessInstanceId, String childProcessInstanceId) {
        // 此时，父流程在callActivity节点


        List<ExecutionInstance> parentExecutionInstanceList = executionQueryService.findActiveExecutionList(
            parentProcessInstanceId);
        assertEquals(1, parentExecutionInstanceList.size());
        ExecutionInstance firstExecutionInstance = parentExecutionInstanceList.get(0);
        assertTrue("callActivity".equals(firstExecutionInstance.getProcessDefinitionActivityId()));

        // 此时，子流程在debit节点
        List<ExecutionInstance> childExecutionInstanceList = executionQueryService.findActiveExecutionList(
            childProcessInstanceId);
        assertEquals(1, childExecutionInstanceList.size());
        ExecutionInstance debitChildExecutionInstance = childExecutionInstanceList.get(0);
        assertTrue("debit".equals(debitChildExecutionInstance.getProcessDefinitionActivityId()));
        return debitChildExecutionInstance;
    }



    private ProcessInstance startParentProcess(ProcessDefinition parentProcessDefinition) {

        //4.启动流程实例
        Map<String, Object> request = new HashMap<String, Object>();
        request.put("smartEngineAction", "pre_order");

        ProcessInstance processInstance = processCommandService.start(
            parentProcessDefinition.getId(), parentProcessDefinition.getVersion(), request
        );

        return processInstance;
    }

    private ExecutionInstance findAndAssertAtPreOrderPhase(ProcessInstance oldProcessInstance) {

        ProcessInstance latestProcessInstance = processQueryService.findById(oldProcessInstance.getInstanceId());

        Assert.assertNotNull(InstanceStatus.running.equals(latestProcessInstance.getStatus()));

        //只有主流程一个实例
        ProcessInstanceQueryParam processInstanceQueryParam = new ProcessInstanceQueryParam();
        processInstanceQueryParam.setStatus(InstanceStatus.running.name());

        List<ProcessInstance> processInstanceList = processQueryService.findList(processInstanceQueryParam);
        Assert.assertEquals(1, processInstanceList.size());

        List<ExecutionInstance> executionInstanceList = executionQueryService.findActiveExecutionList(
            oldProcessInstance.getInstanceId());
        Assert.assertEquals(1, executionInstanceList.size());

        ExecutionInstance firstExecutionInstance = executionInstanceList.get(0);
        assertTrue("pre_order".equals(firstExecutionInstance.getProcessDefinitionActivityId()));


        return firstExecutionInstance;

    }


    private class ProduceProcessInstanceIds {
        private String parentProcessInstanceId;
        private String childProcessInstanceId;

        public String getParentProcessInstanceId() {
            return parentProcessInstanceId;
        }

        public String getChildProcessInstanceId() {
            return childProcessInstanceId;
        }

        public ProduceProcessInstanceIds invoke() {
            // 部署流程定义
            RepositoryCommandService repositoryCommandService = smartEngine
                .getRepositoryCommandService();
            ProcessDefinition parentProcessDefinition = repositoryCommandService
                .deploy("complex/parent-callactivity-process.bpmn20.xml").getFirstProcessDefinition();
            assertEquals(16, parentProcessDefinition.getBaseElementList().size());


            ProcessDefinition bChildProcessDefinition = repositoryCommandService
                .deploy("complex/child-serviceTaskCallActivity.bpmn20.xml").getFirstProcessDefinition();
            assertEquals(7, bChildProcessDefinition.getBaseElementList().size());



            ProcessDefinition aChildProcessDefinition = repositoryCommandService
                .deploy("complex/child-receiveTaskCallActivity.bpmn20.xml").getFirstProcessDefinition();
            assertEquals(5, aChildProcessDefinition.getBaseElementList().size());

            ProcessInstance processInstance = startParentProcess(  parentProcessDefinition);


            //=======1.2 ========= START 111 因为执行到了callActivity节点,会进入子流程。
            // 由于serviceTask会自动结束，所以只会2个流程实例处于活跃状态：主流程和receiveTask子流程两个实例.

            ProcessInstanceQueryParam processInstanceQueryParam = new ProcessInstanceQueryParam();
            //processInstanceQueryParam.setStatus(InstanceStatus.running.name());

            List<ProcessInstance> processInstanceList = processQueryService.findList(processInstanceQueryParam);
            Assert.assertEquals(3, processInstanceList.size());


              processInstanceQueryParam = new ProcessInstanceQueryParam();
            processInstanceQueryParam.setStatus(InstanceStatus.running.name());

             processInstanceList = processQueryService.findList(processInstanceQueryParam);
            Assert.assertEquals(2, processInstanceList.size());




            parentProcessInstanceId = processInstance.getInstanceId();
            childProcessInstanceId = null;

            ProcessInstance child = null;

            for (ProcessInstance instance : processInstanceList) {
                if(instance.getParentExecutionInstanceId() != null){
                    child = instance;
                    break;
                }
            }

            Assert.assertNotNull(child);


            //
            //
            ////ADD ASSERT
            //ProcessInstanceQueryParam parentProcessInstanceQueryParam = new ProcessInstanceQueryParam();
            //parentProcessInstanceQueryParam.setParentInstanceId(processInstance.getInstanceId());
            //List<ProcessInstance> result = processQueryService.findList(parentProcessInstanceQueryParam);
            //Assert.assertEquals(1, result.size());
            //ProcessInstance childProcessInstance = result.get(0);
            //
            //Assert.assertEquals(childProcessInstanceId,childProcessInstance.getInstanceId());
            //Assert.assertNotNull(childProcessInstance.getParentExecutionInstanceId());

            return this;
        }
    }
}
