package cc.smtweb.framework.core.db.dao;

import cc.smtweb.framework.core.annotation.SwTable;
import cc.smtweb.framework.core.cache.CacheManager;
import cc.smtweb.framework.core.common.SwEnum;
import cc.smtweb.framework.core.db.cache.ModelTableCache;
import cc.smtweb.framework.core.db.impl.DefaultEntity;
import cc.smtweb.framework.core.db.vo.ModelField;
import cc.smtweb.framework.core.db.vo.ModelTable;
import cc.smtweb.framework.core.exception.DbException;
import cc.smtweb.framework.core.exception.SwException;
import cc.smtweb.framework.core.util.DateUtil;
import cc.smtweb.framework.core.util.NumberUtil;
import cc.smtweb.framework.core.util.SpringUtil;
import cc.smtweb.framework.core.util.VariableUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.ClassUtils;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 抽象的值对象数据库访问
 *
 * @param <T> 数据库值对象类型
 */
@Slf4j
public abstract class AbstractEntityDao<T> {
    protected ModelTable modelTable;
    protected String tableName;
    protected Map<String, EntityColumn> columns = new HashMap<>();
    protected Class<T> type;
    protected CacheManager cacheManager = SpringUtil.getBean(CacheManager.class);
//	public AbstractEntityDao(String tableName) {
//		this.tableName = tableName;
//	}

    /**
     * 通过值对象类型构造值对象数据库访问
     *
     * @param type 值对象类型
     */
    public AbstractEntityDao(Class<T> type) {
        this.type = type;
        // type.isAnnotationPresent(Table.class);
        SwTable table = type.getAnnotation(SwTable.class);
        Class<? super T> superclass = type.getSuperclass();

        if (table == null && superclass != null) {
            table = superclass.getAnnotation(SwTable.class);
        }

        if (table == null) {
            throw new IllegalAccessError("not find annotation @SwTable");
        }

        tableName = table.value();
        modelTable = ModelTableCache.getInstance().getByName(tableName);
        if (null == modelTable) {
            log.error("未找到缓存表结构：" + tableName);
        }
        if (DefaultEntity.class.isAssignableFrom(type)) {
            for (ModelField field : modelTable.getFields()) {
                add(field, null, null);
            }
        } else {
            for (ModelField field : modelTable.getFields()) {
                String voFieldName = getVoFieldName(modelTable.getAbbr(), field.getName());
                // 处理get method
                try {
                    PropertyDescriptor pd = new PropertyDescriptor(voFieldName, type);
                    // 获得get方法
                    Method readMethod = pd.getReadMethod();
                    Method writeMethod = pd.getWriteMethod();

                    if (readMethod != null && writeMethod != null) {
                        Class<?>[] parameterTypes = writeMethod.getParameterTypes();
                        if (parameterTypes.length == 1 && ClassUtils.isAssignable(parameterTypes[0], readMethod.getReturnType())) {
                            add(field, readMethod, writeMethod);
                        }
                    }
                } catch (IntrospectionException ignore) {
                }
            }
        }
    }

    /**
     * 通过值对象类型构造值对象数据库访问
     *
     * @param tableName 表名
     */
    public AbstractEntityDao(String tableName) {
        this.tableName = tableName;
        this.type = (Class<T>) DefaultEntity.class;
        modelTable = cacheManager.getCache(ModelTableCache.class).getByName(tableName);

        for (ModelField field : modelTable.getFields()) {
            add(field, null, null);
        }
    }

    /**
     * 根据字段名，得到类中的字段属性名
     *
     * @param tbAbbr    表缩写，一般是字段的前缀
     * @param fieldName 字段名
     * @return
     */
    private String getVoFieldName(String tbAbbr, String fieldName) {
        fieldName = fieldName.toLowerCase();
        tbAbbr = tbAbbr.toLowerCase() + "_";
        if (fieldName.startsWith(tbAbbr)) return VariableUtil.underlineToHump(fieldName.substring(tbAbbr.length()));
        return VariableUtil.underlineToHump(fieldName);
    }

    /**
     * 添加值对象的属性访问方法何注解值
     *
     * @param field       字段名
     * @param readMethod  读值对象属性方法
     * @param writeMethod 写值值对象属性方法
     */
    protected void add(ModelField field, Method readMethod, Method writeMethod) {
        EntityColumn beanColumn = new EntityColumn(field, readMethod, writeMethod);
        columns.put(field.getName(), beanColumn);
    }

    protected void updateTime(T obj, int type) {
        ModelField field = modelTable.findFieldByType(type);
        if (field == null) return;
        EntityColumn col = columns.get(field.getName());
        if (col != null) {
            col.writeValue(obj, DateUtil.nowDateTimeLong());
        }
    }

    /**
     * 校验传入的字段，如没有创建时间和最后更改时间，则加上
     *
     * @param fields
     * @return
     */
    protected List<String> adjustFields(String fields, int... types) {
        if (StringUtils.isEmpty(fields)) return null;
        String[] fieldNames = fields.toLowerCase().split(",");
        List<String> listFields = new ArrayList<>(fieldNames.length + 2);
        boolean[] includeTypes = new boolean[types.length];
        for (int i = 0, len = includeTypes.length; i < len; i++) {
            includeTypes[i] = false;
        }

        for (String name : fieldNames) {
            EntityColumn column = this.columns.get(name.trim());
            for (int i = 0, len = types.length; i < len; i++) {
                if (types[i] == column.getField().getFieldType()) {
                    includeTypes[i] = true;
                }
            }
            listFields.add(name);
        }
        for (int i = 0, len = types.length; i < len; i++) {
            if (!includeTypes[i]) {
                ModelField field = modelTable.findFieldByType(types[i]);
                if (field != null) listFields.add(field.getName());
            }
        }
        return listFields;
    }

    /**
     * 拼接插入SQL语句
     *
     * @param obj    值对象
     * @param sql    记录sql的字符缓存
     * @param fields 逗号分割字段列表，设置为null表示使用所有字段
     * @return SQL参数列表
     */
    protected Object[] handleInsert(T obj, StringBuilder sql, String fields) {
        List<Object> result;
        List<String> listFields = adjustFields(fields, SwEnum.FieldType.CREATE_TIME.value, SwEnum.FieldType.LAST_TIME.value);
        sql.append("insert into ").append(modelTable.getSchemaTableName()).append("(");

        updateTime(obj, SwEnum.FieldType.CREATE_TIME.value);
        updateTime(obj, SwEnum.FieldType.LAST_TIME.value);

        if (listFields == null) {
            result = new ArrayList<>(this.columns.size());

            for (EntityColumn column : this.columns.values()) {
                result.add(column.readValue(obj));
                sql.append(column.getField().getName()).append(",");
            }
        } else {
            result = new ArrayList<>(listFields.size());

            for (String name : listFields) {
                EntityColumn column = this.columns.get(name.trim());
                result.add(column.readValue(obj));
                sql.append(column.getField().getName()).append(",");
            }
        }

        sql.setCharAt(sql.length() - 1, ')');

        // values(?,?)
        sql.append(" values(");
        for (int i = result.size(); i > 0; i--) {
            sql.append("?,");
        }
        sql.setCharAt(sql.length() - 1, ')');

        return result.toArray();
    }

    /**
     * 拼接更新SQL语句
     *
     * @param obj         值对象
     * @param sql         记录sql的字符缓存
     * @param fields      逗号分割更新字段列表，设置为null表示使用所有字段
     * @param whereFields 逗号分割查询字段列表，设置为null表示使用ID字段作为查询条件
     * @return SQL参数列表
     */
    protected Object[] handleUpdate(T obj, StringBuilder sql, String fields, String whereFields) {
        EntityColumn idColumn = findIdColumn();

        List<String> listFields = adjustFields(fields, SwEnum.FieldType.LAST_TIME.value);
        updateTime(obj, SwEnum.FieldType.LAST_TIME.value);

        sql.append("update ").append(modelTable.getSchemaTableName()).append(" set ");
        List<Object> result = new ArrayList<>();

        if (listFields == null) {
            for (EntityColumn column : this.columns.values()) {
                if (idColumn != column) {
                    sql.append(column.getField().getName()).append("=?,");
                    result.add(column.readValue(obj));
                }
            }
            sql.setCharAt(sql.length() - 1, ' ');

            // 默认使用Id字段条件
            result.add(idColumn.readValue(obj));

            sql.append("where ").append(idColumn.getField().getName()).append("=?");
        } else {

            for (String name : listFields) {
                name = name.trim();
                EntityColumn beanColumn = getBeanColumn(name);

                sql.append(name).append("=?,");
                result.add(beanColumn.readValue(obj));

            }

            sql.setCharAt(sql.length() - 1, ' ');

            if (StringUtils.isNotBlank(whereFields)) {
                sql.append("where ");
                boolean first = true;
                for (String name : whereFields.split(",")) {
                    name = name.trim();
                    if (first) {
                        first = false;
                    } else {
                        sql.append(" and ");
                    }

                    sql.append(name).append("=?");
                    result.add(readValue(obj, name));
                }
            } else {
                // 默认使用Id字段条件
                result.add(idColumn.readValue(obj));
                sql.append("where ").append(idColumn.getField().getName()).append("=?");
            }
        }
        return result.toArray();
    }

    private EntityColumn findIdColumn() {
        ModelField field = modelTable.findIdField();
        EntityColumn idColumn = field != null ? columns.get(field.getName()): null;
        if (idColumn == null) {
            throw new DbException(tableName + " not define id column");
        }

        return idColumn;
    }

    /**
     * 拼接删除值对象语句
     *
     * @param obj 值对象
     * @param sql 记录sql的字符缓存
     * @return SQL参数列表
     */
    protected Object[] handleDelete(T obj, StringBuilder sql) {
        EntityColumn idColumn = findIdColumn();

        sql.append("DELETE FROM ").append(modelTable.getSchemaTableName()).append(" WHERE ").append(idColumn.getField().getName()).append("=?");

        return new Object[]{idColumn.readValue(obj)};
    }

    /**
     * 拼接删除值对象语句，条件由外部设置
     *
     * @param sql 记录sql的字符缓存
     */
    protected void handleDelete(StringBuilder sql) {
        EntityColumn idColumn = findIdColumn();
        sql.append("DELETE FROM ").append(modelTable.getSchemaTableName()).append(" WHERE ").append(idColumn.getField().getName()).append("=?");
    }

    public Object readValue(T obj, String fieldName) {
        if (obj == null) return null;
        EntityColumn beanColumn = getBeanColumn(fieldName);

        return beanColumn.readValue(obj);
    }

    private EntityColumn getBeanColumn(String fieldName) {
        EntityColumn beanColumn = this.columns.get(fieldName);

        if (beanColumn == null) {
            throw new DbException("not define column:" + fieldName);
        }

        return beanColumn;
    }

    /**
     * 拼接查询SQL语句
     *
     * @param sql    SQL字符缓存
     * @param fields 逗号分割的查询字段列表，传入null表示全部值对象字段
     */
    public void handleSelect(StringBuilder sql, String fields) {
        sql.append("select ");

        if (fields != null) {
            sql.append(fields).append(' ');
        } else {
            for (String fieldName : columns.keySet()) {
                sql.append(fieldName).append(',');
            }
            sql.setCharAt(sql.length() - 1, ' ');
        }

        sql.append("from ").append(modelTable.getSchemaTableName());
    }

    protected void handleSelectOne(StringBuilder sql, String fields) {
        EntityColumn idColumn = findIdColumn();

        sql.append("select ");

        if (fields != null) {
            sql.append(fields).append(' ');
        } else {
            for (EntityColumn field : columns.values()) {
                sql.append(field.getField().getName()).append(",");
            }
            sql.setCharAt(sql.length() - 1, ' ');
        }

        sql.append("from ").append(modelTable.getSchemaTableName()).append(" where ").append(idColumn.getField().getName()).append("=?");
    }

    /**
     * 读取ID值
     *
     * @param entity
     * @return
     */
    public Long readId(T entity) {
        EntityColumn idColumn = findIdColumn();

        Object o = idColumn.readValue(entity);
        if (o == null) return 0L;
        if (o instanceof Number) return ((Number) o).longValue();
        return NumberUtil.getLongIgnoreErr(o.toString());
    }

    public T createBean() {
        try {
            T bean = this.type.newInstance();
            if (bean instanceof DefaultEntity) {
                DefaultEntity b = (DefaultEntity) bean;
                b.init();
                b.setTableName(this.tableName);
                b.setIsNew(true);
            }
            return bean;
        } catch (Exception e) {
            throw new SwException(e);
        }
    }

    public T createBean(long userId) {
        T b = createBean();
        if (b instanceof DefaultEntity) {
            DefaultEntity bean = (DefaultEntity) b;
            ModelTable table = ModelTableCache.getInstance().getByName(tableName);
            ModelField field = table.findFieldByType(SwEnum.FieldType.CREATE_USER.value);
            if (field != null) bean.put(field.getName(), userId);
            field = table.findFieldByType(SwEnum.FieldType.UPDATE_USER.value);
            if (field != null) bean.put(field.getName(), userId);
        }
        return b;

    }
}

