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

#ifndef INC_1_LIBDEVCPP_QGAHONGMEIPLUGINENTRY_H
#define INC_1_LIBDEVCPP_QGAHONGMEIPLUGINENTRY_H

#include "../../src/plugin/QGaCypherPluginEntry.h"
#include "../../src/QGADataPool.h"
#include <mgbus/SocketClient.h>
#include <set>
#include "messageHandle.h"
#include "hmcrypt/lancommn.h"
#include "messageHandle.h"
#include "udgao/QGaUdGaoImpl.h"
#include "udgao/QGaoUdRespGaoImpl.h"

namespace jedge {

    class JE_API  QGaHongmeiPluginEntry :
            public QGaCypherPluginEntry {
    protected:
        uint8_t cypherCode[50];
        std::set<std::string> mGreped;
        QRTimer updateKeyTimer_;

    public:
        explicit QGaHongmeiPluginEntry(std::string name, std::string type = "cypher", int ver = 0) :
            jedge::QGaCypherPluginEntry(std::move(name), std::move(type), ver) {
        }

        ~QGaHongmeiPluginEntry() override { }

        GaoHandler(response,pool,r,p) {
            //用户自定义子协议的调用响应
            return data_pool_->handleUserHandler(ud_gao_area_mgbus_response_, "command", r, p);
        }

        GaoHandler(addBegin,pool,r,p) {
            auto ok = false;
            qlibc::QDataList deviceBeginArray;
            r->getDataList("begin_list", deviceBeginArray);
            size_t deviceBeginArraySize = deviceBeginArray.getItemCount();
            for(auto i = 0; i < deviceBeginArraySize; i++) {
                qlibc::QData ithData;
                deviceBeginArray.getItem(i, ithData);
                ithData.setString("command", "addBeginIt");
                ok = data_pool_->handleUserHandler(ud_gao_area_mgbus_response_, "command",&ithData, nullptr) || ok;
            }
            OkPRspMsg(p, "ok");
            return ok;
        }

        GaoHandler(addResult,pool,r,p) {
            auto ok = false;
            qlibc::QDataList deviceSuccessArray;
            r->getDataList("success_list", deviceSuccessArray);
            size_t deviceSuccessArraySize = deviceSuccessArray.getItemCount();
            for(size_t i = 0; i < deviceSuccessArraySize; i++) {
                qlibc::QData ithData;
                deviceSuccessArray.getItem(i, ithData);
                ithData.setString("command", "addResultSuccessIt");
                /**
                 * 每个设备添加成功后,需要处理三个动作:
                 * (1)注册设备;           (http实现)
                 * (2)上报添加成功;       (mqtt实现)
                 * (3)更新设备状态。       (mqtt实现)
                 */
                ok = data_pool_->handleUserHandler(ud_gao_area_mgbus_response_, "command", &ithData, p) || ok;
            }
            qlibc::QDataList deviceFailArray;
            r->getDataList("failed_list", deviceFailArray);
            size_t deviceFailArraySize = deviceFailArray.getItemCount();
            for(size_t i = 0; i < deviceFailArraySize; i++) {
                qlibc::QData ithData;
                deviceFailArray.getItem(i, ithData);
                ithData.setString("command", "addResultFailIt");
                ok = data_pool_->handleUserHandler(ud_gao_area_mgbus_response_, "command", &ithData, p) || ok;
            }
            //重新cache设备
            data_pool_->runDirectGao("https.request.getBtDevsBySnUrl", r, p);
            OkPRspMsg(p, "ok");
            return ok;
        }

        auto deleteResult(QGAChannelHolder& pool, qlibc::QData *req, qlibc::QData *resp) ->bool {
            auto ok = false;
            qlibc::QDataList deleteList;
            req->getDataList("delete_list", deleteList);
            size_t deleteListCount = deleteList.getItemCount();
            for(size_t i = 0; i < deleteListCount; i++) {
                qlibc::QData ithData;
                deleteList.getItem(i, ithData);
                ithData.setString("command", "deleteResultIt");
                ok = data_pool_->handleUserHandler(ud_gao_area_mgbus_response_, "command", &ithData, resp) || ok;
            }
            //删除设备后,需要场景同步一次, 调用获取所有的在线设备信息
            data_pool_->runDirectGao("base.request.syncScene", req, resp);
            OkPRspMsg(resp, "ok");
            return true;
        }

        GaoHandler(addDevice,pool,r,p) {
            OkPRspMsg(p, "ok");
            return true;
        }

        void buildSiteData(QData &data) override {
            data_pool_->setupMgbusUserHandler(data, ud_gao_area_mgbus_response_);
            bindDeliverGaoPluginHandler(response);
            bindUserDefPluginHandler(ud_gao_area_mgbus_response_command_, addBegin);
            bindUserDefPluginHandler(ud_gao_area_mgbus_response_command_, addResult);
            bindDeliverGaoPluginHandler(deleteResult);

            bindDeliverGaoPluginHandler(addDevice);

            bindDeliverGaoPluginHandlerFunc(debugMode);
            bindDeliverGaoPluginHandlerFunc(encryptMode);
            bindDeliverGaoPluginHandlerFunc(publishResponse2app);
            bindDeliverGaoPluginHandlerFunc(setServerOption);
            bindDeliverGaoPluginHandlerFunc(getServerOption);
            bindDeliverGaoPluginHandlerFunc(isBelong2Family);
            bindDeliverGaoPluginHandlerFunc(getPanelClientParameter);
            bindDeliverGaoPluginHandlerFunc(getSnDeviceList);
        }

        void setDataPool(QGADataPool &pool) override {
            QGaCypherPluginEntry::setDataPool(pool);
            //读取dp的配置
            auto grepCmdSrc = pool.getString("commandSource");
            if(!grepCmdSrc.empty()) {
                QStringList srcs;
                StringUtils::SplitString(grepCmdSrc, ",", srcs);
                for(const auto& k :srcs) {
                    mGreped.emplace(k);
                }
            }
        }

        QGaCypherPluginEntry *duplication(const string &name) override {
            auto* dup = new QGaHongmeiPluginEntry(name, typeKey_, ver_);
            dup->sn_ = sn_;
            return dup;
        }

        void regenerateMqttCypherKey() {
            std::string uploadBuffer; char* tmpCode = nullptr;
            messagehandle::gen_mqtt_comm_key (data_pool_->getDeviceSerialNumber(), uploadBuffer, &tmpCode);
            //上传key
            QInfo("Uploading -->: %s", uploadBuffer.c_str());
            if(!data_pool_->httpSyncPostRaw(R"(http://superapp.mymlsoft.com/saserver/safe/saveOrUpdate)", uploadBuffer, R"(text/plain)")) {
                QErr("Fail to update mqtt cypher key!");
            }
            memset(cypherCode, 0, 50);
            hex_to_bytes(reinterpret_cast<uint8_t *>(tmpCode), cypherCode);
        }

        //mqtt上线后的行为
        void resyncMqttStatus() override {
            regenerateMqttCypherKey();

            //通知app立即更新key
            while(!data_pool_->mqttPublish("d/" + sn_ + "/m", "00")) {
                QWarn("Fail to notify app to update key.");
                SystemUtil::tryWait(3000);
            }


            const QRTimerEngine &te = data_pool_->getOperator().timerEngineRef();
            te->stopTimer(updateKeyTimer_);
            //更新与App通信的Key。
            updateKeyTimer_ =  te->repeatRun([this]lamdaTimerTask(t,p){
                regenerateMqttCypherKey();
                QLog("======>update the key successfuly<======\n");
                return true;
            }, 1000 * 60 * 60* 24, 1000*60*60*12);    //12小时后、每24小时更新一次密钥
        }

        bool decodeMqttMessage(const string &topic, void *payload, int payloadLen, char *buffer, int *len) override {
            string payloadMessage(reinterpret_cast<char*>(payload), 0, payloadLen);
            QLog("==>in dataHooker: %s\n", payloadMessage.c_str());
            if(topic == "d/" + sn_ + "/i") {   //只有sn被设置才能连接到服务器，才会有消息进入到这里
                string out;
                messagehandle::decrypt_mqttReceiveCommand(cypherCode, topic, reinterpret_cast<byte*>(payload), payloadLen,out);
                memset(buffer, 0, out.size() + 1);
                memcpy(buffer, out.data(), out.size());
                *len = (int)out.size();
                return true;
            }
            return false;
        }

        bool encryptMqttMessage(const std::string& payload, std::string& encryptOut) override {
            return messagehandle::encrypt_uploadMessage(cypherCode, payload, encryptOut);
        }

        void prepareMqttParams(QData &config, string &username, string &password, string &clientId) override {
            auto sn = data_pool_->getDeviceSerialNumber();
            username = sn;
            password = config.getString("password");
            clientId =  "d:" + sn;
        }

        void makeMqttHeartBeat() override {
            //发起mqtt心跳
            if(data_pool_!= nullptr) {
                data_pool_->mqttPublish("d/" + sn_ + "/m", "00");
            }
        }

        bool handleMqttMessage(const string &topic, QData &msg) override {
            //过滤command_source
            auto cmdSrc = msg.getString("commandSource");
            if(!cmdSrc.empty() && !mGreped.empty() && mGreped.find(cmdSrc)==mGreped.end()) {
                return true;
            }
            return QGaCypherPluginEntry::handleMqttMessage(topic, msg);
        }
    };
}


#endif //INC_1_LIBDEVCPP_QGAHONGMEIPLUGINENTRY_H
