package com.yanzuoguang.dao.impl;

import com.yanzuoguang.dao.BaseDao;
import com.yanzuoguang.dao.DaoConst;
import com.yanzuoguang.db.impl.AllBeanRowMapper;
import com.yanzuoguang.util.base.ObjectHelper;
import com.yanzuoguang.util.exception.CodeException;
import com.yanzuoguang.util.helper.DateHelper;
import com.yanzuoguang.util.helper.StringHelper;
import com.yanzuoguang.util.vo.InitDao;
import com.yanzuoguang.util.vo.MapRow;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 数据库操作的基本工具类
 * @author 颜佐光
 */
public abstract class BaseDaoImpl extends BaseDaoSql implements BaseDao {

    /**
     * 获取当前主键
     *
     * @return
     */
    private String getIdentity() {
        return StringHelper.toString(this.db.queryCell(this.getClass(), "GET_KEY", "SELECT @@IDENTITY"));
    }

    /**
     * 获取主键名称
     *
     * @return 获取主键名称
     */
    protected String getKey() {
        if (this.table == null) {
            throw new CodeException("类" + this.getClass().getName() + "未发现表结构");
        }
        return this.table.getTable().getKeyName();
    }

    /**
     * 获取主键值
     *
     * @param model 需要获取主键的实体
     * @return
     */
    protected String getKeyString(Object model) {
        String keyField = this.getKey();
        Object key = ObjectHelper.get(model, keyField);
        if (StringHelper.isEmpty(key)) {
            return "";
        }
        String keyString = key.toString();
        if (DaoConst.ZERO .equals(keyString)) {
            keyString = "";
        }
        return keyString;
    }

    /**
     * 设置主键值
     *
     * @param model 需要设置的实体
     * @param key   需要设置的主键值
     */
    protected void setKeyString(Object model, String key) {
        String keyField = this.getKey();
        ObjectHelper.set(model, keyField, key);
    }

    /**
     * 根据输入参数来获取主键
     *
     * @param from 可以为实体或字符串。为实体时必须包含 主键 字段 或者 id 字段。
     * @return
     */
    protected String getInputKey(Object from) {
        String key;
        if (from != null && from.getClass() == String.class) {
            key = StringHelper.toString(from);
        } else {
            key = this.getKeyString(from);
        }
        if (StringHelper.isEmpty(key)) {
            key = StringHelper.toString(ObjectHelper.get(from, "id"));
        }
        if (StringHelper.isEmpty(key)) {
            key = "";
        }
        return key;
    }

    /**
     * 创建数据,当不传入了主键时,则会自动生成主键,传入时不会生成。
     *
     * @param model 需要创建的数据
     * @return 创建的主键编号, 创建成功,返回主键,否则为空
     */
    @Override
    public String create(Object model) {
        // 判断主键是字符串和需要生成主键
        boolean isKeyString = this.table.getTable().getKeyType() == String.class;
        String keyString = this.getKeyString(model);

        //  生成主键
        if (StringHelper.isEmpty(keyString) && isKeyString) {
            keyString = StringHelper.getNewID();
            this.setKeyString(model, keyString);
        }

        // 检测数据合法性
        this.check(DaoConst.OPERATOR_TYPE_CREATE, keyString, model);

        // 执行创建的SQL语句
        int ret = updateSql(DaoConst.CREATE, model);
        // 判断是否需要获取自增编号(主键为整形)
        if (StringHelper.isEmpty(keyString) && !isKeyString) {
            keyString = this.getIdentity();
            this.setKeyString(model, keyString);
        }

        // 最终处理
        this.created(model);

        // 返回执行结果
        String retVal = ret > 0 ? keyString : "";
        return retVal;
    }


    /**
     * 修改数据
     *
     * @param model 需要修改的数据
     * @return 删除的主键编号
     */
    @Override
    public String update(Object model) {
        String keyString = this.getKeyString(model);
        if (StringHelper.isEmpty(keyString)) {
            throw new CodeException("表" + this.table.getTable().getName() + "主键值为空时不能更新");
        }
        this.check(DaoConst.OPERATOR_TYPE_UPDATE, keyString, model);
        SqlData sqlData = this.getSql(DaoConst.UPDATE);
        int ret = updateSql(sqlData, model);
        String retVal = ret > 0 ? keyString : "";
        return retVal;
    }

    /**
     * 检测数据
     *
     * @param operatorType 操作方法类型
     * @param keyString    主键
     * @param model        需要检测的数据
     */
    protected void check(int operatorType, String keyString, Object model) {
        if (model instanceof InitDao) {
            InitDao to = (InitDao) model;
            to.init();
        }
    }

    /**
     * 保存数据
     *
     * @param model 需要保存的数据
     * @return 保存的主键编号
     */
    @Override
    public String save(Object model) {
        String id = this.getKeyString(model);
        if (StringHelper.isEmpty(id)) {
            return create(model);
        } else {
            return update(model);
        }
    }

    /**
     * 删除数据
     *
     * @param model 需要删除的数据
     * @return 删除的主键编号
     */
    @Override
    public int remove(Object model) {
        // 获取来源主键
        Object from = model;
        String keyString = getInputKey(from);
        // 当主键存在值时,直接通过主键删除
        if (!StringHelper.isEmpty(keyString)) {
            // 去掉其他非主键的属性
            from = new HashMap<String, Object>(DaoConst.COLLECTION_INIT_SIZE);
            this.setKeyString(from, keyString);
            // 调用删除日志
            this.check(DaoConst.OPERATOR_TYPE_REMOVE, keyString, from);
        } else {
            List<MapRow> rows = this.query(MapRow.class, DaoConst.LOAD, from);
            for (MapRow row : rows) {
                keyString = this.getKeyString(from);
                if (StringHelper.isEmpty(keyString)) {
                    keyString = StringHelper.toString(AllBeanRowMapper.getLoweRowField(row, this.getKey()));
                }
                // 调用删除日志
                this.check(DaoConst.OPERATOR_TYPE_REMOVE, keyString, row);
            }
        }

        // 调用删除语句
        SqlData sqlData = this.getSql(DaoConst.REMOVE);
        return updateSql(sqlData, from);
    }

    /**
     * 加载数据
     *
     * @param model 加载数据的请求参数
     * @param cls   需要加载的数据的类型
     * @param <T>   返回数据的类型
     * @return 需要返回的数据
     */
    @Override
    public <T extends Object> T load(Object model, Class<T> cls) {
        // 获取来源主键
        Object from = model;
        String key = this.getInputKey(from);

        // 当主键存在时,只通过主键加载
        if (!StringHelper.isEmpty(key)) {
            from = new HashMap<String, Object>(DaoConst.COLLECTION_INIT_SIZE);
            this.setKeyString(from, key);
        }

        // 通过传入数据进行加载
        T to = this.queryFirst(cls, DaoConst.LOAD, from);
        if (to == null) {
            return to;
        }

        // 判断来源主键是否存在,不存在则获取加载后的主键
        if (StringHelper.isEmpty(key)) {
            key = this.getKeyString(to);
        }

        check(DaoConst.OPERATOR_TYPE_LOAD, key, to);

        return to;
    }

    /**
     * 添加统计数据
     *
     * @param cls   类型
     * @param model 实体
     * @param <T>   泛型类型
     * @return 增加统计的数据编号
     */
    public <T extends Object> String addGroup(Class<T> cls, T model) {
        // 判断前台实体
        if (model == null) {
            return "";
        }

        // 获取前台分组的MD5标识
        String md5 = this.getMd5(DaoConst.GROUP_QUERY, model);
        if (!StringHelper.isEmpty(this.table.getTable().getMD5KeyName())) {
            ObjectHelper.set(model, this.table.getTable().getMD5KeyName(), md5);
        }

        // 获取标识的实体
        T from;
        if (this.table.getTable().getKeyType() == String.class) {
            from = model;
            this.setKeyString(model, md5);
        } else if (!StringHelper.isEmpty(this.table.getTable().getMD5KeyName())) {
            Map<String, Object> map = new HashMap<>(DaoConst.COLLECTION_INIT_SIZE);
            map.put(this.table.getTable().getMD5KeyName(), md5);
            from = this.load(map, cls);
        } else {
            from = this.queryFirst(cls, DaoConst.GROUP_QUERY, model);
        }

        // 当没有存在,则创建,否则 UpdateAdd
        if (from == null) {
            return this.create(model);
        } else {
            // 获取后台的编号,并写入到前台传入的数据上面
            String keyField = this.getKey();
            Object key = ObjectHelper.get(from, keyField);
            ObjectHelper.set(model, keyField, key);

            // 执行更新SQL语句
            SqlData sqlData = this.getSql(DaoConst.GROUP_ADD);
            int ret = this.updateSql(sqlData, model);
            if (ret < 1) {
                return this.create(model);
            } else {
                return key.toString();
            }
        }
    }

    /**
     * 根据SQL语句名称和实体获取MD5值
     *
     * @param sqlName SQL语句名称
     * @param model   获取加密之后的实体
     * @param <T>     获取的类型
     * @return 获取加密之后的值
     */
    protected <T extends Object> String getMd5(String sqlName, T model) {
        SqlData sql = this.getSql(sqlName);
        StringBuilder sb = new StringBuilder();
        String date = StringHelper.EMPTY;
        for (SqlDataField field : sql.getSqlDataFields()) {
            if (sb.length() > 0) {
                sb.append(":");
            }
            Object item = ObjectHelper.get(model, field.paraName);
            sb.append(item);

            if (StringHelper.isEmpty(date)) {
                String itemString = String.valueOf(item);
                if (item instanceof Date) {
                    date = DateHelper.getDateTimeString((Date) item);
                } else if (DateHelper.isDateFormat(itemString)) {
                    date = itemString;
                }
            }
        }
        if (StringHelper.isEmpty(date)) {
            return StringHelper.md5(sb.toString());
        } else {
            return StringHelper.getNewMD5Id(DateHelper.getDateTime(date), sb.toString());
        }
    }

    /**
     * 添加统计数据列表
     *
     * @param sqlName 需要执行的SQL语句名称
     * @param model   需要执行的SQL语句的实体
     * @return
     */
    protected int addGroupList(String sqlName, Object model) {
        this.updateSql(sqlName + "_GroupInit", model);
        return this.updateSql(sqlName + "_GroupAdd", model);
    }

    /***
     * 查询数据是否存在,当存在时修改,否则增加
     * @param cls 需要创建的实体的类型
     * @param sqlName   执行的SQL语句
     * @param request 前台参数,不能包含主键以及其他不需要修改的字段
     * @return 保存成功,返回保存的ID,保存失败,返回空值
     */
    public <T extends Object> String saveWith(Class<T> cls, String sqlName, Object request) {
        T from = this.queryFirst(cls, sqlName, request);
        if (from == null) {
            try {
                from = cls.newInstance();
            } catch (Exception ex) {
                throw new CodeException("创建对象" + cls.getName() + "出错", ex);
            }
            ObjectHelper.writeWithFrom(from, request);
            return this.create(from);
        } else {
            ObjectHelper.writeWithFrom(from, request);
            return this.update(from);
        }
    }

    /**
     * 创建成功后的处理
     *
     * @param model 创建的实体数据
     */
    protected void created(Object model) {

    }

    /**
     * 执行是否存在的SQL语句
     *
     * @param sqlName SQL语句名称
     * @param model   实体
     * @param msg     消息
     */
    protected void checkExist(String sqlName, Object model, String msg) {
        MapRow row = this.queryFirst(sqlName, model);
        if (row != null) {
            throw new CodeException(msg);
        }
    }
}