package cn.myapps.listener;

import cn.myapps.authtime.common.dao.PersistenceUtils;
import cn.myapps.common.Environment;
import cn.myapps.common.model.application.Application;
import cn.myapps.common.model.datasource.DataSource;
import cn.myapps.common.util.PropertyUtil;
import cn.myapps.common.util.cache.EhcacheProvider;
import cn.myapps.common.util.cache.MyCacheManager;
import cn.myapps.designtime.application.service.ApplicationDesignTimeService;
import cn.myapps.designtime.common.cache.DesignTimeIndexCacheManager;
import cn.myapps.designtime.common.cache.DesignTimeSerializableCache;
import cn.myapps.designtime.common.service.DesignTimeServiceManager;
import cn.myapps.init.InitSystem;
import cn.myapps.runtime.common.dao.WrapConnection;
import cn.myapps.runtime.dynaform.form.ejb.Form;
import cn.myapps.runtime.dynaform.form.ejb.FormTableProcess;
import cn.myapps.runtime.dynaform.form.ejb.FormTableProcessBean;
import cn.myapps.runtime.dynaform.form.ejb.WordFieldIsEditJob;
import cn.myapps.runtime.dynaform.form.service.FormRunTimeService;
import cn.myapps.runtime.dynaform.form.service.FormRunTimeServiceImpl;
import cn.myapps.runtime.macro.runner.JavaScriptRunner;
import cn.myapps.runtime.scheduler.engine.RegularScheduler;
import cn.myapps.runtime.workflow.notification.ejb.NotificationJob;
import cn.myapps.scheduler.InitRuntimeScheduler;
import cn.myapps.scheduler.NotificationSendPasswordMessageJob;
import cn.myapps.scheduler.SchedulerListenerJob;
import cn.myapps.scheduler.SchedulerUtil;
import cn.myapps.util.RuntimeDaoManager;
import com.bcxin.saas.core.exceptions.SaasBadException;
import com.jamonapi.MonitorFactory;
import com.jamonapi.proxy.MonProxyFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ApplicationContextEvent;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.ContextStoppedEvent;
import org.springframework.util.StopWatch;

import java.io.File;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

public class ApplicationContextEventListener implements ApplicationListener<ApplicationContextEvent>{
	private static final Logger logger = LoggerFactory.getLogger(ApplicationContextEventListener.class);

	@Override
	public void onApplicationEvent(ApplicationContextEvent event) {
		if(event instanceof ContextRefreshedEvent) {
			logger.info("初始化开始");
			long start = System.currentTimeMillis();

			try {
				// 初始化应用真实路径
				Environment evt = Environment.getInstance();

				//设置当前环境的文件工作目录
				String workRootPath = evt.getWorkspaceRootPath();
				File  newPath  =  new  File(workRootPath);
				if(!newPath.exists()) {
					logger.error(workRootPath+"目录不存在，请检查配置文件");
				}

				String res = PropertyUtil.getPath();
				evt.setApplicationRealPath(res);

				DesignTimeIndexCacheManager.getInstance().init(DesignTimeIndexCacheManager.MODE_RUNTIME);

				/**初始化系统文件系统编码**/
				//System.setProperty("file.encoding", "UTF-8"); 强制设置编码会导致mysql驱动无法搜索到中文表单，因此屏蔽。
				//是否需要初始化日志文件
				File file = new File(PropertyUtil.getPath()+ File.separator + "initTable.log");
				boolean isExist = file.exists();

				/**初始化用户以及资源URL**/
				InitSystem.init(isExist);

				String indexIdPath = workRootPath + File.separator + "url.index";
				File index = new File(indexIdPath);
				if(index.exists()) {
					//获取所有应用
					ApplicationDesignTimeService applicationService = DesignTimeServiceManager.applicationDesignTimeService();
					/**
					 * 这边会根据url.index来生产对应的application
					 */
					List<Application> apps = applicationService.list("", "");
					ExecutorService executor = Executors.newFixedThreadPool(5);
					StopWatch stopWatch = new StopWatch();
					try {
						stopWatch.start();
						List<CompletableFuture<Void>> futureList = apps
								.stream()
								.map(request ->
										CompletableFuture.runAsync(() -> {
											try {
												executeAppWarmUp(request, isExist);
											} catch (Exception e) {
												throw new SaasBadException(e);
											}
										}, executor))
								.collect(Collectors.toList());

						CompletableFuture<Void> allCF = CompletableFuture.allOf(futureList.toArray(new CompletableFuture[0]));

						Object result = allCF.join();
						stopWatch.stop();
						logger.error("APP预热执行完毕:{}; 总共耗时:{} 秒", result, stopWatch.getTotalTimeSeconds());
					} finally {
						executor.shutdown();
					}
				}

				Thread.sleep(1000);
				if(!isExist) {
					file.createNewFile();
				}
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				try {
					DesignTimeSerializableCache.flush2SecondCached();
					PersistenceUtils.closeSessionAndConnection();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			logger.info("初始化结束，用时：" + (System.currentTimeMillis() - start) + " ms.");
		}
		else if (event instanceof ContextClosedEvent || event instanceof ContextStoppedEvent ){
			logger.info("开始卸载资源");
			try {
				//清理定时器产生的线程
				SchedulerUtil.destroy();
				//清理定时器产生的线程
				RegularScheduler.shutdown();
				EhcacheProvider cacheProvider = (EhcacheProvider) MyCacheManager.getProviderInstance();
				cacheProvider.destory();

				//关闭文件变更监听
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	private static void executeAppWarmUp(Application application,boolean isExist) throws Exception {
		if (application.getDataSourceDefine() == null) {
			return;
		}

		JavaScriptRunner.preCompile(application);

		//测试数据源是否正确
		Boolean isRight = true;//application.testDB();

		//测试数据源是否正确
		if (isRight && !isExist) {
			//初始化应用对应数据源
			DataSource dataSource = application.getDataSourceDefine();
			RuntimeDaoManager.getApplicationInitDAO(new WrapConnection(dataSource.getSqlDataSource(), dataSource.getConnection(), null, application.getApplicationid()), application.getDataSourceDefine().getDbTypeName())
					.initTables();

			//生成软件库的表单tlk...
			FormRunTimeService formRunTimeService = new FormRunTimeServiceImpl();
			Collection<Form> forms = formRunTimeService.getFormsByApplication(application.getId());
			FormTableProcess tableProcess = new FormTableProcessBean(application.getId());
			for (Form form : forms) {
				try {
					tableProcess.createOrUpdateDynaTable(form, null);
					logger.info("同步" + form.getName() + "表成功！");
				} catch (Exception e) {
					e.printStackTrace();
					logger.error("同步" + form.getName() + "表时报错" + e.getMessage());
				}
			}
		}

		try {
			//应用为激活状态时才开启任务调度
			if (application.isActivated() && isRight) {
				//启动任务调度器
				//先禁用调度器
				//InitRuntimeScheduler.start(application.getId());
				//logger.error("启用定时任务-禁用:{}", application.getUri());
			} else {
				logger.error("未启用:{}定时任务", application.getUrl());
			}
		} catch (Exception ex) {
			ex.getStackTrace();
		}
	}
}
