package com.elab.core.spring.binding;

import com.elab.core.aop.annotations.XmlGroupName;
import com.elab.core.bean.PageModel;
import com.elab.core.dao.BasicBaseDao;
import com.elab.core.dao.IBaseDaoSupport;
import com.elab.core.dao.IExampleDaoSupport;
import com.elab.core.dao.model.ListDynamicSearch;
import com.elab.core.dao.row.JoinRowMapper;
import com.elab.core.spring.base.EntityOperation;
import com.elab.core.spring.common.SqlInfoModel;
import com.elab.core.spring.common.TypeGroupModel;
import com.elab.core.spring.common.utils.BeanUtils;
import com.elab.core.spring.common.utils.DataUtils;
import com.elab.core.spring.method.DefaultSQLBuilderSupport;
import com.elab.core.spring.method.ISQLBuliderSupport;
import com.elab.core.spring.method.ProxyDataProcess;
import com.elab.core.sql.ConfigurableFactory;
import com.elab.core.sql.config.SqlCommandType;
import com.elab.core.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.RowMapper;

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * dao层方法拦截根据指定的类型调用方法
 * <p>
 * 具体dao类执行的方法,根据方法名进行判断类型
 * </p>
 *
 * @author Liukx
 * @create 2018-01-03 11:36
 * @email liukx@elab-plus.com
 **/
public class DaoMethod {

    /**
     * 具体的执行接口
     */
    private Class<?> mapperInterface;

    /**
     * 执行的方法
     */
    private Method method;

    /**
     * DB层的SQL执行器
     */
    private BasicBaseDao basicBaseDao;

    /**
     * 父类的构造参数
     */
    private Class<?> supperGenricType;

    /**
     * 父类的接口
     */
    private String supperInterfaceName;

    /**
     * entity的操作对象
     */
    private EntityOperation entityOperation;

    /**
     * 配置文件对象,将sql.xml描述成对象
     */
    private ConfigurableFactory configurableFactory;

    /**
     * example类型 例如UserExample
     */
    private Class<?> exampleType;

    /**
     * 自动生成的SQL对象接口
     */
    private List<ISQLBuliderSupport> sqlBuilderSupportList = new ArrayList<ISQLBuliderSupport>();

    /**
     * 缓存一些反射的信息
     */
    private Map<String, Class<?>> methodReturnCacheMap = new ConcurrentHashMap<String, Class<?>>();

    /**
     * 默认的API接口描述
     */
    private final String defaultSupperInterface = IBaseDaoSupport.class.getName();

    /**
     * 默认的exaple的API接口对象
     */
    private final String defaultExampleInterface = IExampleDaoSupport.class.getName();

    /**
     * 当前类的sql文件中的groupName对象
     */
    private String xmlGroupName;

    private ProxyDataProcess proxyDataProcess;

    private Logger logger = LoggerFactory.getLogger(DaoMethod.class);

    /**
     * 构建一个DaoMethod的方法对象
     *
     * @param <T>
     * @param mapperInterface     dao的接口類
     * @param method              方法信息
     * @param basicBaseDao        jdbc操作类
     * @param configurableFactory 配置工厂
     * @param proxyDataProcess
     */
    public <T> DaoMethod(Class<?> mapperInterface, Method method, BasicBaseDao basicBaseDao, ConfigurableFactory configurableFactory, ProxyDataProcess proxyDataProcess) {
        // 接口信息
        this.mapperInterface = mapperInterface;
        // 方法信息
        this.method = method;
        // 具体操作类
        this.basicBaseDao = basicBaseDao;
        // 配置工厂
        this.configurableFactory = configurableFactory;
        //默认先将初始化的sql构建加入进来
        this.sqlBuilderSupportList.add(new DefaultSQLBuilderSupport());
        // 类的信息提取
        // 父类的接口类
        this.supperInterfaceName = mapperInterface.getGenericInterfaces()[0].getTypeName();
        // 泛型对象
        List<String> classGenricTypeString = BeanUtils.getClassGenricTypeString(this.supperInterfaceName);
        // 第一个db对象
        this.supperGenricType = BeanUtils.getClassByString(classGenricTypeString.get(0));
        // 第二个,案例对象
        this.exampleType = classGenricTypeString.size() > 1 ? BeanUtils.getClassByString(classGenricTypeString.get(1)) : null;
        // 代理数据执行器
        this.proxyDataProcess = proxyDataProcess;
        // 实体操作类
        this.entityOperation = new EntityOperation(this.supperGenricType, "id");
        if (this.mapperInterface.isAnnotationPresent(XmlGroupName.class)) {
            this.xmlGroupName = this.mapperInterface.getAnnotation(XmlGroupName.class).value();
        }
    }

    /**
     * 具体执行sql的方法
     * 1. 判断sql的类型
     * 2. 根据类型判断调用的方法
     * 3.
     *
     * @param args
     * @return
     */
    public Object execute(Object[] args) throws Exception {
        int argsLength = args.length;
        String sqlSource = method.getName();
        String statementName = getSqlId();
        String tableName = this.entityOperation.tableName;
//        // TODO 待优化 , 可以获取该方法的全称,然后去解析里面的参数名称
//        if (argsLength != 1) {
//            throw new IllegalArgumentException(sqlSource + " 方法 参数尽量保持在1个,可以是Map,可以是Object");
//        }
        Object arg = args[0];
        SqlCommandType sqlType = this.configurableFactory.getSqlConfigurableFactory().getSqlType(statementName);

        if (sqlType == null) {
            // 表示配置文件中没有找到,则会从父类接口去获取
            if (!StringUtils.isEmpty(this.supperInterfaceName)) {
                if (this.supperInterfaceName.startsWith(defaultSupperInterface)) {
                    // 如果是其他增删改类型的方法
                    if (createSql(statementName)) {
                        return execute(args);
                    }
                }

                if (this.defaultExampleInterface.startsWith(defaultExampleInterface)) {
                    // 如果是查找类型的方法
                    if ("find".equals(method.getName())) {
                        return findMethodInvoke(arg);
                    }
                }

            }
            // 抛出异常
            throw new Exception(" 找不到[" + statementName + "]对应的处理sql");
        } else {
            String sql = this.configurableFactory.getSqlConfigurableFactory().getSql(statementName);
            logger.debug(" SQL ID : " + statementName);
            if (sqlType == SqlCommandType.INSERT) {
                if (arg instanceof List) {
                    return this.basicBaseDao.batchOperation(sqlType, statementName, tableName, sql, (List) arg);
                } else {
                    return this.basicBaseDao.insert(statementName, tableName, sql, arg);
                }
            } else if (sqlType == SqlCommandType.UPDATE || sqlType == SqlCommandType.DELETE) {
                if (arg instanceof List) {
                    return this.basicBaseDao.batchOperation(sqlType, statementName, tableName, sql, (List) arg);
                }
                return this.basicBaseDao.update(statementName, tableName, sql, arg);
            } else {
                if (sqlType != SqlCommandType.SELECT) {
                    System.out.println("语句类型有误!");
                    return null;
                }
                return selectInvokeMethod(statementName, sql, args);
            }
        }

    }

    /**
     * 获取sql文件中的对应sql编号
     *
     * @return
     */
    private String getSqlId() {
        String statementName;
        if (StringUtils.isEmpty(this.xmlGroupName)) {
            statementName = mapperInterface.getName() + "." + method.getName();
        } else {
            statementName = this.xmlGroupName + "." + method.getName();
        }
        return statementName;
    }

    /**
     * 执行特殊的find的方法处理过程
     *
     * @param arg
     * @return
     */
    private Object findMethodInvoke(Object arg) {
        List oredCriteriaList = (List) BeanUtils.invokegetGetterMethod(arg, "oredCriteria");
        List<Object> paramsList = new LinkedList<Object>();
        StringBuffer sb = new StringBuffer();
        sb.append("select " + entityOperation.allColumn + " from " + entityOperation.tableName + " ");

        if (oredCriteriaList.size() > 0) {
            for (int i = 0; i < oredCriteriaList.size(); i++) {
                Object o = oredCriteriaList.get(i);
                List criteriaList = (List) BeanUtils.invokegetGetterMethod(o, "criteria");
                boolean flag = true;
                for (int j = 0; j < criteriaList.size(); j++) {
                    if (flag) {
                        sb.append(" where ");
                        flag = false;
                    } else {
                        sb.append(" and ");
                    }
                    Object o2 = criteriaList.get(j);
                    // key 条件
                    String condition = (String) BeanUtils.invokegetGetterMethod(o2, "condition");
                    // 对应的值
                    Object valueObject = BeanUtils.invokegetGetterMethod(o2, "value");
                    Object secondValue = BeanUtils.invokegetGetterMethod(o2, "secondValue");
                    // 如果是在集合的话需要单独处理  比如 in
                    if (valueObject instanceof List) {
                        List valueObject1 = (List) valueObject;
                        sb.append(condition + " ( ");
                        flag = false;
                        for (int k = 0; k < valueObject1.size(); k++) {
                            if (flag) {
                                sb.append(" , ");
                            }
                            Object o1 = valueObject1.get(i);
                            paramsList.add(o1);
                            sb.append(" ? ");
                            flag = true;
                        }
                        sb.append(" )");
                    } else {

                        sb.append(condition);

                        // is null , is not null
                        if (!com.elab.core.utils.ObjectUtils.isEmpty(valueObject)) {
                            paramsList.add(valueObject);
                            sb.append(" ? ");
                        }

                        // between 的情况
                        if (!com.elab.core.utils.ObjectUtils.isEmpty(secondValue)) {
                            sb.append(" and ? ");
                            paramsList.add(secondValue);
                        }
                    }
                }
            }
        }
        Object[] objects = paramsList.toArray();
        logger.debug(" SQL - " + sb.toString() + " \n " + Arrays.toString(objects));
        return this.basicBaseDao.getJdbcTemplate().query(sb.toString(), objects, entityOperation.rowMapper);
    }


    /**
     * 删除第一个and
     *
     * @param sql
     * @return
     */
    private String removeFirstAnd(String sql) {
        if (StringUtils.isEmpty(sql)) {
            return sql;
        }
        return sql.trim().toLowerCase().replaceAll("^\\s*and", "") + " ";
    }


    /**
     * 查询执行方法
     *
     * @param statementName SQL的唯一编号
     * @param sql           SQL执行的内容
     * @param args          参数列表
     * @return
     */
    private Object selectInvokeMethod(String statementName, String sql, Object[] args) throws Exception {
        Class<?> returnType = this.method.getReturnType();
        TypeGroupModel paramsByType = DataUtils.getParamsByType(this.method, args);
        Object arg = args[0];
        // 确定最终要执行的入参对象
        // 这里的处理方式就是要么就是Map、要么就是Object、要么就是普通数组
        if (paramsByType.getMap() != null && paramsByType.getMap().size() > 0) {
            arg = paramsByType.getMap();
        } else if (paramsByType.getObj() != null && paramsByType.getObj().length > 0) {
            arg = paramsByType.getObj()[0];
        }
        // 构建SQL相关的对象信息
        SqlInfoModel sqlInfo = getSqlInfo(statementName, sql, returnType, arg, paramsByType);
        // 这里决定是否代理
        Object proxy = proxyDataProcess.isProxy(sqlInfo);
        if (proxy != null) {
            logger.debug("此次结构被代理 , 触发代理的规则 : " + proxy);
            return proxyDataProcess.proxyQueryData(proxy, sqlInfo);
        } else {
            return processSelectSQL(sql, returnType, paramsByType, arg);
        }
    }

    /**
     * 执行查询SQL
     *
     * @param sql          SQL内容
     * @param returnType   SQL的返回结果
     * @param paramsByType SQL的特殊参数对象
     * @param arg          SQL的正常参数对象
     * @return
     * @throws Exception
     */
    private Object processSelectSQL(String sql, Class<?> returnType, TypeGroupModel paramsByType, Object arg) throws Exception {
        boolean returnsMany = (returnType == List.class);
        boolean returnsMap = (returnType == Map.class);
        boolean returnPage = (returnType == PageModel.class);
        // 如果需要追加动态参数对象处理
        ListDynamicSearch listDynamicSearch = paramsByType.getListDynamicSearch();
        // 是否是返回多个对象,例如List
        if (returnsMany) {
            Class typeClass = getCacheMethodReturnClass();
            if (typeClass == Map.class) {
                return this.basicBaseDao.executeQueryForListMap(sql, arg, listDynamicSearch);
            } else {
                RowMapper rowMapper = paramsByType.getRowMapper();
                if (rowMapper == null) {
                    return this.basicBaseDao.executeQueryList(sql, arg, listDynamicSearch, typeClass);
                } else {
                    return this.basicBaseDao.executeQueryMapper(sql, arg, rowMapper);
                }
            }
        }
        // 返回是Map的
        else if (returnsMap) {
            return this.basicBaseDao.executeQueryMap(sql, arg);
        }
        // 返回是分页对象的
        else if (returnPage) {
            arg = paramsByType.getObj().length == 0 ? paramsByType.getMap() : paramsByType.getObj()[0];
            Class typeClass = getCacheMethodReturnClass();
            PageModel pageModel = paramsByType.getPageModel();

            if (pageModel == null) {
                throw new Exception(" PageModel is not null !!!");
            }

            String sqlCount = "select count(1) from ( " + sql + " ) a";
            int count = this.basicBaseDao.executeQueryCount(sqlCount, arg);
            pageModel.setRowTotal(count);
            String sqlLimit = " " + sql + " " + pageModel.getOrderby() + "  limit " + (pageModel.getCount() - 1) * pageModel.getPageSize() + "," + pageModel.getPageSize();
            List list = null;
            if (typeClass == Map.class) {
                list = this.basicBaseDao.executeQueryForListMap(sqlLimit, arg, listDynamicSearch);
            } else {
                list = this.basicBaseDao.executeQueryList(sqlLimit, arg, typeClass);
            }
            pageModel.setResultSet(list);
            return pageModel;
        } else if (returnType == Integer.class || returnType == Long.class) {
            return this.basicBaseDao.executeQueryCount(sql, arg, listDynamicSearch, returnType);
        }
        // 返回是对象的
        else {
            if (returnType == Object.class) {
                returnType = supperGenricType;
            }
            JoinRowMapper<?> joinRowMapper = JoinRowMapper.newInstance(basicBaseDao, this.configurableFactory, returnType);
            return this.basicBaseDao.executeQueryObject(sql, arg, joinRowMapper);
        }
    }

    /**
     * 构建SQL相关的信息对象
     *
     * @param statementName SQL的id
     * @param sql           SQL的原始内容
     * @param returnType    SQL的返回类型
     * @param arg           SQL的参数
     * @param paramsByType  SQL的特殊参数组对象
     * @return
     */
    private SqlInfoModel getSqlInfo(String statementName, String sql, Class<?> returnType, Object arg,
                                    TypeGroupModel paramsByType) {
        SqlInfoModel sqlInfoModel = new SqlInfoModel();
        sqlInfoModel.setTableName(this.entityOperation.tableName);
        sqlInfoModel.setArgs(arg);
        sqlInfoModel.setSql(sql);
        sqlInfoModel.setReturnType(returnType);
        sqlInfoModel.setSqlId(statementName);
        sqlInfoModel.setReturnClass(getCacheMethodReturnClass());
        sqlInfoModel.setParamGroup(paramsByType);
        sqlInfoModel.setMapType(this.method.getGenericReturnType());
        return sqlInfoModel;
    }


    /**
     * 获取方法返回值
     *
     * @return
     */
    private Class getCacheMethodReturnClass() {
        String methodName = this.method.getName();
        Class typeClass = methodReturnCacheMap.get(methodName);
        if (typeClass == null) {
            Type type = this.method.getGenericReturnType();
            typeClass = BeanUtils.getClassGenricType(type.getTypeName());
            // 可能是父类inteface的方法的情况
            if (typeClass == null) {
                typeClass = this.supperGenricType == null ? Map.class : this.supperGenricType;
            } else {
                methodReturnCacheMap.put(methodName, typeClass);
            }
        }
        return typeClass;
    }

    /**
     * 创建sql的执行方法
     *
     * @param statementName
     * @return
     * @throws Exception
     */
    private boolean createSql(String statementName) throws Exception {
        List<ISQLBuliderSupport> sqlBuliderSupportList = getSqlBuilderSupportList();
        String sql = "";
        boolean isSQLNotNull = false;
        for (int i = 0; i < sqlBuliderSupportList.size(); i++) {
            // 执行每个sql构建器来匹配
            ISQLBuliderSupport sqlBuliderSupport = sqlBuliderSupportList.get(i);
            sql = sqlBuliderSupport.doCreateSQL(entityOperation, method);
            if (!StringUtils.isEmpty(sql)) {
                isSQLNotNull = true;
                break;
            }
        }

        // 注册到sql容器中
        this.configurableFactory.getSqlConfigurableFactory().registerSqlMap(statementName, sql);
        return isSQLNotNull;
    }


    public List<ISQLBuliderSupport> getSqlBuilderSupportList() {
        return sqlBuilderSupportList;
    }

    public void setSqlBuilderSupportList(List<ISQLBuliderSupport> sqlBuilderSupportList) {
        this.sqlBuilderSupportList = sqlBuilderSupportList;
    }
}
