package cc.smtweb.system.bpm.web.engine.dynPage;

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.DbEngine;
import cc.smtweb.framework.core.db.EntityDao;
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.ModelTable;
import cc.smtweb.framework.core.exception.BizException;
import cc.smtweb.framework.core.exception.SwException;
import cc.smtweb.framework.core.mvc.service.SqlNamedPara;
import cc.smtweb.framework.core.mvc.service.TreeHelper;
import cc.smtweb.framework.core.session.UserSession;
import cc.smtweb.framework.core.util.MapUtil;
import cc.smtweb.framework.core.util.NumberUtil;
import cc.smtweb.framework.core.util.SqlUtil;
import cc.smtweb.framework.core.util.StringUtil;
import cc.smtweb.system.bpm.web.design.form.define.*;
import cc.smtweb.system.bpm.web.sys.user.dataRight.DataRightDefine;
import cc.smtweb.system.bpm.web.sys.user.dataRight.DataRightDefineCache;
import cc.smtweb.system.bpm.web.sys.user.dataRightGroup.IDataRightHandler;
import cc.smtweb.system.bpm.web.sys.user.dataRightGroup.IDataRightTreeFactory;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.lang.NonNull;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import static cc.smtweb.framework.core.common.SwConsts.TOTAL_KEY;

/**
 * Created by Akmm at 2022/4/23 10:01
 * 动态页面辅助类
 */
@Slf4j
public class DynPageHelper {
    /**
     * 新建bean
     *
     * @param dataSet
     * @return
     */
    public static SwMap createBean(PageDataset dataSet) {
        if (dataSet.masterTable <= 0L) {
            return new SwMap();
        }
        //主表
        ModelTable masterTable = ModelTableCache.getInstance().get(dataSet.masterTable);
        EntityDao<DefaultEntity> dao = DbEngine.getInstance().findDao(masterTable.getName());
        DefaultEntity bean = dao.createBean();
        bean.setEntityId(DbEngine.getInstance().nextId());
        return bean.getData();
    }

    private static void buildSelectSql(PageDataset dataSet, Map<String, Object> params, SqlNamedPara sqlNamedPara, IBuildSqlListener listener) {
        StringBuilder sql = new StringBuilder(512);
        sql.append(buildSelFieldsSql(dataSet, sqlNamedPara, listener));
        if (StringUtils.isNotEmpty(sqlNamedPara.sql)) {
            sql.append(" where ").append(sqlNamedPara.sql);
        }
        if (dataSet.sortFields != null) {
            String s = "";
            for (PageDatasetSortField sf : dataSet.sortFields) {
                s += "," + sf.field + " " + sf.type;
            }
            if (StringUtils.isNotEmpty(s)) {
                sql.append(" order by ").append(s.substring(1));
            }
        }
        sqlNamedPara.sql = sql.toString();
        sqlNamedPara.page = MapUtil.readInt(params, SwConsts.PARAM_PAGE);
        sqlNamedPara.rows = MapUtil.readInt(params, SwConsts.PARAM_ROWS);
    }

    /**
     * 构建select fields from table
     *
     * @param dataSet
     * @return
     */
    public static SqlNamedPara buildSelectSql(PageDataset dataSet, SwMap params,UserSession us) {
        return buildSelectSqlEx(dataSet, params,us,null);
    }

    public static SqlNamedPara buildSelectSqlEx(PageDataset dataSet,SwMap params,UserSession us, IBuildSqlListener listener) {
        SqlNamedPara sqlNamedPara = buildWhereSql(dataSet, params,us, listener);
        buildSelectSql(dataSet, params, sqlNamedPara, listener);
        return sqlNamedPara;
    }

    //构建树型查询语句，插入parent_id=?
    public static SqlNamedPara buildTreeSelectSql(PageDataset dataSet, SwMap params,UserSession us, long parentId) {
        ModelTable table = ModelTableCache.getInstance().get(dataSet.masterTable);
        if (table == null) throw new SwException("没有找到指定的表：" + dataSet.masterTable);
        SqlNamedPara sqlNamedPara = buildWhereSql(dataSet, params,us, null);
        if (table.getType() == SwEnum.TableType.TYPE_TREE.value) {//是树型结构，才做处理
            ModelField field = table.findFieldByType(SwEnum.FieldType.PARENT_ID.value);
            if (field == null) throw new SwException("树型表（" + table.getName() + "）未定义上级id字段！");

            if (StringUtils.isNotEmpty(sqlNamedPara.sql)) {
                sqlNamedPara.sql = field.getName() + "=:parent_id and (" + sqlNamedPara.sql + ")";
            } else {
                sqlNamedPara.sql = field.getName() + "=:parent_id ";
            }
            sqlNamedPara.addParas("parent_id", parentId);
        }

        buildSelectSql(dataSet, params, sqlNamedPara, null);
        return sqlNamedPara;
    }

    //构建过滤查询语句
    public static SqlNamedPara buildFilterSelectSql(PageDataset dataSet, SwMap params,UserSession us, String text) {
        ModelTable table = ModelTableCache.getInstance().get(dataSet.masterTable);
        if (table == null) throw new SwException("没有找到指定的表：" + dataSet.masterTable);
        SqlNamedPara sqlNamedPara = buildWhereSql(dataSet, params,us, null);
        if (StringUtils.isNotEmpty(text)) {//有文本信息
            StringBuilder sql = new StringBuilder(128);
            ModelField field = table.findFieldByType(SwEnum.FieldType.CODE.value);
            if (field != null) {
                sql.append(" or ").append(field.getName()).append(" like :q_txt");
            }

            field = table.findFieldByType(SwEnum.FieldType.NAME.value);
            if (field != null) {
                sql.append(" or ").append(field.getName()).append(" like :q_txt");
            }

            if (sql.length() > 0) {
                if (StringUtils.isNotEmpty(sqlNamedPara.sql)) {
                    sqlNamedPara.sql = "(" + sqlNamedPara.sql + ") and (" + sql.substring(4) + ")";
                } else {
                    sqlNamedPara.sql = sql.substring(4);
                }
                sqlNamedPara.addParas("q_txt", text);
            }
        }

        buildSelectSql(dataSet, params, sqlNamedPara, null);
        return sqlNamedPara;
    }
    private static Map<String, IBuilderExpr> mapBuilder;
    private static IBuilderExpr baseBuilder;
    static {
        baseBuilder = (opt, field, name, value, args, filter) -> {
            args.put(name, value);
            return field + " " + opt + " :" + name;
        };
        mapBuilder = new HashMap<>();
        mapBuilder.put(SwEnum.OptType.LIKE.value, (opt, field, name, value, args, filter) -> {
            args.put(name, "%" + value + "%");
            return field + " like :" + name;
        });

        mapBuilder.put(SwEnum.OptType.PLIKE.value, (opt, field, name, value, args, filter) -> {
            args.put(name, value + "%");
            return field + " like :" + name;
        });

        mapBuilder.put(SwEnum.OptType.BT.value, (opt, field, name, value, args, filter) -> {
            String[] ss = value.toString().split(",");
            if (ss.length != 2) throw new BizException("介于条件，参数值个数错误！");
            args.put(name + "_1", ss[0]);
            args.put(name + "_2", ss[1]);
            return "(" + field + ">=:" + name + "_1 and " + field + "<=:" + name + "_2)";
        });

        mapBuilder.put(SwEnum.OptType.IN.value, (opt, field, name, value, args, filter) -> {
            if (StringUtil.isEmpty(value.toString())) return "";
            args.put(name, value);
            return field + " in (:" + name + ") ";
        });

        mapBuilder.put(SwEnum.OptType.NOT_IN.value, (opt, field, name, value, args, filter) -> {
            if (StringUtil.isEmpty(value.toString())) return "";
            args.put(name, value);
            return field + " not in (:" + name + ") ";
        });
        mapBuilder.put(SwEnum.OptType.IN_CHILD.value, (opt, field, name, value, args, filter) -> {
            ModelTable table = ModelTableCache.getInstance().get(filter.link);
            if(table==null){
                return "";
            }
            return  TreeHelper.getTreeHelper(table.getName()).buildLikeFrSql(Long.parseLong(value.toString()),field);
        });
    }

    private static String buildSelFieldsSql(PageDataset dataSet, SqlNamedPara sqlNamedPara, IBuildSqlListener listener) {
        StringBuilder sql = new StringBuilder(512);
        //主表
        ModelTable masterTable = ModelTableCache.getInstance().get(dataSet.masterTable);
        //非查询列表，或sql为空，则自己组装select sql
        if (!SwEnum.DatasetType.LIST.value.equals(dataSet.type) || StringUtils.isEmpty(dataSet.sql)) {
            sql.append("select ");
            for (PageDatasetField field : dataSet.fields) {
                if (field.fieldIsCalc()) continue;
                sql.append(field.field);
                //加别名
                if (!field.field.equalsIgnoreCase(field.name)) {
                    sql.append(" ").append(field.name);
                    sqlNamedPara.addFieldAlias(field.field, field.name);
                }
                sql.append(",");
            }
            sql.setCharAt(sql.length() - 1, ' ');
            sql.append(" from ").append(masterTable.getSchemaTableName());
            if (listener != null) listener.buildSelect(dataSet, sql);
            return sql.toString();
        } else {
            return SqlUtil.replaceTable(dataSet.sql);
        }
    }

    /**
     * 构建合计栏sql
     *
     * @param dataSet
     * @param sqlNamedPara
     * @return
     */
    public static SqlNamedPara buildSumSql(PageDataset dataSet, @NonNull SqlNamedPara sqlNamedPara) {
        StringBuilder sql = new StringBuilder(256);
        sql.append("select count(1) " + TOTAL_KEY);
        for (PageDatasetField field : dataSet.fields) {
            if (StringUtils.isEmpty(field.summary)) continue;
            if (field.fieldIsCalc()) continue;
            sql.append(",");
            if (!SwEnum.SummaryType.instance.isText(field.summary)) {
                sql.append(field.summary).append("(").append(field.name).append(") ").append(field.name);
            } else {
                sql.append("'").append(field.summary).append("' ").append(field.name);
            }
        }
        sqlNamedPara.sql = sql.toString() + " from (" + sqlNamedPara.sql + ") xxxxa";
        return sqlNamedPara;
    }

    /**
     * 构建where条件：组合固定和动态条件
     *
     * @param dataSet
     * @param params
     * @return
     */
    private static SqlNamedPara buildWhereSql(PageDataset dataSet, SwMap params,UserSession us, IBuildSqlListener listener) {
        StringBuilder sql = new StringBuilder(512);
        SwMap args = new SwMap();

        if (StringUtils.isNotEmpty(dataSet.fixedCond)) {
            sql.append("(").append(dataSet.fixedCond).append(")");
        }
        //记录归属于固定条件的filter，最后设置参数
        Set<String> setFixedFilter = new HashSet<>();
        for (PageDatasetFilter filter : dataSet.filters) {
            setFixedFilter.add(filter.name);
        }
        if (!dataSet.dynCond.isEmpty()) {
            String s = buildDynCondSql(dataSet, dataSet.dynCond, params,us, args, setFixedFilter);
            if (StringUtils.isNotEmpty(s)) {
                if (sql.length() > 0) sql.append(" and ");
                sql.append(s);
            }
        }
        for (String s : setFixedFilter) {
            args.put(s, MapUtil.readString(params, s, ""));
        }
        if (listener != null) listener.buildWhere(dataSet, sql, args);
        // 设置固定条件数据权限
        for (PageDatasetFilter filter : dataSet.filters) {
            if(!setFixedFilter.contains(filter.name))break;
            if(filter.dataRightType<=0L)break;
            if (sql.length() > 0) sql.append(" and ");
            buildDataRight(filter.dataRightType,filter.sqlName,MapUtil.readString(params, filter.name, ""),sql,args,params,us);
        }
        return new SqlNamedPara(sql.toString(), args);
    }

    /**
     * 构建动态条件
     *
     * @param dataSet
     * @param dynCond
     * @param params
     * @param us
     * @param args
     * @param setFixedFilter
     * @return
     */
    private static String buildDynCondSql(PageDataset dataSet, PageDatasetDynCond dynCond, SwMap params,UserSession us, SwMap args, Set<String> setFixedFilter) {
        if (dynCond.isCondOpt()) {//是and/or
            if (dynCond.children == null || dynCond.children.isEmpty()) return "";
            StringBuilder sql = new StringBuilder(256);
            boolean b = false;
            //递归调用
            for (PageDatasetDynCond dc : dynCond.children) {
                String s = buildDynCondSql(dataSet, dc, params,us, args, setFixedFilter);
                if (StringUtils.isEmpty(s)) continue;

                if (b) sql.append(" ").append(dynCond.opt).append(" ");
                sql.append(s);
                b = true;
            }
            if (sql.length() > 0) {
                return "(" + sql.toString() + ")";
            }
            return "";
        }

        PageDatasetFilter filter = dataSet.findFilterById(dynCond.param);
        if (filter == null) throw new BizException("没有找到filter（" + dynCond.param + "）！");

        boolean isNameSelf = setFixedFilter.contains(filter.name);
        setFixedFilter.remove(filter.name);

        Object value = null;
        if (SwEnum.FilterType.CONST.value.equals(filter.type)) {
            value = filter.value;
            //todo 有变量的情况需要处理
        } else {
            value = params.get(filter.name);
        }
        if (value == null || StringUtils.isEmpty(value.toString())) {
            if(filter.dataRightType>0L){
                StringBuilder frSql = new StringBuilder();
                buildDataRight(filter.dataRightType,filter.sqlName,"",frSql,args,params,us);
                return frSql.toString();
            }
            if (filter.required) {
                throw new BizException("过滤条件不能为空（" + filter.name + "）！");
            }
            return null;
        }
        IBuilderExpr builder = getBuilder(dynCond.opt);
        String ns = isNameSelf ? filter.name: filter.name + "_" + dynCond.hashCode();
        String optWhere = builder.build(dynCond.opt, filter.sqlName, ns, value, args,filter);
        // 添加数据权限
        if(filter.dataRightType>0L){
           StringBuilder frSql = new StringBuilder();
            buildDataRight(filter.dataRightType,filter.sqlName,value.toString(),frSql,args,params,us);
            if(frSql.length()>0){
              if(StringUtil.isEmpty(optWhere)){
                  optWhere = frSql.toString();
              }else {
                  optWhere+= " and "+ frSql.toString();
              }
            }
        }
        return optWhere;
    }
    /**
     * 处理计算字段
     *
     * @param data
     * @param dataset
     */
    public static void setCalcFields(SwMap data, PageDataset dataset) {
        if (!dataset.hasCalcField()) return;
        for (PageDatasetField field : dataset.fields) {
            if (!field.fieldIsCalc()) continue;
            data.put(field.name, NumberUtil.calcExprMapObject(field.expr, data));
        }
    }

    /**
     * 构建数据权限
     * @param dsType 数据权限定义ID
     * @param sqlField sql字段名
     * @param value 值
     * @param sql sql语句
     * @param args 参数
     * @param params 参数
     * @param us 用户session
     */
    private static void buildDataRight(long dsType,String sqlField,String value,StringBuilder sql,SwMap args,SwMap params,UserSession us){
        DataRightDefine drd = DataRightDefineCache.getInstance().get(dsType);
        if(drd==null){return;}
        try{
            IDataRightHandler dataRightHandler = IDataRightTreeFactory.getInstance().getHandler(drd.getCode());
            dataRightHandler.init(us,params);
            dataRightHandler.buildSqlWhere(sqlField,value,sql,args);
        }catch (Exception e){
            log.error("加载数据权限失败：",e);
            throw new SwException(e);
        }
    }

    private static IBuilderExpr getBuilder(String opt) {
        IBuilderExpr builder = mapBuilder.get(opt);
        return builder != null ? builder: baseBuilder;
    }

    interface IBuilderExpr {
        String build(String opt, String field, String name, Object value, Map<String, Object> args,PageDatasetFilter filter);
    }

}
