//
// Created by Ht on 2022/1/8.
//

#ifndef INC_0_LIBDEVCPP_QJAGAFFICSERVICE_H
#define INC_0_LIBDEVCPP_QJAGAFFICSERVICE_H

#include <exlib/QJAServiceEntry.h>
#include "src/QJAGafficContext.h"
#include "src/QJAGafficUtil.h"

namespace jedge {

    static const char* vkey_default_site = "lightjia";       //此版本默认连接虹美平台

    class QJAGafficDeviceService :
#ifdef PLUGIN_ENABLED
            public  QJAPluginService {
#else
            public  jedge::QJAMgService {
#endif

            protected:
        QJAGafficContext& gf_context_;
        std::string       active_site_;

    public:

        explicit QJAGafficDeviceService(jedge::QJAMgServer& opr, const std::string &name, const qlibc::QData &config)
#ifdef PLUGIN_ENABLED
            : jedge::QJAPluginService(opr, name, config), gf_context_(QJAGafficContext::getInstance(opr, *this)) {
#else
            : jedge::QJAMgService(opr, name, config), gf_context_(QJAGafficContext::getInstance(opr, *this)) {
#endif
            auto_remove_call_info = false;
            active_site_ = config.getString("active");
            if(active_site_.empty()) {
                active_site_ = operator_.getRunCachePropertyString(opr.name()+"."+"active",vkey_default_site);
            } else {
                saveActiveSetup();
            }
            setVersionInfo("0.1.0.1");
            BindServiceCommand("cache", "显示cache中的变量 : cache <varKey> [-u,-f] : e.g.: cache deviceInfo/ cache deviceInfo -u : 立即更新数据, -f:显示已缓存数据 ", showCacheVar);
            BindServiceCommand("site", "列举或设置活跃site : site hongmei / site -l ", setActiveSite);
            BindServiceCommand("gao", "触发一个Gao对象 : gao -r/-c/-d/-u mqtt <keyVal> [--userArea kname] [--userIndex kname]... ", runGao);
            BindServiceCommand("rr,reload", "触发gao对象重新加载 : reload ", reload);
            BindServiceCommand("dd", "切换调试消息 : dd ", setDebugMessage);
        }

        ~QJAGafficDeviceService() override {
            QJAGafficContext::clearContext();
        }

        bool loadServiceJAClasses() override {
            //创建 内嵌类
            return QJAMgService::loadServiceJAClasses();
        }

    protected:
        //显示缓存中的变量及其值状态
        bool showCacheVar(qlibc::JCArgNode& param) {
            auto cacheKey = param.getParam(1);         //
            auto site = param.getString("site");    // --site hongmei
            auto full = param.getBool("f");         //  -f
            if(site.empty())
                site = active_site_;
            auto update = param.getBool("u");       // -u
            auto* s = gf_context_.findSite(site);
            if(s!=nullptr) {
                s->showCachedVars(cacheKey, full, update);
            }
            return true;
        }

        //显示缓存中的变量及其值状态
        bool setActiveSite(qlibc::JCArgNode& param) {
            auto listSite = param.getBool("l");
            if(listSite) {
                gf_context_.listLoadedSites();
                return true;
            }
            auto site = param.getParam(1);
            if(!site.empty()) {
                auto *s = gf_context_.findSite(site);
                if (s == nullptr) {
                    QWarn("Site %s not loaded.", site.c_str());
                } else {
                    active_site_ = site;
                    saveActiveSetup();
                }
            } else {
                if(active_site_.empty())
                    active_site_ = gf_context_.getFirstSiteName();
            }
            HVULogR("Active site set to : %s", active_site_);
            return true;
        }

        QJAGafflicSite* getActiveSite(qlibc::JCArgNode &param, std::string& site) {
            if(site.empty())
                site = param.getString("site");
            if(site.empty())
                site = active_site_;
            auto* sp =  gf_context_.findSite(site);
            if(sp== nullptr && site!=active_site_)
                sp = gf_context_.findSite(active_site_);
            if(sp==nullptr) {
                active_site_ = gf_context_.getFirstSiteName();
                sp = gf_context_.findSite(active_site_);
                saveActiveSetup();
            }
            return sp;
        }

        bool setDebugMessage(qlibc::JCArgNode& param) {
            auto site = param.getParam(1);
            auto *s = getActiveSite(param, site);
            if (s == nullptr) {
                QWarn("Site %s not loaded.", site.c_str());
            } else {
                bool dm = !s->isDebuggingMessage();
                if (param.containsKey("m"))
                    dm = param.getBool("m");
                s->setDebuggingMessage(dm);
                MKFLog("Debugging message is set : %s", dm?"on":"off");
            }
            return true;
        }
        bool reload(qlibc::JCArgNode& param) {
            auto site = param.getParam(1);
            auto *s = getActiveSite(param, site);
            if (s == nullptr) {
                QWarn("Site %s not loaded.", site.c_str());
            } else {
                s->reloadSite();
            }
            return true;
        }

        bool runGao(qlibc::JCArgNode& param) {
            auto showResult = param.getBool("i");
            auto list = param.getBool("l");
            if(list) {
                auto grepKey = param.getParam(1);
                std::string site;
                auto *s = getActiveSite(param, site);
                if (s == nullptr) {
                    QWarn("Site %s not loaded.", site.c_str());
                } else {
                    s->listGaoItems(grepKey);
                }
                return true;
            }
            auto keyVal = param.getParam(1);
            if(keyVal.empty()) {
                QWarn("user index-type must be set, e.g. gao <mqtt|mgbus> statusValue ...");
                return true;
            }

            if(keyVal.find('.')!=std::string::npos) { // gao mqtt.xxx.xxx
                std::string site;
                auto *s = getActiveSite(param, site);
                if (s == nullptr) {
                    QWarn("Site %s not loaded.", site.c_str());
                } else {
                    auto* gao = s->findGAO(keyVal);
                    if(gao!= nullptr) {
                        qlibc::QData resp;
                        gao->performAction(&param, &resp);
                        if(showResult)
                            QEMark("Result of gao is : \n%s", resp.toJSONString(true).c_str());
                    } else {
                        QWarn("gao of %s not exits...", keyVal.c_str());
                    }
                }
                return true;
            }

            auto channel = param.getParam(2);
            std::string type ="request";
            if(channel.empty()) {
                channel = "mgbus";
            } else {
                channel = keyVal;
                keyVal = param.getParam(2);
            }

            auto runDeliver = param.getBool("d");
            if(runDeliver) { //gao mqtt -d response
                auto keyType = param.getString("userIndex", "uri");
                std::string site;
                auto *s = getActiveSite(param, site);
                if (s == nullptr) {
                    QWarn("Site %s not loaded.", site.c_str());
                } else {
                    auto* gao = s->findDeliverGAO(channel, keyType, keyVal);
                    if(gao!= nullptr) {
                        qlibc::QData resp;
                        gao->performAction(&param, &resp);
                        if(showResult)
                            QEMark("Result of gao is : \n%s", resp.toJSONString(true).c_str());
                    } else {
                        QWarn("gao of %s.%s.%s.%s not exits...", channel.c_str(), "deliver", keyType.c_str(), keyVal.c_str());
                    }
                }
                return true;
            }

            auto runCycler = param.getBool("c");
            if(runCycler) {
                type = "cycler";
                QCmt("Not implemented yet ... ");
                return true;
            }

            auto runUser = param.getBool("u");
            if(runUser) {
                auto userArea = param.getString("userArea");
                auto userIndex = param.getString("userIndex");
                if(userArea.empty() || userIndex.empty()) {
                    QWarn("user index-type must be set, e.g. gao mgbus --userArea response --userIndex command ...");
                    return true;
                }
                std::string site;
                auto *s = getActiveSite(param, site);
                if (s == nullptr) {
                    QWarn("Site %s not loaded.", site.c_str());
                } else {
                    qlibc::QData resp;
                    param.putString(userIndex, keyVal);
                    if(!s->handleUserHandler(userArea, userIndex, &param, &resp)) {
                        QWarn("User gao (%s.%s: %s) handle failed ", userArea.c_str(), userIndex.c_str(), keyVal.c_str());
                    }
                    if(showResult)
                        QEMark("Result of gao is : \n%s", resp.toJSONString(true).c_str());
                }
            }

            auto runReq = param.getBool("r", true);
            if(runReq) {
                std::string site;
                auto *s = getActiveSite(param, site);
                if (s == nullptr) {
                    QWarn("Site %s not loaded.", site.c_str());
                } else {
                    auto* gao = s->findGAO(channel, type, keyVal);
                    if(gao!= nullptr) {
                        qlibc::QData resp;
                        gao->performAction(&param, &resp);
                        if(showResult)
                            QEMark("Result of gao is : \n%s", resp.toJSONString(true).c_str());
                    } else {
                        QWarn("gao of %s.%s.%s not exits...", channel.c_str(), type.c_str(), keyVal.c_str());
                    }
                }
                return true;
            }
            return true;
        }

        void prepareInnerObjects() override {
            //创建 内嵌对象
            JAObject::prepareInnerObjects();
        }

        void saveActiveSetup() {
            operator_.updateRunCacheProperty(name()+"."+"active", active_site_);
        }

    public:
        //绑定各种内嵌消息响应方法

        //查询有多少个site
        bool listSite(QData &req, QData *resp) {
            Json::Value sites(Json::arrayValue);
            gf_context_.runOnAllSites([&sites](const std::string& k, QJAGafflicSite& o) {
                qlibc::QData d;
                o.getSiteInfo(d);
                sites.append(d.asValueConst());
            });
            OkPRspMsg(resp,"ok");
            resp->put("payload", sites);
            if(!active_site_.empty())
                resp->put("active", active_site_);
            return true;
        }

        //创建或更新一个新的site设置。如果站点不存在，则创建这个站点
        bool addSite(QData &req, QData *resp) {
            return gf_context_.updateSite(req, resp);
        }

        //查询有多少个site
        bool delSite(QData &req, QData *resp) {
            auto siteName = req.getString("site");
            if(gf_context_.removeSiteData(siteName)) {
                if(siteName==active_site_)
                    active_site_ = gf_context_.getFirstSiteName();
                OkPRspMsg(resp, "ok");
            } else {
                FailPRspMsg(resp,"fail to del site : " + siteName);
            }
            return true;
        }

        //设置更新模板的地址,并立即更新依次，来去所有可用的mod名称
        bool setSiteHost(QData &req, QData *resp) {
            auto base = req.getString("base");
            if(!base.empty()) {
                if(!gf_context_.setSiteModeHostBase(base)) {
                    FailPRspMsg(resp, "Invalid host base format, host base not changed.");
                    return true;
                }
            } else {
                QWarn("empty param : base ");
            }
            QStringList mods;
            if(gf_context_.updateSiteModes(mods)) {
                OkPRspMsg(resp, "ok");
                Json::Value results(Json::arrayValue);
                StringUtils::listToJsonArray(mods, results);
                resp->put("payload", results);
            } else {
                FailPRspMsg(resp, "Invalid host base or host not reachable.");
            }
            return true;
        }

        //查询可用的站点模板
        bool getSiteMode(QData &req, QData *resp) {
            Json::Value results(Json::arrayValue);
            QStringList mods;
            gf_context_.getAvailableSiteMod(mods);
            StringUtils::listToJsonArray(mods, results);
            resp->put("payload", results);
            OkPRspMsg(resp,"ok");
            resp->put("payload", results);
            return true;
        }

        void preparePattern() override {
            QJAMgService::preparePattern();
            prepareSelfBind;
            bindHandler(addSite);           //创建一个新的site，主要是
            bindHandler(delSite);           //删除站点测试
            bindHandler(listSite);          //列举已加载的site站点
            bindHandler(setSiteHost);       //设置site的mode服务器、并更新可用的sitemode
            bindHandler(getSiteMode);       //获取可用的siteMode
        }

        bool jaMessageCall(QData &msg, qlibc::QData *rsp) override {
            if(gf_context_.mgbusHandle(msg, rsp))
                return true;
            if(!QJAMgService::jaMessageCall(msg, rsp)){
                FailPRspMsg(rsp, "Not handled.");
                return false;
            }
            return true;
        }

        void startGafficContext() {
            SystemUtil::tryWait(1000);
            gf_context_.initContext();
            gf_context_.runOnAllSites([this](const std::string& k, QJAGafflicSite& o) {
                operator_.threadPool().enqueue([this,k,&o](){
                    SystemUtil::tryWait(2000);
                    auto* param = operator_.getBlankMessage();
                    param->setString("module", operator_.name());
                    param->setString("host", operator_.getRunCachePropertyString("mg_host"));
                    param->setInt("port", operator_.getRunCachePropertyInt("mg_port",8877));
                    o.invokeDPEvent("mgbus","onConnect", *param);
                    operator_.releaseMessage(param);
                });
            });
        }

//等待cpad上线后，再上云端
        bool OnModuleConnected(MgTriggerWatcher &w, const string &k, const QData &m) override {
            //已接入mgbus
            //监听cpad上线，再接入云端。
            auto self = getSelfRef();
            watchMgbusEvent("online", "cpad", [this, self]WatcherCallbackLamda(mm) {
                GetMgbusEventModuleName(mm, mgnet);
                //只监听本地cpad上线
                if(isLocalMgbusEvent(mm)) {
                    startGafficContext();
                }
                return true;
            });
            return QJAMgService::OnModuleConnected(w, k, m);
        }

        bool OnModuleDisconnected(MgTriggerWatcher &w, const string &k, const QData &m) override {
            gf_context_.runOnAllSites([this](const std::string& k, QJAGafflicSite& o) {
                operator_.threadPool().enqueue([this,k,&o](){
                    SystemUtil::tryWait(2000);
                    auto* param = operator_.getBlankMessage();
                    param->setString("module", operator_.name());
                    param->setString("host", operator_.getRunCachePropertyString("mg_host"));
                    param->setInt("port", operator_.getRunCachePropertyInt("mg_port",8877));
                    o.invokeDPEvent("mgbus","onDisconnect", *param);
                    operator_.releaseMessage(param);
                });
            });
            return QJAMgService::OnModuleDisconnected(w, k, m);
        }

        void onServiceStart(const QData &config) override {
            QJAMgService::onServiceStart(config);
//            startGafficContext();
        }

        void onServiceStop() override {
            gf_context_.releaseContext();
            MgService::onServiceStop();
        }
    };

}
#endif //INC_0_LIBDEVCPP_QJAGAFFICSERVICE_H
