package com.artfess.dataAccess.aop;

import com.artfess.base.util.AuthenticationUtil;
import com.artfess.base.util.BeanUtils;
import com.artfess.base.util.StringUtil;
import com.artfess.dataAccess.manager.DataAccessDefinedManager;
import com.artfess.uc.model.User;
import com.artfess.uc.util.ContextUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

/**
 * 数据权限过滤
 * 这里处理update 和 delete 语句  select 语句放在切面处理  可以改为拦截 Executor.update 方法
 * @author liygui
 */
@Intercepts({@Signature(type=StatementHandler.class,method="prepare",args={Connection.class,Integer.class})})
public class DataAccessInterceptor implements Interceptor{

	private Logger logger = LoggerFactory.getLogger(DataAccessInterceptor.class);

	@Autowired
	@Lazy
	DataAccessDefinedManager dataAccessDefinedManager;

	@SuppressWarnings("unchecked")
	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		StatementHandler statementHandler = (StatementHandler)invocation.getTarget();
	    // 通过MetaObject优雅访问对象的属性，这里是访问statementHandler的属性
	    MetaObject metaObject = MetaObject.forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY, SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY, new DefaultReflectorFactory());
	    // 先拦截到RoutingStatementHandler，里面有个StatementHandler类型的delegate变量，其实现类是BaseStatementHandler，然后就到BaseStatementHandler的成员变量mappedStatement
	    MappedStatement mappedStatement = (MappedStatement)metaObject.getValue("delegate.mappedStatement");

	    BoundSql boundSql = statementHandler.getBoundSql();
	    // 原始的SQL语句
	    String sql = boundSql.getSql();
	    // 改造后带过滤条件的sql
		metaObject.setValue("delegate.boundSql.sql", sql);

	    SqlCommandType commondType = mappedStatement.getSqlCommandType();
	    if (commondType.compareTo(SqlCommandType.SELECT) == 0) {
	    	// 查询语句在这里不处理 在切面中已经处理了
	    	return invocation.proceed();
	    }

		ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
		if(attributes==null){
			return invocation.proceed();
		}
		HttpServletRequest request = attributes.getRequest();
		String url = request.getAttribute("url").toString();
		if (StringUtils.isNotEmpty(url)) {
			//根据请求url查询登录人员的数据权限
			List<Map<String, Object>> maps=dataAccessDefinedManager.getDataAccessDefinedList(url);

			if(BeanUtils.isEmpty(maps)){
				// 没有配置数据权限设置的不处理
				return invocation.proceed();
			}

			StringBuffer sqlWhere = new StringBuffer();
			//根据权限配置组装新的sql查询条件
			for (Map<String, Object> item : maps) {
				//1：本人  2：本人及直属下级 3：本组织数据 4：本组织及以下组织数据 5 指定组织 6 指定用户 7：所有
				String type = item.get("accessRangeType").toString();
				String accessRangeId = item.get("accessRangeId").toString();
				if ("1".indexOf(type) != -1) {
					User user= ContextUtil.getCurrentUser();
					if(sqlWhere!=null && StringUtil.isNotEmpty(sqlWhere.toString())){
						sqlWhere.append(" or ");
					}
					sqlWhere.append(" CREATE_BY_ = '").append(user.getId()).append("'");
				}else if ("2".indexOf(type) != -1) {
					List<User> list = ContextUtil.getCurrentUserUnder();
					accessRangeId = AuthenticationUtil.getCurrentUserId();
					if(list!=null && list.size()>0){
						for(User user : list){
							if(user!=null){
								accessRangeId = accessRangeId+","+user.getId();
							}
						}
					}
					if(sqlWhere!=null && StringUtil.isNotEmpty(sqlWhere.toString())){
						sqlWhere.append(" or ");
					}
					sqlWhere.append(" CREATE_BY_ in '( ").append(String.join("','", accessRangeId)).append(")' ");
				}else if ("3".indexOf(type) != -1) {
					if(sqlWhere!=null && StringUtil.isNotEmpty(sqlWhere.toString())){
						sqlWhere.append(" or ");
					}
					sqlWhere.append(" CREATE_ORG_ID_ in '( ").append(String.join("','", accessRangeId)).append(")' ");
				}else if ("4".indexOf(type) != -1) {
					String subOrgIds = AuthenticationUtil.getCurrentUserId();
					if(sqlWhere!=null && StringUtil.isNotEmpty(sqlWhere.toString())){
						sqlWhere.append(" or ");
					}
					sqlWhere.append(" CREATE_ORG_ID_  in '( ").append(String.join("','", subOrgIds)).append(")' ");

				}else if ("5".indexOf(type) != -1) {
					if(sqlWhere!=null && StringUtil.isNotEmpty(sqlWhere.toString())){
						sqlWhere.append(" or ");
					}
					sqlWhere.append(" CREATE_ORG_ID_ in '( ").append(String.join("','", accessRangeId)).append(")' ");

				}else if ("6".indexOf(type) != -1) {
					if(sqlWhere!=null && StringUtil.isNotEmpty(sqlWhere.toString())){
						sqlWhere.append(" or ");
					}
					sqlWhere.append(" CREATE_BY_ in '( ").append(String.join("','", accessRangeId)).append(")' ");
				}
			}

			if(sqlWhere!=null && StringUtil.isNotEmpty(sqlWhere.toString())){
				sql = sql + " and ( " + sqlWhere + ") "  ;
			}

			logger.debug(" custom sql " + sql );
			// 改造后带过滤条件的sql
			metaObject.setValue("delegate.boundSql.sql", sql);
		}
		return invocation.proceed();
	}

	@Override
	public Object plugin(Object target) {
		return Plugin.wrap(target, this);
	}

	@Override
	public void setProperties(Properties properties) {
		logger.debug(" properties " + properties.toString());
	}

	/**
	 *
	 * @param field
	 * @param orgIds
	 * @return  ( field in (1, 2,3 ) or field in (4,5,6) )
	 */
	private String getInSql( String field, Set<String> orgIds ){

		StringBuffer sb = new StringBuffer(" (");

		Iterator<String> iterator = orgIds.iterator();
		List<String> list = new ArrayList<String>();
		int i =1;
		while (iterator.hasNext()) {
			String next = iterator.next();
			if(i>500){
				sb.append( field + " in (" +  String.join(",", list) +")");
				list =  new ArrayList<String>();
				i=1;
			}
			list.add(next);
			i++;
		}

		if(BeanUtils.isNotEmpty(list)){
			sb.append( field + " in (" +  String.join(",", list) +")");
		}

		sb.append(")");
		return sb.toString();
	}

}
