package com.ag.base;

import com.ag.util.StrUtil;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

/**
 * Created by Administrator on 2017/3/28 0028.
 */
public class SqlUtil<T> {

    public String getSaveSql(T t) {
        String tableName = getTableName(t);
        StringBuilder colSql = new StringBuilder();
        StringBuilder valueSql = new StringBuilder();

        Method[] ms = t.getClass().getMethods();
        for (Method m : ms) {
            String colName = getColName(m);
            //??????
            if (colName != null) {
                String colValue = getColValue(m, t);
                //????
                if (colValue != null) {
                    colSql.append("").append(colName).append("").append(",");
                    valueSql.append(colValue).append(",");
                }
            }
        }
        String saveSql = StrUtil.join("insert into ", tableName, "(", subLastStr(colSql.toString()), ") values (", subLastStr(valueSql.toString()), ")");
        return saveSql;
    }

    /*public String getUpdateSql(T t, String paramId) {
        String tableName = getTableName(t);
        StringBuilder setSql = new StringBuilder();
        String pkValue = "";//?????
        Method[] ms = t.getClass().getMethods();
        for (Method m : ms) {
            String colName = getColName(m);
            //??????
            if (colName != null) {
                String colValue = getColValue(m, t, "update");
                //????????
                if (colName.toLowerCase().equals(paramId.toLowerCase())) {
                    pkValue = colValue;
                    continue;
                }
                //????
                if (colValue != null) {
                    setSql.append("").append(colName).append("").append("=").append(colValue).append(",");
                }
            }
        }
        String updateSql = StrUtil.join("update " , tableName , " set " , subLastStr(setSql.toString()) , " where " , paramId , "=" , pkValue);
        return updateSql;
    }*/

    public String getUpdateSql(T t, Object... colNames) {
        return getUpdateSql(t, null, colNames);
    }

    public String getUpdateSql(T t, String[] colArr, Object... colNames) {
        String tableName = getTableName(t);
        StringBuilder setSql = new StringBuilder();
        String needSql = "";
        Method[] ms = t.getClass().getMethods();
        for (Method m : ms) {
            boolean isSet = true;
            String colName = getColName(m);
            //列名不为空
            if (colName != null) {
                for (Object aColName : colNames) {
                    //列名为参数，不能增加进set
                    if (colName.toLowerCase().equals(aColName.toString().toLowerCase())) {
                        isSet = false;
                        break;
                    }
                }
            }
            if (isSet && colName != null) {
                String colValue = getColValue(m, t);
                if (colValue != null) {
                    setSql.append("").append(colName).append("").append("=").append(colValue).append(",");
                } else {
                    if (colArr == null) {
                        continue;
                    }
                    for (String needCol : colArr) {
                        if (colName.toLowerCase().equals(needCol.toLowerCase())) {
                            needSql = StrUtil.join(needSql, colName, "=null,");
                            break;
                        }
                    }
                }
            }
        }
        setSql.append(needSql);
        String whereSql = getWhereSql(t, colNames);
        if (StrUtil.isEmpty(whereSql)) {
            throw new RuntimeException("更新语句where条件不能为空！");
        }
        String updateSql = StrUtil.join("update ", tableName, " set ", subLastStr(setSql.toString()), " where ", whereSql.substring(4, whereSql.length()));
        return updateSql;
    }

    public String getDeleteSql(T t, String pkColName, String deleteColName) {
        String tableName = getTableName(t);
        String setSql = "";
        String whereSql = "";
        Method[] ms = t.getClass().getMethods();
        for (Method m : ms) {
            //????????
            if (deleteColName.equals(getColName(m))) {
                setSql = StrUtil.join("", deleteColName, "=", getColValue(m, t));
            }
            //????where??
            if (pkColName.equals(getColName(m))) {
                whereSql = StrUtil.join("", pkColName, "=", getColValue(m, t));
            }
        }
        String deleteSql = StrUtil.join("update ", tableName, " set ", setSql, " where ", whereSql);
        return deleteSql;
    }

    public String deleteSql(T t, String pkColName) {
        StringBuilder sql = new StringBuilder("delete from ");
        sql.append(getTableName(t)).append(" where ").append(pkColName).append(" = ");
        String pkValue = "";//?????
        Method[] ms = t.getClass().getMethods();
        for (Method m : ms) {
            String colName = getColName(m);
            // ??????
            if (colName != null) {
                String colValue = getColValue(m, t);
                // ????????
                if (colName.toLowerCase().equals(pkColName.toLowerCase())) {
                    pkValue = colValue;
                    break;
                }
            }
        }
        sql.append(pkValue);
        return sql.toString();
    }

    public String deleteEqSql(T t, Object... colName) {
        String tableName = getTableName(t);
        String deleteSql = "delete from " + tableName;
        Method[] ms = t.getClass().getMethods();
        String whereSql = "";
        for (Method m : ms) {
            for (Object aColName : colName) {
                if (aColName.toString().equalsIgnoreCase(getColName(m))) {
                    String val = getColValue(m, t);
                    if (val != null) {
                        whereSql = StrUtil.join(whereSql,
                                " and ", aColName, "=", val);
                    } else {
                        throw new RuntimeException("删除方法中，列名" + aColName + "值不能为空！");
                    }
                    break;
                }
            }
        }
        if (!"".equals(whereSql)) {
            deleteSql += " where " + whereSql.substring(4, whereSql.length());
        } else {
            throw new RuntimeException("不能做整表删除！");
        }
        //System.out.println("deleteSql:"+deleteSql);
        return deleteSql;
    }

    private String getWhereSql(T t, Object... colName) {
        Method[] ms = t.getClass().getMethods();
        String whereSql = "";
        for (Method m : ms) {
            for (Object aColName : colName) {
                if (aColName.toString().equalsIgnoreCase(getColName(m))) {
                    String val = getColValue(m, t);
                    if (val != null) {
                        whereSql = StrUtil.join(whereSql,
                                " and ", aColName, "=", val);
                    }
                    break;
                }
            }
        }
        return whereSql;
    }

    private String getNeedWhereSql(T t, Object... colName) {
        Method[] ms = t.getClass().getMethods();
        String whereSql = "";
        for (Method m : ms) {
            for (Object aColName : colName) {
                if (aColName.toString().equalsIgnoreCase(getColName(m))) {
                    String val = getColValue(m, t);
                    if (val == null){
                        val = "null";
                    }
                    whereSql = StrUtil.join(whereSql, " and ", aColName, "=", val);
                    break;
                }
            }
        }
        return whereSql;
    }


    public String getFindSql(T t, String pkColName) {
        String tableName = getTableName(t);
        Method[] ms = t.getClass().getMethods();
        String whereSql = "";
        for (Method m : ms) {
            String colName = getColName(m);
            //列名不为空
            if (colName != null && pkColName.equalsIgnoreCase(colName)) {
                whereSql = StrUtil.join("", pkColName, "=", getColValue(m, t));
            }
        }
        String findSql = StrUtil.join("select * from ", tableName, " where ", whereSql);
        return findSql;
    }

    public String getFindAllSql(Class<T> claz) {
        String tableName = "";
        try {
            tableName = getTableName(claz.newInstance());
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        String findAllSql = "select * from " + tableName;
        return findAllSql;
    }

    public String getEqCriteriaSql(T t, Object... colName) {
        String tableName = getTableName(t);
        String whereSql = getNeedWhereSql(t, colName);
        String findSql = "select * from " + tableName;
        if (!"".equals(whereSql)) {
            findSql += " where " + whereSql.substring(4, whereSql.length());
        }else {
            findSql += " where 1=2 ";
            //throw new RuntimeException("查询条件不能为空，请检查查询对象参数！");
        }
        //System.out.println("findSql:"+findSql);
        return findSql;
    }

    public String getSelectByEntity(T t, boolean isAllTable){
        String tableName = getTableName(t);
        Method[] ms = t.getClass().getMethods();
        String whereSql = "", val = null;
        for (Method m : ms) {
            String colName = getColName(m);
            if (colName != null){
                val = getColValue(m, t);
            }else {
                val = null;
            }
            if (val != null) {
                whereSql = StrUtil.join(whereSql," and ", colName, "=", val);
            }
        }
        String findSql = "select * from " + tableName;
        if (!"".equals(whereSql)) {
            findSql += " where " + whereSql.substring(4, whereSql.length());
        } else if (!isAllTable){
            findSql += " where 1=2 ";
            //throw new RuntimeException("查询条件不能为空，请检查查询对象参数！");
        }
        return findSql;
    }

    /**
     * ??????????
     * ??????
     */
    protected String getTableName(T t) {
        String tableName = t.getClass().getSimpleName();
        //????????
        tableName = StrUtil.join(tableName.substring(0, 1).toLowerCase(), tableName.substring(1, tableName.length()));
        //??Entity
        //tableName = tableName.substring(0, tableName.indexOf("Entity"));
        //return "t_"+toLower(tableName)+"";
        return "C_" + toLower(tableName);
    }

    /**
     * ??????????
     * ???????????get?????
     */
    public String getColName(Method m) {
        String name = m.getName();
        //???getClass?get*??
        if (!name.startsWith("get") || "getClass".equals(name)) {
            return null;
        } else {
            //????get
            String param = name.substring(3, name.length());
            //?????
            param = firstToLower(param);
            //????????????_
            return toLower(param);
        }
    }

    protected String getFieldName(Method m) {
        String name = m.getName();
        //???getClass?get*??
        if (!name.startsWith("set")) {
            return null;
        } else {
            String param = name.substring(3, name.length());
            return firstToLower(param);
        }
    }

    /**
     * ??value?????????
     */
    public String getColValue(Method m, T t) {
        Object value = null;
        try {
            //??????
            value = m.invoke(t);
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (value == null) {
            return null;
        }
        if (value instanceof Number) {
            return String.valueOf(value);
        }  else if (value instanceof Date) {
            return StrUtil.join("to_date('", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format((Date) value), "','yyyy-MM-dd HH24:mi:ss')");
        } else if (value instanceof Boolean) {
            return String.valueOf(value);
        }  else if (value instanceof String) {
            //??update?????!=null?????????
            //if ("update".equals(type)) {
            if (((String) value).contains("'")) {
                value = ((String) value).replace("'", "''");
            }
            return StrUtil.join("'", value, "'");
            /*} else {
                if (!StringUtils.isEmpty(value)) {
                    return StrUtil.join("'", value, "'");
                }
            }*/
        } /*else {
            return String.valueOf(value);
        }*/
        return null;
    }

    /**
     * ?????
     */
    protected String firstToLower(String param) {
        return StrUtil.join(param.substring(0, 1).toLowerCase(), param.substring(1, param.length()));
    }

    protected String firstToUpper(String param) {
        return StrUtil.join(param.substring(0, 1).toUpperCase(), param.substring(1, param.length()));
    }

    /**
     * ???????_?????
     */
    protected String toLower(String param) {
        StringBuilder sb = new StringBuilder();
        char[] col = param.toCharArray();
        for (char ch : col) {
            if (Character.isUpperCase(ch)) {
                sb.append("_").append(Character.toLowerCase(ch));
            } else {
                sb.append(ch);
            }
        }
        return sb.toString();
    }

    /**
     * ??????
     */
    private String subLastStr(String param) {
        return param.substring(0, param.length() - 1);
    }

    /**
     * ??????????
     */
    private Object getFieldValueByName(Object o, String fieldName) {
        try {
            String firstLetter = fieldName.substring(0, 1).toUpperCase();
            String getter = StrUtil.join("get", firstLetter, fieldName.substring(1));
            Method method = o.getClass().getMethod(getter);
            return method.invoke(o);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * ??????????id1,id2,id3??
     *
     * @param lst
     * @param fieldName "userId"
     * @return
     */
    public String getAddInIds(List<T> lst, String fieldName) {
        if (CollectionUtils.isEmpty(lst)){
            return "''";
        }
        String ids = "";
        for (T t : lst) {
            Object obj = getFieldValueByName(t, fieldName);
            if (obj instanceof String) {
                ids = StrUtil.join(ids, "'", obj, "',");
            } else {
                ids = StrUtil.join(ids, obj, ",");
            }
        }
        return subLastStr(ids);
    }


    public T getEntity(Class<T> tClz) {
        try {
            return tClz.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    public String findInIds(Class<T> tClz, String ids, String idColName){
        if (StrUtil.isEmpty(ids)){
            ids = "''";
        }
        if (StrUtil.isEmpty(idColName)){
            idColName = "ID";
        }
        if (!ids.startsWith("'")){
            ids = StrUtil.converIdsToSql(ids);
        }
        String sql = getFindAllSql(tClz);
        return StrUtil.join(sql, " where ", idColName, " in (", ids, ")");
    }
    /*public T getEntity() {
        Class clz = getClass();
        Type sType = clz.getGenericSuperclass();
        Type[] generics = ((ParameterizedType) sType).getActualTypeArguments();
        Class<T> tClz = (Class<T>) generics[0];
        try {
            return tClz.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }*/

    /*
    public T packPOJO(Map<String, String> map, Class<T> clz){
        T t = this.getEntity(clz);
        if(t == null){
            return t;
        }
        Method[] ms=t.getClass().getMethods();
        for(Method m: ms) {
            String fieldName = getFieldName(m);
            if(fieldName == null){
                continue;
            }
            if(!StringUtils.isEmpty(map.get(fieldName))){
                setMethodValue(t, m, map.get(fieldName));
                continue;
            }
            String colName = toLower(fieldName).toUpperCase();
            if(!StringUtils.isEmpty(map.get(colName))){
                setMethodValue(t, m, map.get(colName));
                continue;
            }
        }
        return t;
    }

    private void setMethodValue(T t, Method m, String paramVal) {
        String paramType = m.getParameterTypes()[0].toString();
        try {
            if(paramType.indexOf("String") > -1){
                m.invoke(t, paramVal);
            }else if(paramType.indexOf("Integer") > -1){
                m.invoke(t, Integer.valueOf(paramVal));
            }else if(paramType.indexOf("Double") > -1){
                m.invoke(t, Double.valueOf(paramVal));
            }else if(paramType.indexOf("Date") > -1){
                m.invoke(t, new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").parse(paramVal));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }*/

    /*private void setFieldValue(T t, Field field, String paramVal) {
        String fieldType = field.getType().toString();
        String fieldName = field.getName();
        if(fieldType.indexOf("String") > -1){
            String setMethodName = "set"+firstToUpper(fieldName);
        }else if(fieldType.indexOf("Integer") > -1){

        }else if(fieldType.indexOf("Double") > -1){

        }else if(fieldType.indexOf("Date") > -1){

        }
    }*/


    public static void main(String[] args) {

        //System.out.println(new SqlUtil().toLower("dci22!@第sndDdV21"));
        /*List<BaseCategory> lst = new ArrayList<>();
        for (int i=0 ; i<100; i++){*/
        BaseCategory bc = new BaseCategory();
        bc.setId("a");
        //bc.setTenancyId("b00");
        bc.setIsAll((short)55);
        /*bc.setNotes("ddd");
        bc.setEnabled(new BigDecimal(0));*/
        //bc.setCreatedBy("admin's");
        //bc.setCreatedOn(new Date());
            /*lst.add(bc);
        }*/
        long on = System.currentTimeMillis();
        //for (BaseCategory bc : lst){
//        System.out.println(new SqlUtil<BaseCategory>().getSaveSql(bc));
//        System.out.println(new SqlUtil<BaseCategory>().getUpdateSql(bc, "tenancy_Id"));
//        System.out.println(new SqlUtil<BaseCategory>().getUpdateSql(bc, "tenancy_Id", "notes"));
//        System.out.println(new SqlUtil<BaseCategory>().getUpdateSql(bc, new String[]{"updated_On", "is_All", "tenancy_Id"}, "created_by"));
        //}
        System.out.println(new SqlUtil<>().getEqCriteriaSql(bc, "tenancy_Id", "byagent"));
        System.out.println(new SqlUtil<>().getSelectByEntity(bc, false ));

        System.out.println(new SqlUtil<>().getEqCriteriaSql(new BaseCategory(), "tenancy_Id", "byagent"));
        System.out.println(new SqlUtil<>().getSelectByEntity(new BaseCategory(), false ));
        long end = System.currentTimeMillis();
        System.out.println(end - on);

/*
        SqlUtil<SettlementHead> wbUtil = new SqlUtil<SettlementHead>();
        SettlementHead wb = new SettlementHead();
        wb.setId(UUIDUtil.getUUID());
        wb.setBillNo(null);
        wb.setEnabled(1);
        wb.setInvoiceType(6);
        wb.setTicketDate(new Date());
        String sql = wbUtil.getSaveSql(wb);
        wb.setPaymentCustomerId("778");
        System.out.println(sql);
        System.out.println(wbUtil.getUpdateSql(wb, "ID", "BILL_NO"));
        /*long lg1 = System.currentTimeMillis();
        SqlUtil<WbCompany> wbUtil = new SqlUtil<WbCompany>();
        Map<String, String> map = new HashMap<String, String>();
        //request中的属性获取策略，目前是属性名 > 字段名大写
        map.put("companyId", "bea3f0f49ba3496ca5c846a6e9580a66");
        //map.put("companyId", UUIDUtil.getUUID());

        map.put("companyName", "dixms");
        map.put("addR", "地址");
        map.put("createtime", "2014-07-08 12:30:00");
        map.put("COMPANY_ID", "779");
        //map.put("COUNTRY", "98");
        map.put("IS_NUM", "56");
        WbCompany wb = wbUtil.packPOJO(map, WbCompany.class);
        String sql = wbUtil.getSaveSql(wb);
        String usql = wbUtil.getUpdateSql(wb, "COMPANY_ID");
        long lg2 = System.currentTimeMillis();
        System.out.println(sql);
        System.out.println(usql);
        System.out.println(lg2 - lg1);*/

    }

    static class BaseCategory{
        private String id;
        private String tenancyId;
        private String description;
        private String notes;
        private Short isAll;
        private String createdBy;
        private Date createdOn;
        private String updatedBy;
        private Date updatedOn;
        private Integer effectived;
        private BigDecimal enabled;
        private String bizScope;
        private Integer byagent;

        public String getId() {
            return id;
        }

        public void setId(String id) {
            this.id = id;
        }

        public String getTenancyId() {
            return tenancyId;
        }

        public void setTenancyId(String tenancyId) {
            this.tenancyId = tenancyId;
        }

        public String getDescription() {
            return description;
        }

        public void setDescription(String description) {
            this.description = description;
        }

        public String getNotes() {
            return notes;
        }

        public void setNotes(String notes) {
            this.notes = notes;
        }


        public String getCreatedBy() {
            return createdBy;
        }

        public void setCreatedBy(String createdBy) {
            this.createdBy = createdBy;
        }

        public Date getCreatedOn() {
            return createdOn;
        }

        public void setCreatedOn(Date createdOn) {
            this.createdOn = createdOn;
        }

        public String getUpdatedBy() {
            return updatedBy;
        }

        public void setUpdatedBy(String updatedBy) {
            this.updatedBy = updatedBy;
        }

        public Date getUpdatedOn() {
            return updatedOn;
        }

        public void setUpdatedOn(Date updatedOn) {
            this.updatedOn = updatedOn;
        }

        public Integer getEffectived() {
            return effectived;
        }

        public void setEffectived(Integer effectived) {
            this.effectived = effectived;
        }


        public String getBizScope() {
            return bizScope;
        }

        public void setBizScope(String bizScope) {
            this.bizScope = bizScope;
        }

        public Integer getByagent() {
            return byagent;
        }

        public void setByagent(Integer byagent) {
            this.byagent = byagent;
        }

        public Short getIsAll() {
            return isAll;
        }

        public void setIsAll(Short isAll) {
            this.isAll = isAll;
        }

        public BigDecimal getEnabled() {
            return enabled;
        }

        public void setEnabled(BigDecimal enabled) {
            this.enabled = enabled;
        }
    }
}
