package org.beetl.sql.core;

import org.beetl.sql.clazz.ClassDesc;
import org.beetl.sql.clazz.NameConversion;
import org.beetl.sql.clazz.SQLType;
import org.beetl.sql.clazz.TableDesc;
import org.beetl.sql.clazz.kit.*;
import org.beetl.sql.core.db.DBAutoGeneratedSql;
import org.beetl.sql.core.db.DBStyle;
import org.beetl.sql.core.engine.PageQuery;
import org.beetl.sql.core.engine.template.SQLErrorInfo;
import org.beetl.sql.core.engine.template.SQLTemplateEngine;
import org.beetl.sql.core.engine.template.TemplateContext;
import org.beetl.sql.core.loader.SQLLoader;
import org.beetl.sql.core.mapping.BeanProcessor;
import org.beetl.sql.core.meta.MetadataManager;
import org.beetl.sql.core.page.PageRequest;
import org.beetl.sql.core.page.PageResult;
import org.beetl.sql.core.query.LambdaQuery;
import org.beetl.sql.core.query.Query;

import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.*;

import static org.beetl.sql.clazz.kit.AutoSQLEnum.*;
import static org.beetl.sql.core.db.DBAutoGeneratedSql.PAGE;


/**
 * Beetsql 操作入口
 *
 * @author xiandafu
 */
public class SQLManager implements DataAPI {


	protected Map<String, SQLManager> group = new HashMap<>();
	/**sqlManager名字**/
	protected String name;
	Interceptor[] inters = {};
	SQLTemplateEngine sqlTemplateEngine = null;
	boolean offsetStartZero = false;
	// 映射jdbc Result到java对象的工具类，跟sqlId相关
	Map<String, BeanProcessor> processors = new HashMap<String, BeanProcessor>();
	// 默认的映射工具类
	BeanProcessor defaultBeanProcessors = null;
	Map<String, IDAutoGen> idAutoGenMap = new HashMap<String, IDAutoGen>();
	SqlIdFactory sqlIdFactory;
	String charset;
	boolean isProduct = false;
	MapperBuilder mapperBuilder = null;
	ThreadLocal<QueryConfig> queryConfigLocal = ThreadLocal.withInitial(() -> new QueryConfig());
	private DBStyle dbStyle;
	private SQLLoader sqlLoader;
	/*数据库连接管理*/
	private ConnectionSource ds = null;
	/* 名字转换器 */
	private NameConversion nc = null;
	private MetadataManager metaDataManager;
	private ClassLoaderKit classLoaderKit = null;


	protected SQLManager() {
		Class c = null;
		try {
			//不直接new的原因是因为maven循环依赖引用
			c = Class.forName("org.beetl.sql.mapper.DefaultMapperBuilder");
		} catch (ClassNotFoundException e) {
			throw new IllegalStateException(e);
		}
		mapperBuilder = (MapperBuilder) BeanKit.newInstance(c);
		mapperBuilder.setSqlManager(this);

	}

	/**
	 * 使用这个创建更加的简洁, 并且用户不需要理解更多的 构造函数
	 *
	 * @param ds 数据源
	 * @return SQLManager构建器
	 */
	public static SQLManagerBuilder newBuilder(ConnectionSource ds) {
		return new SQLManagerBuilder(ds);
	}

	/**
	 * 快速上手的简洁构建器
	 *
	 * @param driver   驱动
	 * @param url      url
	 * @param userName userName
	 * @param password password
	 * @return SQLManager构建器
	 */
	public static SQLManagerBuilder newBuilder(String driver, String url, String userName, String password) {
		ConnectionSource source = ConnectionSourceHelper.getSimple(driver, url, userName, password);
		return newBuilder(source);
	}


	@Override
	public <T> Query<T> query(Class<T> clazz) {
		return new Query<T>(this, clazz);
	}

	@Override
	public <T> LambdaQuery<T> lambdaQuery(Class<T> clazz) {
		if (BeanKit.queryLambdasSupport) {
			return new LambdaQuery<T>(this, clazz);
		} else {
			throw new UnsupportedOperationException("需要Java8以上");
		}
	}


	public boolean isOffsetStartZero() {
		return offsetStartZero;
	}


	/**
	 * 是否是生产模式:生产模式MetadataManager ，不查看sql文件变化,默认是false
	 *
	 * @return
	 */
	public boolean isProductMode() {
		return this.isProduct;
	}

	/**
	 * 不执行数据库操作，仅仅得到一个sql模板执行后的实际得sql和相应的参数
	 *
	 * @param id
	 * @param paras
	 * @return
	 */
	public SQLResult getSQLResult(SqlId id, Object paras) {
		return getSQLResult(id,paras,false);
	}

	public SQLResult getSQLResult(SqlId id, Object paras,boolean isUpdate) {
		SQLExecutor script = getScript(id);
		//false和true不影响
		Map map = script.beforeExecute(null, paras, isUpdate);
		return script.run(map);
	}


	/**
	 * 内部使用，
	 *
	 * @param source
	 * @param inputParas
	 * @return
	 */
	public SQLResult getSQLResult(SQLSource source, Object inputParas) {
		ExecuteContext ctx = ExecuteContext.instance(this);
		ctx.sqlId = source.id;
		ctx.sqlSource = source;
		SQLExecutor script = dbStyle.buildExecutor(ctx);
		Map map = script.beforeExecute(null, inputParas, false);
		SQLResult result = script.run(map);
		return result;
	}


	public SQLResult getSQLResult(SqlId id, Object paras, TemplateContext ctx) {
		SQLExecutor script = getScript(id);
		Map parasMap = script.beforeExecute(null, paras, false);
		return script.run(parasMap, ctx);
	}

	/**
	 * 得到指定sqlId的sqlscript对象
	 *
	 * @param sqlId
	 * @return
	 */
	public SQLExecutor getScript(SqlId sqlId) {
		SQLSource source = sqlLoader.querySQL(sqlId);
		if (source == null) {
			throw sqlLoader.getException(sqlId);
		}
		ExecuteContext context = ExecuteContext.instance(this).initSQLSource(source);
		context.fill(queryConfigLocal);
		SQLExecutor script = dbStyle.buildExecutor(context);
		return script;
	}

	public boolean containSqlId(SqlId sqlId) {
		return sqlLoader.exist(sqlId);
	}

	@Override
	public SQLErrorInfo validateSqlId(SqlId id) {
		SQLSource source = this.sqlLoader.querySQL(id);
		if (source == null) {
			throw new IllegalArgumentException(id + " 不存在");
		}
		String sqlTemplate = source.template;

		return sqlTemplateEngine.validate(sqlTemplate);


	}


	public SQLManager viewType(Class view) {
		this.queryConfigLocal.get().setViewClass(view);
		return this;
	}

	public SQLManager resultSetMapper(Class resultSetMapperClass) {
		this.queryConfigLocal.get().setResultSetClass(resultSetMapperClass);
		return this;
	}

	public SQLManager rowMapper(Class rowMapperClass) {
		this.queryConfigLocal.get().setRowMapperClass(rowMapperClass);
		return this;
	}


	/**
	 * 得到增删改查模板
	 *
	 * @param cls          clz
	 * @param autoSQLEnum ConstantEnum
	 * @return BaseSQLExecutor
	 */
	public SQLExecutor getScript(Class<?> cls, AutoSQLEnum autoSQLEnum) {

		SqlId id = this.sqlIdFactory.buildIdentity(cls, autoSQLEnum);
		QueryConfig queryConfig = this.queryConfigLocal.get();
		Class viewType = null;
		if (queryConfig != null) {
			viewType = queryConfig.getViewClass();
			if (viewType != null) {
				id = id.toView(viewType);
				queryConfig.setViewClass(null);
			}

		}

		ExecuteContext context = ExecuteContext.instance(this);
		SQLSource tempSource = this.sqlLoader.queryAutoSQL(id);

		if (tempSource != null) {
			context.initSQLSource(tempSource);
			return dbStyle.buildExecutor(context);
		}


		SQLType sqlType = null;
		switch (autoSQLEnum) {
			case SELECT_BY_ID: {
				tempSource = this.dbStyle.genSelectById(cls, viewType);
				sqlType = SQLType.SELECT;
				break;
			}
			case SELECT_BY_IDS: {
				tempSource = this.dbStyle.genSelectByIds(cls, viewType);
				sqlType = SQLType.SELECT;
				break;
			}
			case EXIST_BY_ID: {
				tempSource = this.dbStyle.genExistSql(cls);
				sqlType = SQLType.SELECT;
				break;
			}
			case SELECT_BY_TEMPLATE: {
				tempSource = this.dbStyle.genSelectByTemplate(cls, viewType);
				sqlType = SQLType.SELECT;
				break;
			}
			case SELECT_COUNT_BY_TEMPLATE: {
				tempSource = this.dbStyle.genSelectCountByTemplate(cls);
				sqlType = SQLType.SELECT;
				break;
			}
			case DELETE_BY_ID: {
				tempSource = this.dbStyle.genDeleteById(cls);
				sqlType = SQLType.SELECT;
				break;
			}
			case SELECT_ALL: {
				tempSource = this.dbStyle.genSelectAll(cls, viewType);
				sqlType = SQLType.SELECT;
				break;
			}
			case UPDATE_ALL: {
				tempSource = this.dbStyle.genUpdateAll(cls);
				sqlType = SQLType.UPDATE;
				break;
			}
			case UPDATE_BY_ID: {
				tempSource = this.dbStyle.genUpdateById(cls);
				sqlType = SQLType.UPDATE;
				break;
			}

			case UPDATE_TEMPLATE_BY_ID: {
				tempSource = this.dbStyle.genUpdateTemplate(cls);
				sqlType = SQLType.UPDATE;
				break;
			}

			case INSERT: {
				tempSource = this.dbStyle.genInsert(cls);
				sqlType = SQLType.INSERT;
				break;
			}

			case INSERT_TEMPLATE: {
				tempSource = this.dbStyle.genInsertTemplate(cls);
				sqlType = SQLType.INSERT;
				break;
			}
			case DELETE_TEMPLATE_BY_ID:
				tempSource = this.dbStyle.genDeleteById(cls);
				sqlType = SQLType.DELETE;
				break;
			case LOCK_BY_ID: {
				tempSource = this.dbStyle.genSelectByIdForUpdate(cls, viewType);
				sqlType = SQLType.SELECT;
				break;
			}
			default: {
				throw new UnsupportedOperationException();
			}
		}
		tempSource.setId(id);
		tempSource.setSqlType(sqlType);
		tempSource.setAutoGenerated(true);
		context.initSQLSource(tempSource);
		sqlLoader.addSQL(id, tempSource);
		return dbStyle.buildExecutor(context);
	}

	/* ============ 查询部分 ================== */

	/****
	 * 获取为分页语句
	 *
	 * @param selectId
	 * @return
	 */
	protected SQLExecutor getPageSqlScript(Class mapping, SqlId selectId) {
		SqlId pageId = selectId.toPage();
		ExecuteContext ctx = ExecuteContext.instance(this);

		SQLSource source = sqlLoader.querySQL(pageId);
		if (source != null) {
			ctx.initSQLSource(source);
			return dbStyle.buildExecutor(ctx);
		}


		//新创建一个
		SQLSource script = sqlLoader.querySQL(selectId);
		String template = script.getTemplate();
		String pageTemplate = dbStyle.getRangeSql().toTemplateRange(mapping, template);
		source = new SQLSource(pageId, pageTemplate);
		source.setSqlType(SQLType.SELECT);
		source.version = script.version;
		sqlLoader.addSQL(pageId, source);
		ctx.initSQLSource(source);

		return dbStyle.buildExecutor(ctx);

	}


	/**
	 * 通过sqlId进行查询，查询结果映射到clazz上，输入条件是个Bean，
	 * Bean的属性可以被sql语句引用，如bean中有name属性,即方法getName,则sql语句可以包含 name属性，如select *
	 * from xxx where name = #name#
	 *
	 * @param sqlId sql标记
	 * @param clazz 需要映射的Pojo类
	 * @param paras Bean
	 * @return Pojo集合
	 */

	@Override
	public <T> List<T> select(SqlId sqlId, Class<T> clazz, Object paras) {
		SQLExecutor script = getScript(sqlId);
		return script.select(clazz, paras);
	}

	/**
	 * 根据sqlId查询目标对象
	 * @param sqlId
	 * @param clazz
	 * @return
	 */
	@Override
	public <T> List<T> select(SqlId sqlId, Class<T> clazz) {
		return this.select(sqlId, clazz, null);
	}


	@Override
	public <T> PageResult<T> pageQuery(SqlId sqlId, Class<T> clazz, Object paras, PageRequest request) {

		if (request instanceof PageQuery) {
			//兼容2.0，参数来自PageQuery的getParas方法，而不是paras。2.0调用需要设置paras为null
			//不推荐
			paras = request.getParas();
		}

		Long totalRow = null;
		List<T> list;
		boolean totalRequired = request.isTotalRequired();
		String orderBy = request.getOrderBy();
		SqlId sqlCountId = sqlId.toCount();
		boolean hasCountSQL = false;
		if (request.isTotalRequired()) {
			hasCountSQL = this.sqlLoader.exist(sqlCountId);
			// 需要查询行数
			try {
				if (hasCountSQL) {
					//使用专有的统计总数的sql
					totalRow = this.selectUnique(sqlCountId, paras, Long.class);
				} else {
					//使用当前sql转化成统计总数sql,page函数完成此功能
					SQLExecutor script = getScript(sqlId);
					Map pageParas = script.beforeExecute(Long.class, paras, false);
					pageParas.put(PAGE, Boolean.TRUE);
					totalRow = script.selectUnique(Long.class, pageParas);

				}

			} catch (BeetlSQLException ex) {
				if (ex.code == BeetlSQLException.UNIQUE_EXCEPT_ERROR) {
					throw new BeetlSQLException(BeetlSQLException.PAGE_QUERY_ERROR,
							"翻页语句格式出错，期望总数查询SQL,遗漏了page函数或者sql是分组查询?");
				} else {
					throw ex;
				}
			}
			if (totalRow == null) {
				totalRow = 0L;
			}
		}

		if (totalRow == null || totalRow != 0) {

			long size = request.getPageSize();
			Object start = request.getStart(this.offsetStartZero);
			;
			SQLExecutor sqlExecutor = this.getScript(sqlId);
			SQLExecutor pageSqlEx = getPageSqlScript(clazz, sqlId);
			Map pageParas = pageSqlEx.beforeExecute(clazz, paras, false);
			if (pageParas.containsKey(PAGE)) {
				//如果paras是Map，那么求count的时候，会塞进一个PAGE标志，这里需要删除
				pageParas.remove(PAGE);
			}
			dbStyle.getRangeSql().addTemplateRangeParas(pageParas, start, size);
			if (!StringKit.isEmpty(orderBy)) {
				pageParas.put(DBAutoGeneratedSql.ORDER_BY, orderBy);
			}

			list = (List<T>) pageSqlEx.select(clazz, pageParas);
		} else {
			list = Collections.EMPTY_LIST;
		}

		PageResult pageReqeust = totalRequired ? request.of(list, totalRow) : request.of(list);
		return pageReqeust;
	}


	/**
	 * 根据主键查询 获取唯一记录，如果纪录不存在，将会抛出异常
	 *
	 * @param clazz
	 * @param pk    主键
	 * @return
	 */

	@Override
	public <T> T unique(Class<T> clazz, Object pk) {
		SQLExecutor script = getScript(clazz, SELECT_BY_ID);
		return script.unique(clazz, pk);
	}


	/* =========模版查询=============== */

	/**
	 * @param clazz
	 * @param pk
	 * @return 如果没有找到，返回null
	 */

	@Override
	public <T> T single(Class<T> clazz, Object pk) {
		SQLExecutor script = getScript(clazz, SELECT_BY_ID);
		return script.single(clazz, pk);
	}


	@Override
	public <T> List<T> selectByIds(Class<T> clazz, List<?> pks) {
		SQLExecutor script = getScript(clazz, SELECT_BY_IDS);
		Map paras = new HashMap();
		paras.put("ids", pks);
		return script.select(clazz, paras);
	}

	/**
	 * 一个行级锁实现，类似select * from xx where id = ? for update
	 *
	 * @param clazz
	 * @param pk
	 * @return
	 */

	@Override
	public <T> T lock(Class<T> clazz, Object pk) {
		SQLExecutor script = getScript(clazz, LOCK_BY_ID);
		return script.single(clazz, pk);
	}

	/**
	 * btsql自动生成查询语句，查询clazz代表的表的所有数据。
	 *
	 * @param clazz
	 * @return
	 */

	@Override
	public <T> List<T> all(Class<T> clazz) {
		SQLExecutor script = getScript(clazz, SELECT_ALL);
		return script.select(clazz, null);
	}


	/**
	 * 查询记录数
	 *
	 * @param clazz
	 * @return
	 */

	@Override
	public long allCount(Class<?> clazz) {
		SQLExecutor script = getScript(clazz, SELECT_COUNT_BY_TEMPLATE);
		return script.singleSelect(Long.class, new HashMap<>());
	}

	@Override
	public <T> List<T> all(Class<T> clazz, Object start, Long pageSize) {
		String sql = "select * from " + this.nc.getTableName(clazz);
		String pageSql = this.dbStyle.getRangeSql().toRange(sql, start, pageSize);
		return this.execute(new SQLReady(pageSql), clazz);
	}


	@Override
	public <T> T templateOne(T t) {
		// 改为只查询一条记录
		if (t == null) {
			throw new NullPointerException();
		}
		long start = this.offsetStartZero ? 0 : 1;
		Class target = t.getClass();
		SQLExecutor script = getScript(target, SELECT_BY_TEMPLATE);
		String sqlTemplate = script.getExecuteContext().sqlSource.template;
		String pageSqlTemplate = dbStyle.getRangeSql().toTemplateRange(target, sqlTemplate);

		Map<String, Object> param = script.beforeExecute(target, t, false);
		this.dbStyle.getRangeSql().addTemplateRangeParas(param, start, 2);
		List<T> list = this.execute(pageSqlTemplate, target, param);
		if (list.isEmpty()) {
			return null;
		} else if (list.size() > 1) {
			throw new BeetlSQLException(BeetlSQLException.UNIQUE_EXCEPT_ERROR, "模板查询期望只返回一条结果集");
		} else {
			return list.get(0);
		}
	}


	@Override
	public <T> List<T> template(T t) {
		SQLExecutor script = getScript(t.getClass(), SELECT_BY_TEMPLATE);
		return (List<T>) script.select(t.getClass(), t);
	}


	// ========== 取出单个值 ============== //

	/**
	 * 查询总数
	 *
	 * @param t
	 * @return
	 */

	@Override
	public <T> long templateCount(T t) {
		return templateCount(t.getClass(), t);
	}


	protected <T> long templateCount(Class<T> target, Object paras) {
		SQLExecutor script = getScript(target, SELECT_COUNT_BY_TEMPLATE);
		Long l = script.singleSelect(Long.class, paras);
		return l;
	}


	/**
	 * 将查询结果返回成Long类型
	 *
	 * @param id
	 * @param paras
	 * @return
	 */

	@Override
	public Long longValue(SqlId id, Object paras) {
		return this.selectSingle(id, paras, Long.class);
	}

	/**
	 * 将查询结果返回成Integer类型
	 *
	 * @param id
	 * @param paras
	 * @return
	 */

	@Override
	public Integer intValue(SqlId id, Object paras) {
		return this.selectSingle(id, paras, Integer.class);
	}


	/**
	 * 将查询结果返回成BigDecimal类型
	 *
	 * @param id
	 * @param paras
	 * @return
	 */

	@Override
	public BigDecimal bigDecimalValue(SqlId id, Object paras) {
		return this.selectSingle(id, paras, BigDecimal.class);
	}


	/**
	 * 返回查询的第一行数据，如果有未找到，返回null
	 *
	 * @param sqlId
	 * @param paras
	 * @param target
	 * @return
	 */

	@Override
	public <T> T selectSingle(SqlId sqlId, Object paras, Class<T> target) {
		SQLExecutor script = getScript(sqlId);
		return script.singleSelect(target, paras);
	}


	/**
	 * 返回一行数据，如果有多行或者未找到，抛错
	 *
	 * @param id
	 * @param paras
	 * @param target
	 * @return
	 */

	@Override
	public <T> T selectUnique(SqlId id, Object paras, Class<T> target) {
		SQLExecutor script = getScript(id);
		return script.selectUnique(target, paras);
	}


	@Override
	public <T> List<T> select(SqlId sqlId, Object paras, Class<T> clazz, Object start, long size) {
		SQLExecutor newSqlEx = getPageSqlScript(clazz, sqlId);
		return newSqlEx.select(clazz, paras, start, size);
	}


	/**
	 * delete from user where 1=1 and id= #id#
	 * <p>
	 * 根据Id删除数据：支持联合主键
	 *
	 * @param clazz
	 * @param pkValue
	 * @return
	 */

	@Override
	public int deleteById(Class<?> clazz, Object pkValue) {

		SQLExecutor script = getScript(clazz, DELETE_BY_ID);
		return script.deleteById(clazz, pkValue);
	}

	/**
	 * 删除对象, 通过对象的主键
	 *
	 * @param obj 对象,必须包含了主键，实际上根据主键来删除
	 * @return
	 */

	@Override
	public int deleteObject(Object obj) {
		Class target = obj.getClass();
		SQLExecutor script = getScript(target, DELETE_TEMPLATE_BY_ID);
		return script.update(target, obj);
	}

	// ============= 插入 =================== //

	/**
	 * 通用插入操作
	 *
	 * @param paras
	 * @return
	 */

	@Override
	public int insert(Object paras) {
		return this.insert(paras.getClass(), paras);
	}


	/**
	 * 通用模板插入
	 *
	 * @param paras
	 * @return
	 */

	@Override
	public int insertTemplate(Object paras) {
		return this.insertTemplate(paras.getClass(), paras);
	}


	/**
	 * 插入一行记录
	 *
	 * @param clazz 对应的表
	 * @param paras 对应的数据，可以是map
	 * @return
	 */

	@Override
	public int insert(Class clazz, Object paras) {
		return generalInsert(clazz, paras, false);
	}


	/**
	 * 模板插入，非空值插入到数据库，并且获取到自增主键的值
	 *
	 * @param clazz
	 * @param paras
	 * @return
	 */

	@Override
	public int insertTemplate(Class clazz, Object paras) {
		return generalInsert(clazz, paras, true);
	}


	/**
	 * 是否有此对象
	 * @param clazz
	 * @param pk 主健
	 * @return
	 */

	@Override
	public boolean exist(Class<?> clazz, Object pk) {
		SQLExecutor script = getScript(clazz, EXIST_BY_ID);
		return script.existById(clazz, pk);

	}

	protected int generalInsert(Class clazz, Object paras, boolean template) {
		SQLExecutor script = getScript(clazz, template ? INSERT_TEMPLATE : INSERT);
		return script.insert(clazz, paras);
	}

	protected void assignAutoId(Object bean, Object id) {
		Class target = bean.getClass();
		String table = this.nc.getTableName(target);
		ClassDesc desc = this.metaDataManager.getTable(table).genClassDesc(target, nc);

		if (desc.getIdCols().isEmpty()) {
			return;
		} else {
			Method getterMethod = (Method) desc.getIdMethods().get(desc.getIdAttrs().get(0));

			String name = getterMethod.getName();
			//TODO..
			String setterName = name.replaceFirst("get", "set");
			try {
				Method setterMethod = target.getMethod(setterName, new Class[]{getterMethod.getReturnType()});
				Object value = BeanKit.convertValueToRequiredType(id, getterMethod.getReturnType());
				setterMethod.invoke(bean, new Object[]{value});
			} catch (Exception ex) {
				throw new UnsupportedOperationException("autoAssignKey failure " + ex.getMessage());
			}
		}
	}


	/**
	 * 批量插入
	 *
	 * @param clazz
	 * @param list
	 */

	@Override
	public int[] insertBatch(Class clazz, List<?> list) {
		SQLExecutor script = getScript(clazz, INSERT);
		int max = dbStyle.getMaxBatchCount();
		int[] ret = script.insertBatch(clazz, list);
		return ret;

	}


	/**
	 * 插入，并获取主键,主键将通过paras所代表的表名来获取
	 *
	 * @param sqlId
	 * @param paras
	 * @return
	 */

	@Override
	public int insert(SqlId sqlId, Object paras) {
		SQLExecutor script = getScript(sqlId);
		return script.insert(null, paras);

	}


	/**
	 *
	 * 插入单条，并获取自增主键值，因为此接口并未指定实体对象，因此需要keyName来指明数据库主键列
	 * 对于一次插入多条，不支持TODO，需要试一下
	 * @param sqlId
	 * @param paras
	 * @param cols,需要得到数据库自动生成的值
	 */

	@Override
	public Object[] insert(SqlId sqlId, Object paras, String[] cols) {
		SQLExecutor script = getScript(sqlId);
		return script.insert(null, paras, cols);
	}

	/**
	 * 先判断是否主键为空，如果为空，则插入，如果不为空，则从数据库
	 *  出一条，如果未取到，则插入一条，其他情况按照主键更新
	 *
	 * @param obj
	 * @return 如果是插入操作，返回true，如果是更新，返回false
	 */

	@Override
	public boolean upsert(Object obj) {
		return this.upsert(obj, false);
	}

	/**
	 * 先判断是否主键为空，如果为空，则插入，如果不为空，则从数据库
	 * 取出一条，如果未取到，则插入一条，其他情况按照主键更新
	 * @param obj
	 * @return 如果是插入操作，返回true，如果是更新，返回false
	 */

	@Override
	public boolean upsertByTemplate(Object obj) {
		return this.upsert(obj, true);
	}


	/**
	 * 先判断是否主键为空，如果为空，则插入，如果不为空，则从数据库
	 * 取出一条，如果未取到，则插入一条，其他情况按照主键更新
	 * @param obj 待更新/插入的实体对象
	 * @param template
	 * @return 如果是插入操作，返回true，如果是更新，返回false
	 */
	protected boolean upsert(Object obj, boolean template) {
		Class c = obj.getClass();
		String tableName = this.nc.getTableName(c);
		TableDesc table = this.metaDataManager.getTable(tableName);
		ClassDesc classDesc = table.genClassDesc(this.nc);
		List<String> idProperties = classDesc.getIdAttrs();
		if (idProperties.size() != 1) {
			throw new BeetlSQLException(BeetlSQLException.ID_EXPECTED_ONE_ERROR, "upsert方法期望只有一个主键");

		}
		Object pk = BeanKit.getBeanProperty(obj, idProperties.get(0));
		if (pk == null) {
			//插入
			if (template) {
				this.insertTemplate(obj);
			} else {
				this.insert(obj);
			}

			return true;
		}
		Object dbValue = this.single(c, pk);
		if (dbValue == null) {
			if (template) {
				this.insertTemplate(obj);
			} else {
				this.insert(obj);
			}

			return true;
		}

		//更新
		if (template) {
			this.updateTemplateById(obj);
		} else {
			this.updateById(obj);
		}
		return false;


	}


	/**
	 * 更新一个对象
	 *
	 * @param obj
	 * @return
	 */

	@Override
	public int updateById(Object obj) {
		Class target = obj.getClass();
		SQLExecutor script = getScript(target, UPDATE_BY_ID);
		return script.update(target, obj);
	}

	/**
	 * 为null的值不参与更新，如果想更新null值，请使用updateById
	 *
	 * @param obj
	 * @return 返回更新的条数
	 */

	@Override
	public int updateTemplateById(Object obj) {
		Class target = obj.getClass();
		SQLExecutor script = getScript(target, UPDATE_TEMPLATE_BY_ID);
		return script.update(target, obj);
	}

	/**
	 * @param c     c对应的表名
	 * @param paras 参数，仅仅更新paras里包含的值，paras里必须带有主键的值作为更新条件
	 * @return 返回更新的条数
	 */

	@Override
	public int updateTemplateById(Class c, Map paras) {
		SQLExecutor script = getScript(c, UPDATE_TEMPLATE_BY_ID);
		return script.update(c, paras);
	}

	/**
	 * 按照模板更新
	 *
	 * @param c
	 * @param obj
	 * @return
	 */

	@Override
	public int updateTemplateById(Class c, Object obj) {
		SQLExecutor script = getScript(c, UPDATE_TEMPLATE_BY_ID);
		return script.update(c, obj);
	}

	/****
	 * 批量更新
	 *
	 * @param list,包含同一个类型的pojo
	 * @return
	 */

	@Override
	public int[] updateByIdBatch(List<?> list) {
		if (list == null || list.isEmpty()) {
			return new int[0];
		}
		Class target = list.get(0).getClass();
		SQLExecutor script = getScript(list.get(0).getClass(), UPDATE_BY_ID);
		return script.updateBatch(target, list);
	}

	/**
	 * 执行sql更新（或者删除）操作
	 *
	 * @param sqlId
	 * @param obj
	 * @return 返回更新的条数
	 */

	@Override
	public int update(SqlId sqlId, Object obj) {
		SQLExecutor script = getScript(sqlId);
		return script.update(null, obj);
	}

	/**
	 * 执行sql更新（或者删除）操作
	 *
	 * @param sqlId
	 * @return 返回更新的条数
	 */

	@Override
	public int update(SqlId sqlId) {
		SQLExecutor script = getScript(sqlId);
		return script.update(null, null);
	}

	/**
	 * 执行sql更新（或者删除语句)
	 *
	 * @param sqlId
	 * @param paras
	 * @return 返回更新的条数
	 */

	@Override
	public int update(SqlId sqlId, Map<String, Object> paras) {
		SQLExecutor script = getScript(sqlId);
		return script.update(null, paras);
	}

	/**
	 * 对pojo批量更新执行sql更新语句，list包含的对象是作为参数，所有属性参与更新
	 *
	 * @param sqlId
	 * @param list
	 * @return 返回更新的条数
	 */

	@Override
	public int[] updateBatch(SqlId sqlId, List<?> list) {
		SQLExecutor script = getScript(sqlId);
		return script.updateBatch(list);
	}

	/**
	 * 批量模板更新方式，list包含的对象是作为参数，非空属性参与更新
	 *
	 * @param clz
	 * @param list
	 * @return
	 */

	@Override
	public int[] updateBatchTemplateById(Class clz, List<?> list) {
		SQLExecutor script = getScript(clz, UPDATE_TEMPLATE_BY_ID);
		return script.updateBatch(list);
	}


	/**
	 * 更新指定表
	 *
	 * @param clazz
	 * @param param 参数
	 * @return
	 */

	@Override
	public int updateAll(Class<?> clazz, Object param) {

		SQLExecutor script = getScript(clazz, UPDATE_ALL);
		return script.update(clazz, param);
	}


	/**
	 * 直接执行语句,sql是模板
	 *
	 * @param sqlTemplate
	 * @param clazz
	 * @param paras
	 * @return
	 */

	@Override
	public <T> List<T> execute(String sqlTemplate, Class<T> clazz, Object paras) {


		SqlId id = this.sqlIdFactory.buildTemplate(sqlTemplate);
		SQLSource source = sqlLoader.queryAutoSQL(id);
		if (source == null) {
			source = new SQLSource(id, sqlTemplate);
			source.setSqlType(SQLType.SELECT);
			this.sqlLoader.addSQL(id, source);
		}
		ExecuteContext executeContext = ExecuteContext.instance(this).initSQLSource(source);
		SQLExecutor script = dbStyle.buildExecutor(executeContext);
		return script.select(clazz, paras);
	}


	public TableDesc getTableDesc(String table) {
		return this.metaDataManager.getTable(table);
	}


	public ClassDesc getClassDesc(Class target) {
		String table = this.nc.getTableName(target);
		TableDesc desc = this.metaDataManager.getTable(table);
		return desc.genClassDesc(target, this.nc);
	}

	/**
	 * 直接执行sql查询语句，sql是模板
	 *
	 * @param sqlTemplate
	 * @param clazz
	 * @param paras
	 * @return
	 */

	@Override
	public <T> List<T> execute(String sqlTemplate, Class<T> clazz, Map paras) {
		SqlId id = this.sqlIdFactory.buildTemplate(sqlTemplate);
		SQLSource source = sqlLoader.queryAutoSQL(id);
		if (source == null) {
			source = new SQLSource(id, sqlTemplate);
			source.setSqlType(SQLType.SELECT);
			this.sqlLoader.addSQL(id, source);
		}
		ExecuteContext executeContext = ExecuteContext.instance(this).initSQLSource(source);
		SQLExecutor script = dbStyle.buildExecutor(executeContext);
		return script.select(clazz, paras);
	}

	/**
	 * 直接执行sql模版语句，sql是模板
	 *
	 * @param sqlTemplate
	 * @param clazz
	 * @param paras
	 * @param start
	 * @param size
	 * @return
	 */

	@Override
	public <T> List<T> execute(String sqlTemplate, Class<T> clazz, Object paras, Object start, long size) {
		SqlId id = this.sqlIdFactory.buildTemplate(sqlTemplate).toPage();
		SQLSource source = sqlLoader.queryAutoSQL(id);
		if (source == null) {
			String pageSql = this.dbStyle.getRangeSql().toTemplateRange(clazz, sqlTemplate);
			source = new SQLSource(id, pageSql);
			source.setSqlType(SQLType.SELECT);
			this.sqlLoader.addSQL(id, source);
		}

		Map pageParas = new HashMap();
		this.dbStyle.getRangeSql().addTemplateRangeParas(pageParas, start, size);
		ExecuteContext executeContext = ExecuteContext.instance(this).initSQLSource(source);
		SQLExecutor script = dbStyle.buildExecutor(executeContext);
		Map map = script.beforeExecute(clazz, paras, false);
		map.putAll(pageParas);
		return script.select(clazz, map);
	}


	/**
	 * sql 模板分页查询，记得使用page函数
	 *
	 * @param sqlTemplate select #page(*)# from user where name=#userName# ....
	 * @param clazz
	 * @param request
	 * @return
	 */

	@Override
	public <T> PageResult<T> executePageQuery(String sqlTemplate, Class<T> clazz, Object paras,
			PageRequest<T> request) {
		SqlId id = this.sqlIdFactory.buildTemplate(sqlTemplate);
		SQLSource source = sqlLoader.queryAutoSQL(id);
		if (source == null) {
			source = new SQLSource(id, sqlTemplate);
			source.setSqlType(SQLType.SELECT);
			this.sqlLoader.addSQL(id, source);
		}

		return this.pageQuery(id, clazz, paras, request);
	}


	/**
	 * 直接执行sql更新，sql是模板
	 *
	 * @param sqlTemplate
	 * @param paras
	 * @return
	 */
	@Override
	public int executeUpdate(String sqlTemplate, Object paras) {
		SqlId id = this.sqlIdFactory.buildTemplate(sqlTemplate);
		SQLSource source = sqlLoader.queryAutoSQL(id);
		if (source == null) {
			source = new SQLSource(id, sqlTemplate);
			source.setSqlType(SQLType.UPDATE);
			this.sqlLoader.addSQL(id, source);
		}

		ExecuteContext executeContext = ExecuteContext.instance(this).initSQLSource(source);
		SQLExecutor script = dbStyle.buildExecutor(executeContext);

		return script.update(null, paras);
	}


	/**
	 * 直接执行sql语句查询，sql语句已经是准备好的，采用preparedstatment执行
	 *
	 * @param clazz
	 * @param p
	 * @return 返回查询结果
	 */

	@Override
	public <T> List<T> execute(SQLReady p, Class<T> clazz) {
		SqlId id = this.sqlIdFactory.buildSql(p.getSql());
		return executeWithId(id, p, clazz);
	}

	@Override
	public <T> T executeQueryOne(SQLReady p, Class<T> clazz) {
		SqlId id = this.sqlIdFactory.buildSql(p.getSql());
		List<T> list = executeWithId(id, p, clazz);
		if (list.isEmpty()) {
			return null;
		} else if (list.size() == 1) {
			return list.get(0);
		} else {
			throw new BeetlSQLException(BeetlSQLException.UNIQUE_EXCEPT_ERROR, "unique查询，找到多条记录 " + list.size());
		}

	}

	private <T> List<T> executeWithId(SqlId id, SQLReady p, Class<T> clazz) {

		SQLSource source = new SQLSource(id, p.getSql());
		ExecuteContext executeContext = ExecuteContext.instance(this).initSQLSource(source);

		SQLExecutor script = dbStyle.buildExecutor(executeContext);
		return script.sqlReadySelect(clazz, p);
	}


	/**
	 * 本地分页查询
	 * @param p
	 * @param clazz
	 * @param pageRequest
	 * @param <T>
	 * @return
	 */
	@Override
	public <T> PageResult<T> execute(SQLReady p, Class<T> clazz, PageRequest<T> pageRequest) {

		String sql = p.getSql();
		Long count = null;
		List<T> list = null;
		if (pageRequest.isTotalRequired()) {
			String countSql = PageKit.getCountSql(sql);
			List<Long> countList = execute(new SQLReady(countSql, p.getArgs()), Long.class);
			count = countList.get(0);
			if (count == null || count == 0) {
				list = new ArrayList<>();
				return pageRequest.of(list, 0L);
			}


		}

		long pageSize = pageRequest.getPageSize();
		Object offset = pageRequest.getStart(offsetStartZero);
		String pageSql = this.dbStyle.getRangeSql().toRange(sql, offset, pageSize);

		SqlId id = this.sqlIdFactory.buildSql(p.getSql());
		id.type = SqlId.Type.page;
		list = executeWithId(id, new SQLReady(pageSql, p.getArgs()), clazz);
		return pageRequest.isTotalRequired() ? pageRequest.of(list, count) : pageRequest.of(list);


	}

	/**
	 * 直接执行sql语句，用于删除或者更新，sql语句已经是准备好的，采用preparedstatment执行
	 *
	 * @param p
	 * @return 返回更新条数
	 */

	@Override
	public int executeUpdate(SQLReady p) {
		SqlId id = this.sqlIdFactory.buildSql(p.getSql());
		SQLSource source = new SQLSource(id, p.getSql());
		source.setSqlType(SQLType.UPDATE);
		ExecuteContext executeContext = ExecuteContext.instance(this).initSQLSource(source);
		SQLExecutor script = dbStyle.buildExecutor(executeContext);
		return script.sqlReadyExecuteUpdate(p);
	}


	@Override
	public int[] executeBatchUpdate(SQLBatchReady batch) {
		SqlId id = this.sqlIdFactory.buildSql(batch.getSql());
		SQLSource source = new SQLSource(id, batch.getSql());
		source.setSqlType(SQLType.UPDATE);
		ExecuteContext executeContext = ExecuteContext.instance(this).initSQLSource(source);
		SQLExecutor script = dbStyle.buildExecutor(executeContext);
		return script.sqlReadyBatchExecuteUpdate(batch);
	}


	/**
	 * 自己用Connection执行jdbc，通常用于存储过程调用，或者需要自己完全控制的jdbc
	 *
	 * @param onConnection
	 * @return
	 */

	public <T> T executeOnConnection(OnConnection<T> onConnection) {
		Connection conn = null;
		onConnection.setSqlManagaer(this);
		try {
			conn = onConnection.getConn(getDs());
			return onConnection.call(conn);
		} catch (SQLException e) {
			throw new BeetlSQLException(BeetlSQLException.SQL_EXCEPTION, e);
		} finally {
			// 非事务环境提交
			if (!getDs().isTransaction()) {
				try {
					if (!conn.getAutoCommit()) {
						conn.commit();
					}
					conn.close();

				} catch (SQLException e) {
					throw new BeetlSQLException(BeetlSQLException.SQL_EXCEPTION, e);
				}

			}
		}
	}


	public SQLLoader getSqlLoader() {
		return sqlLoader;
	}

	public void setSqlLoader(SQLLoader sqlLoader) {
		this.sqlLoader = sqlLoader;
	}

	public ConnectionSource getDs() {
		return ds;
	}

	/**
	 * 设置ConnectionSource，参考ConnectionSourceHelper
	 *
	 * @param ds
	 */
	public void setDs(ConnectionSource ds) {
		this.ds = ds;
	}

	/**
	 * 获取 NameConversion
	 *
	 * @return
	 */
	public NameConversion getNc() {
		return nc;
	}

	/**
	 * 设置NameConversion
	 *
	 * @param nc
	 */
	public void setNc(NameConversion nc) {
		this.nc = nc;
		this.dbStyle.setNameConversion(nc);
	}

	/**
	 * 得到当前sqlmanager的数据库类型
	 *
	 * @return
	 */
	public DBStyle getDbStyle() {
		return dbStyle;
	}

	public void setDbStyle(DBStyle dbStyle) {
		this.dbStyle = dbStyle;

	}

	/**
	 * 得到sql模板引擎
	 *
	 * @return
	 */
	public SQLTemplateEngine getSqlTemplateEngine() {
		return this.sqlTemplateEngine;
	}

	/**
	 * 得到MetaDataManager，用来获取数据库元数据，如表，列，主键等信息
	 *
	 * @return
	 */
	public MetadataManager getMetaDataManager() {
		return metaDataManager;
	}

	public void setMetaDataManager(MetadataManager metaDataManager) {
		this.metaDataManager = metaDataManager;
	}

	/**
	 * 得到所有的Interceptor
	 *
	 * @return
	 */
	public Interceptor[] getInters() {
		return inters;
	}

	/**
	 * 设置Interceptor
	 *
	 * @param inters
	 */
	public void setInters(Interceptor[] inters) {
		this.inters = inters;
	}

	/**
	 * 设置一种id算法用于注解AssignId("xxx"),这样，对于应用赋值主键，交给beetlsql来处理了
	 *
	 * @param name
	 * @param alorithm
	 */
	public void addIdAutoGen(String name, IDAutoGen alorithm) {
		this.idAutoGenMap.put(name, alorithm);
	}

	/**
	 * 根据某种算法自动计算id
	 *
	 * @param name
	 * @param param
	 * @return
	 */
	protected Object getAssignIdByIdAutoGen(String name, String param, String table) {
		IDAutoGen idGen = idAutoGenMap.get(name);
		if (idGen == null) {
			throw new BeetlSQLException(BeetlSQLException.ID_AUTOGEN_ERROR, "未发现自动id生成器:" + name + " in " + table);
		}
		return idGen.nextID(param);

	}

	/**
	 * 获取特殊的BeanPorcessor
	 *
	 * @return
	 */
	public Map<String, BeanProcessor> getProcessors() {
		return processors;
	}

	/**
	 * 为指定的sqlId提供一个处理类，可以既可以是一个sqlId，也可以是namespace部分，
	 * 所有属于namesapce的都会被此BeanProcessor 处理
	 *
	 * @param processors
	 */
	public void setProcessors(Map<String, BeanProcessor> processors) {
		this.processors = processors;
	}

	/**
	 * 得到默认的jdbc到bean的处理类
	 *
	 * @return
	 */
	public BeanProcessor getDefaultBeanProcessors() {
		return defaultBeanProcessors;
	}

	/**
	 * 设置默认的jdbc 到 bean的映射处理类，用户可以自己扩展处理最新的类型
	 *
	 * @param defaultBeanProcessors
	 */
	public void setDefaultBeanProcessors(BeanProcessor defaultBeanProcessors) {
		this.defaultBeanProcessors = defaultBeanProcessors;
	}

	public <T> T getMapper(Class<T> mapperInterface) {
		return mapperBuilder.getMapper(mapperInterface);
	}

	public ClassLoaderKit getClassLoaderKit() {
		if (classLoaderKit == null) {
			this.classLoaderKit = new ClassLoaderKit();

		}
		return classLoaderKit;
	}

	/**
	 * 设置classloder，如果没有，pojo的初始化使用ContextClassLoader或者加载Beetlsql的classLoader
	 *
	 * @param classLoaderKit
	 */
	public void setClassLoaderKit(ClassLoaderKit classLoaderKit) {
		this.classLoaderKit = classLoaderKit;

	}

	/**
	 * 为不存在的表设置一个数据库真正的表，以用于获取metadata
	 * 主要用于数据库分库分表
	 *
	 *
	 * @param virtualTable
	 * @param realTable
	 */
	public void addVirtualTable(String realTable, String virtualTable) {
		this.metaDataManager.addTableVirtual(realTable, virtualTable);
	}

	public SqlIdFactory getSqlIdFactory() {
		return sqlIdFactory;
	}

	public void setSqlIdFactory(SqlIdFactory sqlIdFactory) {
		this.sqlIdFactory = sqlIdFactory;
	}

	public String getCharset() {
		return charset;
	}

	public void setCharset(String charset) {
		this.charset = charset;
	}

	public boolean isProduct() {
		return isProduct;
	}

	public void setProduct(boolean product) {
		isProduct = product;
	}

	public void setSQLTemplateEngine(SQLTemplateEngine sqlTemplateEngine) {
		this.sqlTemplateEngine = sqlTemplateEngine;
	}

	public SQLManager use(String name) {
		throw new UnsupportedOperationException("需要使用MultipleSQLManager子类");
	}

	public String getName() {
		return this.name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public void register() {
		if (this.getName() == null) {
			throw new IllegalStateException("必须设定一个名字 ");
		}
		SQLManagerBuilder.sqlManagerMap.put(this.getName(), this);
	}

	public void addSqlManagerInGroup(String name, SQLManager sqlManager) {
		this.group.put(name, sqlManager);
	}

	public SQLManager sqlManagerInGroup(String name) {
		return this.group.get(name);
	}

}
