package cn.myapps.runtime.excutor.async;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.sql.DataSource;

import com.bcxin.saas.core.exceptions.SaasNofoundException;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.jamonapi.proxy.MonProxyFactory;

import cn.myapps.authtime.common.dao.PersistenceUtils;
import cn.myapps.common.model.application.Application;
import cn.myapps.designtime.application.service.ApplicationDesignTimeService;
import cn.myapps.designtime.common.service.DesignTimeServiceManager;
import cn.myapps.runtime.common.dao.WrapConnection;

public class RuntimeDaoCommandExecutor {
	private Logger logger = LoggerFactory.getLogger(RuntimeDaoCommandExecutor.class);
	
	private RuntimeDaoCommandGroup commandGroup;
	
	public RuntimeDaoCommandExecutor(RuntimeDaoCommandGroup commandGroup) {
		this.commandGroup = commandGroup;
	}
	
	public void execCommandGroup() throws SQLException {
		Map<String, WrapConnection> connMap = PersistenceUtils.getRuntimeDBConn().get();
		if (connMap == null) {
			connMap = new ConcurrentHashMap<>();
		}

		try {
			//开启事务
			for (RuntimeDaoCommand command : commandGroup.getCommandList()) {
				execCommand(command);
			}
			//提交事务
			for (WrapConnection conn : connMap.values()) {
				conn.commit();
				conn.setAutoCommit(true);
			}
		} catch (Exception e) {
			//回滚事务
			for (WrapConnection conn : connMap.values()) {
				try {
					conn.commit();
					conn.setAutoCommit(true);
				} catch (SQLException e1) {
					e1.printStackTrace();
				} finally {
					if (conn != null && !conn.isClosed()) {
						conn.close();
					}
				}
			}
		}
	}
	
	private void execCommand(RuntimeDaoCommand command) throws ClassNotFoundException, InstantiationException,
			IllegalAccessException, NoSuchMethodException, InvocationTargetException, Exception {
		logger.debug("Command Exec Begin: " + command);
		String clazzName = command.getClazzName();
// 不能用spring bean，会被拦截器拦截形成循环调用
		Class<?> clazz = Class.forName(clazzName);
		Object obj = clazz.newInstance();

// 这种连接 setConnection(WrapConnection conn)
		Method connMethod = clazz.getMethod("setConnection", WrapConnection.class);
		connMethod.invoke(obj, getConnection(command.getApplicationId()));

		Method[] methods = clazz.getMethods();

		Method method = null;
		for (int i = 0; i < methods.length; i++) {
			if (methods[i].getName().equals(command.getMethodName())) {
				Class<?>[] pClazzs = methods[i].getParameterTypes();
				boolean flag = pClazzs.length == command.getArgs().length;
				if (flag) {
					for (int j = 0; j < pClazzs.length; j++) {
						if (!pClazzs[j].isAssignableFrom(command.getArgs()[j].getClass())) {
							if (pClazzs[j].equals(int.class) && command.getArgs()[j].getClass().equals(Integer.class)) {
							} else if (pClazzs[j].equals(float.class)
									&& command.getArgs()[j].getClass().equals(Float.class)) {
							} else if (pClazzs[j].equals(double.class)
									&& command.getArgs()[j].getClass().equals(Double.class)) {
							} else if (pClazzs[j].equals(char.class)
									&& command.getArgs()[j].getClass().equals(Character.class)) {
							} else if (pClazzs[j].equals(boolean.class)
									&& command.getArgs()[j].getClass().equals(Boolean.class)) {
							} else if (pClazzs[j].equals(long.class)
									&& command.getArgs()[j].getClass().equals(Long.class)) {
							} else if (pClazzs[j].equals(short.class)
									&& command.getArgs()[j].getClass().equals(Short.class)) {
							} else if (pClazzs[j].equals(byte.class)
									&& command.getArgs()[j].getClass().equals(Byte.class)) {
							} else {
								flag = false;
							}
						}
					}
					if (flag) {
						logger.debug("Command Execing... : " + command);
						method = methods[i];
					}					
				}
			}
		}

		if (method != null)
			method.invoke(obj, command.getArgs());
		logger.debug("Command Exec Finished: " + command);

	}

	private WrapConnection getConnection(String applicationId) throws Exception {
		WrapConnection conn = null;
		Map<String, WrapConnection> connMap = PersistenceUtils.getRuntimeDBConn().get();
		try {
			if (connMap != null) {
				conn = (WrapConnection) connMap.get(applicationId);
			} else {
				connMap = new java.util.concurrent.ConcurrentHashMap<String, WrapConnection>();
				PersistenceUtils.getRuntimeDBConn().set(connMap);
			}

			if (conn == null || conn.isClosed()) {
				DataSource ds = getDataSource(applicationId);
				if (ds == null) {
					return null;
				}
				conn = new WrapConnection(ds, ds.getConnection(), null, applicationId);
				connMap.put(applicationId, conn);
				if (conn.getMetaData().getDriverName().equals("jTDS Type 4 JDBC Driver for MS SQL Server and Sybase")) {
					conn.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);
				}
			}
			
			//默认打开事务
			conn.setAutoCommit(false);
		} catch (Exception e) {
			e.printStackTrace();
			throw e;
		}

		return (WrapConnection) MonProxyFactory.monitor(conn);
	}

	private DataSource getDataSource(String applicationId) throws Exception {
		ApplicationDesignTimeService service = DesignTimeServiceManager.applicationDesignTimeService();
		Application app = service.findById(applicationId);
		if(app==null) {
			throw new SaasNofoundException(String.format("6.获取数据源的时候, 找不到应用(%s)", applicationId));
		}

		cn.myapps.common.model.datasource.DataSource ds = app.getDataSourceDefine();

		DataSource dataSource = PersistenceUtils.getDataSource(ds);

		return dataSource;
	}
}