/**
 * 
 */
package org.springblade.datasource;

import cn.hutool.core.util.StrUtil;
import com.alibaba.druid.util.JdbcUtils;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.tool.exception.ServiceException;
import org.springblade.datasource.constants.CubeConstants;
import org.springblade.datasource.enums.*;
import org.springblade.datasource.model.TableColumnDto;
import org.springblade.datasource.model.TableViewDto;
import org.springblade.datasource.support.DynamicDataSourceSupport;
import org.springblade.etl.entity.bi.ReportDatasource;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author wookvn
 *
 */
@Slf4j
public abstract class AbstractGmetryDialect implements IGmetryDialect {

	protected final Map<ColumnTypeEnum, String> columnTypeMap = new ConcurrentHashMap<>();
	
	protected final Map<FunctionMethodEnum, String> functionMethodMap = new ConcurrentHashMap<>();
	
	protected final Map<DateFormatEnum, String> dateFormatMap = new ConcurrentHashMap<>();
	
	@Override
	public List<TableViewDto> listTableViewsByDatasource(ReportDatasource datasource) {
		return listTableViewsByDatasource(datasource, null);
	}
	
	@Override
	public List<TableColumnDto> listTableColumnsByTableViewName(ReportDatasource datasource, String tableViewName) {
		if(datasource == null) return null;
		List<TableColumnDto> list = new ArrayList<TableColumnDto>();
		Connection connection = null;
		try {
			connection = DynamicDataSourceSupport.createConnection(datasource);
			DatabaseMetaData metaData = connection.getMetaData();
			String schemaPattern = dbSchema(datasource);
			ResultSet rs = metaData.getColumns(null, schemaPattern, tableViewName, "%");
			ResultSet pkRs = metaData.getPrimaryKeys(null, schemaPattern, tableViewName);
			ResultSet indexInfo = metaData.getIndexInfo(null, schemaPattern, tableViewName,false,false);
			List<String> primaryKeyColumnList = new ArrayList<>();
			List<String> indexList = new ArrayList<>();
			while(pkRs.next()) {
				primaryKeyColumnList.add(pkRs.getString("COLUMN_NAME"));
			}
			while (indexInfo.next()) {
				indexList.add(indexInfo.getString("INDEX_NAME"));
			}
			while(rs.next()) {
				TableColumnDto columnDto = new TableColumnDto();
				columnDto.setColumnComments(rs.getString("REMARKS"));
				columnDto.setDataType(rs.getString("TYPE_NAME"));
				columnDto.setColumnName(rs.getString("COLUMN_NAME"));
				columnDto.setColumnSize(rs.getInt("COLUMN_SIZE"));
				columnDto.setDecimalDigits(rs.getInt("DECIMAL_DIGITS"));
				columnDto.setColumnType(ViewColumnTypeEnum.resolve(rs.getInt("DATA_TYPE")));
				columnDto.setDetailColumnType(DetailColumnTypeEnum.resolve(rs.getInt("DATA_TYPE")));
				//IS_NULLABLE - YES/NO  NULLABLE - 1(允许为空)/2(不确定)/0(不允许为空)
				columnDto.setNullAbleStatus("yes".equalsIgnoreCase(rs.getString("IS_NULLABLE")) ? 1 : 0);
				columnDto.setPrimaryKeyStatus(primaryKeyColumnList.contains(rs.getString("COLUMN_NAME")) ? 1 : 0);
				columnDto.setIndexStatus(indexList.contains(rs.getString("COLUMN_NAME")) ? 1 : 0);
				list.add(columnDto);
			}
			rs.close();
		} catch (Exception e) {
			log.error(e.getMessage(), e);
		} finally {
			JdbcUtils.close(connection);
		}
		return list;
	}
	
	@Override
	public String getColumnType(ColumnTypeEnum columnType) {
		String value = columnTypeMap.get(columnType);
		if(StrUtil.isEmpty(value)) {
			throw new ServiceException("数据类型不存在|"+columnType);
		}
		return value;
	}
	
	@Override
	public String formatDateTime(DateFormatEnum dateTimeFormat, String columnSql) {
		String value = dateFormatMap.get(dateTimeFormat);
		if(StrUtil.isEmpty(value)) {
			throw new ServiceException("日期类型不存在|"+dateTimeFormat);
		}
		return formatMethodFunction(FunctionMethodEnum.DATE_TO_STR, columnSql, value);
	}
	
	@Override
	public String formatMethodFunction(FunctionMethodEnum functionMethod, Object... args) {
		String value = functionMethodMap.get(functionMethod);
		if(StrUtil.isEmpty(value)) {
			throw new ServiceException("函数方法不存在|"+functionMethod);
		}
		return String.format(value, args);
	}
	
	@Override
	public String buildCreateViewSql(String viewName, String sqlStatement) {
		return String.format(CubeConstants.CREATE_REPLACE_VIEW_SQL_FORMAT, viewName, sqlStatement);
	}
	
	@Override
	public String convertDataType(ViewColumnTypeEnum oldColumnType, ViewColumnTypeEnum newColumnType, String columnSql,
			DateFormatEnum dateFormat) {
		FunctionMethodEnum functionMethod = null;
		if(oldColumnType == ViewColumnTypeEnum.STRING && newColumnType == ViewColumnTypeEnum.DATETIME) {
			functionMethod = FunctionMethodEnum.STR_TO_DATE;
		}else if(oldColumnType == ViewColumnTypeEnum.NUMBER && newColumnType == ViewColumnTypeEnum.DATETIME) {
			functionMethod = FunctionMethodEnum.NUMBER_TO_DATE;
		}else if(oldColumnType == ViewColumnTypeEnum.DATETIME && newColumnType == ViewColumnTypeEnum.STRING) {
			functionMethod = FunctionMethodEnum.DATE_TO_STR;
		}
		if(oldColumnType == null || oldColumnType == newColumnType || functionMethod == null) {
			return columnSql;
		}
		String dateTimeFormat = "";
		if(dateFormat != null && (oldColumnType == ViewColumnTypeEnum.DATETIME || newColumnType == ViewColumnTypeEnum.DATETIME)) {
			dateTimeFormat = dateFormatMap.get(dateFormat);
			if(StrUtil.isEmpty(dateTimeFormat)) {
				throw new ServiceException("日期类型不存在|"+dateTimeFormat);
			}
		}
		return formatMethodFunction(functionMethod, columnSql, dateTimeFormat);
	}

	@Override
	public List<TableViewDto> listTableViewsByDatasource(ReportDatasource datasource,String tableName) {
		if(datasource == null) return null;
		List<TableViewDto> list = new ArrayList<TableViewDto>();
		Connection connection = null;
		try {
			connection = DynamicDataSourceSupport.createConnection(datasource);
			DatabaseMetaData metaData = connection.getMetaData();
			String schemaPattern = dbSchema(datasource);
			ResultSet rs = metaData.getTables(null, schemaPattern, tableName, new String[] {"TABLE", "VIEW"});
			while(rs.next()) {
				list.add(new TableViewDto(rs.getString("TABLE_NAME"), rs.getString("REMARKS"), datasource.getId()));
			}
			rs.close();
		} catch (Exception e) {
			log.error(e.getMessage(), e);
		} finally {
			JdbcUtils.close(connection);
		}
		return list;
	}
	
	/**
	 * 获取dbSchema
	 * @param dbUser
	 * @param dbName
	 * @return
	 */
	public String dbSchema(ReportDatasource datasource) {
		return datasource.getDsDb();
	}

}
