package cc.smtweb.framework.core.db;

import cc.smtweb.framework.core.cache.AbstractCache;
import cc.smtweb.framework.core.cache.CacheManager;
import cc.smtweb.framework.core.common.SwConsts;
import cc.smtweb.framework.core.common.SwEnum;
import cc.smtweb.framework.core.common.SwMap;
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.ModelLinkName;
import cc.smtweb.framework.core.db.vo.ModelTable;
import cc.smtweb.framework.core.exception.BizException;
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.StringUtil;
import org.apache.commons.lang3.StringUtils;

import java.util.*;

/**
 * Created by Akmm at 2022/5/6 15:43
 * 实体辅助类
 */
public class EntityHelper {
    private static Map<String, IFormatter> mapFormatter;

    static {
        mapFormatter = new HashMap<>();
        mapFormatter.put(SwEnum.DataType.BOOL.value, value -> {
            if (StringUtil.strToBool(value)) return "是";
            return "";
        });
        mapFormatter.put(SwEnum.DataType.CURRENCY.value, value -> {
            long v = NumberUtil.getLongIgnoreErr(value);
            return NumberUtil.toStdAmountString(v / 100.0);

        });
        mapFormatter.put(SwEnum.DataType.DATE.value, DateUtil::formatDate);
        mapFormatter.put(SwEnum.DataType.DATETIME.value, DateUtil::formatDateTime);
        mapFormatter.put(SwEnum.DataType.TIME.value, DateUtil::formatTime);
        mapFormatter.put(SwEnum.DataType.POS.value, value -> {
            long v = NumberUtil.getLongIgnoreErr(value);
            return NumberUtil.toStdNumberString(v / 1000000.0, 6);
        });
    }

    public static Object[] getFieldValueArgs(List<String> fieldNames, DefaultEntity bean) {
        Object[] args = new Object[fieldNames.size()];
        fillFieldValueArgs(fieldNames, bean, args);
        return args;
    }

    public static void fillFieldValueArgs(List<String> fieldNames, DefaultEntity bean, Object[] args) {
        ModelTable modelEntity = bean.getModelTable();
        if (modelEntity == null) return;
        //获取实体类字段对象；
        int i = 0;
        for (String fieldName : fieldNames) {
            String fname = fieldName.toLowerCase();  //转换为小写，因此在bean中定义的字段都是小写的；
            Object v = bean.getData().get(fname);
            String value = v != null ? v.toString(): "";
            final ModelField field = modelEntity.findField(fname);
            if (field != null) {
                if (StringUtil.isEmpty(value) && StringUtil.isNotEmpty(field.getDefaultValue())) {
                    value = field.getDefaultValue();
                }
                args[i++] = SwEnum.DataType.getDataValue(field.getDataType(), value);
            } else {
                args[i++] = v;
            }
        }
    }


    public static String nameString(List<String> fieldNameList, String separator, String afterLast) {
        StringBuilder returnString = new StringBuilder();
        if (fieldNameList.size() < 1) {
            return "";
        }
        int i = 0;
        for (; i < fieldNameList.size() - 1; i++) {
            returnString.append(fieldNameList.get(i));
            returnString.append(separator);
        }
        returnString.append(fieldNameList.get(i));
        returnString.append(afterLast);
        return returnString.toString();
    }

    //获取字段别名
    private static String getFieldAlias(Map<String, String> mapFieldAlias, String field) {
        return (mapFieldAlias != null && mapFieldAlias.containsKey(field)) ? mapFieldAlias.get(field): field;
    }

    /**
     * 添加关联字段值
     *
     * @param tableName
     * @param bean
     */
    public static void loadBeanText(String tableName, SwMap bean, Map<String, String> mapFieldAlias) {
        //添加关联字段值
        ModelTable table = ModelTableCache.getInstance().getByName(tableName);
        if (table == null) return;
        loadBeanLink(table, bean, mapFieldAlias);
        setFieldDisplayText(table, bean, mapFieldAlias);
    }

    private static void loadBeanLink(ModelTable table, SwMap bean, Map<String, String> mapFieldAlias) {
        List<ModelLinkName> listLink = table.findLinkeNames();
        if (listLink.isEmpty()) return;

        //有缓存的，从缓存拿，无缓存的放Map，最后从数据库去拿
        Map<String, List<String>> mapIds = new HashMap<>();

        for (ModelLinkName l : listLink) {
            final String fieldName = getFieldAlias(mapFieldAlias, l.getFieldName());
            String value = bean.readString(fieldName);
            if (StringUtils.isEmpty(value)) continue;
            String[] ids = StringUtils.split(value, ",");
            if (l.getLinkTable().isNeedCache()) {
                EntityDao dao = DbEngine.getInstance().findDao(l.getLinkTable().getName());
                AbstractCache cache = CacheManager.getIntance().getCache(l.getLinkTable().getName());
                String names = "";
                for (String sId : ids) {
                    Object b = cache.get(sId);
                    String sn = (String) dao.readValue(b, l.getLinkNameField());
                    if (StringUtils.isNotEmpty(sn)) names += "," + sn;
                }
                bean.put(fieldName + SwConsts.TEXT_SUFFIX, names.length() > 1 ? names.substring(1): "");
            } else {
                List<String> list = mapIds.computeIfAbsent(l.getLinkTable().getName(), k -> new ArrayList<>());
                Collections.addAll(list, ids);
            }
        }


        if (mapIds.isEmpty()) return;
        //数据库查询
        Map<String, Map<String, String>> mapValue = new HashMap<>();
        for (Map.Entry<String, List<String>> entry : mapIds.entrySet()) {
            EntityDao dao = DbEngine.getInstance().findDao(entry.getKey());
            mapValue.put(entry.getKey(), dao.queryNames(entry.getValue()));
        }
        //加值

        for (ModelLinkName l : listLink) {
            if (!mapValue.containsKey(l.getLinkTable().getName())) continue;
            Map<String, String> mapV = mapValue.get(l.getLinkTable().getName());

            final String fieldName = getFieldAlias(mapFieldAlias, l.getFieldName());
            String value = bean.readString(fieldName);
            if (StringUtils.isEmpty(value)) continue;

            String[] ids = StringUtils.split(value, ",");
            String names = "";
            for (String sId : ids) {
                String sn = mapV.get(sId);
                if (StringUtils.isNotEmpty(sn)) names += "," + sn;
            }
            bean.put(fieldName + SwConsts.TEXT_SUFFIX, names.substring(1));
        }
    }

    public static void loadBeanText(String tableName, List<SwMap> listData, Map<String, String> mapFieldAlias) {
        if (listData.isEmpty()) return;
        //添加关联字段值
        ModelTable table = ModelTableCache.getInstance().getByName(tableName);
        if (table == null) return;
        loadBeanLink(table, listData, mapFieldAlias);
        for (SwMap bean : listData) {
            setFieldDisplayText(table, bean, mapFieldAlias);
        }
    }

    /**
     * 设置非关联字段中金额、日期等类型字段的显示值
     *
     * @param table
     * @param bean
     * @param mapFieldAlias
     */
    private static void setFieldDisplayText(ModelTable table, SwMap bean, Map<String, String> mapFieldAlias) {
        for (ModelField field : table.getFields()) {
            IFormatter formatter = mapFormatter.get(field.getDataType());
            if (formatter == null) continue;
            final String fieldName = getFieldAlias(mapFieldAlias, field.getName());
            String value = bean.readString(fieldName);
            if (StringUtils.isNotEmpty(value)) {
                bean.put(fieldName + SwConsts.TEXT_SUFFIX, formatter.format(value));
            }
        }
    }

    /**
     * 加载关联字段的Text
     *
     * @param table         表定义
     * @param listData      数据
     * @param mapFieldAlias 字段别名关系
     */
    private static void loadBeanLink(ModelTable table, List<SwMap> listData, Map<String, String> mapFieldAlias) {
        List<ModelLinkName> listLink = table.findLinkeNames();
        if (listLink.isEmpty()) return;

        //有缓存的，从缓存拿，无缓存的放Map，最后从数据库去拿
        Map<String, List<Long>> mapIds = new HashMap<>();
        for (SwMap row : listData) {
            for (ModelLinkName l : listLink) {
                final String fieldName = getFieldAlias(mapFieldAlias, l.getFieldName());
                String value = row.readString(fieldName);
                if (StringUtils.isEmpty(value)) continue;
                String[] ids = StringUtils.split(value, ",");
                if (l.getLinkTable().isNeedCache()) {
                    EntityDao dao = DbEngine.getInstance().findDao(l.getLinkTable().getName());
                    AbstractCache cache = CacheManager.getIntance().getCache(l.getLinkTable().getName());
                    String names = "";
                    for (String sId : ids) {
                        Object b = cache.get(sId);
                        String sn = (String) dao.readValue(b, l.getLinkNameField());
                        if (StringUtils.isNotEmpty(sn)) names += "," + sn;
                    }
                    if (StringUtils.isNotEmpty(names)) {
                        row.put(fieldName + SwConsts.TEXT_SUFFIX, names.substring(1));
                    } else {
                        row.put(fieldName + SwConsts.TEXT_SUFFIX, "");
                    }
                } else {
                    List<Long> list = mapIds.computeIfAbsent(l.getLinkTable().getName(), k -> new ArrayList<>());
                    for (String id : ids) {
                        list.add(NumberUtil.getLongIgnoreErr(id));
                    }
                }
            }
        }

        if (mapIds.isEmpty()) return;
        //数据库查询
        Map<String, Map<String, String>> mapValue = new HashMap<>();
        for (Map.Entry<String, List<Long>> entry : mapIds.entrySet()) {
            EntityDao dao = DbEngine.getInstance().findDao(entry.getKey());
            mapValue.put(entry.getKey(), dao.queryNames(entry.getValue()));
        }
        //加值
        for (SwMap row : listData) {
            for (ModelLinkName l : listLink) {
                if (!mapValue.containsKey(l.getLinkTable().getName())) continue;
                Map<String, String> mapV = mapValue.get(l.getLinkTable().getName());

                final String fieldName = getFieldAlias(mapFieldAlias, l.getFieldName());
                String value = row.readString(fieldName);
                if (StringUtils.isEmpty(value)) continue;

                String[] ids = StringUtils.split(value, ",");
                String names = "";
                for (String sId : ids) {
                    String sn = mapV.get(sId);
                    if (StringUtils.isNotEmpty(sn)) names += "," + sn;
                }
                row.put(fieldName + SwConsts.TEXT_SUFFIX, names.substring(1));
            }
        }
    }

    //检查表记录是否被引用
    public static void checkExists(long tableId, long id) {
        ModelTable fkTable = ModelTableCache.getInstance().get(tableId);
        if (fkTable == null) throw new BizException("待检查表(" + tableId + ")为空！");
        checkExists(fkTable, id);
    }

    public static void checkExists(String tableName, long id) {
        ModelTable fkTable = ModelTableCache.getInstance().getByName(tableName);
        if (fkTable == null) throw new BizException("待检查表(" + tableName + ")为空！");
        checkExists(fkTable, id);
    }

    /**
     * 检查记录是否被使用
     *
     * @param fkTable
     * @param id
     */
    public static void checkExists(ModelTable fkTable, long id) {
        Set<ModelTable> list = ModelTableCache.getInstance().getByLink(fkTable.getId());
        if (list == null || list.isEmpty()) return;
        for (ModelTable table : list) {
            StringBuilder sql = new StringBuilder(256);
            List<Object> args = new ArrayList<>();
            for (ModelField field : table.getFields()) {
                if (field.getLink() == fkTable.getId()) {
                    sql.append(" or ").append(field.getName()).append("=?");
                    args.add(id);
                }
            }
            if (sql.length() == 0) continue;
            if (DbEngine.getInstance().isExists("select 1 from " + table.getSchemaTableName() + " where " + sql.substring(4), args.toArray())) {
                throw new BizException("该记录被表【" + table.getTitle() + "(" + table.getName() + ")】引用，不能删除！");
            }
        }
    }

    /**
     * 获取带数据库的表名，用于sql
     *
     * @param tableName
     * @return
     */
    public static String getSchemaTableName(String tableName) {
        ModelTable table = ModelTableCache.getInstance().getByName(tableName);
        if (table == null) throw new SwException("没有找到表定义：" + tableName);
        return table.getSchemaTableName();
    }

    /**
     * bean是否为空
     *
     * @param bean
     * @return
     */
    public static boolean isEmpty(DefaultEntity bean) {
        return bean == null || bean.isEmpty() || bean.getEntityId() <= 0L;
    }

    public static String getSqlInIds(Collection<? extends DefaultEntity> listBean) {
        if (null == listBean || listBean.isEmpty()) return "";
        StringBuilder s = new StringBuilder(128);
        Set<Long> set = new HashSet<>();
        for (DefaultEntity bean : listBean) {
            if (bean.getEntityId() <= 0L) continue;
            if (set.contains(bean.getEntityId())) continue;
            set.add(bean.getEntityId());

            s.append(bean.getEntityId()).append(",");
        }
        if (s.length() == 0) return "";
        return s.substring(0, s.length() - 1);
    }
}
