package com.ch.jedge.jbot.agent;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.ch.jedge.jbot.dict.JedgeWordItem;
import com.ch.jedge.jbot.knbase.JedgeGptApp;
import com.ch.jedge.jbot.knbase.JedgeGptDomain;
import com.ch.jedge.jbot.knbase.JedgeLlmIntent;
import com.ch.jedge.jbot.knbase.intent.JedgeGdeQuery;
import com.ch.jedge.jbot.knbase.pool.JedgeGdeKnItemPool;
import com.ch.jedge.jbot.session.JedgeGdeKeywordPool;
import com.ch.jedge.jbot.session.JedgeServiceSession;
import com.changhong.qlib.util.StringUtils;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;

import static com.ch.jedge.jbot.agent.JedgeGdeConsts.ival_max_active_domain_count;
import static com.ch.jedge.jbot.agent.JedgeGdeConsts.sval_default_sys_role;

/**
 *  核心的对话管理器，维护当前与用户的聊天状态：
 *      · 应用
 *      · 领域
 *      · 主题
 *      · 意图
 *      · 当前任务
 *      · 任务执行状态
 *      · 用户信息（包括授权信息等）
 *
 *      ` 知识池
 *      ` 关键字池
 *      ` 可用意图列表 ( 当前应用\当前领域下的可用意图表 )
 */

public class JedgeGdeSession {
    private final JedgeGptApp mGdeApp; // 应用主题, 如检测中心,检测较准中心,启睿科检测较准中心
    protected final Queue<String> user_queries_ = new LinkedList<>();

    private final long sid;

    private String sRole = sval_default_sys_role;

    private final String srcMod;
    private final String cbUri;

    private final Object accCtrl = new Object();
    //当前的Domain
    private JedgeGptDomain activeDomain;        // 领域主题, 如业务咨询
    private int  activeDomainLifeTime = 0;    //领域活跃时间，10分钟无对话，自动切换到home

    private final JedgeGdeKeywordPool knPool;
    //记录当前的知识池
    private final JedgeGdeKnItemPool knItemPool;
    private final JedgeGdeAgentRunner agentRunner;

    //意图池?
    private final Map<String, JedgeLlmIntent> mContextIntents = new HashMap<>();        //从关键字到意图的映射。记住上下文的意图和执行结果。
    private final Map<String, JedgeGptDomain> mDomainEntry = new HashMap<>();           //Domain入口关键字

    //调取聊天的背景关键字
    public JedgeGdeSession(JedgeGptApp app, String srcMod, String cbUri, long sid) {
        this.mGdeApp = app;
        this.srcMod = srcMod;
        this.cbUri = cbUri;
        this.sid = sid;
        this.knPool = new JedgeGdeKeywordPool(this);
        this.knItemPool = new JedgeGdeKnItemPool(app,this);
        this.agentRunner = new JedgeGdeAgentRunner(mGdeApp, this);
        if(mGdeApp!=null)
            mGdeApp.activeCommonWords();
    }

    public void returnGptResponseMessage(String msg) {
        mGdeApp.getBotService().returnGptResponseMessage(srcMod, cbUri, msg, false, false);
    }

    public void returnGptErrorMessage(String msg) {
        mGdeApp.getBotService().returnGptResponseMessage(srcMod, cbUri, msg, true, true);
    }

    public void returnGptFinalMessage(String msg) {
        mGdeApp.getBotService().returnGptResponseMessage(srcMod, cbUri, msg, true, false);
    }

    /**
     *  在当前的Domain中处理 gQuery
     *  利用大模型调用，在一轮对话以后、确定获得当前的主题？
     *  · 检查关键字池中的关键字，如果出现了切换条件，则对功能域进行切换，
     */
    public void updateTopic(JedgeGdeQuery gQuery) {
        //处理并生成多种关键字组合，利用大模型生成最大可能的聊天主题(仅限于当前领域可接受的主题)
        //调用一个 LLM call 获得主题
        // mActiveTopic = knItemPool.getActiveSubject();   //设定当前的聊天主题？ 只有在当前聊天后才能进行

        //基于当前的 domain
        //切换 Domain 。在对话中，如果提到了某个 domain 的关键字，且比重较高，则切换之。

        String nextDomain = matchNextactiveDomain(this, gQuery);
        if(nextDomain!=null && !nextDomain.equals(activeDomain.getDomainName())) {
            switchActiveDomain(nextDomain);
            // SystemUtils.tryWait(200);
            // mGdeApp.prepareWords(gQuery.query_, gQuery);     //重新识别意图?
        }
        activeDomainLifeTime = ival_max_active_domain_count;
    }

    public void loadDomainGateWord(JedgeGptDomain a) {
        //加载：从可切换关键字到Domain的映射。但可能面临重叠的情况，
        for(JedgeWordItem item : a.getGlobalWordDict().getWordItems()) {
            if(item.isGated) {
                synchronized (mDomainEntry) {
                    mDomainEntry.put(item.word, a);
                }
            }
        }
        a.activeDomainWordDict(true);     //将gate的关键字进入分词库
    }

    //TODO::从session的关键字池中， 从qQuery的词汇中，计算进入其它领域的概率。
    // mDomainEntry
    // 当前是采用关键字匹配的方式进行切换
    public String matchNextactiveDomain(JedgeGdeSession gs, JedgeGdeQuery gQuery) {
        synchronized (mDomainEntry) {
            for(String word : gQuery.sel_words_) {
                JedgeGptDomain a = mDomainEntry.get(word);
                if(a!=null)
                    return a.getDomainName();
            }
        }
        return null;
    }

    public void switchActiveDomain(String homeDomain) {
        JedgeGptDomain newDomain = mGdeApp.getDomainByName(homeDomain);
        //如果
        if(newDomain!=null) { //更新当前Session中的一些关键字
            synchronized (accCtrl) {
                if (this.activeDomain != null) {
                    activeDomain.clearWordDict(false);
                }
                activeDomain = newDomain;
                activeDomain.activeDomainWordDict(false);
//                SystemUtils.tryWait(50);    //避免立即cut
                activeDomainLifeTime = ival_max_active_domain_count;
                knItemPool.pushKeyWord(activeDomain, activeDomain.getSubjectTitle(), activeDomainLifeTime);
            }
            sRole = activeDomain.domainRole;
            if(JedgeGptApp.isDebugMode())
                mGdeApp.getMgModule().highLog(String.format("Domain switched : %s, role : %s",
                        activeDomain.getSubjectTitle(), activeDomain.domainRole));
        }
    }

    public void updateContextKnowledge(JedgeGdeQuery gQuery) {
        //  提取关键字可能涉及到的知识
        for( String key : gQuery.sel_words_ ) {
            //检查是否已在关键字Pool中
            if(knItemPool.existsWord(activeDomain, key)) {
                knItemPool.refreshKeyWord(key);
                continue;
            }
            knItemPool.pushKeyWord(activeDomain, key, 0);    //Key可以找到对应的知识。若 domain 更新，则需要重新检索。
        }
    }

    //根据当前的关键字和对话，判定当前的意图
    public JedgeLlmIntent parseIntent(JedgeGptApp app, JedgeServiceSession s, JedgeGdeQuery query) {
        //TODO::切换意图和角色
        /**
         * 根据query和s中的关键字，进行一个组合，   得到多个词向量，用词向量去向量数据库中，检索对应的意图。
         * 得到意图的描述数据，然后，执行这个意图。
         * 意图的特征是： 在上下文，环境中，提到了哪些关键字， 哪些动词表示了对意图或目标的描述。
         */

        return new JedgeLlmIntent(app, s, "");
    }

    public long getSessionId() {
        return sid;
    }

    public String getActiveContent(JedgeGdeQuery query) {
        return knItemPool.getActiveContent(query);
    }

    //  一个异步答复机器
    private void retriveQueryString() {
        while(true) {
            try {
                String query = null;
                {
                    if (!user_queries_.isEmpty()) {
                        query = user_queries_.poll();
                    }
                }
                if(query==null) break;    //无数据，跳出循环

                mGdeApp.getMgModule().highLog(String.format("用户输入：%s", query));

                // 分析出一个语义结构
                //每个session，对语义数据进行入栈，然后进行语义分析，生成一个当前Query的语义结构，然后检测敏感语义，
                //然后使用调度器，用分析后的语义结构进行知识或动作检索，转发当前的语义结构，

                //对返回应答或本地模型问答，进行可信度校验。  （需要设计一种校验的机制）
                //
                JedgeGdeQuery gdeQuery = analyzeWords(query);

                //如果未匹配到任意意图：
                if(gdeQuery==null) {
                    if(JedgeGptApp.isDebugMode())
                        returnGptFinalMessage("【本次输入内容无应答】");
                    continue;
                }

                //语义过滤:: 过滤敏感词、需要禁止提问的主题、话题，涉及敏感的数据等
                //针对单句话，进行过滤，  短语，在一个上下文语境中，解释其中的含义。
                //短语如果缺少对话内容，则应该从上下文中，提取对应的词进行匹配。
                //如果有动词或动词性短语，则匹配意图，否则融入到上下文，并提取默认的意图。
                if (!checkUserQueryValid(gdeQuery)) {
                    returnGptErrorMessage("该话题涉及机密或其它不适合讨论的问题，请更换话题内容。");
                    //TODO::清空当前对话状态
                    continue;
                }

                //进入调度阶段时，生成一个调度动作 （执行API、 查询知识库、 或者生成一个本地模型应答， 或者一个外部转发调度）
                // 将意图放入队列， 待意图执行器执行完毕后,调度执行。若意图发现缺少参数，则会触发提问。
                // 若意图被更新时，也会立即触发对意图的执行。

                agentRunner.saveDialogQuery(gdeQuery);

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public void refreshQARange() {
        knPool.refreshQARange();    //刷新对话距离
    }

    public void knItemPoolHeartBeat() {
        retriveQueryString();
        agentRunner.refreshStatus();        //触发意图行为的调度执行
        knItemPool.onKnItemHeartbeat();
        knPool.onKnItemHeartbeat();
        activeDomainLifeTime--;
        if(activeDomainLifeTime<1)
            if(activeDomain!=null && !mGdeApp.getHomeDomainKey().equals(activeDomain.getDomainName()))
                switchActiveDomain(mGdeApp.getHomeDomainKey());
            else
                activeDomainLifeTime = ival_max_active_domain_count;
        //处理用户输入会话
    }

    /**
     * 分析意图:
     *      分词，
     *      匹配短语    用n-gram算法，匹配出可能合法的短语及其词性，
     *
     *      提取名词、专有名词或动词，  推导出意图，
     *
     *          如果不能推导出意图, 则将本句意图与上句意图合并, 作为上一个意图的补充参数.
     *          如果上一个意图为空,则
     *
     *      对知识切片的处理：应该是遍历文本后、自动生成，
     *
     */

    // 对
    public JedgeGdeQuery analyzeWords(String word) {
        JedgeGdeQuery gdeQuery = new JedgeGdeQuery(mGdeApp, this, word);

        //分词
        gdeQuery.pushWords(mGdeApp.cutWords(word));  //保存words
        //处理短语，将 i_words 处理成关键短语+关键字的组合序列
        //n_grams算法分析短语
        mGdeApp.scanTerms(gdeQuery);

        //增强知识，根据对话过程中的关键字，提取知识库内容到知识缓存中，
        // 构造一个key/action/knContent/qrIntent的序列。
        recallKnItems(gdeQuery);

        //分析意图，利用关键动作+词性的匹配模式，在上下文中，去匹配意图
        //简单的意图匹配，当前的query的意图，是否复合当前的知识领域。
        if(!matchIntents(gdeQuery)) {
            return null;
        }
        return gdeQuery;
    }

    private void recallKnItems(JedgeGdeQuery intent) {
        //增强知识到session缓存中，
        for( Object word : intent.words_ ) {
            if(word instanceof JSONObject
                //专有名词，去检索内容
            ) {
                String wt = ((JSONObject) word).getString("t");
                if("n".equals(wt) || "nz".equals(wt) || wt.startsWith("nr")) {
                    String keyText = ((JSONObject) word).getString("w");
                    //获得相关的知识
                    mGdeApp.getMgModule().highLog(String.format("Getting KN>>%s", keyText));
                    String KnContent = getMilvusContentFromVectorDb(keyText);
                    if (StringUtils.isValidStr(KnContent)) {
                        //TODO::创建一个知识缓存数据

                    }
                }
            }
        }
    }

    private String getMilvusContentFromVectorDb(String keyText) {
        JSONArray vecArr = mGdeApp.getBotService().getVectorOfString(keyText);
        if(vecArr==null || vecArr.isEmpty()) {
            return null;
        }
        return mGdeApp.getBotService().getKnowledgeFromMilvus(sid, vecArr);
    }

    //根据词和短语的构造情况，快速分类意图。
    // 快速提出本地可以处理的意图。如果存在多语句，则？
    private boolean matchIntents(JedgeGdeQuery intent) {
        //TODO::从缓存的知识和关键字中，提取所有可能的关键字，用最大概率的方法，组合出一个意图关键字序列
        // 向APP的意图库，进行相似度检索，命中一个意图

        return true;
    }

    /**
     * 需要支持多种敏感词检验算法
     */
    //检查有词义的关键字,是否存在敏感词汇
    //检查关键字的组合基于上下文能否推理出敏感词汇
    public boolean checkUserQueryValid(JedgeGdeQuery intent) {

        return true;
    }

    //分发模型的任务
    public void saveDialogQuery(JedgeGdeQuery intent) {
        returnGptFinalMessage("暂未实现。");

        //实现一个意图调度器，
        //根据匹配，用于生成一个动作：（向特定的大模型发起一个调用，或者向特定的API执行器发起一个调用）
    }

    public String getServiceRole() {
        return StringUtils.isValidStr(sRole)?sRole:sval_default_sys_role;
    }

    public void pushQuery(String query) {
        synchronized (user_queries_) {
            user_queries_.add(query);
        }
    }

    public String getComposedPromptByActiveKey(JedgeServiceSession ss, String prompt, boolean helping) {
        return knPool.getComposedPrompt(ss, prompt, helping);
    }

    public JedgeGptApp getGdeApp() {
        return mGdeApp;
    }

    public JedgeGdeKeywordPool getKnPool() {
        return knPool;
    }
}
