package com.ch.jedge.jbot.intent;

import com.alibaba.fastjson.JSONArray;
import com.ch.jedge.jbot.intent.param.JedgeDataSetter;
import com.ch.jedge.jbot.knbase.JedgeGptApp;
import com.changhong.jedge.JMgbusModual;
import com.changhong.jedge.JMgbusService;
import com.changhong.jedge.JMgbusUtil;
import com.changhong.qlib.intf.QIData;
import com.changhong.qlib.util.NumberUtils;
import com.changhong.qlib.util.StringUtils;

import java.util.*;

import static com.ch.jedge.jbot.JEJBotLLMConst.ival_max_timeout_count;

/**
 *  判断用户意图
 */
public class JedgeIntentTester {
    private final JMgbusModual module;
    private final JMgbusService service;

    public static final String sval_cmd_test_domain_ = "%s,从用户文本中抽取实体,从:\"%s\"中判断用户最可能的唯一一个意图领域,用结构化方式的json格式输出，格式:%s.只输出json内容,不输出其他。用户输入:{query}, 你的输出是:";
    //    public static final String sval_cmd_pick_params_ = "你的角色是 %s, 基于提供的数据，对用户语句提取参数值并替换为对应的参数取值,根据需要尽量分成多条指令,所提取的参数项及其取值范围只能在上述操作操作项列表范围内, 从用户文本中抽取实体参数,用结构化方式的json格式输出，只输出json部分，输出格式如下：%s。:\"{query}\"";
    public static final String sval_cmd_pick_params_ = "你的角色是 %s, 从用户文本中抽取实体参数,用结构化方式的json格式输出，只输出json部分，输出格式如下：%s。只输出json内容,不输出其他。准确区分用户的意图,一个动作意图只生成一条数据,多个动作意图尽量拆分为多条结构数据。:用户输入:{query}, 你的输出是:";
    public static final String sval_default_out_format_ =  "{\"domain\":\"意图领域\",\"exKeyWords\":[\"知识关键字1\",\"知识关键字2\",...]}";
    public String donmainTestTemplateString = "";
    public String jsonIntentFormat = sval_default_out_format_;

    protected final Map<String, JedgeDomainDef> domainDefMap = new HashMap<>();

    public JedgeIntentTester getSubDomainTester(String domain) {
        return null;
    }

    public static class JedgeIntentParam {
        public final String paramDisc;
        public final String paramKey;
        public final String dataSet;

        public JedgeIntentParam(String paramKey, String paramDisc, String dataSet) {
            this.paramKey = paramKey;
            this.paramDisc = paramDisc;
            this.dataSet = dataSet;
        }
    }

    public static class JedgeDomainDef {
        protected final JMgbusService service;
        public final String domainDisc;
        public final String roleDisc;

        protected final Map<String, JedgeIntentParam> paramDefMap = new HashMap<>();
        private final int dmid;

        private int ttl;
        String intentClassifyTemplateString;
        String outFormat = "{\"command\":\"操作意图\",\"params\":[\"参数0\",\"参数1 \",...]}";
        private JedgeDataSetter dataPicker;     //同一个意图，可能有多种不同的picker？
        private final List<String> dataSetList = new ArrayList<>();
        private String cbMod;
        private String cbUri;

        public JedgeDomainDef(String domainDisc, String roleDisc, JMgbusService service) {
            this.ttl = -1;
            this.dmid = NumberUtils.randomInt(0x7FFFFFF);
            this.domainDisc = domainDisc;
            this.roleDisc = roleDisc;
            this.service = service;
            rebuildIntentClassifyTemplate();
        }

        public void addDataPicker(QIData req) {
            String paramFormat = req.getString("pf");
            this.dataPicker = new JedgeDataSetter(service, paramFormat);
            String dataSet = req.getString("ds");
            this.dataSetList.addAll(Arrays.asList(dataSet.split(",")));
            this.outFormat = paramFormat;
            rebuildIntentClassifyTemplate();
        }

        public void addIntentParamDef(String key, JedgeIntentParam intentDef) {
            if(key.indexOf(',')>0) {
                String[] keyList = key.split(",");
                synchronized (paramDefMap) {
                    for (String kk : keyList) {
                        paramDefMap.put(kk, intentDef);
                    }
                }
            } else {
                synchronized (paramDefMap) {
                    paramDefMap.put(key, intentDef);
                }
            }
        }

        private void rebuildIntentClassifyTemplate() {
            String dataSet = prepareDataset();   //刷新当前意图所涉及的参数的数值
            intentClassifyTemplateString = dataSet +
                    String.format(sval_cmd_pick_params_, roleDisc, outFormat);
//            JESocketClient.exMarkLogSt("意图的template:\n"+intentClassificationTemplate);
        }

        public String getIntentClassificationTemplate(String finalPrompt) {
//            return intentClassifyTemplateString.replaceAll("\\{query}", finalPrompt);
            return intentClassifyTemplateString.replace("{query}", finalPrompt);
        }

        private String prepareDataset() {
            StringBuilder dataSet = new StringBuilder();
            for(String dsKey : dataSetList) {
                dataSet.append(String.format("\"%s\"的取值范围(及取值说明):",dsKey));
                dataSet.append(dataPicker.getDatasetValueDefine(dsKey)).append("\n");
            }

            //根据各个param的定义，增加数据集，并请求这些数据集。
            return dataSet.toString();
        }

        public int getDomainId() {
            return dmid;
        }

        public int addDataSet(int dsid, String srcMod, String cbUri, String dsKey, JSONArray words, boolean nullable) {
            //注册参数集，很多数据需要缓存、定时由对方推送。也有些属于静态的。
            int dsId = dataPicker.addDataSet(dsid, srcMod, cbUri, dsKey, words, nullable);
            rebuildIntentClassifyTemplate();
            return dsId;
        }

        public boolean updateDataSet(int dsid, String dsKey, JSONArray words) {
            int dsId = dataPicker.updateDataset2Cache(dsid, dsKey, words);
            rebuildIntentClassifyTemplate();
            return dsId>0;
        }

        public void onHeartbeat() {
            if(dataPicker!=null)
                dataPicker.onHeartbeat();
        }

        public QIData makeActionCall(QIData req) {
            if(StringUtils.isValidStr(cbMod) && StringUtils.isValidStr(cbUri)) {
                return service.postServiceRequest(cbMod, cbUri, req);
            }
            //TODO::默认请求
            return JMgbusUtil.MgbusResult(601,"无返回控制通道");
        }

        public void setActionCall(String src, String cbUri) {
            this.cbMod = src;
            this.cbUri = cbUri;
        }
    }

    public JedgeIntentTester(JMgbusModual module, JMgbusService service) {
        this.module = module;
        this.service = service;
    }

    public JedgeDomainDef getIntentTester(String domain) {
        if(StringUtils.isValidStr(domain)) {
            synchronized (domainDefMap) {
                return domainDefMap.get(domain);
            }
        }
        return null;
    }

    public void onHeartbeat() {
        synchronized (domainDefMap) {
            List<String> toDel = new ArrayList<>();
            for(String key : domainDefMap.keySet()) {
                JedgeDomainDef domain = domainDefMap.get(key);
                if(domain!=null) {
                    if(domain.ttl>-1) domain.ttl ++;
                    if(domain.ttl > ival_max_timeout_count ) {
                        //超时退出
                        toDel.add(key);
                    } else
                        domain.onHeartbeat();
                }
            }
            boolean removed = false;
            for(String dmKey :toDel) {
                domainDefMap.remove(dmKey);
                if(JedgeGptApp.isDebugMode())
                    module.infoLog(String.format("Domain超时移除:%s", dmKey));
                removed = true;
            }
            if(removed)
                rebuildDomainTestTemplateString();
        }
    }

    public String getIntentTestPrompt(String query) {
//        return donmainTestTemplateString.replaceAll("\\{query}", query);
        return donmainTestTemplateString.replace("{query}", query);
    }

    public QIData addNewDataSet(QIData req) {
//        if(JedgeGptApp.isDebugMode())
//            module.infoLog("add dataSet : " + req);
        String dmKey = req.getString("domain");
        String dsKey = req.getString("key");
        JSONArray words = req.getJsonArray("payload");
        boolean nullable = req.getBoolean("na", true);
        synchronized (domainDefMap) {
            if(domainDefMap.containsKey(dmKey)) {
                JedgeDomainDef old = domainDefMap.get(dmKey);
                if(old!=null) {
                    old.ttl = 0;
                    int dmid = req.getInteger("dmid");
                    if (dmid > 0 && dmid != old.getDomainId()) {
                        //覆盖Warn
                    }
                    String cbUri = req.getString("cbUri");
                    String srcMod = JMgbusUtil.GetMgModSource(req);
                    int dsid = req.getInteger("dsid");
                    int dsId = old.addDataSet(dsid, srcMod, cbUri, dsKey, words, nullable);
                    if(dsId>-1)
                        return JMgbusUtil.MgbusResult(200, "添加数据集成功:" + dsKey).putInteger("dsid", dsId);
                }
            }
        }
        return JMgbusUtil.MgbusResult(401, String.format("向领域%s添加数据集失败",dmKey));
    }

    public QIData updateDataset(QIData req) {
        if(JedgeGptApp.isDebugMode())
            module.infoLog("update dataSet : " + req);
        String dmKey = req.getString("domain");
        String dsKey = req.getString("key");
        JSONArray words = req.getJsonArray("payload");
        synchronized (domainDefMap) {
            if(domainDefMap.containsKey(dmKey)) {
                JedgeDomainDef old = domainDefMap.get(dmKey);
                if(old!=null) {
                    old.ttl = 0;
                    int dmid = req.getInteger("dmid");
                    if (dmid > 0 && dmid != old.getDomainId()) {
                        //覆盖Warn
                    }
                    int dsid = req.getInteger("dsid");
                    if(old.updateDataSet(dsid, dsKey, words))
                        return JMgbusUtil.MgbusResult(200, "添加数据集成功:" + dsKey);
                }
            }
        }
        return JMgbusUtil.MgbusResult(401, String.format("向领域%s更新数据集失败",dmKey));
    }

    public QIData addNewDomain(QIData req) {
//        if(JedgeGptApp.isDebugMode())
//            module.infoLog("add domain : " + req);
        String dmKey = req.getString("key");
        JedgeDomainDef domainDef;
        synchronized (domainDefMap) {
            if(domainDefMap.containsKey(dmKey)) {
                JedgeDomainDef old = domainDefMap.get(dmKey);
                int dmid = req.getInteger("dmid");
                if(old!=null && dmid==old.getDomainId()) {
                    old.ttl = 0;
                    return JMgbusUtil.MgbusResult(200, "领域添加成功").putInteger("dmid", dmid);
                } //覆盖
                // return JMgbusUtil.MgbusResult(401, String.format("领域%s已经存在",dmKey));
            }
            domainDef = new JedgeDomainDef(req.getString("dmd"), req.getString("rl"), service);
            //添加消息参数提取器
            domainDef.addDataPicker(req);
            domainDef.setActionCall(req.getString("src"),req.getString("cbUri"));
            domainDef.ttl = 0;  //动态添加的domain
            domainDefMap.put(dmKey, domainDef);
        }
        rebuildDomainTestTemplateString();
        QIData result = JMgbusUtil.MgbusResult(200, "领域添加成功");
//        module.highLog(String.format("领域模块注册成功:%s", dmKey));
        result.putInteger("dmid", domainDef.getDomainId());
        return result;
    }

    public QIData removeDomain(QIData req) {
        int dmid = req.getInteger("dmid");
        if(dmid>0) {
            String dmKey = req.getString("key");
            if(StringUtils.isValidStr(dmKey)) {
                synchronized (domainDefMap) {
                    JedgeDomainDef dmDef = domainDefMap.get(dmKey);
                    if(dmDef!=null && dmDef.getDomainId()==dmid) {
                        domainDefMap.remove(dmKey);
                        module.highLog(String.format("领域模块移除成功:%s", dmKey));
                        return JMgbusUtil.MgbusResult(200,"removed " + dmKey);
                    }
                }
            }
        }
        return JMgbusUtil.MgbusResult(401, "No dmid or mismatched.");
    }

    public void loadIntentDatasets(String path) {
//        domainDefMap.put("其它问题", new JedgeDomainDef("与上述内容无关的其它对话内容", "智能助手", service));
        rebuildDomainTestTemplateString();
    }

    private void rebuildDomainTestTemplateString() {
        StringBuilder domainSet = new StringBuilder();
        StringBuilder domainDisc = new StringBuilder();
        int i=1;
        synchronized (domainDefMap) {
            for (String key : domainDefMap.keySet()) {
                JedgeDomainDef def = domainDefMap.get(key);
                if (domainSet.length() > 0) {
                    domainSet.append(",");
                    domainDisc.append(";\n");
                }
//                domainSet.append(String.format("%d %s", i, key));
                domainSet.append(String.format("%s", key));
                domainDisc.append(String.format("意图领域%d,%s,涉及%s",i, key, def.domainDisc));
                i++;
            }
        }
        domainSet.append(",其它问题");
//        domainSet.append(String.format(",%d 其它问题", i));
        String intentRange = String.format("%s", domainSet);
        domainDisc.append(String.format(",意图领域%d,其它问题,与上述内容无关的其它对话内容.\n", i));
        donmainTestTemplateString = String.format(sval_cmd_test_domain_, domainDisc, intentRange, jsonIntentFormat);
//        module.markLog("\n领域的template:"+templateString);
    }

}
