package com.hotent.base.interceptor;

import com.hotent.base.util.BeanUtils;
import net.sf.jsqlparser.parser.CCJSqlParserManager;
import net.sf.jsqlparser.util.TablesNamesFinder;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.springframework.util.Assert;

import java.io.StringReader;
import java.lang.reflect.Field;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

/**
 * MyBatis的返回结果拦截器
 *
 * @company 广州宏天软件股份有限公司
 * @author heyifan
 * @email heyf@jee-soft.cn
 * @date 2020年7月27日
 */
@Intercepts({@Signature(type=ResultSetHandler.class,method="handleResultSets",args={Statement.class})})
public class ResultSetInterceptor implements Interceptor{
	private static CCJSqlParserManager sqlParserManager = new CCJSqlParserManager();
	private static TablesNamesFinder tablesNamesFinder = new TablesNamesFinder();

	private List<ResultSetFilter> filters = new ArrayList<>();

	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		try {
			Object target = invocation.getTarget();
			MappedStatement ms = getByPropertyName(target, "mappedStatement");
			BoundSql boundSql = getByPropertyName(target, "boundSql");
			// MappedStatement ID
			String id = ms.getId();
			String sql = boundSql.getSql();
			// sql中的表名
			List<String> tableNames = getTableNames(sql);
			// 对过滤链进行排序
			// Collections.sort(this.filters, Comparator.comparing(ResultSetFilter::getOrder));

			Object result = invocation.proceed();
			// 遍历所有过滤链
			this.filters.forEach(f -> {
				// 判断当前过滤器是否支持该sql
				if(f.support(id, sql, tableNames)) {
					// 对结果集进行处理
					f.handle(result);
				}
			});
			return result;
		}
		catch(Exception e) {
			e.printStackTrace();
		}
		return invocation.proceed();
	}

	@Override
	public Object plugin(Object target) {
		if(target instanceof ResultSetHandler) {
			if(BeanUtils.isEmpty(this.filters)) {
				return target;
			}
			return Plugin.wrap(target, this);
		}
		return target;
	}

	/**
	 * 通过对象中指定属性名的属性对象
	 * @param <C>
	 * @param obj
	 * @param name
	 * @return
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public <C> C getByPropertyName(Object obj, String name) throws Exception {
		Class<?> c = obj.getClass();
		Field f = c.getDeclaredField(name);
		f.setAccessible(true);
		return (C)f.get(obj);
	}

	/**
	 * 从SQL语句中提取表名
	 * @param sql
	 * @return
	 * @throws Exception
	 */
    public List<String> getTableNames(String sql) throws Exception {
		net.sf.jsqlparser.statement.Statement statement = sqlParserManager.parse(new StringReader(sql));
		return tablesNamesFinder.getTableList(statement);
	}

	public void setFilters(List<ResultSetFilter> filters) {
		Assert.notNull(filters, "filters can not be empty.");
		this.filters = filters;
	}

	/**
	 * 添加过滤器
	 * @param filter
	 */
	public void addFilters(ResultSetFilter filter) {
		Assert.notNull(filter, "filters can not be empty.");
		this.filters.add(filter);
	}
}
