/*
 *    Copyright (c) 2018-2025, lengleng All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * Neither the name of the pig4cloud.com developer nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 * Author: lengleng (wangiegie@gmail.com)
 */

package org.springblade.flow.engine.custom.config;

import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType;
import org.flowable.common.engine.api.delegate.event.FlowableEventListener;
import org.flowable.common.engine.api.engine.EngineLifecycleListener;
import org.flowable.common.engine.impl.history.HistoryLevel;
import org.flowable.engine.ProcessEngineConfiguration;
import org.flowable.engine.cfg.HttpClientConfig;
import org.flowable.engine.impl.cfg.DelegateExpressionFieldInjectionMode;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.parse.BpmnParseHandler;
import org.flowable.variable.api.types.VariableType;
import org.springblade.flow.engine.custom.behavior.factory.CustomActivityBehaviorFactory;
import org.springblade.flow.engine.custom.engine.CustomSpringProcessEngineConfiguration;
import org.springblade.flow.engine.custom.handler.CustomProcessParseHandler;
import org.springblade.flow.engine.custom.handler.CustomReceiveTaskParseHandler;
import org.springblade.flow.engine.custom.handler.CustomSequenceFlowParseHandler;
import org.springblade.flow.engine.custom.handler.CustomUserTaskParseHandler;
import org.springblade.flow.engine.custom.idgen.ActivitiIDWorker;
import org.springblade.flow.engine.custom.life.CustomProcessEngineLifecycleListener;
import org.springblade.flow.engine.custom.manager.CustomHistoryManager;
import org.springblade.flow.engine.custom.type.*;
import org.springblade.flow.engine.custom.variables.type.ArrayOrCollectionOrMapVariableType;
import org.springblade.flow.engine.custom.variables.type.JsonVariableType;
import org.springblade.flow.engine.custom.xml.impl.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.transaction.PlatformTransactionManager;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author
 * @date
 * @desc
 */
@Configuration
public class ActivitiConfig {
	@Resource
	private  DataSource dataSource;
	@Resource
	private  PlatformTransactionManager transactionManager;
	@Resource
	private ActivitiIDWorker activitiIDWorker;
	@Resource
	private CustomProcessEngineLifecycleListener customProcessEngineLifecycleListener;
	@Resource
	private FirstActivityAutoCompleteListener firstActivityAutoCompleteListener;
	@Resource
	private TaskCreatedInitListener taskCreatedInitListener;
	@Resource
	private CustomProcessParseHandler customProcessParseHandler;
	@Resource
	private CustomUserTaskParseHandler customUserTaskParseHandler;
	@Resource
	private CustomReceiveTaskParseHandler customReceiveTaskParseHandler;
	@Resource
	private CustomSequenceFlowParseHandler customSequenceFlowParseHandler;

	@Resource
	private ProcessStartedListener processStartedListener;
	@Resource
	private TaskCompletedListener taskCompletedListener;
	@Resource
	private ProcessCompletedListener processCompletedListener;
	@Resource
	private ActivityStartedListener activityStartedListener;
	@Resource
	private HistoryActivityInstanceEndedListener historyActivityInstanceEndedListener;
	@Resource
	private ArrayOrCollectionOrMapVariableType arrayOrCollectionOrMapVariableType;
	@Resource
	private JsonVariableType jsonVariableType;
	@Resource
	private CustomActivityBehaviorFactory customActivityBehaviorFactory;
//	@Resource
//	private CustomProcessDefinitionCache customProcessDefinitionCache;
//	@Resource
//	private CustomBpmnModelCache customBpmnModelCache;
//	@Resource
//	private CustomBpmnDeployer customBpmnDeployer;
//	@Resource
//	private CustomProcessEngineConfigurator customProcessEngineConfigurator;
	@Bean
	@Primary
	public CustomSpringProcessEngineConfiguration getCustomSpringProcessEngineConfiguration() {
		CustomSpringProcessEngineConfiguration config = new CustomSpringProcessEngineConfiguration();
		// 流程图节点上字体设置
		config.setActivityFontName("宋体");
		//流程图 注释节点上字体设置
		config.setAnnotationFontName("宋体");
//		流程图 连线节点上字体设置
		config.setLabelFontName("黑体");
		//数据源
		config.setDataSource(dataSource);
		//事务管理器Spring 让Spring管理事务
		config.setTransactionManager(transactionManager);
		//数据库类型
		config.setDatabaseType(ProcessEngineConfigurationImpl.DATABASE_TYPE_MYSQL);
		//如果为true, 当表不存在 则创建
		config.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
		//设置id生成器
		config.setIdGenerator(activitiIDWorker);
		//设置流程引擎生命周期监听器
		List<EngineLifecycleListener> engineLifecycleListenerList=new ArrayList<>();
		engineLifecycleListenerList.add(customProcessEngineLifecycleListener);
		config.setEngineLifecycleListeners(engineLifecycleListenerList);
//		config.setProcessEngineLifecycleListener(customProcessEngineLifecycleListener);
		//不使用自带的 act_id_前缀的表
//		config.setDbIdentityUsed(Boolean.FALSE);
		//这个可以防止XML注入
		config.setEnableSafeBpmnXml(true);
		//默认也是utf-8
		config.setXmlEncoding("UTF-8");
		/**
		 * 流程执行相关的实例计数器功能，默认是关闭的，开启后在 ACT_RU_EXECUTION 表的后几个带有 *COUNT 的字段会显示计数信息
		 */
		config.setEnableExecutionRelationshipCounts(true);
		//此配置默认也是audit
		//需要使用act_hi的表
		config.setDbHistoryUsed(true);
		config.setHistory(HistoryLevel.AUDIT.getKey());
		config.setHistoryLevel(HistoryLevel.AUDIT);
		//批量插入的阈值
		config.setBulkInsertEnabled(true);
		config.setMaxNrOfStatementsInBulkInsert(200);
		//只在批量挂起和批量启用的时候有效
		config.setBatchSizeTasks(50);
		config.setBatchSizeProcessInstances(50);
		//部署时是否生成流程图片
		config.setCreateDiagramOnDeploy(true);
		//启用事件日志 接入ElasticSearch之后可以使用es做一些常见的统计数据 也即流程门户 也可以放入MongoDB中
		config.setEnableDatabaseEventLogging(true);
		config.setEnableHistoricTaskLogging(true);
		config.setDisableIdmEngine(true);//屏蔽idm引擎 因为没必要生成idm的几张表
		config.setFlowable5CompatibilityEnabled(false);
		config.setValidateFlowable5EntitiesEnabled(false);
		config.setAsyncExecutorActivate(false);
		config.setEnableEntityLinks(true);
		config.setEnableLogSqlExecutionTime(true);
		config.setEnableProcessDefinitionHistoryLevel(true);//流程定义的历史级别 -需要在process中添加扩展元素
		config.setExpressionCacheEnabled(true);
		config.setEnableProcessDefinitionInfoCache(true);//启动流程节点缓存
		/**
		 *为ServiceTask做准备
		 * 如果为ServcieTask添加了class方式的监听器, 并且里面定义了字段 但是没有在监听器里面初始化 则就会报错
		 * 使用此模式之后就不会报错了
		 */
		config.setDelegateExpressionFieldInjectionMode(DelegateExpressionFieldInjectionMode.MIXED);

		config.setEnableLogSqlExecutionTime(true);

		//cache
		//流程定义缓存
//		config.setProcessDefinitionCacheLimit()使用redis之后 此配置项就无效了
//		config.setProcessDefinitionCache(customProcessDefinitionCache);
//		config.setEnableProcessDefinitionInfoCache(false);
//		config.setBpmnDeployer(customBpmnDeployer);
//		List<ProcessEngineConfigurator> processEngineConfiguratorList = new ArrayList<>();
//		processEngineConfiguratorList.add(customProcessEngineConfigurator);
//		config.setConfigurators(processEngineConfiguratorList);

		//全局监听器
		Map<String, List<FlowableEventListener>> typeEventListeners=new HashMap<>();
		List<FlowableEventListener> taskCreatedEventListeners=new ArrayList<>();
		taskCreatedEventListeners.add(taskCreatedInitListener);
		taskCreatedEventListeners.add(firstActivityAutoCompleteListener);
		List<FlowableEventListener> processStartedEventListeners = new ArrayList<>();
		processStartedEventListeners.add(processStartedListener);
		List<FlowableEventListener> taskCompletedListeners=new ArrayList<>();
		taskCompletedListeners.add(taskCompletedListener);
		List<FlowableEventListener> processCompletedListeners= new ArrayList<>();
		processCompletedListeners.add(processCompletedListener);
		List<FlowableEventListener> activityStartedListeners = new ArrayList<>();
		activityStartedListeners.add(activityStartedListener);
		List<FlowableEventListener> historyActivityStartedListeners=new ArrayList<>();
		historyActivityStartedListeners.add(historyActivityInstanceEndedListener);
		typeEventListeners.put(FlowableEngineEventType.HISTORIC_ACTIVITY_INSTANCE_ENDED.name(),historyActivityStartedListeners);
		typeEventListeners.put(FlowableEngineEventType.ACTIVITY_STARTED.name(),activityStartedListeners);
		typeEventListeners.put(FlowableEngineEventType.TASK_CREATED.name(),taskCreatedEventListeners);
		typeEventListeners.put(FlowableEngineEventType.PROCESS_STARTED.name(),processStartedEventListeners);
		typeEventListeners.put(FlowableEngineEventType.TASK_COMPLETED.name(),taskCompletedListeners);
		typeEventListeners.put(FlowableEngineEventType.PROCESS_COMPLETED.name(),processCompletedListeners);
		config.setTypedEventListeners(typeEventListeners);
		//对象解析器
		List<BpmnParseHandler> bpmnParseHandlerList = new ArrayList<>();
		bpmnParseHandlerList.add(customProcessParseHandler);
		bpmnParseHandlerList.add(customUserTaskParseHandler);
		bpmnParseHandlerList.add(customReceiveTaskParseHandler);
		bpmnParseHandlerList.add(customSequenceFlowParseHandler);
		config.setCustomDefaultBpmnParseHandlers(bpmnParseHandlerList);


		//变量类型处理
		List<VariableType> variableTypeList = new ArrayList<>();
		variableTypeList.add(arrayOrCollectionOrMapVariableType);
		variableTypeList.add(jsonVariableType);
		config.setCustomPreVariableTypes(variableTypeList);

//		//行为类工厂设置
		config.setActivityBehaviorFactory(customActivityBehaviorFactory);


		//设置
//		List<SessionFactory> sessionFactories = new ArrayList<>();
//		sessionFactories.add(customGroupEntityManagerFactory);
//		sessionFactories.add(customProcessHistoryManagerSessionFactory);
//		config.setCustomSessionFactories(sessionFactories);

//		config.setGroupEntityManager(new CustomGroupEntityManager(config,config.getGroupDataManager()));
		config.setHistoryManager(new CustomHistoryManager(config));
		/**
		 * 直接在初始化的时候把二次开发的converter添加进来
		 */
		BpmnXMLConverter.addConverter(new CustomUserTaskXMLConverter());
		BpmnXMLConverter.addConverter(new CustomReceiveTaskXMLConverter());
		BpmnXMLConverter.addConverter(new CustomSequenceFlowXMLConverter());
		BpmnXMLConverter.addConverter(new CustomCallActivityXMLConverter());
		BpmnXMLConverter.addConverter(new CustomManualTaskXMLConverter());

		//Http任务相关设置
		HttpClientConfig httpClientConfig=new HttpClientConfig();
		httpClientConfig.setConnectTimeout(10000);//连接超时
		httpClientConfig.setConnectionRequestTimeout(10000);//连接请求超时
		httpClientConfig.setSocketTimeout(10000);//双端建立socket连接
		httpClientConfig.setRequestRetryLimit(3);//请求失败之后重试次数
		config.setHttpClientConfig(httpClientConfig);

		//如果 includeProcessVariables 或者 includeTaskProcessVariable 那么  listPage的时候 哪怕自己设置的是比如10  内部SQL执行也会给你改成20000
		//当使用此配置项即可解决此问题, 反正基本分页最多也只分200条
		//先注释此limit 因为其存在 联查问题, todo
//		config.setHistoricProcessInstancesQueryLimit(200);
//		config.setExecutionQueryLimit(200);
//		config.setTaskQueryLimit(200);
//		config.setHistoricTaskQueryLimit(200);
		config.setKnowledgeBaseCacheLimit(200);
		config.setProcessDefinitionCacheLimit(200);
		return config;
	}

	@Bean
	@Primary
	public TaskExecutor primaryTaskExecutor() {
		return new ThreadPoolTaskExecutor();
	}

}
