package cn.orm.util.metadata;

import java.sql.SQLException;
import java.util.*;
import javax.sql.DataSource;
import org.apache.commons.lang3.StringUtils;

import cn.orm.util.metadata.base.DBServer;
import cn.orm.util.metadata.base.Field;
import cn.orm.util.metadata.database.DatabaseConfig;
import cn.orm.util.metadata.database.Dialect;
import cn.orm.util.metadata.database.IntrospectedColumn;
import cn.orm.util.metadata.database.IntrospectedTable;
import cn.orm.util.metadata.database.MatchType;
import cn.orm.util.metadata.database.SimpleDataSource;
import cn.orm.util.metadata.util.DBMetadataUtil;
import cn.util.core.IsKit;
import cn.util.core.LogKit;
import cn.util.core.TypeKit;

public class DBMetaData 
{
	private static DBMetadataUtil dbMetadataUtils;
	private static SimpleDataSource dataSource;

	//选择配置后
	public static DatabaseConfig config;
	public static List<String> catalogs;
	public static List<String> schemas;
	private static String dbName = "";
	
	//表映射 key:表名; IntrospectedTable表信息
	private static Map<String, IntrospectedTable> tableMap;
	private static Map<String, List<IntrospectedColumn>> columnMap;

	//所有表信息
	private static List<IntrospectedTable> tables;
	//所有表的表名字信息
	private static List<String> tableNameList = new ArrayList<String>();
	//某个表的所有列名信息<表名，列名>
	private static Map<String, List<String>> columnNameMap = new HashMap<String, List<String>>();
	//某个表的主键信息<表名，主键>
	private static Map<String, List<String>> keyNameMap = new HashMap<String, List<String>>();
	//列属性key：表名&列名
	private static Map<String, Field> fieldMap = new HashMap<String, Field>();//字段映射表
	//所有表外键。每个表的命名<表名&字段，是否外键>  
	private static List<Map<String, Object>> foreignkeyList = new ArrayList<>();

	//某个表的所有列名及中文注释信息<表名，<列名,注释>>。 用于打印报表
	private static Map<String,LinkedHashMap<String,Object>> columnCommentMap = new LinkedHashMap<String,LinkedHashMap<String,Object>>();

	
	public static boolean testConnection() {
		return dbMetadataUtils.testConnection();
	}
	public static void init(DBServer server) {
		dataSource = new SimpleDataSource(
				server.getDialect(), server.getUrl(), server.getUser(), server.getRealPwd());
		dbMetadataUtils = new DBMetadataUtil(dataSource);
		try {
			initConfig();
		} catch (SQLException e) {
			throw new RuntimeException(e);
		}
	}
	//获取某数据库中所有表的信息
	public static void initTables(DataSource dataSource,String dbName) throws SQLException 
	{
		DBMetaData.dbName = dbName;

		DBMetadataUtil dbMetadataUtils = new DBMetadataUtil(
				new SimpleDataSource(Dialect.MYSQL,dataSource));

		String sql = "SELECT t.table_name,k.column_name,k.referenced_column_name"
				+" FROM information_schema.table_constraints t"
				+" JOIN information_schema.key_column_usage k"
				+" USING (constraint_name,table_schema,table_name)"
				+" WHERE t.constraint_type='PRIMARY KEY'"
				+" AND t.table_schema='" + dbName + "'";
		setForeignkeyList(dbMetadataUtils.query(sql));

		//获取所有表信息
		List<IntrospectedTable> tableList = dbMetadataUtils.introspectTables(
				dbMetadataUtils.getDefaultConfig(dbName)
				);

		LogKit.info("成功获取到元数据的表共有： "+ tableList.size()); 
		//设置tables所有表、tableMap<表名，表>、columnMap<表名，所有列>变量
		DBMetaData.setTables(tableList);
	}
	//外键处理
	public static List<Map<String, Object>> getForeignkeyList() {
		return foreignkeyList;
	}
	public static Boolean getForeignkey(String tableName,String colName) {
		Boolean result = false;
		if(IsKit.isNotNullOrEmpty(foreignkeyList)) {
			for(Map<String, Object> value: foreignkeyList){
				return TypeKit.objToBoolean(value.get(tableName+"&"+colName));
			}
		}
		return result;
	}

	private static void setForeignkeyList(List<Map<String, Object>> foreignkeyList) {
		DBMetaData.foreignkeyList = foreignkeyList;
	}


	public static void initTables() throws SQLException {
		if (DBMetaData.config != null) {
			DBMetaData.setTables(dbMetadataUtils.introspectTables(DBMetaData.config));
		}
	}
	public static void setTables(List<IntrospectedTable> tables) 
	{
		if (IsKit.isEmpty(tables)) {
			tables = new ArrayList<IntrospectedTable>(0);
		}
		sortTables(tables); 
		DBMetaData.tables = tables;
		tableMap = new HashMap<String, IntrospectedTable>();
		columnMap = new HashMap<String, List<IntrospectedColumn>>();
		if (tables != null && tables.size() > 0) 
		{
			//获取所有表信息
			for (IntrospectedTable table : tables) 
			{
				tableNameList.add(table.getName());
				tableMap.put(table.getName(), table);
				columnMap.put(table.getName(), table.getAllColumns());
			}

			//构造所有表的列集合与主键集合
			if(IsKit.isNotNullOrEmpty(tableNameList))
			{
				for (String tableName : tableNameList) {
					IntrospectedTable table = getTable(tableName);

					LinkedHashMap<String,Object> commentMap = new LinkedHashMap<>();
					List<String> columnNameList = new ArrayList<>();
					List<String> keyNameList = new ArrayList<>();
					//获取给定表的列信息
					List<IntrospectedColumn> columnList  = table.getAllColumns();

					Field field = new Field();
					for (IntrospectedColumn column:columnList) 
					{
						String colName = column.getName();
						columnNameList.add(colName);
						String commentName = column.getRemarks();
						if(IsKit.isNullOrEmpty(commentName)) {
							commentName = "未定义";
						}
						
						//构造所有表的所有列的属性
						field.setForeignkey(getForeignkey(tableName,colName));
						field.setSize(column.getLength());
						field.setComment(commentName);
						field.setDefaultValue(column.getDefaultValue());
						field.setNullable(column.isNullable());
						field.setPrimarykey(column.isPk());
						field.setJdbcType(column.getJdbcType());
						field.setName(column.getName());
						field.setJdbcTypeName(column.getJdbcTypeName());
						field.setJavaTypeName(column.getFullyQualifiedJavaType().getFullyQualifiedName());
						fieldMap.put(tableName+"&"+colName, field);
						field = new Field();
						
						commentMap.put(colName,commentName);
												
						if(column.isPk()) {
							keyNameList.add(colName);		
						}
					}
					columnCommentMap.put(tableName,commentMap);
					columnNameMap.put(tableName, columnNameList);
					keyNameMap.put(tableName, keyNameList);
				}
			}
		}
	}

	/**
	 * 查询表
	 *
	 * @param searchText
	 * @param searchComment
	 * @param matchType
	 * @param caseSensitive
	 * @return
	 */
	public static List<IntrospectedTable> selectTables(
			String searchText, String searchComment, MatchType matchType, boolean caseSensitive) {
		List<IntrospectedTable> answer = new ArrayList<IntrospectedTable>();
		if (IsKit.isNullOrEmpty(tables)) {
			return answer;
		}
		if (StringUtils.isEmpty(searchText) && StringUtils.isEmpty(searchComment)) {
			return tables;
		}
		for (IntrospectedTable table : tables) {
			if (table.filter(searchText, searchComment, matchType, caseSensitive)) {
				answer.add(table);
			}
		}
		sortTables(answer);
		return answer;
	}

	/**
	 * 查询字段
	 *
	 * @param searchText
	 * @param searchComment
	 * @param matchType
	 * @param caseSensitive
	 * @return
	 */
	public static List<IntrospectedColumn> selectColumns(
			String searchText, String searchComment, MatchType matchType, boolean caseSensitive) {
		List<IntrospectedColumn> answer = new ArrayList<IntrospectedColumn>();
		if (IsKit.isNullOrEmpty(tables)) {
			return answer;
		}
		if (StringUtils.isEmpty(searchText) && StringUtils.isEmpty(searchComment)) {
			return null;
		}
		for (IntrospectedTable table : tables) {
			for (IntrospectedColumn column : table.getAllColumns()) {
				if (column.filter(searchText, searchComment, matchType, caseSensitive)) {
					answer.add(column);
				}
			}
		}
		sortColumns(answer);
		return answer;
	}
	private static void sortColumns(List<IntrospectedColumn> columns) {
		if (IsKit.isNotEmpty(columns)) {
			Collections.sort(columns, new Comparator<IntrospectedColumn>() {
				@Override
				public int compare(IntrospectedColumn o1, IntrospectedColumn o2) {
					int result = o1.getTableName().compareTo(o2.getTableName());
					if (result == 0) {
						result = o1.getName().compareTo(o2.getName());
					}
					return result;
				}
			});
		}
	}

	//字符大小写互换
	public static String convertLetterByCase(String value) {
		return dbMetadataUtils.convertLetterByCase(value);
	}

	private static void sortTables(List<IntrospectedTable> tables) {
		if (IsKit.isNotEmpty(tables)) {
			Collections.sort(tables, new Comparator<IntrospectedTable>() {
				@Override
				public int compare(IntrospectedTable o1, IntrospectedTable o2) {
					return o1.getName().compareTo(o2.getName());
				}
			});
		}
	}
	public static void setColumnMap(Map<String, List<IntrospectedColumn>> columnMap) {
		DBMetaData.columnMap = columnMap;
	}

	/**
	 * 初始化默认的Config
	 *
	 * @throws SQLException
	 */
	private static void initConfig() throws SQLException {
		DBMetaData.catalogs = dbMetadataUtils.getCatalogs();
		DBMetaData.schemas = dbMetadataUtils.getSchemas();
		DBMetaData.config = dbMetadataUtils.getDefaultConfig(dbName);
	}

	//返回某表的所有列
	public static List<String> getColumnsNameList(String tableName) {
		return columnNameMap.get(tableName);
	}
	public static String[] getColumnsName(String tableName) {
		return columnNameMap.get(tableName).toArray(new String[]{});
	}
	public static LinkedHashMap<String,Object> getColumnCommentMap(String tableName) {
		return columnCommentMap.get(tableName);
	}
	//获取表中某列的所有属性
	public static Field getFields(String dbName,String colName) {
		if(IsKit.isNullOrEmpty(colName) || IsKit.isNullOrEmpty(dbName)) {
			return null;
		}
		return fieldMap.get(dbName+"&"+colName);
	}
	public static Map<String, Field> getFieldMap() {
		return fieldMap;
	}

	//返回某表的主键 toArray()
	public static List<String> getKeysList(String tableName) {
		return keyNameMap.get(tableName);
	}
	public static String[] getKeys(String tableName) {
		//对于控制 (String[]) 会报错
		return getKeysList(tableName).toArray(new String[]{});
	}

	public static List<String> getTableNamesList() {
		return tableNameList;
	}
	public static String[] getTableNames() {
		return tableNameList.toArray(new String[]{});
	}


	public static Map<String, List<String>> getKeyNameMap() {
		return keyNameMap;
	}
	public static Map<String, List<String>> getColumnNameMap() {
		return columnNameMap;
	}
	public static Map<String, List<IntrospectedColumn>> getColumnMap() {
		return columnMap;
	}
	public static List<IntrospectedColumn> getTableColumns(String tableName) {
		return columnMap.get(tableName);
	}
	public static IntrospectedTable getTable(String tableName) {
		return tableMap.get(tableName);
	}
	public static List<IntrospectedTable> getAllTables() {
		return tables;
	}
}

