//
// Created by Ht on 2021/9/26.
//

#ifndef INC_0_LIBDEVCPP_CPAD_MGSERVERMANAGERSERVICE_H
#define INC_0_LIBDEVCPP_CPAD_MGSERVERMANAGERSERVICE_H

#include <src/service/MgbusServerService.h>
#include <mgbus/QMgbusModule.h>
#include <mgbus/MgTriggerManager.h>
#include <iam/credential.h>


namespace jedge {

    //目前暂时只支持android和cygwin
#if defined(ANDROID) || defined(CYGWIN)
    #define CHISID_ENABLED 0
#if CHISID_ENABLED
    static const char* sval_iam_cache_fn_ = "iam_cache.json";
#endif
#endif

    /**
     *  客户端通道
     */
    class JE_API MgbusChannel : public QMgbusModule {
    protected:
        jedge::MgBusServer& server_;
        std::string remote_mgnode_name_;
        bool        cloud_mode_:1;
#ifdef CHISID_ENABLED
        std::string iamToken;
        long        iamAccTime;
#endif
    public:
        explicit MgbusChannel(jedge::MgBusServer& server,
                              const qlibc::QData &initConfig, const std::string &name, SocketMode mgMode = typeUDP,
                              const httplib::RThreadPool& pool = nullptr, const qlibc::QRTimerEngine& engine = nullptr,
                              const qlibc::QRTriggerEngine &tr_engine = nullptr) :
                QMgbusModule(initConfig, name, mgMode, pool, engine, tr_engine),
                server_(server),
                remote_mgnode_name_(),
                cloud_mode_(false) {}

    protected:
        bool onClientAuth(const string &sockKey, const string &cmd, qlibc::QData &msg, SocketMode mode) override;

    public:;

        ~MgbusChannel() override {}

    protected:
        void afterMgbusConnected() override;

    public:
        void doServerShutdown() override;

        bool doServerStart(const qlibc::QData &data) override;

        bool start(const qlibc::QData &config) override;

        //服务器在获取接入指令时，根据name的格式，判断接入节点的类型。
        //通常为普通类型
        const string &name() override;

        inline const std::string &getRemoteNodeName() {
            return remote_mgnode_name_;
        }

        inline void setRemoteNodeName(const std::string &channelkey) {
            remote_mgnode_name_ = channelkey;
        }

        bool isLocalMessage(std::string &modName) override;

        void shutdown() override;

        bool checkMessageLegal(qlibc::QData &message) override;

        void postLoginMessageEx(const qlibc::QData *aux, bool heartBeat = false) override;

        void changeModuleName(const std::string& newname) override;

        void setCloudMode();

        inline bool isCloudMode() {
            return cloud_mode_;
        }

    protected:
        void prepareService(const qlibc::QData &config) override;

        void onClientOffline(ChannelOperator &opr, const std::string &host, int port, SocketMode mode) override;

        bool startMgbusChannel(SocketMode mode);

        void onMgbusClientLoginOk(const qlibc::QData &msg) override;

    };

    /**
     *  mgbus 节点
     */
    class MgServerManagerService :
            public MgbusServerService {
    protected:
        QCMDnsClient&      cmdns_client_;

        qlibc::QSharedObjectHolder<MgNode>           mgbus_nodes_;           //作为服务端,记录节点域,用于通过云端向指定的域推送指令
        qlibc::QSharedObjectHolder<MgNode>           mgbus_nodes_snd_;       //作为服务端,记录sn,用于通过云端向指定的sn发送指令
        qlibc::QSharedObjectHolder<MgbusChannel>     mgbus_channels_;        //作为客户端
        qlibc::QSharedObjectHolder<MgbusChannel>     mgbus_channels_named_;  //作为客户端，用名字索引记录节点域
        qlibc::QSharedObjectHolder<int>              handling_queue_;        //作为客户端

        std::string       mgbus_watch_key_;                             //watch的key
        std::string       host_name_;                                   //主机名
        std::string       host_sn_;                                     //主机串号，cpad上线时将被修改
        std::string       host_nickName_;                               //主机设备命名
        std::string       host_Ip_;                                     //主机IP地址
        std::string       host_MAC_;                                    //主机MAC

        std::string       local_host_;                                  //本机地址、防止自己接入自己
        std::string       scan_base_;                                   //扫描地址范围
        bool              local_scan_ = false;                          //是否进行本地扫描
        int               scan_started_             = 0;                //开始扫描
        qlibc::QRTimer    mgs_node_sync_timer_;                         //各节点之间、定时同步上线节点信息
        qlibc::QRTimer    mgs_watch_shutdown_delayer_;                  //各节点之间、定时同步上线节点信息
        qlibc::QSharedObjectHolder<MgbusChannel>*    mgbus_channel_pool_ = nullptr;
//#ifdef DEBUG
        std::mutex        pr_m_;
//#endif

#ifdef CHISID_ENABLED
        std::string chisPath;
        // std::string iamCacheFileFn;
        IAM::IAMContextR aimCtx;
        qlibc::QData verifiedMacs;
#endif

    public:

#ifdef CHISID_ENABLED
    bool checkChisIdVerified(const std::string& iamToken, std::string& targetMac) {
        if(verifiedMacs.containsKey(iamToken)){
            targetMac = verifiedMacs.asValue().operator[]("mac").asString();
            return true;
        }
        return false;
    }
#endif

        explicit MgServerManagerService(MgBusServer &opr, const std::string &name, const qlibc::QData& config);

        ~MgServerManagerService() override {
            clearChannelsInPool();
        };

        void releaseNodes() override;

        void markLocalAddress(const std::string& local, int port);

        bool moduleUpdated(qlibc::QData &msg, qlibc::QData *rsp);

        bool updateNodeConnected(qlibc::QData &msg, qlibc::QData *rsp);

        bool qryMod(qlibc::QData &msg, qlibc::QData *rsp);

        bool qryConn(qlibc::QData &msg, qlibc::QData *rsp);

        bool find(qlibc::QData &msg, qlibc::QData *rsp);

        bool scan(qlibc::QData &msg, qlibc::QData *rsp);

        //将channel形成一个channel池。避免频繁关闭channel。
        qlibc::QSharedObjectHolder<MgbusChannel> & getChannelPool();

        void clearChannelsInPool();

        inline void iterateNode(const qlibc::QSharedObjectHolder<MgNode>::SharedObjAccessor & accessor) {
            mgbus_nodes_.invokeOnAllObject(accessor, true);
        }

        inline std::shared_ptr<MgNode> findNode(const std::string &nodeKey) override {
            return mgbus_nodes_.findObject(nodeKey);
        }

        inline std::shared_ptr<MgbusChannel> findChannel(const std::string &nodeKey) {
            return mgbus_channels_.findObject(nodeKey);
        }

        inline std::shared_ptr<MgbusChannel> findChannelByName(const std::string &nodeKey) {
            return mgbus_channels_named_.findObject(nodeKey);
        }

        void onServiceStart(const qlibc::QData &config) override;

        void onServiceStop() override;

        void removeNode(const std::string &nodeKey) override;

        void addNode(const std::string &nodeKey, MgNodeRef& node) override;

        bool deliverLocalMultiMgbusMessage(const std::string &dest, qlibc::QData &message);

        bool deliverRemoteMultiMgbusMessage(qlibc::QData &msg);

        bool existsNode(const std::string &nodeKey) override;

        void removeChannel(const std::string &channelKey, const std::string &lk = "");

        inline const std::string& getMgbusSerialNumber() {
            return host_sn_;        //sn
        }

        bool reIndexNode(const string &nodeKey, const string &newKey) override;

        inline const std::string& getMgbusHostNickName() {
            return host_nickName_;  //**面板 etc.
        }

        inline const std::string& getHostMac() {
            return host_MAC_;  //**MAC 地址 etc.
        }

        inline const std::string& getHostIp() {
            return host_Ip_;  //**面板 IP etc.
        }

        inline const std::string& getMgHostName() {
            return host_name_;      //sn.local
        }

        inline const std::string& getMgsServerAddr() {
            return local_host_;
        }

        inline void checkLocalHostIp(const std::string& sockKey) {
            if(!local_host_.empty()) return;
            QStringList sl;
            NetUtils::getHostIpList(sl);
            for(const auto& ip : sl) {
                auto sockPre = StringUtils::getSubStrBefore(sockKey,".",2);
                auto hostPre = StringUtils::getSubStrBefore(ip,".",2);
                if(sockPre==hostPre && ip!="127.0.0.1") {
                    local_host_ = ip;
                    if(host_Ip_.empty())
                        host_Ip_ = ip;
                    return;
                }
            }
        }

        void trigExistsNodes(jedge::MgEvent& event, jedge::MgTriggerWatcher &watcher);

        void trigExistsModules(jedge::MgEvent& event, jedge::MgTriggerWatcher &watcher);

        void listMgbusChannels(QStringList &nodeList);

        void listNodes(QStringList &nodeList);

        const std::string &getHostSerialNumber();

        MgNodeRef tryLoginMgbusNode(QJsonSocketServer &server, SocketMode mode, const std::string &sockKey,
                                  const std::string &nameKey, qlibc::QData &msg) override;

        void preparePattern() override;

        void getLoggedMgbusNodeSnList(QStringList &snList);

        void getServerNodeKeyList(QStringList &snList);

        //查询config配置信息
        bool query(qlibc::QData &msg, qlibc::QData *rsp);

        void postModuleConnected(const std::shared_ptr<MgbusChannel>& channel);

        void postMgbusNodeConnected(MgbusChannel* channel);

        void listConnectedHosts();

    protected:
        void appendMgbusHostInfo(qlibc::QData &message, bool forClound = false);

        void addRemoteModule(MgNode &node, const std::string &module, bool uptype);

        void queryHostDeviceSN();

        void updateMgbusConnectionName();

        void repeatMgbusNodeStatus();

        bool checkMgbusHostConnected(const char *host, int port, bool forceRefresh = false);

        void rematchNamedChannels(const std::shared_ptr<MgbusChannel>& channel);

        void handleMgbusServer(const CMdnsServiceQueried services[], int svc_cnt, const char* ip, int port);

        void connectMgbusHost(const std::string &host, int port, bool postInitMod=true);

        void loginToClound();

        void tryFindOtherMgbusNodes();

        void clearUnconnectedHandlingQueue();

        bool checkServiceSocketAvailable(const string &host, int port);

        bool checkLocalHost(const char *host, int port);

        void onHostInfoUpdated();
    };
}

#endif //INC_0_LIBDEVCPP_CPAD_MGSERVERMANAGERSERVICE_H
