package cc.smtweb.framework.core.db;

import cc.smtweb.framework.core.common.SwEnum;
import cc.smtweb.framework.core.db.dao.AbstractEntityDao;
import cc.smtweb.framework.core.db.dao.EntityColumn;
import cc.smtweb.framework.core.db.impl.DefaultEntity;
import cc.smtweb.framework.core.db.jdbc.JdbcEngine;
import cc.smtweb.framework.core.db.vo.ModelField;
import cc.smtweb.framework.core.exception.BizException;
import cc.smtweb.framework.core.util.CommUtil;
import lombok.Getter;
import org.apache.commons.lang3.StringUtils;

import java.text.MessageFormat;
import java.util.*;

/**
 * 提供数据对象Dao操作
 *
 * @author xkliu
 */
public class EntityDao<T> extends AbstractEntityDao<T> {
    @Getter
    protected JdbcEngine jdbcEngine;

    public EntityDao(Class<T> type, JdbcEngine jdbcEngine) {
        super(type);
        this.jdbcEngine = jdbcEngine;
    }

    public EntityDao(String tableName, JdbcEngine jdbcEngine) {
        super(tableName);
        this.jdbcEngine = jdbcEngine;
    }

    /**
     * 获取数据库唯一id
     *
     * @return 返回ID值
     */
    public long nextId() {
        return this.jdbcEngine.nextId();
    }

    /**
     * 根据PO对象的ID值更新其余所有字段
     *
     * @param entity PO对象
     * @return 更新数量
     */
    public int updateEntity(T entity) {
        return updateEntity(entity, null, null);
    }

    /**
     * 使用ID字段更新单行数据
     *
     * @param entity PO值对象，对象属性是需要更新的值
     * @param fields 需要更新额字段列表，逗号分隔
     * @return 更新数量
     */
    public int updateEntity(T entity, String fields) {
        return updateEntity(entity, fields, null);
    }

    public int updateEntityEx(T entity, String... exfields) {
        if (exfields == null || exfields.length == 0) {
            return updateEntity(entity, null, null);
        }
        Set<String> excludeFields = new HashSet<>(exfields.length);
        for (String f : exfields) {
            excludeFields.add(f.toLowerCase());
        }
        StringBuilder fields = new StringBuilder(512);
        for (ModelField field : modelTable.getFields()) {
            if (!excludeFields.contains(field.getName())) {
                fields.append(",").append(field.getName());
            }
        }
        return updateEntity(entity, fields.substring(1), null);
    }

    /**
     * 指定自定义条件更新对象
     *
     * @param entity      PO值对象，对象属性是需要更新的值和更新条件值
     * @param fields      需要更新额字段列表，逗号分隔
     * @param whereFields 更新条件字段列表，逗号分隔
     * @return 更新数量
     */
    public int updateEntity(T entity, String fields, String whereFields) {
        StringBuilder sb = new StringBuilder();
        Object[] params = this.handleUpdate(entity, sb, fields, whereFields);

        return jdbcEngine.update(sb.toString(), params);
    }

    /**
     * 用PO对象所有字段入单行数据
     *
     * @param type   PO对象字段范围的类，是entity的父类
     * @param entity PO对象
     * @return 更新数量
     */
    public int insertEntity(Class<? super T> type, T entity) {
        return insertEntity(type, entity, null);
    }

    /**
     * 用PO对象所有字段入单行数据
     *
     * @param entity PO对象
     * @return 更新数量
     */
    public int insertEntity(T entity) {
        return insertEntity(entity.getClass(), entity, null);
    }

    /**
     * 插入单行数据
     *
     * @param entity PO对象
     * @param fields 逗号分隔的字段列表
     * @return 更新数量
     */
    public int insertEntity(T entity, String fields) {
        return insertEntity(entity.getClass(), entity, fields);
    }

    private int insertEntity(Class<?> type, T entity, String fields) {
        StringBuilder sb = new StringBuilder();

        Object[] params = handleInsert(entity, sb, fields);

        return jdbcEngine.update(sb.toString(), params);
    }

    /**
     * 批量插入单行数据
     *
     * @param entities PO对象列表
     * @param fields   逗号分隔的字段列表
     * @return 更新数量
     */
    public int[] batchInsertEntity(Collection<T> entities) {
        StringBuilder sql = new StringBuilder();

        sql.append("insert into ").append(modelTable.getSchemaTableName()).append("(");
        List<EntityColumn> insertColumns = new ArrayList<>(this.columns.size());

        for (EntityColumn column : this.columns.values()) {
            sql.append(column.getField().getName()).append(",");
            insertColumns.add(column);
        }

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

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

        // 参数列表
        List<Object[]> listParams = new ArrayList<>(entities.size());
        for (T obj : entities) {
            List<Object> params = new ArrayList<>(this.columns.size());

            for (EntityColumn column : this.columns.values()) {
                params.add(column.readValue(obj));
            }

            listParams.add(params.toArray());
        }

        return jdbcEngine.batchUpdate(sql.toString(), listParams);
    }

    public void batchUpdateEntity(Collection<T> list) {
        if (CommUtil.isEmpty(list)) return;
        batchUpdateEntity(list, null);
    }

    //批量更新
    public void batchUpdateEntity(Collection<T> list, String fields) {
        if (CommUtil.isEmpty(list)) return;

        List<String> listFields = adjustFields(fields, SwEnum.FieldType.CREATE_TIME.value, SwEnum.FieldType.LAST_TIME.value);
        List<String> fieldList = new ArrayList<>();
        if (listFields == null) {
            for (EntityColumn column : this.columns.values()) {
                if (column.getField().getName().equals(modelTable.getIdField())) continue;
                fieldList.add(column.getField().getName());
            }
        } else {
            for (String name : listFields) {
                if (name.equals(modelTable.getIdField())) continue;
                fieldList.add(name);
            }
        }

        String tableName = this.modelTable.getSchemaTableName();
        String tableFields = EntityHelper.nameString(fieldList, "=?, ", "=? ");
        String tablePks = this.modelTable.getIdField() + " =? ";
        String sql = MessageFormat.format("UPDATE {0} SET \n  {1}\n  WHERE {2}", tableName, tableFields, tablePks);

        fieldList.add(this.modelTable.getIdField());
        List<Object[]> params = new ArrayList<>(list.size());
        list.forEach(k -> {
            params.add(EntityHelper.getFieldValueArgs(fieldList, (DefaultEntity) k));
        });
        DbEngine.getInstance().batchUpdate(sql, params);
    }


    /**
     * 根据ID值删除单行数据
     *
     * @param entity PO对象
     * @return 删除数量
     */
    public int deleteEntity(T entity) {
        StringBuilder sb = new StringBuilder();

        Object[] params = handleDelete(entity, sb);

        return jdbcEngine.update(sb.toString(), params);
    }

    /**
     * 根据ID值删除单行数据
     *
     * @param id 记录主建值
     * @return 删除数量
     */
    public int deleteEntity(Long id) {
        StringBuilder sb = new StringBuilder();
        handleDelete(sb);

        return jdbcEngine.update(sb.toString(), id);
    }

    /**
     * 根据ID值删除单行数据
     *
     * @param whereSql Where条件SQL语句，以where开头
     * @param params   条件的值，可以多个
     * @return 删除数量
     */
    public int deleteEntity(String whereSql, Object... params) {
        StringBuilder sb = new StringBuilder("DELETE FROM ");
        sb.append(modelTable.getSchemaTableName()).append(" ").append(whereSql);

        return jdbcEngine.update(sb.toString(), params);
    }

    public int deleteEntity(Collection<Long> ids) {
        if (CommUtil.isEmpty(ids)) return 0;
        return deleteEntity(" where " + modelTable.getIdField() + " in (" + CommUtil.getSqlInIds(ids) + ")");
    }

    public int batchDelete(Collection<? extends DefaultEntity> list) {
        if (CommUtil.isEmpty(list)) return 0;
        List<Long> ids = new ArrayList<>(list.size());
        for (DefaultEntity bean : list) {
            ids.add(bean.getEntityId());
        }
        return deleteEntity(" where " + modelTable.getIdField() + " in (" + CommUtil.getSqlInIds(ids) + ")");
    }

    private void setTableName(T bean) {
        if (bean instanceof DefaultEntity) {
            ((DefaultEntity) bean).setTableName(this.tableName);
        }
    }

    private void setTableName(List<T> list) {
        if (!list.isEmpty() && list.get(0) instanceof DefaultEntity) {
            for (T bean : list) {
                ((DefaultEntity) bean).setTableName(this.tableName);
            }
        }
    }

    /**
     * 查询单行数据，返回bean
     */
    public T queryEntity(Long id) {
        return queryEntity(id, null);
    }

    public T queryEntity(Long id, String fields) {
        StringBuilder sb = new StringBuilder();
        handleSelectOne(sb, fields);

        List<T> list = jdbcEngine.query(sb.toString(), type, id);

        if (list != null && !list.isEmpty()) {
            T bean = list.get(0);
            setTableName(bean);
            return bean;
        }

        return null;
    }

    /**
     * 查询对象所有数据，返回列表
     */
    public List<T> query() {
        return query(null);
    }

    /**
     * 查询对象所有数据，返回列表
     */
    public Map<String, String> queryNames(List<Long> ids) {
        if (ids == null || ids.isEmpty()) return new HashMap<>();
        ModelField field = modelTable.findFieldByType(SwEnum.FieldType.NAME.value);
        if (field == null) return new HashMap<>();

        StringBuilder sb = new StringBuilder();
        handleSelect(sb, modelTable.getIdField() + "," + field.getName());
        sb.append("\nwhere " + modelTable.getIdField() + " in (" + CommUtil.getSqlInIds(ids) + ")");

        return jdbcEngine.query(sb.toString(), rs -> {
            Map<String, String> map = new HashMap<>();
            while (rs.next()) {
                map.put(rs.getString(1), rs.getString(2));
            }
            return map;
        });
    }

    /**
     * 查询对象所有数据，返回列表
     */
    public List<T> query(String fields) {
        if (StringUtils.isEmpty(fields) && columns.isEmpty()) return null;
        StringBuilder sb = new StringBuilder();
        handleSelect(sb, fields);

        List<T> list = jdbcEngine.query(sb.toString(), type);
        setTableName(list);
        return list;
    }

    public T queryEntityWhere(String sqlWhere, Object... params) {
        List<T> list = queryWhere(sqlWhere, params);
        if (CommUtil.isEmpty(list)) return null;
        return list.get(0);
    }

    /**
     * 查询对象所有数据，返回列表
     */
    public List<T> queryWhere(String sqlWhere, Object... params) {
        StringBuilder sb = new StringBuilder();
        handleSelect(sb, null);
        if (StringUtils.isNotEmpty(sqlWhere)) {
            sb.append(" where ").append(sqlWhere);
        }

        List<T> list = jdbcEngine.query(sb.toString(), type, params);
        setTableName(list);
        return list;
    }

    //原sql（select fields from table）后追加一段自定义，一般用于join
    public List<T> queryEx(String sqlAdd, Object... params) {
        StringBuilder sb = new StringBuilder();
        handleSelect(sb, null);
        if (StringUtils.isNotEmpty(sqlAdd)) {
            sb.append(sqlAdd);
        }

        List<T> list = jdbcEngine.query(sb.toString(), type, params);
        setTableName(list);
        return list;
    }

    public List<Long> queryIdListWhere(String sqlWhere, Object... params) {
        StringBuilder sb = new StringBuilder();
        handleSelect(sb, modelTable.getIdField());
        if (StringUtils.isNotEmpty(sqlWhere)) {
            sb.append(" where ").append(sqlWhere);
        }

        return jdbcEngine.queryLongList(sb.toString(), type, params);
    }

    /**
     * 检查表字段是否违反唯一索引
     *
     * @param bean
     * @param fields
     */
    public void checkUnique(T bean, String... fields) {
        if (fields.length == 0) return;
        String ss = "", sTitle = "";
        List<Object> args = new ArrayList<>(fields.length + 1);
        args.add(readId(bean));
        for (String f : fields) {
            if (StringUtils.isEmpty(f)) continue;
            ss += " and " + f + "=?";
            args.add(readValue(bean, f));
            sTitle += "+" + modelTable.findFieldTitle(f);
        }
        if (jdbcEngine.isExists("select 1 from " + modelTable.getSchemaTableName() + " where " + modelTable.getIdField() + " <> ? " + ss, args.toArray()))
            throw new BizException(sTitle.substring(1) + " 不能重复！");
    }
}
