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

import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.annotation.EnumValue;
import lombok.AllArgsConstructor;
import lombok.Getter;

import java.util.Map;
import java.util.function.Function;

import org.springblade.datasource.constants.DsConstant;
import org.springblade.datasource.model.DatasourceDto;

/**
 * @author wookvn
 *	数据源
 */
@Getter
@AllArgsConstructor
public enum DsTypeEnum {

	/**
	 * zeroDateTimeBehavior=convertToNull - 解决java.sql.SQLException: Zero date value prohibited 
	 * allowPublicKeyRetrieval=true - 解决mysql8.0 Public Key Retrieval is not allowed
	 * rewriteBatchedStatements=true - 低版本的mysql驱动批量插入优化
	 */
	mysql(0, false, "com.mysql.cj.jdbc.Driver", "select 1", 3000, 1000000,
			args -> String.format("select count(*) from (select 1 from %s limit %s) a", args), 
			ds -> String.format("jdbc:mysql://%s:%s/%s", ds.getDsIp(), ds.getDsPort(), ds.getDsDb()) + "?useSSL=false&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&serverTimezone=GMT%2B8",
			sql -> StrUtil.containsAnyIgnoreCase(sql, " limit ")){
		@Override
		public Map<String, String> jdbcConnectionPropTimeout() {
			//https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-reference-configuration-properties.html
			return MapUtil.builder("connectTimeout", String.valueOf(DsConstant.CONNECT_TIMEOUT * 5))
					.put("socketTimeout", String.valueOf(DsConstant.READ_TIMEOUT * 18))
					.map();
		}
		@Override
		public Map<String, String> jdbcConnectionPropDefault() {
			return null;
		}
	},
	
	oracle(1, false, "oracle.jdbc.OracleDriver", "select 1 from dual", 5000, 1000000,
			args -> String.format("SELECT count(1) FROM %s WHERE ROWNUM <= %s", args), 
			ds -> String.format("jdbc:oracle:thin:@%s:%s/%s", ds.getDsIp(), ds.getDsPort(), ds.getDsDb()),
			sql -> StrUtil.containsAnyIgnoreCase(sql, " top ", "row_number()", " offset ")){
		@Override
		public Map<String, String> jdbcConnectionPropTimeout() {
			//https://docs.oracle.com/en/database/oracle/oracle-database/21/jajdb/oracle/jdbc/OracleConnection.html
			//http://www.javamonamour.org/2012/09/oraclenetconnecttimeout.html
			//oracle.net.READ_TIMEOUT for jdbc versions < 10.1.0.5 oracle.jdbc.ReadTimeout for jdbc versions >=10.1.0.5
			return MapUtil.builder("oracle.net.CONNECT_TIMEOUT", String.valueOf(DsConstant.CONNECT_TIMEOUT))
					.put("oracle.net.READ_TIMEOUT", String.valueOf(DsConstant.READ_TIMEOUT * 2))
					.put("oracle.jdbc.ReadTimeout", String.valueOf(DsConstant.READ_TIMEOUT * 2))
					.map();
		}
		@Override
		public Map<String, String> jdbcConnectionPropDefault() {
			return null;
		}
	},
	
	sqlserver(2, false, "com.microsoft.sqlserver.jdbc.SQLServerDriver", "select 1", 3000, 1000000,
			args -> String.format("SELECT rows FROM sysindexes WHERE id = OBJECT_ID('%s') AND indid < 2", args), 
			ds -> String.format("jdbc:sqlserver://%s:%s;DatabaseName=%s;encrypt=true;trustServerCertificate=true", ds.getDsIp(), ds.getDsPort(), ds.getDsDb()),
			sql -> StrUtil.containsAnyIgnoreCase(sql, " top ", "fetch next")){
		@Override
		public Map<String, String> jdbcConnectionPropTimeout() {
			//https://learn.microsoft.com/en-us/sql/connect/jdbc/understand-timeouts?view=sql-server-ver16
			return MapUtil.builder("loginTimeout", String.valueOf(DsConstant.CONNECT_TIMEOUT/1000))
					.put("queryTimeout", String.valueOf(DsConstant.READ_TIMEOUT/1000))
					.put("socketTimeout", String.valueOf(DsConstant.READ_TIMEOUT))
					.put("lockTimeout", String.valueOf(DsConstant.READ_TIMEOUT))
					.map();
		}
		@Override
		public Map<String, String> jdbcConnectionPropDefault() {
			return null;
		}
	},
	
	postgresql(3, false, "org.postgresql.Driver", "select 1", 3000, 1000000,
			args -> String.format("select count(*) from (select 1 from %s limit %s) a", args), 
			ds -> String.format("jdbc:postgresql://%s:%s/%s?currentSchema=%s", ds.getDsIp(), ds.getDsPort(), ds.getDsDb(), ds.getDbSchema()),
			sql -> StrUtil.containsAnyIgnoreCase(sql, " limit ", " offset ")){
		@Override
		public Map<String, String> jdbcConnectionPropTimeout() {
			//https://jdbc.postgresql.org/documentation/use/
			return MapUtil.builder("loginTimeout", String.valueOf(DsConstant.CONNECT_TIMEOUT/1000))
					.put("connectTimeout", String.valueOf(DsConstant.CONNECT_TIMEOUT/1000))
					.put("socketTimeout", String.valueOf(DsConstant.READ_TIMEOUT/1000))
					.map();
		}
		@Override
		public Map<String, String> jdbcConnectionPropDefault() {
			return null;
		}
	},
	
	clickhouse(4, false, "com.clickhouse.jdbc.ClickHouseDriver", "select 1", 50000, 100000000,
			args -> String.format("select count(1) from %s", args), 
			ds -> String.format("jdbc:clickhouse://%s:%s/%s", ds.getDsIp(), ds.getDsPort(), ds.getDsDb()),
			sql -> StrUtil.containsAnyIgnoreCase(sql, " limit ")){
		@Override
		public Map<String, String> jdbcConnectionPropTimeout() {
			return MapUtil.builder("socket_timeout", String.valueOf(DsConstant.READ_TIMEOUT))
					.put("connect_timeout", String.valueOf(DsConstant.CONNECT_TIMEOUT))
					.map();
		}
		@Override
		public Map<String, String> jdbcConnectionPropDefault() {
			return null;
		}
	},
	
	kingbase(5, true, "com.kingbase8.Driver", "select 1", 3000, 1000000, 
			args -> String.format("select count(*) from (select 1 from %s limit %s) a", args), 
			ds -> String.format("jdbc:kingbase8://%s:%s/%s?currentSchema=%s", ds.getDsIp(), ds.getDsPort(), ds.getDsDb(), ds.getDbSchema()),
			sql -> StrUtil.containsAnyIgnoreCase(sql, " limit ", " offset ")){
		@Override
		public Map<String, String> jdbcConnectionPropTimeout() {
			//https://help.kingbase.com.cn/v8/development/client-interfaces/jdbc/jdbc-2.html#id2
			return MapUtil.builder("loginTimeout", String.valueOf(DsConstant.CONNECT_TIMEOUT/1000))
					.put("connectTimeout", String.valueOf(DsConstant.CONNECT_TIMEOUT/1000))
					.put("socketTimeout", String.valueOf(DsConstant.READ_TIMEOUT/1000))
					.map();
		}
		@Override
		public Map<String, String> jdbcConnectionPropDefault() {
			return null;
		}
	},
	
	dameng(6, false, "dm.jdbc.driver.DmDriver", "select 1", 3000, 1000000, 
			args -> String.format("select count(*) from (select 1 from %s limit %s) a", args), 
			ds -> String.format("jdbc:dm://%s:%s/%s?schema=%s&zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=utf-8", ds.getDsIp(), ds.getDsPort(), ds.getDsDb(), ds.getDbSchema()),
			sql -> StrUtil.containsAnyIgnoreCase(sql, " limit ", "row_number()", " top ")){
		@Override
		public Map<String, String> jdbcConnectionPropTimeout() {
			//https://eco.dameng.com/document/dm/zh-cn/pm/jdbc-rogramming-guide.html#4.5.4%20DM%20%E6%89%A9%E5%B1%95%E8%BF%9E%E6%8E%A5%E5%B1%9E%E6%80%A7%E7%9A%84%E4%BD%BF%E7%94%A8
			return MapUtil.builder("connectTimeout", String.valueOf(DsConstant.CONNECT_TIMEOUT))
					.put("socketTimeout", String.valueOf(DsConstant.READ_TIMEOUT))
					.put("sessionTimeout", String.valueOf(DsConstant.READ_TIMEOUT))
					.map();
		}
		@Override
		public Map<String, String> jdbcConnectionPropDefault() {
			return null;
		}
	},

	uxdb(7, false, "com.uxsino.uxdb.Driver", "select 1", 3000, 1000000,
			args -> String.format("select count(*) from (select 1 from %s limit %s) a", args),
			ds -> String.format("jdbc:uxdb://%s:%s/%s?transfer=true", ds.getDsIp(), ds.getDsPort(), ds.getDsDb()),
			sql -> StrUtil.containsAnyIgnoreCase(sql, " limit ", " offset ")){
		@Override
		public Map<String, String> jdbcConnectionPropTimeout() {
			return MapUtil.builder("connectTimeout", String.valueOf(DsConstant.CONNECT_TIMEOUT/1000))
					.put("loginTimeout", String.valueOf(DsConstant.CONNECT_TIMEOUT/1000))
					.put("socketTimeout", String.valueOf(DsConstant.READ_TIMEOUT/1000))
					.put("queryTimeout", String.valueOf(DsConstant.READ_TIMEOUT/1000))
					.map();
		}
		@Override
		public Map<String, String> jdbcConnectionPropDefault() {
			return null;
		}
	},
	// jdbc:xugu://121.204.249.37:5138/platomix
	// com.xugu.cloudjdbc.Driver
	xugu(8, false, "com.xugu.cloudjdbc.Driver", "select 1", 3000, 1000000,
			args -> String.format("select count(1) from %s?char_set=UTF8", args),
			ds -> String.format("jdbc:xugu://%s:%s/%s", ds.getDsIp(), ds.getDsPort(), ds.getDsDb()),
			sql -> StrUtil.containsAnyIgnoreCase(sql, "  rownum ")){
		@Override
		public Map<String, String> jdbcConnectionPropTimeout() {
			return MapUtil.builder("loginTimeout", String.valueOf(DsConstant.CONNECT_TIMEOUT/10000))
					.put("maxWaitTimeout", String.valueOf(DsConstant.READ_TIMEOUT/10000))
					.map();
		}
		@Override
		public Map<String, String> jdbcConnectionPropDefault() {
			return null;
		}
	},

	//通用版本
	common(-1, false, "common", "common", 0, 0, args -> null, t -> null, t -> null){
		@Override
		public Map<String, String> jdbcConnectionPropTimeout() { return null; }
		@Override
		public Map<String, String> jdbcConnectionPropDefault() {return null;}
	};
	
	@EnumValue
	private Integer value;
	/**
	 * 数据库查询出来的字段名是否都是小写
	 */
	private boolean fieldLowerCase;
	private String driverClassName;
	private String validationQuery;
	/**
	 * 批量入库数量
	 */
	private int batchSize;
	/**
	 * 查询最大限制数量
	 */
	private int selectLimitVal;
	/**
	 * 校验数据量大小函数
	 */
	private Function<Object[], String> checkLimitValueFunction;
	private Function<DatasourceDto, String> driverUrlFunction;
	/**
	 * 是否包含分页语句
	 */
	private Function<String, Boolean> hasLimitStatementFunction;
	
	/**
	 * jdbc驱动连接超时配置 [测试连接超时]
	 * @return
	 */
	public abstract Map<String, String> jdbcConnectionPropTimeout();
	/**
	 * jdbc驱动默认连接配置
	 * @return
	 */
	public abstract Map<String, String> jdbcConnectionPropDefault();

	/**
	 * 解析
	 * @param value
	 * @return
	 */
	public static DsTypeEnum resolve(Integer value) {
		DsTypeEnum[] values = DsTypeEnum.values();
		for(DsTypeEnum dsType : values) {
			if(value != null && dsType.getValue().intValue() == value) {
				return dsType;
			}
		}
		return DsTypeEnum.mysql;
	}
}
