package com.yanzuoguang.dao.impl;

import com.yanzuoguang.dao.DaoConst;
import com.yanzuoguang.dao.TableAnnotation;
import com.yanzuoguang.util.helper.StringHelper;
import com.yanzuoguang.util.base.MethodField;
import com.yanzuoguang.util.base.ObjectHelper;

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

/**
 * 表结构的基本信息
 *
 * @author 颜佐光
 */
public class TableStruct {

    /**
     * 表名称
     */
    private String name;

    /**
     * 主键名称
     */
    private TableFieldVo key;

    /**
     * MD5KEY
     */
    private TableFieldVo md5Key;

    /**
     * 其他字段
     */
    private List<TableFieldVo> fields = new ArrayList<TableFieldVo>();

    public List<TableFieldVo> getFields() {
        return fields;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public TableFieldVo getKey() {
        return key;
    }

    public TableFieldVo getMd5Key() {
        return md5Key;
    }

    public String getKeyName() {
        return this.key.inputName;
    }

    public String getMD5KeyName() {
        return md5Key != null ? md5Key.inputName : null;
    }

    public Class<?> getKeyType() {
        return this.key.type;
    }

    /**
     * 构造函数
     */
    public TableStruct() {
        this.name = "";
        this.key = new TableFieldVo(DaoConst.ID_FIELD);
    }

    /**
     * 通过实体的字段来创建表结构信息
     *
     * @param name 表名称
     * @param cls  关联的实体,主键放在第一位,其他字段放到后面;需要注意的是必需和表结构对应起来,会有隐性BUG,比如说在实体中增加了字段,会导致增加修改失败
     */
    public TableStruct(String name, Class<?> cls) {
        this.name = name;

        // 获取实体中的所有字段信息,包含get、set、field
        HashMap<String, MethodField> fields = ObjectHelper.getTypeField(cls);

        // 遍历字段
        for (Map.Entry<String, MethodField> entry : fields.entrySet()) {

            // 字段信息获取
            MethodField field = entry.getValue();
            if (field.field == null && field.getMethod == null) {
                continue;
            }

            String fieldName = field.name;
            String fieldInputName = field.name;
            Class<?> fieldType = String.class;
            TableAnnotation annotation = null;
            if (field.field != null) {
                annotation = field.field.getAnnotation(TableAnnotation.class);
                fieldType = field.field.getType();
            } else if (field.getMethod != null) {
                annotation = field.getMethod.getAnnotation(TableAnnotation.class);
                fieldType = field.getMethod.getReturnType();
            }
            if (annotation != null) {
                fieldName = annotation.value();
            }

            TableFieldVo vo = new TableFieldVo(fieldName, fieldInputName, fieldType);
            // 判断是否属于统计字段
            if ("md5key".equals(vo.inputLName)) {
                this.md5Key = vo;
            }

            // 判断是否是主键,获取主键数据类型
            if (this.key == null) {
                this.key = vo;
            } else {
                this.fields.add(vo);
            }
        }
    }

    public TableFieldVo getField(String name) {
        if (StringHelper.isEmpty(name)) {
            return null;
        }
        for (TableFieldVo fieldVo : this.fields) {
            if (fieldVo.lName.equals(name.toLowerCase()) || fieldVo.inputLName.equals(name.toLowerCase())) {
                return fieldVo;
            }
        }
        return null;
    }

    /**
     * 判断是否包含版本号字段
     *
     * @return
     */
    public TableFieldVo getVersion() {
        return this.getField(DaoConst.VERSON);
    }

    /**
     * 判断是否包含remove字段
     *
     * @return
     */
    private TableFieldVo getRemove() {
        return this.getField(DaoConst.ISREMOVE);
    }

    /**
     * 通过表结构自动生成SQL语句
     *
     * @return
     */
    public void init(TableSqlCache table) {
        table.setTable(this);
        if (!StringHelper.isEmpty(this.name)) {
            table.add(releaseSqlCreate(), releaseSqlUpdate(), releaseSqlRemove(), releaseSqlLoad());
        }
    }

    /**
     * 生成创建的SQL语句
     *
     * @return
     */
    private SqlData releaseSqlCreate() {
        // 生成添加的SQL语句
        String text = DaoConst.INSERT_MODEL.replace(DaoConst.TABLE_CODE, this.name);
        SqlData sql = new SqlData(DaoConst.CREATE, text);
        String flag = "";
        if (this.getKeyType() == String.class) {
            sql.addParaConst(this.key.inputName, DaoConst.FIELD_CODE, this.key.name, DaoConst.VALUES_CODE, "?");
            flag = ",";
        }
        for (TableFieldVo field : this.fields) {
            sql.addParaConst(field.inputName, DaoConst.FIELD_CODE, flag + field.name, DaoConst.VALUES_CODE, flag + "?");
            flag = ",";
        }
        return sql;
    }

    /**
     * 生成修改的SQL语句
     *
     * @return
     */
    private SqlData releaseSqlUpdate() {
        // 生成添加的SQL语句
        String text = DaoConst.UPDATE_MODEL.replace(DaoConst.TABLE_CODE, this.name);
        SqlData sql = new SqlData(DaoConst.UPDATE, text);
        TableFieldVo removeField = this.getRemove();
        TableFieldVo versionField = this.getVersion();
        sql.addParaConst(this.key.inputName,
                DaoConst.FIELD_CODE, "" + this.key.name + "=" + this.key.name,
                DaoConst.WHERE_CODE, " AND " + this.key.name + "=?");
        for (TableFieldVo field : this.fields) {
            if (field == removeField || field == versionField) {
                continue;
            }
            sql.addParaConst(field.inputName, DaoConst.FIELD_CODE, "," + field.name + "=?");
        }
        if (removeField != null) {
            sql.addParaConst(removeField.inputName, DaoConst.WHERE_CODE, " AND " + removeField.name + "=0");
        }
        if (versionField != null) {
            sql.addParaConst(versionField.inputName,
                    DaoConst.FIELD_CODE, "," + versionField.name + "=1+" + versionField.name,
                    DaoConst.WHERE_CODE, " AND " + versionField.name + "=?");
        }
        return sql;
    }

    /**
     * 生成删除的SQL语句
     *
     * @return
     */
    private SqlData releaseSqlRemove() {
        TableFieldVo removeField = this.getRemove();
        TableFieldVo versionField = this.getVersion();
        if (removeField != null) {
            // 生成添加的SQL语句
            String text = DaoConst.UPDATE_MODEL.replace(DaoConst.TABLE_CODE, this.name);
            SqlData sql = new SqlData(DaoConst.REMOVE, text);
            sql.addCode(DaoConst.FIELD_CODE, removeField.name + "=1");
            if (versionField != null) {
                sql.addCode(DaoConst.FIELD_CODE, "," + versionField.name + "=1+" + versionField.name);
            }
            addWhereField(sql, "");
            return sql;
        } else {
            String text = DaoConst.REMOVE_MODEL.replace(DaoConst.TABLE_CODE, this.name);
            SqlData sql = new SqlData(DaoConst.REMOVE, text);
            addWhereField(sql, "");
            return sql;
        }
    }

    /**
     * 生成加载的SQL语句
     *
     * @return
     */
    private SqlData releaseSqlLoad() {
        // 生成添加的SQL语句
        String text = DaoConst.LOAD_MODEL.replace(DaoConst.TABLE_CODE, this.name);
        SqlData sql = new SqlData(DaoConst.LOAD, text);
        addWhereField(sql, "a.");
        return sql;
    }

    /**
     * 当前当前表所有字段的等于SQL语句
     *
     * @param sql
     */
    private void addWhereField(SqlData sql, String tag) {
        TableFieldVo removeField = this.getRemove();
        sql.add(this.key.inputName, " AND " + tag + this.key.name + "=?");
        for (TableFieldVo field : this.fields) {
            sql.add(field.inputName, " AND " + tag + field.name + "=?");
        }
        if (removeField != null) {
            sql.addConst(DaoConst.ISREMOVE_CONTANS, " AND " + tag + removeField.name + "=0");
        }
    }

    /**
     * 生成统计的SQL语句
     *
     * @param sqlTableData 需要生成的实体
     * @param whereFields  WHERE字段
     * @param updateFields 需要增加的值的字段
     */
    public void addGroupSql(TableSqlCache sqlTableData, TableFieldString whereFields, TableFieldString updateFields) {
        sqlTableData.add(this.releaseSqlWhere(DaoConst.GROUP_QUERY, DaoConst.LOAD_MODEL, whereFields));
        sqlTableData.add(this.releaseSql(DaoConst.GROUP_ADD, DaoConst.UPDATE_MODEL, "{FIELD}={FIELD}+?", updateFields, new TableFieldString(this.key.name)));
    }

    /**
     * 生成SQL语句
     *
     * @param name
     * @param model
     * @param whereFields
     * @return
     */
    public SqlData releaseSqlWhere(String name, String model, TableFieldString whereFields) {
        return this.releaseSql(name, model, "", new TableFieldString(), whereFields);
    }

    /**
     * 生成SQL语句
     *
     * @param name        SQL语句名称
     * @param model       SQL语句模板
     * @param valueModel  值字段模板
     * @param valueFields 值字段
     * @param whereFields WHERE字段
     * @return
     */
    public SqlData releaseSql(String name, String model, String valueModel, TableFieldString valueFields, TableFieldString whereFields) {
        // 参数字段
        List<String> paraFields = new ArrayList<String>();

        // 生成添加的SQL语句
        StringBuilder sbField = new StringBuilder();
        for (String field : valueFields.getFields()) {
            if (sbField.length() > 0) {
                sbField.append(",");
            }
            sbField.append(valueModel.replace(DaoConst.FIELD_CODE, field));
            paraFields.add(field);
        }

        StringBuilder sbWhere = new StringBuilder();
        for (String field : whereFields.getFields()) {
            sbWhere.append(" AND ");
            sbWhere.append(field);
            sbWhere.append("=?");
            paraFields.add(field);
        }

        String[] fields = new String[paraFields.size()];
        fields = paraFields.toArray(fields);
        // 生成修改的SQL语句
        String sql = model.replace(DaoConst.TABLE_CODE, this.name).replace(DaoConst.FIELD_CODE, sbField.toString()).replace(DaoConst.WHERE_CODE, sbWhere.toString());
        SqlData ret = new SqlData(name, sql, fields);
        return ret;
    }

    /**
     * 生成根据某些字段不存在则保存的SQL语句
     *
     * @param tableStruct
     * @param sqlName
     * @param whereFields
     */
    public void addSaveWithSql(TableSqlCache tableStruct, String sqlName, TableFieldString whereFields) {
        tableStruct.add(this.releaseSqlWhere(sqlName, DaoConst.LOAD_MODEL, whereFields));
    }


    /**
     * 生成判断数据是否存在的SQL语句
     *
     * @param sqlTableData
     * @param sqlName
     * @param fields
     */
    public void addExist(TableSqlCache sqlTableData, String sqlName, String[] fields) {
        String text = DaoConst.LOAD_MODEL.replace(DaoConst.TABLE_CODE, this.name);
        SqlData sql = new SqlData(sqlName, text);
        sql.addPara(this.key.inputName, DaoConst.WHERE_CODE, " AND a." + this.key.name + "<>?");
        for (String field : fields) {
            sql.addParaConst(field, DaoConst.WHERE_CODE, " AND a." + field + "=?");
        }
        if (getRemove() != null) {
            sql.addParaConst(DaoConst.ISREMOVE_CONTANS, DaoConst.WHERE_CODE, " AND a." + DaoConst.ISREMOVE + "=0");
        }
        sqlTableData.add(sql);
    }
}