#ifndef DEVICE_CTRL_MQTT_CLI_H
#define DEVICE_CTRL_MQTT_CLI_H
#include <memory>
#include <thread>
#include "mqtt_client_cpp.hpp"
#include "mqtt_msg.h"
#include "common/mitt/mitt.h"
#include "common/spdlog/logging.h"
#include "mqtt/mqtt_config.pb.h"
#include "app/bean.h"

namespace zsy
{
    class MQTTCli : public Bean
    {
        inline static tbb::concurrent_unordered_map<std::string, std::shared_ptr<MQTTCli> > MQTT_CLIS;
        inline static std::shared_mutex MQTT_CLIS_MTX;
        using CLI = MQTT_NS::callable_overlay<MQTT_NS::sync_client<MQTT_NS::tcp_endpoint<boost::asio::basic_stream_socket<boost::asio::ip::tcp>, boost::asio::strand<boost::asio::io_context::basic_executor_type<std::allocator<void>, 0> > > > >;
        using CLI_PTR = std::shared_ptr<CLI>;
        using packet_id_t = CLI::packet_id_t;
        std::atomic_bool running;
        std::shared_ptr<boost::asio::io_context> ioc;

        CLI_PTR cli;
        MQTTConfig_Cli config;
        std::unique_ptr<std::jthread> eventLoop;
        using MQTTEventPool = oneapi::tbb::concurrent_unordered_map<std::string, std::shared_ptr<Mitt::MittEvent<const MQTTMsg &> > >;
        MQTTEventPool eventPool;
        using MqttCallback = std::function<void(const MQTTMsg &)>;

        void run();

        bool onConnAck(bool sp, const MQTT_NS::connect_return_code &connack_return_code);

        void onClose();

        void onError(const mqtt::error_code &ec);

        bool onMessage(const MQTT_NS::optional<packet_id_t> &packet_id, const MQTT_NS::publish_options &pubopts, const MQTT_NS::buffer &topic_, const MQTT_NS::buffer &payload_);

        void reconnect();

        void doConnect();

    public:
        void subscribe(std::string topic, const std::function<void(const MQTTMsg &)> &handler, uint8_t qos = 0);

        void publish(std::string topic, std::string payload, uint8_t qos = 0);

        std::string name() const noexcept override;

        std::string sn() const noexcept override;

        static void init();

        static void destroy(const std::string &sn = "");

        static std::shared_ptr<MQTTCli> get(const std::string &sn);

        static std::shared_ptr<MQTTCli> getRemoteCli();

        static std::shared_ptr<MQTTCli> getLocalCli();

        explicit MQTTCli(const MQTTConfig_Cli &config);

        ~MQTTCli() override;
    };
}

#endif //DEVICE_CTRL_MQTT_CLI_H