//
// Created by Ht on 2020/7/27.
//

#ifndef LIBDEVCPP_QHTTPCLIENT_H
#define LIBDEVCPP_QHTTPCLIENT_H

#include <qlibc/qlibc.h>
#include <string>
#include <mgbus/ChannelOperator.h>
#include <mgbus/MgDataChannel.h>
#include <qlibc/QObjectEventHolder.h>

#include "QHttpCommon.h"

namespace jedge {
    //

    static const char* sval_default_http_host = "@df";

    using HttpASyncCallback = JE_API std::function<bool(const std::shared_ptr<httplib::Client>& fromClient, const string &uri, qlibc::QData &message)>;
    using QHttpClientR = std::shared_ptr<httplib::Client>;

    using HttpRawEncryptor = std::function<bool(std::string&, std::string&)>;
    using HttpRawDecryptor = std::function<bool(std::string&, std::string&)>;

    class JE_API QHttpUtil {
    public:
        static void httpResultToResponse(httplib::Response &http_res, qlibc::QData &response, HttpRawDecryptor* decryptor = nullptr);
    };

    class JE_API QHttpClient :
            public MgDataChannel,
            public qlibc::QObjectEventHolder {

    protected:
        qlibc::QSharedObjectHolder<httplib::Client> http_clients_;
        std::unordered_map<std::string , std::string>  prefixs_;

        HttpRawEncryptor*   encryptor_ = nullptr;
        HttpRawDecryptor*   decryptor_ = nullptr;

        unsigned long time_out_secs_;           //客户端自动清理时间，默认客户端不清理,0表示永久不清理
        qlibc::QRTimer  timer_check_timeout_ = nullptr;
        bool quit_ = false;

        HttpASyncCallback *handler_ = nullptr;
        std::recursive_mutex  http_client_mutex_;

    public:
        explicit QHttpClient(ChannelOperator &opr, unsigned long auto_clear = 10 * 60 * 1000);  // ten minutes

        virtual ~QHttpClient();

        QHttpClientR addHost(const std::string& name, const std::string& host, int port = 80);

        //未找到时，直接使用默认Client
        QHttpClientR findHttpClient(const std::string& name);

        void setASyncHttpResponseCallback(const HttpASyncCallback& callback);

        void removeASyncHttpResponseCallback();

        bool postHttpRaw(const std::string& httpUri, const std::string& payload, const char* httpContent);

        QHttpClientR prepareHost(const std::string& name, const std::string& host, int port);

        void    setClientBase(const std::string& hostKey, const std::string& prefix);

        bool checkAddUriPrefix(std::string& uri, const std::string& hostKey);

        bool     postSyncHttpRequestByHost(const std::string& name, qlibc::QData& request, qlibc::QData& response);

        bool     postASyncHttpRequestByHost(const std::string& name, qlibc::QData& reqeust);

        bool     postSyncHttpRequestByUri(const std::string& uri, qlibc::QData& request, qlibc::QData& response);

        bool     postASyncHttpRequestByUri(const std::string& uri, qlibc::QData& reqeust);

    protected:
        bool doPostHttpRequest(const std::shared_ptr<httplib::Client>& client, const std::string& uri, const qlibc::QData &request, qlibc::QData &response) const;

        bool doPostAsyncHttpRequest(const std::shared_ptr<httplib::Client>&client, qlibc::QData &request) const;

        virtual void handleASyncHttpResponse(const std::shared_ptr<httplib::Client>& fromClient, const string &uri, qlibc::QData &message) const;

    public:

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

        bool postHttpRequest(const std::string &uri, const qlibc::QData &data, qlibc::QData& resp) const;

        void clearHttpClients();

        void setEncryptor(const HttpRawEncryptor& encryptor) {
            delete encryptor_;
            encryptor_ = new HttpRawEncryptor(encryptor);
        }

        void setDecryptor(const HttpRawDecryptor& encryptor) {
            delete decryptor_;
            decryptor_ = new HttpRawDecryptor(encryptor);
        }

        void checkParamEncryption(qlibc::QData &msg) {
            if(encryptor_== nullptr || !msg.containsKey("$param")) return;
            auto orgStr = StringUtils::valueToJsonString(msg.asValue().operator[]("$param"));
            std::string encryptedStr;
            if((*encryptor_)(orgStr, encryptedStr))
                msg.putString("$param", encryptedStr);
        }

    protected:
        void checkClientTimeout();
    };

}


#endif //LIBDEVCPP_QHTTPCLIENT_H
