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

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.springblade.datasource.AbstractGmetryDialect;
import org.springblade.datasource.constants.DsConstant;
import org.springblade.datasource.enums.DateFormatEnum;
import org.springblade.datasource.enums.DetailColumnTypeEnum;
import org.springblade.datasource.enums.DsTypeEnum;
import org.springblade.datasource.enums.FunctionMethodEnum;
import org.springblade.datasource.enums.ViewColumnTypeEnum;
import org.springblade.datasource.model.DatasourceDto;
import org.springblade.datasource.model.FileStructureDto;
import org.springblade.datasource.model.TableColumnDto;
import org.springblade.datasource.util.DatasourceUtil;
import org.springframework.stereotype.Component;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author wookvn
 *
 */
@Slf4j
@Component
public class ClickhouseGmetryDialect extends AbstractGmetryDialect {
	
	{
		//数据类型
		columnTypeMap.put(ViewColumnTypeEnum.NUMBER, "Decimal64(4)");
		columnTypeMap.put(ViewColumnTypeEnum.STRING, "String");
		columnTypeMap.put(ViewColumnTypeEnum.DATETIME, "Datetime");
		columnTypeMap.put(ViewColumnTypeEnum.BIGINT, "UInt64");
		columnTypeMap.put(ViewColumnTypeEnum.INT, "UInt32");
		columnTypeMap.put(ViewColumnTypeEnum.LARGE_STRING, "String");
		
		//函数方法
		functionMethodMap.put(FunctionMethodEnum.DATE_TO_STR, "formatDateTime(%s,'%s','Asia/Shanghai')");
		functionMethodMap.put(FunctionMethodEnum.STR_TO_DATE, "parseDateTimeBestEffortOrNull(%s,'Asia/Shanghai')");
		functionMethodMap.put(FunctionMethodEnum.NUMBER_TO_DATE, "parseDateTimeBestEffortOrNull(toString(%s),'Asia/Shanghai')");
		functionMethodMap.put(FunctionMethodEnum.NUMBER_JOINT_PERCENT, "concat(toString(%s),'%%')");
		functionMethodMap.put(FunctionMethodEnum.EXTRACT_YEAR, "formatDateTime(%s,'%%Y','Asia/Shanghai')");
		functionMethodMap.put(FunctionMethodEnum.EXTRACT_QUARTER, "formatDateTime(%s,'%%YQ%%q','Asia/Shanghai')");
		functionMethodMap.put(FunctionMethodEnum.EXTRACT_MONTH, "formatDateTime(%s,'%%Y%%m','Asia/Shanghai')");
		functionMethodMap.put(FunctionMethodEnum.EXTRACT_WEEK, "formatDateTime(%s,'%%Y-%%V','Asia/Shanghai')");
		functionMethodMap.put(FunctionMethodEnum.EXTRACT_DAY, "formatDateTime(%s,'%%Y%%m%%d','Asia/Shanghai')");
		functionMethodMap.put(FunctionMethodEnum.EXTRACT_HOUR, "formatDateTime(%s,'%%H:00:00','Asia/Shanghai')");
		functionMethodMap.put(FunctionMethodEnum.EXTRACT_MINUTE, "formatDateTime(%s,'%%H:%%M:00','Asia/Shanghai')");
		functionMethodMap.put(FunctionMethodEnum.EXTRACT_SECOND, "formatDateTime(%s,'%%H:%%M:%%S','Asia/Shanghai')");
		functionMethodMap.put(FunctionMethodEnum.EXTRACT_FULL, "formatDateTime(%s,'%%Y%%m%%d %%H:%%M:%%S','Asia/Shanghai')");

		//日期格式化
		dateFormatMap.put(DateFormatEnum.yyyy, "%Y");
		dateFormatMap.put(DateFormatEnum.yyyyMM, "%Y%m");
		dateFormatMap.put(DateFormatEnum.yyyy_MM, "%Y-%m");
		dateFormatMap.put(DateFormatEnum.yyyy$MM, "%Y/%m");
		dateFormatMap.put(DateFormatEnum.yyyyMMdd, "%Y%m%d");
		dateFormatMap.put(DateFormatEnum.yyyy_MM_dd, "%Y-%m-%d");
		dateFormatMap.put(DateFormatEnum.yyyy$MM$dd, "%Y/%m/%d");
		dateFormatMap.put(DateFormatEnum.yyyyMMddhh$mm$ss,"%Y%m%d %H:%M:%S");
		dateFormatMap.put(DateFormatEnum.yyyy_MM_ddhh$mm$ss,"%Y-%m-%d %H:%M:%S");
		dateFormatMap.put(DateFormatEnum.yyyy$MM$ddhh$mm$ss,"%Y/%m/%d %H:%M:%S");
		dateFormatMap.put(DateFormatEnum.hh,"%H");
		dateFormatMap.put(DateFormatEnum.hh$mm,"%H:%M");
		dateFormatMap.put(DateFormatEnum.hh$mm$ss,"%H:%M:%S");
	}

	//create table user_test(ID UInt64, NAME Nullable(String), PRIMARY KEY (ID)) ENGINE=MergeTree COMMENT '测试用户表';
	@Override
	public List<String> buildCreateTableSql(String tableName, String tableComment, List<FileStructureDto> structureList) {
		StringBuilder fieldSql = new StringBuilder();
		String primaryKey = "";
		for(FileStructureDto item : structureList) {
			String _columnType = getColumnType(item.getColumnType(), item.getColumnSize(), item.getDecimalDigits());
			String columnType = StrUtil.equalsAnyIgnoreCase(item.getColumnName(), DsConstant.COLUMN_USER_ID, DsConstant.COLUMN_PRIMARY_KEY)
					? _columnType : "Nullable(" + _columnType + ")";
			fieldSql.append(String.format("%s %s COMMENT '%s',", item.getColumnName(), columnType, item.getComment()));
			if(item.isPrimaryKey()) {
				primaryKey = item.getColumnName();
			}
		}
		fieldSql.append("PRIMARY KEY(" + primaryKey + ")");
		return Arrays.asList(String.format("create table %s (%s) ENGINE=MergeTree COMMENT '%s'", tableName, fieldSql.toString(), tableComment));
	}
	
	/**
	 * clickhouse查询列 - 因为clickhouse的列备注不返回，需要特殊处理
	 */
	@Override
	public List<TableColumnDto> listTableColumnsByTableViewName(DatasourceDto datasource, String tableViewName) {
		List<TableColumnDto> dataList = new ArrayList<TableColumnDto>();
		Connection connection = null;
		Statement statement = null;
		try {
			connection = DatasourceUtil.createConnection(datasource);
			statement = connection.createStatement();
			ResultSet rs = statement.executeQuery("DESCRIBE TABLE "+datasource.getDsDb()+"."+tableViewName);
			while(rs.next()) {
				TableColumnDto columnDto = new TableColumnDto();
				String dataType = rs.getString("type");
				columnDto.setColumnComments(rs.getString("comment"));
				columnDto.setDataType(dataType);
				columnDto.setColumnName(rs.getString("name"));
				columnDto.setColumnSize(0);
				columnDto.setDecimalDigits(0);
				columnDto.setNullAbleStatus(StrUtil.containsIgnoreCase(dataType, "nullable") ? 1 : 0);
				if(StrUtil.containsIgnoreCase(dataType, "string")) {
					columnDto.setColumnType(ViewColumnTypeEnum.STRING);
					columnDto.setDetailColumnType(DetailColumnTypeEnum.STRING);
				}else if(StrUtil.containsAnyIgnoreCase(dataType, "date", "time")) {
					columnDto.setColumnType(ViewColumnTypeEnum.DATETIME);
					columnDto.setDetailColumnType(DetailColumnTypeEnum.DATETIME);
				}else if(StrUtil.containsAnyIgnoreCase(dataType, "float", "decimal")) {
					columnDto.setColumnType(ViewColumnTypeEnum.NUMBER);
					columnDto.setDetailColumnType(DetailColumnTypeEnum.FLT);
				}else if(StrUtil.containsIgnoreCase(dataType, "bigint")) {
					columnDto.setColumnType(ViewColumnTypeEnum.NUMBER);
					columnDto.setDetailColumnType(DetailColumnTypeEnum.LONG);
				}else{
					columnDto.setColumnType(ViewColumnTypeEnum.NUMBER);
					columnDto.setDetailColumnType(DetailColumnTypeEnum.INT);
				}
				dataList.add(columnDto);
			}
			rs.close();
		} catch (Exception e) {
			log.error(e.getMessage(), e);
		} finally {
			IoUtil.close(connection);
			IoUtil.close(statement);
		}
		return dataList;
	}

	@Override
	public String dbSchema(DatasourceDto datasource) {
		return datasource.getDsDb();
	}

	@Override
	public DsTypeEnum dsType() {
		return DsTypeEnum.clickhouse;
	}
}
