package com.engine.htxc.commonutil.db;

import com.alibaba.fastjson.JSON;
import com.engine.htsc.payment.util.PayUtil;
import com.engine.integration.util.StringUtils;
import com.google.common.base.CaseFormat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import weaver.conn.RecordSet;
import weaver.conn.RecordSetTrans;
import weaver.general.Util;
import weaver.interfaces.htsc.GlobatTools.JAVATools;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Title: ecology-9
 * @Company: 泛微软件
 * @author: K1810050
 * @version: 1.0
 * @CreateDate: 2022/8/23 15:40
 * @Description: 实体类增删改查接口
 * @ModifyLog:
 **/
public class EntityCrudUtil {


    public static final String PKEY = "id";


    private static final Logger logger= LoggerFactory.getLogger(EntityCrudUtil.class);

    /**
     * 系统管理员id 1
     */
    public static final int SYSTEM_USER_ID = 1;
    /**
     * 默认用户类型 0
     */
    private static final int DEFAULT_CREATER_TYPE = 0;

    public static SqlContext getSqlContext(String tablename, Object entity, Class<?> entityClass){
        List<String> columns=new ArrayList<>();
        List<Object> values=new ArrayList<>();
        List<String> allColumns=new ArrayList<>();
        getColumnsAndValues(entity,true,entityClass,columns,values,allColumns);
        return new SqlContext(tablename,columns,values,allColumns);
    }

    public static boolean transInsert(RecordSetTrans rst, String tablename, Object entity, Class<?> entityClass) throws Exception {
        if(StringUtils.isBlank(tablename) || entity==null){
            return false;
        }
        final SqlContext sqlContext = getSqlContext(tablename, entity, entityClass);
        final String sql = buildInsertSql(sqlContext);
        logger.info("sql:[{}]",sql);
        logger.info("param:[{}]", JSON.toJSONString(sqlContext.getValues()));
        if(sqlContext.getValues().isEmpty()){
            return false;
        }
        return rst.executeUpdate(sql,sqlContext.getValues().toArray());
    }

    public static String buildInsertSql(SqlContext sqlContext){
        return " insert into "+sqlContext.getTableName() +"("+ sqlContext.getColumns().stream().map(EntityCrudUtil::camalCase2underscore).collect(Collectors.joining(",")) +") values("+String.join(",",Collections.nCopies(sqlContext.getValues().size(),"?"))+")";
    }

    /**
     * 插入实体类
     * @param tablename
     * @param entity
     * @param entityClass
     * @return
     */
    public static boolean  insertEntity(String tablename, Object entity, Class<?> entityClass){
        if(StringUtils.isBlank(tablename) || entity==null){
            return false;
        }
        final SqlContext sqlContext = getSqlContext(tablename, entity, entityClass);
        if(sqlContext.getValues().isEmpty()){
            return false;
        }
        final RecordSet rs = new RecordSet();
        return  rs.executeUpdate(buildInsertSql(sqlContext),sqlContext.getValues());
    }
    private static String getMethodName(String str) {
        char[] cs=str.toCharArray();
        cs[0]-=32;
        return String.valueOf(cs);
    }
    private static void getColumnsAndValues(Object entity, boolean ignoreNull, Class<?> entityClass, List<String> columns, List<Object> values, List<String> allColumns) {
        Arrays.stream(entityClass.getDeclaredFields()).forEach(field -> {
            //获取值
            final Method method;
            try {
                method = entityClass.getMethod("get"+getMethodName(field.getName()));
                final Object value = method.invoke(entity);
                if(!ignoreNull||value!=null){
                    //获取列
                    columns.add(field.getName());
                    values.add(value);
                }
                allColumns.add(field.getName());

            } catch (NoSuchMethodException|InvocationTargetException|IllegalAccessException e) {
                logger.error("获取列失败",e);
            }

        });
    }

    /**
     * 根据id删除
     * @param tablename
     * @param clazz
     * @param entity
     * @return
     */
    public Boolean deleteById(String tablename,Class<?> clazz,Object entity){
        if (StringUtils.isBlank(tablename)) {
            return false;
        }
        try {
            String id  = Util.null2String(clazz.getMethod("get" + getMethodName(PKEY)).invoke(entity));
            if (StringUtils.isBlank(id)) {
                return false;
            }
            Map<String, String> wheremap = new HashMap<>(1);
            wheremap.put(PKEY, id);
            return  JAVATools.deletesql(tablename, wheremap);
        } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            logger.error("deleteById error:",e);
        }
        return false;
    }

    /**
     * 根据id更新
     * @param tablename
     * @param clazz
     * @param entity
     * @return
     */
    public Boolean updateById(String tablename,Class<?> clazz,Object entity) {
        if(StringUtils.isBlank(tablename)){
            return false;
        }
        try {
            Field[] fields = clazz.getDeclaredFields();
            String id  = Util.null2String(clazz.getMethod("get" + getMethodName(PKEY)).invoke(entity));
            if (StringUtils.isBlank(id)) {
                return false;
            }
            Map<String, String> fieldMap = new HashMap<>(5);
            Map<String, String> whereMap = new HashMap<>(1);
            for (Field f : fields) {
                Method m  = clazz.getMethod("get" + getMethodName(f.getName()));
                if (PKEY.equals(f.getName())) {
                    if (!StringUtils.isBlank(Util.null2String(m.invoke(entity)))) {
                        whereMap.put(f.getName(), Util.null2String(m.invoke(entity)));
                    }
                } else if (m.invoke(entity) != null) {
                    fieldMap.put(f.getName(), Util.null2String(m.invoke(entity)));
                }
            }
            return fieldMap.size() > 0 && whereMap.size() == 1 && JAVATools.Updatesql(fieldMap, tablename, whereMap);
        } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
            logger.error("updateById error:",e);
        }
        return false;


    }

    /**
     * 根据id查询
     * @param tablename
     * @param id
     * @param clazz
     * @return
     */
    public Object selectById(String tablename,String id,Class<?> clazz){
        if (StringUtils.isBlank(tablename)) {
            return null;
        }

        try {
            Object result = clazz.newInstance();
            Map<String, String> wheremap = new HashMap<>(1);
            wheremap.put(PKEY, id);
            Map<String, Object> tablemap = JAVATools.gettablevalue(tablename, wheremap);
            Field[] methodsarray = clazz.getDeclaredFields();
            Arrays.stream(methodsarray).forEach(f -> {
                try {
                    Method m = clazz.getMethod("set" + getMethodName(f.getName()), f.getType());
                    if (tablemap.get(f.getName()) != null) {
                        if (Integer.class.isAssignableFrom(f.getType())) {
                            if (!StringUtils.isBlank(Util.null2String(tablemap.get(f.getName())))) {
                                Integer strvaluex = Util.getIntValue(Util.null2String(tablemap.get(f.getName())));
                                m.invoke(result, strvaluex);
                            }
                        } else if (String.class.isAssignableFrom(f.getType())) {
                            String strvalue = Util.null2String(tablemap.get(f.getName()));
                            m.invoke(result, strvalue);
                        }
                    }
                } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                    logger.error("selectById error:",e);
                }
            });
            return result;
        } catch (InstantiationException | IllegalAccessException e) {
            logger.error("selectById error:",e);
        }
        return null;
    }

    /**
     * 查询合同表主键id
     * @param uuid
     * @return
     */
    public static int getModeDataIdByUuid(String tableName,String uuid){
        final RecordSet recordSet = new RecordSet();
        recordSet.executeQuery("select id from "+tableName+" where modeuuid=? ", uuid);
        if(recordSet.next()){
            return recordSet.getInt("id");
        }
        return -1;
    }

    /**
     * 回滚情况下同一个事务
     * @param uuid
     * @return
     */
    public static int getModeDataIdByUuid(RecordSetTrans rst,String tableName,String uuid) throws Exception {
        rst.executeQuery("select id from "+tableName+" where modeuuid=? ", uuid);
        if (rst.next()) {
            return rst.getInt("id");
        }
        return -1;
    }


    public static <T> T createModeBaseAttributes(String modeTableName,Class<T> clazz){
        try {
            final T t = clazz.newInstance();
            return buildModeBaseAttributes(modeTableName,t,clazz);
        } catch (InstantiationException | IllegalAccessException e) {
            logger.error("创建mode对象错误：",e);
        }
        return null;
    }

    public static <T> T buildModeBaseAttributes(String modeTableName, T modeEntity, Class<T> clazz){
        final int modeId = PayUtil.getFormModeIdByCubeName(modeTableName);
        final LocalDateTime now = LocalDateTime.now();
        final Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            Object value;
            final String name = field.getName();
            switch (name.toLowerCase()){
                case "formmodeid":
                    value=modeId;
                    break;
                case "modedatacreater":
                    value= SYSTEM_USER_ID;
                    break;
                case "modedatacreatertype":
                    value=DEFAULT_CREATER_TYPE;
                    break;
                case "modedatacreatedate":
                    value= now.format(DateTimeFormatter.ISO_LOCAL_DATE);
                    break;
                case "modedatacreatetime":
                    value= now.format(DateTimeFormatter.ofPattern("HH:mm:ss"));
                    break;
                case "modeuuid":
                    value= UUID.randomUUID().toString();
                    break;
                default:
                    continue;
            }

            try {
                final Method setter = clazz.getMethod(getSetterMethodName(name),field.getType());
                setter.invoke(modeEntity,value);
            } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                logger.error("构建mode公共属性错误：",e);
            }
        }
        return modeEntity;
    }
    private static String getSetterMethodName(String feildName) {
        return "set"+getMethodName(feildName);
    }


    public static <T> T selectOne(String tableName,T entity,Class<T> clazz){
        final List<T> list = selectListByEntity(tableName, entity, clazz);
        if(list.size()==1){
            return list.get(0);
        }
        logger.error("查询单条数据出错");
        return null;
    }

    /**
     * 传入实体类非空字段值作为查询条件 返回对应数据,只支持字段javabean 字段和表字段完全一致
     * @param tableName
     * @param entity
     * @param clazz
     * @param <T>
     * @return
     */
    public static  <T> List<T> selectListByEntity(String tableName,T entity,Class<T> clazz){
        final SqlContext sqlContext = getSqlContext(tableName, entity, clazz);

        String sql=buildSelectSql(sqlContext);
       return selectList(sql,clazz,sqlContext.getValues());
    }


    public static  <T> List<T> selectListWithOrderSql(String tableName,String orderSql,T entity,Class<T> clazz){
        final SqlContext sqlContext = getSqlContext(tableName, entity, clazz);
        String sql= buildOrderSql(orderSql, sqlContext);
        final List<Object> params = sqlContext.getValues();
        return selectList(sql, clazz, params);
    }

    private static String buildOrderSql(String orderSql, SqlContext sqlContext) {
        return buildSelectSql(sqlContext)+" "+orderSql;
    }

    /**
     * 传入实体类非空字段值作为查询条件 返回对应数据,只支持字段javabean 字段和表字段完全一致
     * sql 中占位符个数必须和实体类非空值字段个数一致
     * @param sql
     * @param entity
     * @param clazz
     * @param <T>
     * @return
     */
    public static  <T> List<T> selectListBySql(String sql,T entity,Class<T> clazz){
        final SqlContext sqlContext = getSqlContext("", entity, clazz);
        final List<Object> params = sqlContext.getValues();
        return selectList(sql, clazz, params);
    }

    public static <T> List<T> selectList(String sql, Class<T> clazz, List<Object> params) {
        logger.info("sql:[{}]",sql);
        logger.info("values:[{}]", params);
        if(params.isEmpty()){
            logger.warn("查询条件参数为空");
        }
        final RecordSet recordSet = new RecordSet();
        recordSet.executeQuery(sql, params.toArray());

        List<T> resultList=new ArrayList<>();
        while (recordSet.next()){
            try {
                T result = convert2JavaBean(clazz, recordSet);
                resultList.add(result);
            } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
                logger.error("selectByEntity error:",e);
            }
        }

        return resultList;
    }

    private static <T> T convert2JavaBean(Class<T> clazz, RecordSet recordSet) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        T result = clazz.newInstance();

        Field[] methodsArray = clazz.getDeclaredFields();
        for (Field f : methodsArray) {
            Method m = clazz.getMethod("set" + getMethodName(f.getName()), f.getType());
            final String value = recordSet.getString(f.getName().toUpperCase());
            if (Integer.class.isAssignableFrom(f.getType())) {
                if (!StringUtils.isBlank(value)) {
                    Integer strvaluex = Util.getIntValue(value);
                    m.invoke(result, strvaluex);
                }
            } else if (String.class.isAssignableFrom(f.getType())) {
                m.invoke(result, value);
            }

        }
        return result;
    }

    private static String buildSelectSql(SqlContext sqlContext) {

        String sql="select "+ sqlContext.getAllColumns().stream().map(colomn->camalCase2underscore(colomn)).collect(Collectors.joining(",")) +" from  "+sqlContext.getTableName()+" ";
        if(sqlContext.getValues().isEmpty()){
            return sql;
        }
        sql+=" where " + sqlContext.getColumns().stream().map(column -> camalCase2underscore(column) + "=?").collect(Collectors.joining(" and "));
        return sql;
    }



    /**
     * 驼峰转蛇形
     * @param selected
     * @return
     */
    public static String camalCase2underscore(String selected){
        return CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE,selected);
    }
}
