package cc.smtweb.system.bpm.web.design.form;

import cc.smtweb.framework.core.common.R;
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.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.variable.SwVariableFactory;
import cc.smtweb.framework.core.session.UserSession;
import cc.smtweb.framework.core.util.JsonUtil;
import cc.smtweb.framework.core.util.MapUtil;
import cc.smtweb.framework.core.util.SpringUtil;
import cc.smtweb.framework.core.util.StringUtil;
import cc.smtweb.system.bpm.spring.BpmConfigBean;
import cc.smtweb.system.bpm.util.CodeGenerator;
import cc.smtweb.system.bpm.util.IdeaUtil;
import cc.smtweb.system.bpm.web.design.db.ModelCatalogCache;
import cc.smtweb.system.bpm.web.design.db.ModelProjectCache;
import cc.smtweb.system.bpm.web.design.form.define.*;
import cc.smtweb.system.bpm.web.engine.flow.entity.Comment;
import cc.smtweb.system.bpm.web.sys.user.menuPlan.MenuPlanHelper;
import cc.smtweb.system.bpm.web.sys.user.role.RoleHelper;
import com.fasterxml.jackson.core.JsonProcessingException;
import org.apache.commons.lang3.StringUtils;

import java.util.*;

/**
 * Created by Akmm at 2022/4/20 18:45
 * 辅助类
 */
public class ModelFormHelper {
    public static final String KEY_EVENT_PATH = "eventPath";
    public static final String KEY_BILL_TYPE = "billType";
    //分组类别-list
    public static final String PAGE_TYPE_LIST = "list";
    //分组类别-card
    public static final String PAGE_TYPE_CARD = "card";
    //分组类别-view
    public static final String PAGE_TYPE_VIEW = "view";
    //opts里面的配置关键字
    public static final String OPT_CONFIG = "config";
    public static final String OPT_MODEL = "model";
    public static final String OPT_PAGE_TYPE = "pageType";
    public static final String OPT_PAGE = "page";


    /**
     * 获取页面的pageName，格式：module.name
     *
     * @param form
     * @return
     */
    public static String getPageName(ModelForm form) {
        return ModelProjectCache.getInstance().getModule(form.getPrjId()) + "." + form.getName();
    }

    //获取form的模型
    public static String getFormModel(ModelForm form) {
        SwMap opts = form.getOpts();
        if (opts == null) return null;
        SwMap config = opts.readMap(OPT_CONFIG);
        if (config == null) return null;
        return config.readString(OPT_MODEL);
    }

    /**
     * 从缓存获取Form对象
     *
     * @param formId
     * @return
     */
    public static ModelForm getFromCache(String formId) {
        ModelFormCache cache = ModelFormCache.getInstance();
        if (cache == null) return null;
        ModelForm form = cache.get(formId);
        return form != null ? form: cache.getByPageName(formId);
    }

    /*public static PageDatasets parsePageDataset(long formId) {
        ModelForm form = getFromCache(formId);
        if (form == null) return null;
        return parsePageDataset(form.getDataset());
    }*/

    public static PageDatasets parsePageDataset(String jsonStr) {
        if (StringUtils.isEmpty(jsonStr)) return null;
        PageDataset[] list = JsonUtil.parse(jsonStr, PageDataset[].class);
        return new PageDatasets(list);
    }

    /**
     * 根据json字符串解析对象
     *
     * @param jsonStr
     * @return
     */
    public static PageModel parsePageInfo(String jsonStr) {
        if (StringUtils.isEmpty(jsonStr)) return null;
        return JsonUtil.parse(jsonStr, PageModel.class);
    }

    public static SwMap parseFormOption(String jsonStr) {
        if (StringUtils.isEmpty(jsonStr)) return null;
        return JsonUtil.parse(jsonStr, SwMap.class);
    }

    /**
     * 页面传回的content，加工处理成待保存的json串
     * 主要处理内容：
     * 1、model的一些maxlen、默认值等信息，如果和数据库一致，则去掉
     * 2、db中的只保留表、字段和别名
     *
     * @param form
     * @return
     */
    public static String buildSaveModel(ModelForm form, String jsonStr) {
        PageDatasets datasets = form.getDatasets();
        PageModel pageInfo = parsePageInfo(jsonStr);
        if (pageInfo == null) return "";
        for (Map<String, Object> model : pageInfo.model) {
            buildSaveModelFields(datasets, model, true);
            buildSaveModelFields(datasets, model, false);
        }
        return JsonUtil.encodeString(pageInfo);
    }

    //处理model的fields和filters
    private static void buildSaveModelFields(PageDatasets datasets, Map<String, Object> model, boolean isField) {
        String db = (String) model.get("dataset");
        //没有配置db，配置有误，不处理
        if (StringUtils.isEmpty(db)) throw new BizException("model未配置数据集db，无法解析！");
        PageDataset pds = datasets.findById(db);
        String key = isField ? "fields": "filters";
        List<Map<String, Object>> fields = (List<Map<String, Object>>) model.get(key);
        if (fields == null || fields.isEmpty()) return;
        for (Map<String, Object> field : fields) {
            String fn = (String) field.get("field");
            if (StringUtils.isEmpty(fn)) throw new BizException("model[" + db + "]." + key + "未配置字段名field，无法解析！");
            BaseDatasetField pdf = isField ? pds.findFieldByName(fn): pds.findFilterByName(fn);
            if (pdf == null) throw new BizException("model[" + db + "]." + key + "未找到定义的数据集字段(" + fn + ")，无法解析！");

            if (StringUtil.isStrEquals(MapUtil.readString(field, "label"), pdf.label)) {
                field.remove("label");
            }

            //必填字段
            if (pdf.isFieldNotNull() == MapUtil.readBool(field, "required")) {
                field.remove("required");
            }

            final SwEnum.DataTypeBean dtb = SwEnum.DataType.instance.getByValue(pdf.dataType);
            if (dtb != null && MapUtil.readInt(field, "maxlength") == dtb.dataLength) {
                field.remove("maxlength");
            }
        }
    }

    /**
     * 保存的数据集，删除掉一些不必要的字段信息
     *
     * @param jsonStr
     * @return
     */
    public static String buildSaveDataset(String jsonStr) {
        PageDatasets datasets = parsePageDataset(jsonStr);
        if (datasets == null || datasets.list == null) return "";
        for (PageDataset dataSet : datasets.list) {
            buildSaveDataSetFields(dataSet.fields);
            buildSaveDataSetFields(dataSet.filters);
        }
        return JsonUtil.encodeString(datasets.list);
    }

    /**
     * 构建待保存的数据集
     *
     * @return
     * @throws JsonProcessingException
     */
    public static void buildSaveDataSetFields(List<? extends BaseDatasetField> fields) {
        ModelTable table = null;
        for (BaseDatasetField field : fields) {
            if (field.table <= 0) continue;
            if (table == null || table.getEntityId() != field.table) {
                table = ModelTableCache.getInstance().get(field.table);
            }
            if (table == null) continue;
            ModelField tf = table.findFieldByName(field.field);
            if (tf == null) continue;
            field.table_text = null;
            if (StringUtil.isStrEquals(tf.getTitle(), field.label)) field.label = null;
            if (StringUtil.isStrEquals(tf.getDataType(), field.dataType)) field.dataType = null;
            if (StringUtil.isStrEquals(tf.getRemark(), field.remark)) field.remark = null;
            if (StringUtil.isStrEquals(tf.getEditor(), field.editor)) field.editor = null;
        }
    }

    /**
     * 构建请求的数据集，补充表定义信息
     *
     * @return
     * @throws JsonProcessingException
     */
    public static String buildReqDataset(PageDatasets datasets) {
        if (datasets == null || datasets.list == null) return "";
        return JsonUtil.encodeString(datasets.list);
    }


    /**
     * 根据数据库存储的信息，加工处理成页面要的，与buildsave逆向
     *
     * @param form
     * @return
     */
    public static String buildReqModel(ModelForm form) {
        PageDatasets datasets = form.getDatasets();
        PageModel pageInfo = parsePageInfo(form.getContent());
        if (pageInfo == null || pageInfo.model == null) return "";
        for (Map<String, Object> model : pageInfo.model) {
            String db = (String) model.get("dataset");
            //没有配置db，配置有误，不处理
            if (StringUtils.isEmpty(db)) continue;
            PageDataset dataSet = datasets.findById(db);
            if (dataSet == null) continue;
            buildReqModelFields(dataSet, model, true);
            buildReqModelFields(dataSet, model, false);
        }
        return JsonUtil.encodeString(pageInfo);
    }

    //处理model的fields和filters
    private static void buildReqModelFields(PageDataset dataSet, Map<String, Object> model, boolean isField) {
        String key = isField ? "fields": "filters";
        List<Map<String, Object>> fields = (List<Map<String, Object>>) model.get(key);
        if (fields == null || fields.isEmpty()) return;
        for (Map<String, Object> field : fields) {
            String fn = (String) field.get("field");
            if (StringUtils.isEmpty(fn)) continue;
            BaseDatasetField pdf = isField ? dataSet.findFieldByName(fn): dataSet.findFilterByName(fn);
            if (pdf == null) continue;
            if (!field.containsKey("label")) {
                field.put("label", pdf.label);
            }
            //必填字段
            if (!field.containsKey("required")) {
                field.put("required", pdf.isFieldNotNull());
            }

            if (!field.containsKey("maxlength")) {
                final SwEnum.DataTypeBean dtb = SwEnum.DataType.instance.getByValue(pdf.dataType);
                if (dtb != null) {
                    field.put("maxlength", dtb.dataLength);
                }
            }
            field.put("type", pdf.type);
            field.put("linkDb", pdf.linkDb);
            field.put("linkField", pdf.linkField);
            field.put("value", pdf.value);
            field.put("dependExpr", pdf.dependExpr);
            field.put("defValue", pdf.defValue);
            if (!isField) {
                PageDatasetFilter filter = (PageDatasetFilter) pdf;
                field.put("trigger", filter.trigger);
            }
        }
    }

    /**
     * 根据数据库存储的信息，加工处理成页面要的，与buildsave逆向
     *
     * @param form
     * @return
     */
    public static String buildEngineModel(ModelForm form, SwMap params, UserSession us) {
        return buildEngineModel(form, params, us, true);
    }

    public static String buildEngineModel(ModelForm form, SwMap params, UserSession us, boolean incExtra) {
        PageDatasets datasets = form.getDatasets();
        if (datasets == null || datasets.list == null) return "";
        String service = form.getService();
        if (StringUtils.isEmpty(service) && form.getParent() > 0L) {
            service = ModelFormCache.getInstance().get(form.getParent()).getService();
        }
        SwMap ret = new SwMap();
        PageModel pageInfo = parsePageInfo(form.getContent());
        if (pageInfo == null) return "";
        ret.put("pageId", form.getEntityId());
        ret.put("pageName", getPageName(form));
        ret.put("label", form.getTitle());
        ret.put("service", service);
        ret.put("module", ModelProjectCache.getInstance().getModule(form.getPrjId()));
        // 加载权限
        long menuId = params.readLong("menuId");
        String fullPath = params.readString("fullPath");
        long findMenuId = MenuPlanHelper.findMenuId(menuId,form.getId(),fullPath);
        Map<String,Object> permission = RoleHelper.getFuncAndFieldRight(us.getUserId(),us.getPartyId(),findMenuId);
        ret.put("menuId",findMenuId>0L? findMenuId: menuId);
        ret.put("permission",permission);
        buildOptsEx(form, ret);
        //form不用管
        if (pageInfo.form != null) ret.put("form", pageInfo.form);
        List<SwMap> listModel = new ArrayList<>();
        ret.put("model", listModel);
        //构建Model
        for (Map<String, Object> model : pageInfo.model) {
            String db = (String) model.get("dataset");
            PageDataset dataSet = datasets.findById(db);
            if (dataSet == null) throw new BizException("未找到指定的数据集定义！" + db);

            listModel.add(buildEngineModelMap(dataSet, model));
        }
        if (incExtra) {
            ret.put("extra", buildEngineExtra(pageInfo.option, params, us));
        }
        return JsonUtil.encodeString(ret);
    }

    //构建model部分
    private static SwMap buildEngineModelMap(PageDataset dataSet, Map<String, Object> model) {
        SwMap ret = new SwMap();

        ret.put("id", dataSet.id);
        ret.put("name", dataSet.name);
        ret.put("label", dataSet.label);
        ret.put("type", dataSet.type);
        ret.put("idField", dataSet.idField);
        ret.put("lazy", dataSet.lazy);
        ret.put("canEdit", dataSet.canEdit);
        ret.put("virtual", dataSet.virtual);
        ret.put("linkClob", model.get("linkClob"));
        ret.put("fields", model.get("fields"));
        ret.put("filters", model.get("filters"));

        buildReqModelFields(dataSet, model, true);
        buildReqModelFields(dataSet, model, false);

        return ret;
    }

    //根据option构建Extra部分
    private static SwMap buildEngineExtra(Map<String, Object> option, SwMap params, UserSession us) {
        SwMap map = new SwMap();
        if (option == null) return map;
        //控件信息
        SwMap widget = new SwMap();
        map.put("widget", widget);

        List widgetIds = (List) option.get("widgetRef");
        for (Object v : widgetIds) {
            String id = v.toString();
            ModelForm widgetForm = getFromCache(id);
            if (widgetForm == null) throw new BizException("没有找到指定的控件定义！id=" + id);
            SwMap w = new SwMap();
            widget.put("w" + id, w);
            SwMap widgetOpts = widgetForm.getOpts();
            String service = widgetForm.getService();
            if (StringUtils.isEmpty(service) && widgetForm.getParent() > 0L) {
                service = ModelFormCache.getInstance().get(widgetForm.getParent()).getService();
            }
            w.put("service", service);
            if (widgetOpts != null) w.putAll(widgetOpts);
            w.put("define", buildEngineModel(widgetForm, params, us, false));
        }
        //构建变量
        SwMap mapVar = new SwMap();
        map.put("vars", mapVar);
        List<String> vars = (List<String>) option.get("vars");
        for (String var : vars) {
            mapVar.put(var, SwVariableFactory.getInstance().calcVar(var, params, us));
        }
        return map;
    }

    //获取控件的filter信息
    public static List<SwMap> buildWidgetFilter(ModelForm bean) {
        List<SwMap> listRet = new ArrayList<>();
        PageDatasets datasets = bean.getDatasets();
        if (datasets == null || datasets.list == null) return listRet;
        for (PageDataset ds : datasets.list) {
            for (PageDatasetFilter filter : ds.filters) {
                if (SwEnum.FilterType.INPUT.value.equals(filter.type) || SwEnum.FilterType.PARAM.value.equals(filter.type)) {
                    SwMap fret = new SwMap();
                    fret.put("name", filter.name);
                    fret.put("label", filter.label);
                    listRet.add(fret);
                }
            }
        }
        listRet.sort((o1, o2) -> StringUtil.chineseCompare(o1.readString("label"), o2.readString("label")));
        return listRet;
    }

    /**
     * 构建一些扩展属性，目前主要是eventPath
     *
     * @param opts
     */
    public static void buildOptsEx(ModelForm bean, SwMap opts) {
        String eventPath = getEventPath(bean);
        if (StringUtils.isNotEmpty(eventPath)) {
            opts.put(KEY_EVENT_PATH, eventPath + "." + bean.getName());
        }
        SwMap formOpts = bean.getOpts();
        opts.put(OPT_CONFIG, formOpts.get(OPT_CONFIG));
        opts.put(OPT_PAGE_TYPE, formOpts.readString(OPT_PAGE_TYPE));
    }

    public static String getCodePath(ModelForm bean) {
        String moduleName = ModelProjectCache.getInstance().getModule(bean.getPrjId());
        if (StringUtils.isEmpty(moduleName)) return null;

        BpmConfigBean bpmConfigBean = SpringUtil.getBean(BpmConfigBean.class);
        Map<String, String> mapIdeaModules = IdeaUtil.getModules(bpmConfigBean.getCodeJavaPath(), bpmConfigBean.getMode());
        if (mapIdeaModules == null || mapIdeaModules.isEmpty())
            throw new BizException("没有定义idea项目的路径(smtweb.bpm.codeJavaPath)！");
        String codeJavaPath = mapIdeaModules.get(moduleName);
        if (StringUtils.isEmpty(codeJavaPath)) {
            throw new BizException("没有找到对应项目在idea中Module的路径(" + moduleName + ")！");
        }
        return codeJavaPath;
    }

    public static String getEventPath(ModelForm bean) {
        String moduleName = ModelProjectCache.getInstance().getModule(bean.getPrjId());
        if (StringUtils.isNotEmpty(moduleName)) {
            String ep = moduleName;
            String cn = ModelCatalogCache.getInstance().getFullName(bean.getMcId());
            if (StringUtils.isNotEmpty(cn)) ep = ep + "." + cn;

            String pn = ModelFormHelper.getFormParentName(bean);
            if (StringUtils.isNotEmpty(pn)) ep += "." + pn;

            return ep;
        }
        return "";
    }

    /**
     *
     * @param form
     * @param tmplId
     */
    public static void buildSaveModelByTmpl(ModelForm form, String tmplId) {
        buildSaveModelByTmplExt(form, new SwMap(), tmplId);
    }

    /**
     * 根据向导生成model
     *
     * @param form
     * @param tmplId
     * @param modelExt 额外的属性配置
     */
    public static void buildSaveModelByTmplExt(ModelForm form,SwMap modelExt, String tmplId) {
        PageDatasets datasets = form.getDatasets();
        SwMap tmplModel = JsonUtil.parse(form.getTmpl(), SwMap.class);
        tmplModel.putAll(modelExt);
        tmplModel.put("title", form.getTitle());
        tmplModel.put("name", form.getName());
        tmplModel.put("datasets", JsonUtil.bean2MapList(form.getDatasets().list));
        SwMap layout = tmplModel.readMap("layout");
        //用到的自定义控件
        Set<Long> setWidget = new HashSet<>();
        for (String key : layout.keySet()) {
            List<Map<String, Object>> groups = layout.readListMap(key);
            for (Map<String, Object> group : groups) {
                String type = MapUtil.readString(group, "type");
                switch (type) {
                    case PAGE_TYPE_CARD:
                        buildTmplFields(MapUtil.readListMap(group, "fields"), datasets, setWidget, true);
                        break;
                    case PAGE_TYPE_LIST:
                        buildTmplFields(MapUtil.readListMap(group, "fields"), datasets, setWidget, true);
                        buildTmplFields(MapUtil.readListMap(group, "cfilters"), datasets, setWidget, false);
                        buildTmplFields(MapUtil.readListMap(group, "sfilters"), datasets, setWidget, false);
                        break;
                }
            }
        }
        String widgetRef = "";
        if (!setWidget.isEmpty()) {
            for (long s : setWidget) {
                widgetRef += ",\"" + s + "\"";
            }
            widgetRef = widgetRef.substring(1);
        }
        tmplModel.put("widgetRef", widgetRef);
        tmplModel.put("tmplType", tmplId);
        final String model = CodeGenerator.getInstance().generatePage(tmplModel, tmplId);
        form.setContent(model);
//        form.setContent(buildSaveModel(form));
    }

    private static void buildTmplFields(List<Map<String, Object>> fields, PageDatasets datasets, Set<Long> setWidget, boolean isField) {
        if (fields == null) return;
        PageDataset dataset = null;
        for (Map<String, Object> field : fields) {
            final String dsId = MapUtil.readString(field, "dataset");
            if (dataset == null || !dataset.id.equals(dsId)) {
                dataset = datasets.findById(dsId);
                if (dataset == null) throw new BizException("没有找到指定数据集：" + dsId);
            }
            String fn = MapUtil.readString(field, "field");
            BaseDatasetField pdf;
            if (!isField) {
                pdf = dataset.findFilterById(fn);
            } else {
                pdf = dataset.findFieldById(fn);
                if (pdf == null) pdf = dataset.findFilterByName(fn);
            }
            if (pdf == null) throw new BizException("model[" + dsId + "]未找到定义的数据集字段(" + fn + ")，无法解析！");
            field.put("id", pdf.id);
            field.put("field", pdf.field);
            field.put("name", pdf.name);
            String label = (String) field.get("field_label");
            field.put("label", StringUtils.isEmpty(label) ? pdf.label: label);
            field.put("required", pdf.isFieldNotNull());
            final SwEnum.DataTypeBean dtb = SwEnum.DataType.instance.getByValue(pdf.dataType);
            if (dtb != null) {
                field.put("maxlength", dtb.dataLength);
            } else {
                field.put("maxlength", 0);
            }
            Map lookup = (Map) field.get("lookup");
            if (lookup != null) {
                long widgetId = MapUtil.readLong(lookup, "widgetId", -1L);
                if (widgetId > 0) {
                    setWidget.add(widgetId);
                }
            }
        }
    }

    public static void buildTableFields(List<SwMap> ret, String tableName) {
        ModelTable table = ModelTableCache.getInstance().getByName(tableName);
        if (table == null) throw new SwException("没有找到表定义信息(table=" + tableName + ")！");

        for (ModelField field : table.getFields()) {
            SwMap col = new SwMap(2);
            ModelFormHelper.buildFieldInfo(table, field, col);
            ret.add(col);
        }
    }

    /**
     * 页面设计时，返回的字段信息，带控件
     *
     * @param table
     * @param field
     * @param col
     */
    public static void buildFieldInfo(ModelTable table, ModelField field, SwMap col) {
        col.put("name", field.getName());
        col.put("dataType", field.getDataType());
        col.put("fieldType", field.getFieldType());
        col.put("null", field.getNotNull());
        col.put("default", field.getDefaultValue());
        col.put("title", field.getTitle());
        col.put("link", field.getLink());
        col.put("table", table.getId());
        col.put("table_text", table.getTitle());

        SwEnum.DataTypeBean dtb = SwEnum.DataType.instance.getByValue(field.getDataType());
        col.put("editor", dtb != null ? dtb.editor: SwEnum.EditorType.INPUT.value);

        if (field.getLink() == 0L) return;

        Set<ModelForm> set = ModelFormCache.getInstance().getListByTable(field.getLink(), SwEnum.FormType.WIDGET.value);
        if (set == null || set.isEmpty()) return;
        ModelForm form = set.iterator().next();
        if (form == null) return;
        SwMap widget = new SwMap();
        col.put("widget", widget);

        widget.put("widgetId", form.getId());
        widget.put("widgetId_text", form.getTitle());
        SwMap opts = form.getOpts();
        if (opts != null) {
            col.put("editor", SwEnum.WidgetType.getEditor(opts.readString("widgetType")));
        }
    }

    /*//js文件的路径
    public static String getJsPath(ModelForm form) {
        if (form == null) throw new BizException("页面定义对象不能为空！");
        String moduleName = ModelProjectCache.getInstance().getModule(form.getPrjId());
        BpmConfigBean bpmConfigBean = SpringUtil.getBean(BpmConfigBean.class);
        Map<String, String> mapIdeaModules = IdeaUtil.getModules(bpmConfigBean.getCodeJavaPath());
        if (mapIdeaModules == null || mapIdeaModules.isEmpty())
            throw new BizException("没有定义idea项目的路径(smtweb.bpm.codeJavaPath)！");
        String codePath = mapIdeaModules.get(moduleName);
        if (StringUtils.isEmpty(codePath)) {
            throw new BizException("没有找到对应项目在idea中Module的路径(" + moduleName + ")！");
        }
        codePath += "/src/main/resources/static/event/" + moduleName + "/";
        //加上目录
        String cn = ModelCatalogCache.getInstance().getFullName(form.getMcId());
        if (StringUtils.isNotEmpty(cn)) {
            codePath += cn.replaceAll("\\.", "/");
        }
        return codePath;
    }*/

    public static String getFormParentName(ModelForm form) {
        if (form.getParent() <= 0) return null;
        ModelForm pf = ModelFormCache.getInstance().get(form.getParent());
        if (pf != null) return pf.getName();
        return null;
    }
}
