package com.bstek.bdf3.dbconsole.service;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import javax.sql.DataSource;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang.ArrayUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.ConnectionCallback;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.support.JdbcUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.StringUtils;

import com.bstek.bdf3.dbconsole.DbConstants;
import com.bstek.bdf3.dbconsole.DbType;
import com.bstek.bdf3.dbconsole.jdbc.dialect.IDialect;
import com.bstek.bdf3.dbconsole.model.ColumnInfo;
import com.bstek.bdf3.dbconsole.model.DbInfo;
import com.bstek.bdf3.dbconsole.model.SqlWrapper;
import com.bstek.bdf3.dbconsole.service.impl.DbCommonServiceImpl;
import com.bstek.bdf3.dbconsole.utils.SpringJdbcUtils;
import com.bstek.bdf3.dbconsole.utils.UserConfigUtils;

@Service(DbService.BEAN_ID)
public class DbService extends DbCommonServiceImpl {

	public static final String BEAN_ID = "bdf3.dbconsole.dbService";

	
	@Autowired
	private DataSource dataSource;
	
	/**
	 * 初始化默认数据库配置信息
	 * 
	 * @return 返回DbInfo对象
	 * @throws Exception
	 */
	public DbInfo initDefaultDbInfo() throws Exception {
		DbInfo dbInfo = new DbInfo();
		dbInfo.setId(DbConstants.DEFAULTDATASOURCE);
		Connection conn = null;
		try {
			conn = dataSource.getConnection();
			DatabaseMetaData metaData = conn.getMetaData();
			dbInfo.setDbType(metaData.getDatabaseProductName());
			dbInfo.setName("默认连接" + dbInfo.getDbType());
			dbInfo.setUrl(metaData.getURL());
			dbInfo.setUsername(metaData.getUserName());
			dbInfo.setProductName(metaData.getDatabaseProductName());
			dbInfo.setProductVersion(metaData.getDatabaseProductVersion());
		} finally {
			JdbcUtils.closeConnection(conn);
		}
		return dbInfo;

	}

	/**
	 * 查询用户的所有数据库连接信息
	 * 
	 * @return 返回DbInfo的集合
	 * @throws Exception
	 */
	public List<DbInfo> findDbInfos() throws Exception {
		List<DbInfo> list = new ArrayList<DbInfo>();
		String userName = UserConfigUtils.getUserName();
		list = this.getConsoleDbInfoManager().findDbInfosByUser(userName);
		list.add(this.initDefaultDbInfo());
		return list;
	}

	/**
	 * 修改表名称
	 * 
	 * @param dbInfoId
	 * @param tableName
	 * @param newTableName
	 * @throws Exception
	 */
	public void alertTableName(String dbInfoId, String tableName, String newTableName) throws Exception {
		IDialect dialect = getDBDialectByDbInfoId(dbInfoId);
		String sql = dialect.getTableRenameSql(tableName, newTableName);
		String[] sqls = new String[] { sql };
		this.updateSql(dbInfoId, sqls);

	}

	/**
	 * 删除表
	 * 
	 * @param dbInfoId
	 * @param tableName
	 * @throws Exception
	 */
	public void deleteTable(String dbInfoId, String tableName) throws Exception {
		String sql = " drop table " + tableName;
		String[] sqls = new String[] { sql };
		this.updateSql(dbInfoId, sqls);
	}

	/**
	 * 删除表内数据
	 * 
	 * @param dbInfoId
	 * @param tableName
	 * @throws Exception
	 */
	public void deleteTableData(String dbInfoId, String tableName) throws Exception {
		String sql = " delete from " + tableName;
		String[] sqls = new String[] { sql };
		this.updateSql(dbInfoId, sqls);
	}

	/**
	 * 删除表列
	 * 
	 * @param dbInfoId
	 * @param tableName
	 * @param columnName
	 * @throws Exception
	 */
	public void deleteColumn(String dbInfoId, String tableName, String columnName) throws Exception {
		String sql = "alter table " + tableName + "  drop column " + columnName;
		String[] sqls = new String[] { sql };
		this.updateSql(dbInfoId, sqls);
	}

	/**
	 * 插入表新列
	 * 
	 * @param dbInfoId
	 * @param columnInfo
	 * @throws Exception
	 */
	public void insertColumn(String dbInfoId, ColumnInfo columnInfo) throws Exception {
		com.bstek.bdf3.dbconsole.jdbc.dialect.ColumnInfo dbColumnInfo = new com.bstek.bdf3.dbconsole.jdbc.dialect.ColumnInfo();
		BeanUtils.copyProperties(dbColumnInfo, columnInfo);
		String tableName = columnInfo.getTableName();
		String columnName = columnInfo.getColumnName();
		boolean isprimaryKey = columnInfo.isIsprimaryKey();
		List<String> primaryKeys = findTablePrimaryKeys(dbInfoId, tableName);
		if (isprimaryKey) {
			primaryKeys.add(columnName);
			dbColumnInfo.setListPrimaryKey(primaryKeys);
			String pkName = this.findSqlServerPKIndex(dbInfoId, tableName);
			log.debug("pkName:" + pkName);
			if (StringUtils.hasText(pkName)) {
				dbColumnInfo.setPkName(pkName);
			}
		}
		IDialect dBDialect = getDBDialectByDbInfoId(dbInfoId);
		String sql = dBDialect.getNewColumnSql(dbColumnInfo);
		String[] sqls = sql.split(";");
		this.updateSql(dbInfoId, sqls);

	}

	/**
	 * 更新表列
	 * 
	 * @param dbInfoId
	 * @param oldColumnInfo
	 * @param newColumnInfo
	 * @throws Exception
	 */
	public void updateColumn(String dbInfoId, ColumnInfo oldColumnInfo, ColumnInfo newColumnInfo) throws Exception {
		com.bstek.bdf3.dbconsole.jdbc.dialect.ColumnInfo oldDbColumnInfo = new com.bstek.bdf3.dbconsole.jdbc.dialect.ColumnInfo();
		BeanUtils.copyProperties(oldDbColumnInfo, oldColumnInfo);
		com.bstek.bdf3.dbconsole.jdbc.dialect.ColumnInfo newDbColumnInfo = new com.bstek.bdf3.dbconsole.jdbc.dialect.ColumnInfo();
		BeanUtils.copyProperties(newDbColumnInfo, newColumnInfo);
		String tableName = oldColumnInfo.getTableName();
		boolean oldPrimaryKey = oldColumnInfo.isIsprimaryKey();
		boolean newPrimaryKey = newColumnInfo.isIsprimaryKey();
		List<String> primaryKeys = null;
		if (oldPrimaryKey != newPrimaryKey) {
			primaryKeys = findTablePrimaryKeys(dbInfoId, tableName);
			if (newPrimaryKey && !oldPrimaryKey) {
				primaryKeys.add(newDbColumnInfo.getColumnName().toUpperCase());
			} else if (!newPrimaryKey && oldPrimaryKey) {
				primaryKeys.remove(newDbColumnInfo.getColumnName().toUpperCase());
			}
			newDbColumnInfo.setListPrimaryKey(primaryKeys);
			String pkName = this.findSqlServerPKIndex(dbInfoId, tableName);
			if (StringUtils.hasText(pkName)) {
				newDbColumnInfo.setPkName(pkName);
			}
		}
		IDialect dBDialect = getDBDialectByDbInfoId(dbInfoId);
		String sql = dBDialect.getUpdateColumnSql(oldDbColumnInfo, newDbColumnInfo);
		String[] sqls = sql.split(";");
		this.updateSql(dbInfoId, sqls);
	}

	/**
	 * 创建表
	 * 
	 * @param dbInfoId
	 * @param tableName
	 * @throws Exception
	 */
	public void createTable(String dbInfoId, String tableName) throws Exception {
		IDialect dBDialect = getDBDialectByDbInfoId(dbInfoId);
		String sql = dBDialect.getCreateDefaultTableSql(tableName);
		String[] sqls = sql.split(";");
		this.updateSql(dbInfoId, sqls);
	}

	/**
	 * 定义支持的数据库类型,目前支持四种数据库类型
	 * 
	 * @return 返回支持的数据库类型的集合
	 */
	public List<String> loadDbTypes() {
		List<String> dbInfoList = new ArrayList<String>();
		try {
			for (DbType type : DbType.values()) {
				dbInfoList.add(type.name());
			}
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		return dbInfoList;

	}

	/**
	 * 批处理更新操作
	 * 
	 * @param dbInfoId
	 * @param sqls
	 * @return 返回更新的列的数量
	 * @throws Exception
	 */
	public int[] updateSql(String dbInfoId, String[] sqls) throws Exception {
		final String[] fsqls = this.getFormatArrays(sqls);
		if (log.isDebugEnabled()) {
			for (String s : fsqls) {
				log.debug(s);
			}
		}
		DataSource ds = getDataSourceByDbInfoId(dbInfoId);
		final TransactionTemplate transactionTemplate = SpringJdbcUtils.getTransactionTemplate(ds);
		return transactionTemplate.execute(new TransactionCallback<int[]>() {
			public int[] doInTransaction(TransactionStatus status) {
				JdbcTemplate jdbcTemplate = SpringJdbcUtils.getJdbcTemplate(transactionTemplate);
				int[] i = jdbcTemplate.batchUpdate(fsqls);
				return i;
			}
		});
	}

	/**
	 * 更新表操作
	 * 
	 * @param dbInfoId
	 * @param sql
	 * @param args
	 * @return 返回更新的列的数量
	 * @throws Exception
	 */
	public int updateSql(String dbInfoId, final String sql, final Object[] args) throws Exception {
		log.debug(sql);
		List<SqlWrapper> list = new ArrayList<SqlWrapper>();
		list.add(new SqlWrapper(sql, args));
		int[] ints = updateSql(dbInfoId, list);
		return ints.length > 0 ? ints[0] : 0;
	}

	/**
	 * 对sqlwrapper进行更新
	 * 
	 * @param dbInfoId
	 * @param listSqlWrapper
	 * @return 返回更新的数量
	 * @throws Exception
	 */
	public int[] updateSql(String dbInfoId, final List<SqlWrapper> listSqlWrapper) throws Exception {
		if (log.isDebugEnabled()) {
			for (SqlWrapper sw : listSqlWrapper) {
				log.debug(sw.getSql());
			}
		}
		DataSource ds = getDataSourceByDbInfoId(dbInfoId);
		final TransactionTemplate transactionTemplate = SpringJdbcUtils.getTransactionTemplate(ds);
		return transactionTemplate.execute(new TransactionCallback<int[]>() {
			public int[] doInTransaction(TransactionStatus status) {
				List<Integer> list = new ArrayList<Integer>();
				JdbcTemplate jdbcTemplate = SpringJdbcUtils.getJdbcTemplate(transactionTemplate);
				for (SqlWrapper sw : listSqlWrapper) {
					if (StringUtils.hasText(sw.getSql().trim())) {
						Integer i = jdbcTemplate.update(sw.getSql(), sw.getArgs());
						list.add(i);
					}
				}
				int[] ints = ArrayUtils.toPrimitive((Integer[]) list.toArray(new Integer[list.size()]));
				return ints;
			}
		});
	}

	private String[] getFormatArrays(String[] args) {
		String[] newString = new String[] {};
		List<String> list = new ArrayList<String>();
		for (String s : args) {
			if (org.apache.commons.lang.StringUtils.isNotEmpty(s.trim())) {
				list.add(s);
			}
		}
		newString = list.toArray(new String[list.size()]);
		return newString;
	}

	/**
	 * 查找sqlserver的主键索引
	 * 
	 * @param dbInofId
	 * @param tableName
	 * @return 返回主键索引的值
	 * @throws Exception
	 */
	public String findSqlServerPKIndex(String dbInofId, final String tableName) throws Exception {
		DataSource ds = getDataSourceByDbInfoId(dbInofId);
		JdbcTemplate jdbcTemplate = SpringJdbcUtils.getJdbcTemplate(ds);
		String s = jdbcTemplate.execute(new ConnectionCallback<String>() {
			public String doInConnection(Connection con) throws SQLException, DataAccessException {
				String pkName = null;
				if (con.getMetaData().getURL().toLowerCase().contains("sqlserver")) {
					CallableStatement call = con.prepareCall("{call sp_pkeys(?)}");
					call.setString(1, tableName);
					ResultSet rs = call.executeQuery();
					while (rs.next()) {
						pkName = rs.getString("PK_NAME");
					}
				}
				return pkName;

			}
		});
		return s;
	}

}