package cc.smtweb.framework.core.mvc.service;

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.db.DbEngine;
import cc.smtweb.framework.core.db.EntityDao;
import cc.smtweb.framework.core.db.EntityHelper;
import cc.smtweb.framework.core.db.cache.ModelTableCache;
import cc.smtweb.framework.core.db.impl.DefaultEntity;
import cc.smtweb.framework.core.db.vo.ModelTable;
import cc.smtweb.framework.core.exception.BizException;
import cc.smtweb.framework.core.exception.SwException;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Created by Akmm at 2022/3/17 19:56
 */
public class TreeHelper<T extends DefaultEntity> {
    private static Map<String, Class<? extends TreeHelper>> mapDef;
    private static Map<String, TreeHelper> mapInstance;

    static {
        mapDef = new HashMap<>();
        mapInstance = new ConcurrentHashMap<>();
    }

    private String tableName;

    private ModelTable table;
    protected AbstractCache<T> cache;
    private EntityDao<T> dao;

    private String fieldParent;
    private String fieldLevel;
    private String fieldLevelCode;
    private String fieldId;


    public static TreeHelper getTreeHelper(String tableName) {
        TreeHelper bean = mapInstance.get(tableName);
        if (bean != null) return bean;
        Class<? extends TreeHelper> clazz = mapDef.get(tableName);
        if (clazz == null) {
            bean = new TreeHelper(tableName);
        } else {
            try {
                bean = clazz.newInstance();
            } catch (Exception e) {
                throw new SwException(e);
            }
        }
        mapInstance.put(tableName, bean);
        return bean;
    }

    public static void regTreeHelper(String tableName, Class<? extends TreeHelper> clazz) {
        mapDef.put(tableName, clazz);
    }

    //先禁止外部创建
    protected TreeHelper(String tableName) {
        this.tableName = tableName;
        table = ModelTableCache.getInstance().getByName(tableName);
        cache = CacheManager.getIntance().getCache(tableName);
        dao = DbEngine.getInstance().findDao(tableName);

        fieldParent = table.findFieldNameByType(SwEnum.FieldType.PARENT_ID.value);
        fieldLevelCode = table.findFieldNameByType(SwEnum.FieldType.LEVEL_CODE.value);
        fieldLevel = table.findFieldNameByType(SwEnum.FieldType.LEVEL.value);
        fieldId = table.getIdField();
    }

    public long getParentId(T bean) {
        return bean.getLong(fieldParent);
    }

    public long getLevel(T bean) {
        return bean.getLong(fieldLevel);
    }

    public String getLevelCode(T bean) {
        return bean.getStr(fieldLevelCode);
    }

    public Collection<T> getChildren(T bean) {
        return getChildren(bean.getEntityId());
    }

    public Collection<T> getChildren(long id) {
        return cache.getListByKey(SwConsts.KEY_PARENT_ID, String.valueOf(id));
    }

    public List<T> getChildren(long id, Comparator<T> comparator) {
        Collection<T> set = cache.getListByKey(SwConsts.KEY_PARENT_ID, String.valueOf(id));

        List<T> list = set == null ? new ArrayList<>(): new ArrayList<>(set);
        if (comparator != null)
            list.sort(comparator);
        return list;
    }

    public void checkParent(T bean) {
        if (table.getType() != SwEnum.TableType.TYPE_TREE.value) return;
        if (!table.isNeedCache()) throw new BizException("请将树型表定义为需要缓存！");
        long pId = getParentId(bean);
        if (bean.getEntityId() == pId) throw new BizException("上级节点不能为自己！");
        if (pId <= 0) return;
        T parent = cache.get(pId);
        if (parent == null) return;
        String plevelcode = SwConsts.SPLIT_CHAR + parent.getStr(fieldLevelCode) + SwConsts.SPLIT_CHAR;
        if (plevelcode.contains(SwConsts.SPLIT_CHAR + bean.getEntityId() + SwConsts.SPLIT_CHAR)) {
            throw new BizException("上级节点不能为自己的下级！");
        }
    }

    public List<T> resetTreeLevel(T bean) {
        List<T> list = new ArrayList<>();
        if (table.getType() != SwEnum.TableType.TYPE_TREE.value) return list;
        if (!table.isNeedCache()) throw new BizException("请定义为需要缓存！");

        T oldBean = cache.get(bean.getEntityId());
        if (bean.getLong(fieldParent) != oldBean.getLong(fieldParent)) {
            resetParentChildren(bean, list);
        }
        return list;
    }
    // 拼接查询本级及下级sql片段
    public String buildLikeFrSql(long id,String sqlField){
        T bean = cache.get(id);
        if(bean==null){
          return "1<>1";
        }
        String childStr = "'"+getLevelCode(bean)+ SwConsts.SPLIT_CHAR+ bean.getEntityId()+"'";
        String schemaTableName = EntityHelper.getSchemaTableName(tableName);
        return String.format(" exists ( select %s from %s tt where tt.%s = %s and (tt.%s=%s or tt.%s like %s))",
                fieldId,schemaTableName,fieldId,sqlField,fieldId,id,fieldLevelCode,childStr);
    }

    private void resetParentChildren(T bean, List<T> list) {
        T parent = cache.get(getParentId(bean));
        if (parent != null) {
            bean.put(fieldLevelCode, getLevelCode(parent) + SwConsts.SPLIT_CHAR + parent.getEntityId());
            bean.put(fieldLevel, getLevel(parent) + 1);
            dao.updateEntity(bean, fieldLevelCode+","+fieldLevel);
            list.add(bean);
        }
        Collection<T> children = getChildren(bean);
        if (children != null && !children.isEmpty()) {
            for (T child : children) {
                resetParentChildren(child, list);
            }
        }
    }
}
