//
// Created by Ht on 2022/10/21.
//

#ifndef IOT_EDGE_VDALNEW_QLEVELEDMERGEQUEUE_H
#define IOT_EDGE_VDALNEW_QLEVELEDMERGEQUEUE_H

#include <exlib/QJAServer.h>
#include "QClassifiedQueue.h"

namespace jedge {

    class JE_API QVdalTransMessager {
    public:
        virtual void setClassifiedQueue(qlibc::QClassifiedQueue *q) = 0;
        virtual void handleIncomeMessage(qlibc::QData &msg) = 0;
        virtual void handleConfirmReplayMessage(qlibc::QData &msg) = 0;
    };

    class JE_API QLeveledMergeQueue {
    protected:
        MgBusHolder &opr_;
        QVdalTransMessager   &msgr_;
        qlibc::QClassifiedQueue    q_;
        bool                quit_:1;
        bool                active_:1;
        std::mutex          vs_m_;
        std::condition_variable vs_w_;

        QStringList         empty_mcl_;
        std::vector<QStringList*> mergable_lists_;
        qlibc::QPObjectHolder<QStringList> mergableCommandsTable;


    public:
        explicit QLeveledMergeQueue(MgBusHolder &opr, QVdalTransMessager& messager, const qlibc::QData &config) :
                opr_(opr),msgr_(messager),
                q_(opr,config.getObjFmtString("consts.merge_queue.classified"),config.getObjFmtString("consts.merge_queue.message_key")),
                quit_(false), active_(false), mergableCommandsTable(false) {
            q_.setTimeouts(config.getObjFmtData("consts.merge_queue.timeouts"));
            q_.setMessageReleaser([this](qlibc::QData* m) {
                opr_.releaseMessage(m);
            });
            buildMergeCommandList(config);
        }

        virtual ~QLeveledMergeQueue () {
            for(auto* kl:mergable_lists_)
                delete kl;
            mergable_lists_.clear();
        }

        void setClassifiedQueue(){
            msgr_.setClassifiedQueue(&q_);
        }

/*        bool handleMessageFromMoga(qlibc::QData &req, qlibc::QData *response){
            req.setBool("~comfirmed", true);
            string model_name = operator_.name();
            model_name = StringUtils::toUpperCase(model_name);
            QInfo("%s-Moga received:%s", operator_.name().c_str(),req.toJSONString().c_str());
            if(vdd_context_.handleMogaMessage(operator_, req,response)) {
                OkPRsp(response);
                return true;
            }
            return true;
        }*/

        void handleMessageFromQuque() {
            while (!quit_) {
                active_ = true;
//                SystemUtil::SysSleep(800);  //TODO:: Android正式调试时去掉此行
                auto* msg = q_.retreiveMessage(); //优先从分级队列中取
                if(msg != nullptr ) {
                    if(  msg->getString("cmd_source") == "pending"   ) {
                        QInfo("retrieve a PENDING message...:%s",msg->toJSONString().c_str());
                        msgr_.handleConfirmReplayMessage(*msg);
                        continue;
                    } else {
                        msg->setBool("~local",false);
                        QInfo("retreive msg = %s",msg->toJSONString().c_str());
                        if( msg->getString("command") == "scanDevice"){
                            opr_.threadPool().enqueue([this,msg]() {
                                msgr_.handleIncomeMessage(*msg);
                            });
                        }else{
                            msgr_.handleIncomeMessage(*msg);
                        }
                        continue;
                    }
                }
            }
            QLog("Quit message handle queue.");
            SINGLE_LOCK(vs_m_);
            active_ = false;
            vs_w_.notify_all();
        }

        QStringList& getMergeCommandList(const std::string &src) {
            auto mcl= mergableCommandsTable.findObject(src);
            if(mcl==nullptr) mcl = &empty_mcl_;
            return *mcl;
        }

        bool isMergingCommand(const std::string &src,const QStringList& mergeCmdList) {
            for(const auto& mk : mergeCmdList)
                if(mk==src) return true;
            return false;
        }

        void mergeMessageOperation(qlibc::QDataPList &msgQueue, const std::string &deviceId, const std::string &cmd) {
            //对msgQueue中的指令进行幂等清理
            if(msgQueue.empty()) return;
            std::vector<qlibc::QData*> msglist;
            for(auto* msg:msgQueue){
                auto mcmd = msg->getString("command");
                auto mdid = msg->getString("device_id");
                auto cmdlist  = getMergeCommandList(cmd);
                if(deviceId == mdid && isMergingCommand(mcmd, cmdlist)) {
                    msglist.emplace_back(msg);
                }
            }
            for(auto* msg: msglist) {
                msgQueue.remove(msg);
                QWarn("merging remove msg-%s", q_.makeMessageLog(msg).c_str());
                opr_.releaseMessage(msg);
            }
        }

        void clearMessageOperationForDevice(qlibc::QDataPList &msgQueue, const std::string &deviceId) {
            //对msgQueue中的涉及deviceId的设备指令进行清理
            if(msgQueue.empty()) return;
            std::vector<qlibc::QData*> msglist;
            for(auto* msg : msgQueue){
                if(msg->getString("device_id") == deviceId){
                    msglist.emplace_back(msg);
                }
            }
            for(auto* msg: msglist){
                msgQueue.remove(msg);
                QWarn("clear remove msg-%s", q_.makeMessageLog(msg).c_str());
                opr_.releaseMessage(msg);
            }
        }
        void clearLevelMessageOperation(qlibc::QDataPList &msgQueue) {
            if(msgQueue.empty()) return;
            std::vector<qlibc::QData*> msglist;
            for(auto* msg : msgQueue){
                msgQueue.remove(msg);
                QWarn("clear queue... remove msg-%s", q_.makeMessageLog(msg).c_str());
                opr_.releaseMessage(msg);
            }
        }
        void clearOtherSceneMessage(qlibc::QDataPList &msgQueue, const std::string &sceneId, const std::string &scExId) {
            //对msgQueue中的涉及deviceId的设备指令进行清理
            if(msgQueue.empty()) return;
            std::vector<qlibc::QData*> msglist;
            for(auto* msg : msgQueue){
                if(msg->getString("scene_id") != sceneId || msg->getString("scene_dx_id") != scExId) {
                    msglist.emplace_back(msg);
                }
            }
            for(auto* msg: msglist) {
                msgQueue.remove(msg);
                QMark("scene clean remove msg-%s on pushing %s(%s)", q_.makeMessageLog(msg).c_str(), sceneId.c_str(), scExId.c_str());
                opr_.releaseMessage(msg);
            }
        }

        bool deviceNotEmpty(qlibc::QDataPList &msgQueue, const std::string &deviceId) {
            //对msgQueue中的涉及deviceId的设备指令进行清理
            if(msgQueue.empty()) return false;
            for(auto* msg : msgQueue){
                if(msg->getString("device_id") == deviceId){
                    return true;
                }
            }
            return false;
        }

        void buildMergeCommandList(const qlibc::QData& data) {
            auto* mgCmdList = data.getObjFmtValueRef("consts.merge_queue.commands");
            if(mgCmdList== nullptr || !mgCmdList->isArray()) return;
            for(auto& cfg : *mgCmdList) {
                if(!cfg.isString())
                    continue;
                auto str = cfg.asString();
                if(StringUtils::isNotValidStr(str) || StringUtils::startsWith(str,"//"))
                    continue;
                auto *kl = new QStringList;
                StringUtils::SplitString(str,",",*kl);
                mergable_lists_.emplace_back(kl);
                for(const auto& k : *kl) {
                    if(!k.empty())
                        mergableCommandsTable.appendNew(k, kl);
                }
            }
        }

        /*
         *  TODO::此部分代码，需要抽象为用 port.json 进行描述
         * 指令处理策略：
         * 优先级：cfg > cpad > app > manual场景 > auto场景
         * 1、最高级cfg的指令进入，清空其余所有指令；
         * 2、cpad指令进入，幂等操作后入队列，清空manual和auto队列；
         * 3、app 指令进入，幂等操作后入队列；
         * 4、manual场景指令进入，判断是否是第一条场景指令，若是清空之前的manual队列；若不是则幂等操作入队列，清空auto队列；
         * 5、auto  场景指令进入，判断是否是第一条场景指令，若是清空之前的auto  队列；若不是则幂等操作入队列；*/
        void initMergedQueue() {
            q_.setLevelMessageChecker("cfg", [this](qlibc::QDataPListVector& q, unsigned char level, qlibc::QData* m)->bool {
                //清理所有非管理指令
                QInfo("cfg command going into queue, CLEAR ALL CONTROL COMMAND...");
                for(unsigned l = level+1; l<q.size(); l++) {
                    auto* kl = q[l];
                    if(kl!= nullptr) kl->clear();
                }
                auto cmd = m->getString("command");
                auto deviceId = m->getString("device_id");
                mergeMessageOperation(*q[level], deviceId, cmd);
                return true;//指令可以插入队列
            });

            q_.setLevelMessageChecker("cpad", [this](qlibc::QDataPListVector& q, unsigned char level, qlibc::QData* m)->bool {
                //管理指令不为空，各后续指令被拒绝
                if(!q[0]->empty() || q_.getActiveLevel()==0){
                    QErr("cfg command is executing... Ignoring msg-%s", q_.makeMessageLog(m).c_str());
                    return false;
                }
                auto deviceId = m->getString("device_id");
                auto cmd = m->getString("command");
                //单机指令、
                // 1、对所有cpad的队列的相同操作指令计算幂等，只保留最后的指令；
                if(!q[level]->empty()) {
                    mergeMessageOperation(*q[level], deviceId, cmd);
                }
                // 2、清空所有对当前设备的app和场景操作，避免对cpad指令结果进行干扰；
                for(auto i=level+1; i<q_.lastLevel(); i++)
                    clearMessageOperationForDevice(*q[i], deviceId);
//lmm::改为清空manual和auto队列？
//                for(auto i=level+1; i<q_.lastLevel(); i++)
//                    clearLevelMessageOperation(*q[i]);

                return true;
            });

            q_.setLevelMessageChecker("app", [this](qlibc::QDataPListVector& q, unsigned char level, qlibc::QData* m)->bool {
                //管理指令不为空，各后续指令被拒绝，但cpad中有指令，app指令可被缓存
                if(!q[0]->empty() || q_.getActiveLevel()==0){
                    QErr("cfg command is executing... Ignoring msg-%s", q_.makeMessageLog(m).c_str());
                    return false;
                }
                auto deviceId = m->getString("device_id");
                //高优先级队列中已存在对某设备的操作， 不能插入
                if(deviceNotEmpty(*q[level-1], deviceId))
                    return false;
                //本级操作指令进行幂等
                auto cmd = m->getString("command");
                //单机指令、
                // 1、对所有app的队列的相同操作指令计算幂等，只保留最后的指令；
                if(!q[level]->empty()) {
                    mergeMessageOperation(*q[level], deviceId, cmd);
                }
                // 2、清空所有场景对该设备的场景操作，避免当前指令结果进行干扰；
                //清空manual和auto队列？？
                for(auto i=level+1; i<q_.lastLevel(); i++)
                    clearMessageOperationForDevice(*q[i], deviceId);
                return true;
            });

            q_.setLevelMessageChecker("manual", [this](qlibc::QDataPListVector& q, unsigned char level, qlibc::QData* m)->bool {
                //管理指令不为空，各后续指令被拒绝
                if(!q[0]->empty() || q_.getActiveLevel()==0){
                    QErr("cfg command is executing... Ignoring msg-%s", q_.makeMessageLog(m).c_str());
                    return false;
                }
                // 1、判断是否为新场景指令，是需要清空之前的场景指令；
                auto isFirst = m->getBool("first");
                if(isFirst) {
                    auto sid = m->getString("scene_id");
                    auto seid = m->getString("scene_ex_id");
                    clearOtherSceneMessage(*q[level], sid, seid);
                }
                auto deviceId = m->getString("device_id");
                //2、判断高优先级队列中已存在对某设备的操作， 不能插入
                for(auto lv=1;lv<level;lv++)
                    if(deviceNotEmpty(*q[lv], deviceId))
                        return false;
                // 3、本级指令幂等操作；
                auto cmd = m->getString("command");
                for(auto lv=level;lv<q_.lastLevel();lv++)
                    if(!q[lv]->empty()) {    //本级幂等
                        mergeMessageOperation(*q[lv], deviceId, cmd);
                    }
                //todo::清空auto中所有指令？
                return true;
            });

            q_.setLevelMessageChecker("auto", [this](qlibc::QDataPListVector& q, unsigned char level, qlibc::QData* m)->bool {
                //管理指令不为空，各后续指令被拒绝
                if(!q[0]->empty() || q_.getActiveLevel()==0) {
                    QInfo("cfg command is executing... Ignoring cmd-%s", q_.makeMessageLog(m).c_str());
                    return false;
                }

                //2、判断高优先级队列中已存在对某设备的操作， 不能插入
                auto deviceId = m->getString("device_id");
                for(auto lv=1;lv<level;lv++)
                    if(deviceNotEmpty(*q[lv], deviceId)){
                        QInfo("cmd for device is in queue [%d], Ignoring cmd-%s..", lv, q_.makeMessageLog(m).c_str());
                        return false;
                    }
                //3、本级指令幂等操作
                auto cmd = m->getString("command");
                mergeMessageOperation(*q[level], deviceId, cmd);
                return true;
            });

            q_.setLevelMessageChecker("pending", [this](qlibc::QDataPListVector& q, unsigned char level, qlibc::QData* m)->bool {
                QErr("check Pending Queued Messages = %s, Level = %d",m->toJSONString().c_str(),level);
                //管理指令不为空，各后续指令被拒绝
                if( !q[0]->empty() || q_.getActiveLevel()==0 )  {
                    QErr("q_.getActiveLevel() = %d",q_.getActiveLevel());
                    QInfo("cfg command is executing... Ignoring cmd-%s", q_.makeMessageLog(m).c_str());
                    return false;
                }

                //2、判断高优先级队列中已存在对某设备的操作， 不能插入
                auto deviceId = m->getString("device_id");
                QErr("deviceId = %s",deviceId.c_str());
                for(auto lv=1;lv<level;lv++)
                    if(deviceNotEmpty(*q[lv], deviceId)){
                        QInfo("cmd for device is in queue [%d], Ignoring cmd-%s..", lv, q_.makeMessageLog(m).c_str());
                        return false;
                    }
                //3、本级指令幂等操作
                auto cmd = m->getString("command");
                QErr("command = %s",cmd.c_str());
                mergeMessageOperation(*q[level], deviceId, cmd);
                return true;
            });

            opr_.threadPool().enqueue([this]() {
                handleMessageFromQuque();
            });
        }

        void stopMergedQueue() {
            quit_ = true;
            q_.clearMessageQueue();
            SINGLE_LOCK(vs_m_);
            vs_w_.wait_for(LOCKER_NAME(vs_m_), std::chrono::milliseconds(200), [this]() { return !active_; });
        }

        bool pushIncomeMessage(qlibc::QData &req, qlibc::QData *rsp) {
            req.setBool("~comfirmed", true);
            auto* mm = opr_.getBlankMessage();
            mm->setInitData(req);
            QInfo("pushIncomeMessage:%s", mm->toJSONString().c_str());
            q_.pushMessage(mm);
#if DEBUG
//            q_.debugShowAll("pushed");
#endif
            OkPRsp(rsp);
            return true;
        }
    };

    using QLeveledMergeQueueR = std::shared_ptr<QLeveledMergeQueue>;
}

#endif //IOT_EDGE_VDALNEW_QLEVELEDMERGEQUEUE_H
