package com.cim.idm.solution.service.management;

import com.cim.idm.framework.exception.ErrorSignal;
import com.cim.idm.framework.orm.support.ObjectHelper;
import com.cim.idm.framework.util.support.InvokeUtils;
import com.cim.idm.wmsextend.generic.errorHandler.CustomException;
import com.cim.idm.solution.service.ORMHeader;
import com.cim.idm.solution.service.ORMTemplate;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

/**
 * @author 10862131
 * @date 2022年08月24日 14:23
 */
public class ORMUtil {

    private static Log logger = LogFactory.getLog(ORMUtil.class);
	private static final Logger log = LoggerFactory.getLogger(ORMUtil.class);

    public static Log getLogger() {
        return logger;
    }

    public static Logger getLog() {
        return log;
    }
    
    public static final String SERVICE_PATH = "com.zi.mes.solution.service.management.impl";

    public static final String SUPPORT_PATH = "com.zi.mes.solution.service.management.support";

    public static Object createDataInfo(Class dataObject) {
        Object createdInfo = null;

        try {
            createdInfo = (Object) Class.forName(dataObject.getName()).newInstance();

        } catch (Exception e) {
            logger.warn(e, e);
        }

        if (createdInfo != null) {
            for (Field column : createdInfo.getClass().getDeclaredFields()) {
                if (column.isAnnotationPresent(ORMTemplate.class)) {
                    ORMTemplate annotation = column.getAnnotation(ORMTemplate.class);

                    String name = annotation.name();
                    String type = annotation.type();
                    String dataType = annotation.dataType();
                    String initial = annotation.initial();

                    // specific data type setting
                    if ("timestamp".equalsIgnoreCase(dataType)) {
                        ObjectHelper.setFieldValue(createdInfo, name, null);
                    } else if (!initial.isEmpty()) {
                        ObjectHelper.setFieldValue(createdInfo, name, initial);
                    }
                } else {

                }


            }
        }

        return createdInfo;
    }

    public static String getTableNameByClassName(Class clazz) {
        String tableName = clazz.getSimpleName();


        tableName = new StringBuffer().append(getHeader(clazz)).append(tableName).toString();

        return tableName;
    }

    public static String getHistoryTableNameByClassName(Class clazz, boolean isBrief) {


        StringBuffer tableNameBuffer = new StringBuffer().append(getHeader(clazz)).append(clazz.getSimpleName());

        if (isBrief) {
            tableNameBuffer.append("Hist");
        } else {
            tableNameBuffer.append("History");
        }

        return tableNameBuffer.toString();
    }

    public static String getConditionSql(String sql, String condition) {
        String whereStr = "where ";

        if (condition.toLowerCase().trim().startsWith(whereStr)) {

            sql = new StringBuffer(sql).append(" ").append(condition.trim()).toString();
        } else {

            sql = new StringBuffer(sql).append(" WHERE ").append(condition).toString();
        }
        return sql;
    }

    public static String getKeySql(String sql, Object dataInfo) {
        StringBuffer sCondition = new StringBuffer(sql).append(" WHERE 1=1 ");

        if (dataInfo != null) {
            for (Field column : dataInfo.getClass().getDeclaredFields()) {
                // only by annotation presentation
                if (column.isAnnotationPresent(ORMTemplate.class)) {
                    ORMTemplate annotation = column.getAnnotation(ORMTemplate.class);

                    String name = annotation.name();
                    String type = annotation.type();
                    String dataType = annotation.dataType();
                    String initial = annotation.initial();

                    if ("key".equalsIgnoreCase(type)) {
                        sCondition.append(" AND ").append(name).append("=").append("?");
                    }
                }
            }
        }

        return sCondition.toString();
    }

    public static String getUpdateSql(Class dataInfo, String tableName) {
        StringBuffer sql = new StringBuffer();
        StringBuffer subSql = new StringBuffer(" ").append("WHERE 1=1").append(" ");

        sql.append("UPDATE").append(" ").append(tableName).append(" ").append("SET").append(" ");

        for (Field column : dataInfo.getDeclaredFields()) {
            if (column.isAnnotationPresent(ORMTemplate.class)) {
                ORMTemplate annotation = column.getAnnotation(ORMTemplate.class);

                String name = annotation.name();
                String type = annotation.type();

                String initial = annotation.initial();

                // never key update
                // never null update
                if (!"never".equalsIgnoreCase(initial)) {
                    if ("key".equalsIgnoreCase(type)) {
                        subSql.append(" AND ").append(name).append("=").append("?");
                    } else {
                        sql.append(name).append("=").append("?").append(",");
                    }
                }
            }
        }

        String refinedSql = StringUtils.removeEnd(sql.toString(), ",");

        sql = new StringBuffer(refinedSql).append(subSql.toString());

        return sql.toString();
    }

    public static String getDeleteSql(Class dataInfo, String tableName) {
        StringBuffer sql = new StringBuffer();
        StringBuffer subSql = new StringBuffer(" ").append("WHERE 1=1").append(" ");

        sql.append("DELETE").append(" ").append(tableName).append(" ");

        for (Field column : dataInfo.getDeclaredFields()) {
            if (column.isAnnotationPresent(ORMTemplate.class)) {
                ORMTemplate annotation = column.getAnnotation(ORMTemplate.class);

                String name = annotation.name();
                String type = annotation.type();


                // never key update
                if ("key".equalsIgnoreCase(type)) {
                    subSql.append(" AND ").append(name).append("=").append("?");
                }

            }
        }

        String refinedSql = StringUtils.removeEnd(sql.toString(), ",");

        sql = new StringBuffer(refinedSql).append(subSql.toString());

        return sql.toString();
    }

    public static String getInsertSql(Class dataInfo, String tableName) {
        StringBuffer sql = new StringBuffer();
        sql.append("INSERT").append(" ").append("INTO").append(" ").append(tableName).append(" ");

        StringBuilder attrBuilder = new StringBuilder("");
        StringBuilder valueBuilder = new StringBuilder("");

        for (Field column : dataInfo.getDeclaredFields()) {
            if (column.isAnnotationPresent(ORMTemplate.class)) {
                ORMTemplate annotation = column.getAnnotation(ORMTemplate.class);

                String name = annotation.name();
                String dataType = annotation.dataType();
                String initial = annotation.initial();

                if ("never".equalsIgnoreCase(initial)) {
                    continue;
                }

                {// column generation
                    if (!attrBuilder.toString().isEmpty()) {
                        attrBuilder.append(",");
                    }

                    attrBuilder.append(name);
                }

                {// value generation : default setting demanded?
                    if (!valueBuilder.toString().isEmpty()) {
                        valueBuilder.append(",");
                    }
                    boolean existed = ("timestamp".equalsIgnoreCase(dataType) || "systemtime".equalsIgnoreCase(dataType))
                            && "current".equalsIgnoreCase(initial);
                    if (existed) {
                        valueBuilder.append("SYSDATE");
                    } else {
                        valueBuilder.append("?");
                    }
                }

            }
        }

        sql.append("(").append(attrBuilder).append(")").append(" VALUES ").append("(").append(valueBuilder).append(")");

        return sql.toString();
    }

    public static String getSql(Class dataInfo, String tableName) {
        StringBuffer sql = new StringBuffer();

        sql.append("SELECT").append(" ");

        for (Field column : dataInfo.getDeclaredFields()) {
            if (column.isAnnotationPresent(ORMTemplate.class)) {
                ORMTemplate annotation = column.getAnnotation(ORMTemplate.class);

                String name = annotation.name();


                sql.append(name);
                sql.append(",");
            }
        }

        String refinedSql = StringUtils.removeEnd(sql.toString(), ",");

        sql = new StringBuffer(refinedSql).append(" ").append("FROM").append(" ").append(tableName);

        return sql.toString();
    }

    public static Object loadServiceProxy(Class clazz)
            throws CustomException {
        String serviceName = new StringBuffer(SERVICE_PATH).append(".")
                .append(clazz.getSimpleName()).append("ServiceImpl").toString();


        Object serviceClass = InvokeUtils.newInstance(serviceName, new Class[0], new Object[0]);

        if (serviceClass != null) {
            return serviceClass;
        } else {
            throw new CustomException("SYS-8001", serviceName);
        }
    }

    public static Object loadSupportServiceProxy(Class clazz)
            throws CustomException {
        String serviceName = new StringBuffer(SUPPORT_PATH).append(".").append("ftp").append(".")
                .append(clazz.getSimpleName()).toString();


        Object serviceClass = InvokeUtils.newInstance(serviceName, new Class[0], new Object[0]);

        if (serviceClass != null) {
            return serviceClass;
        } else {
            throw new CustomException("SYS-8001", serviceName);
        }
    }

    /**
     * validate key parameter, delivering parameter array
     *
     * @param dataInfo
     * @param args
     * @return
     * @author swcho
     * @since 2014.04.29
     */
    public static String validateKeyParam(Object dataInfo, Object[] args) {
        // key info validating
        try {
            if (dataInfo == null) {
                throw new Exception("dataInfo is null");
            }

            int keyCnt = 0;

            for (Field column : dataInfo.getClass().getDeclaredFields()) {
                // only by annotation presentation
                if (column.isAnnotationPresent(ORMTemplate.class)) {
                    ORMTemplate annotation = column.getAnnotation(ORMTemplate.class);
                    String type = annotation.type();
                    if ("key".equalsIgnoreCase(type)) {
                        keyCnt++;
                    }
                }
            }

            if (keyCnt != args.length) {
                throw new Exception("key value is null");
            }
        } catch (Exception ex) {
            // throw new nanoFrameDBErrorSignal(ErrorSignal.NullPointKeySignal, "", sql, "Key can't be null");
            return ErrorSignal.NullPointKeySignal;
        }

        return "";
    }

    /**
     * make primary key parameters
     *
     * @param dataInfo
     * @return temp
     * @author swcho
     * @since 2014.04.29
     */
    public static List<Object> makeKeyParam(Object dataInfo) {
        List<Object> temp = new ArrayList<Object>();

        if (dataInfo == null) {
            return temp;
        }

        int idx = 0;

        // find non-key binding at first
        for (Field column : dataInfo.getClass().getDeclaredFields()) {
            // only by annotation presentation
            if (column.isAnnotationPresent(ORMTemplate.class)) {
                ORMTemplate annotation = column.getAnnotation(ORMTemplate.class);

                String name = annotation.name();
                String type = annotation.type();

                if ("key".equalsIgnoreCase(type)) {
                    temp.add(idx, ObjectHelper.getFieldValue(dataInfo, name));
                    idx++;
                }
            }
        }

        return temp;
    }

    /**
     * make primary non-key parameters
     *
     * @param dataInfo
     * @return temp
     * @author swcho
     * @since 2014.04.29
     */
    public static List<Object> makeNonKeyParam(Object dataInfo) {
        List<Object> temp = new ArrayList<Object>();

        if (dataInfo == null) {
            return temp;
        }

        int idx = 0;

        // find non-key binding at first
        for (Field column : dataInfo.getClass().getDeclaredFields()) {
            // only by annotation presentation
            if (column.isAnnotationPresent(ORMTemplate.class)) {
                ORMTemplate annotation = column.getAnnotation(ORMTemplate.class);

                String name = annotation.name();
                String type = annotation.type();

                String initial = annotation.initial();

                if (!"key".equalsIgnoreCase(type)
                        // ORM does not require null : prohibited

                        && !"never".equalsIgnoreCase(initial)) {
                    temp.add(idx, ObjectHelper.getFieldValue(dataInfo, name));
                    idx++;
                }
            }
        }

        return temp;
    }

    /**
     * dynamic CTORM mapping header
     *
     * @param clazz
     * @return StringBuilder
     * @author swcho
     * @since 2015-10-29
     */
    public static String getHeader(Class clazz) {
        String headerName = "";
        String connector = "";

        if (clazz.isAnnotationPresent(ORMHeader.class)) {
            ORMHeader header = (ORMHeader) clazz.getAnnotation(ORMHeader.class);

            if (header != null) {
                headerName = header.tag();
                connector = header.divider();
            }
        }

        return new StringBuilder(headerName).append(connector).toString();
    }
}
