#include "mqtt_svr.h"
#include "common/logging.h"
#include "common/sys_util.h"

namespace zsy
{
    void MqttSvr::run()
    {
        svr->listen();
        LOGGER_INFO("MQTT 服务启动成功");
        ioc->run();
        LOGGER_INFO("MQTT 服务已停止");
    }

    void MqttSvr::close_proc(con_sp_t const &con)
    {
        try
        {
            {
                std::lock_guard<std::mutex> lock(connections_mutex);
                connections->erase(con);
            }
            std::lock_guard<std::mutex> lock(subs_mutex);
            auto &idx = subs->get<tag_con>();
            auto r = idx.equal_range(con);
            idx.erase(r.first, r.second);
        } catch (...)
        {
            LOGGER_ERROR("MQTT 服务关闭失败");
        }
    }

    MqttSvr::MqttSvr(uint16_t port)
        : connections(std::make_shared<std::set<con_sp_t> >()),
          subs(std::make_shared<mi_sub_con>())
    {
        LOGGER_INFO("MQTT 服务正在启动，端口：{}", port);

        ioc = std::make_shared<boost::asio::io_context>();

        svr = std::make_shared<MQTT_NS::server<> >(
            boost::asio::ip::tcp::endpoint(
                boost::asio::ip::tcp::v4(),
                boost::lexical_cast<std::uint16_t>(port)),
            *ioc);

        svr->set_error_handler([](MQTT_NS::error_code ec)
        {
            LOGGER_ERROR("MQTT 服务启动失败: {}", SysUtil::str_w_a(SysUtil::str_a_w( ec.message(),"GBK" )));
        });
        svr->set_accept_handler([this](con_sp_t spep)
        {
            auto &ep = *spep;
            std::weak_ptr<con_t> wp(spep);

            using packet_id_t = typename std::remove_reference_t<decltype(ep)>::packet_id_t;

            ep.start_session(std::move(spep));

            ep.set_close_handler([this, wp]
            {
                LOGGER_INFO("MQTT 客户端断开连接");
                auto sp = wp.lock();
                close_proc(sp);
            });
            ep.set_error_handler([this, wp](MQTT_NS::error_code ec)
            {
                LOGGER_ERROR("MQTT 客户端断开连接: {}", SysUtil::str_w_a(SysUtil::str_a_w( ec.message(),"GBK" )));
                auto sp = wp.lock();
                close_proc(sp);
            });

            ep.set_connect_handler([this, wp](MQTT_NS::buffer client_id, MQTT_NS::optional<MQTT_NS::buffer> username, MQTT_NS::optional<MQTT_NS::buffer> password, MQTT_NS::optional<MQTT_NS::will> will_, bool clean_session, std::uint16_t keep_alive)
            {
                using namespace MQTT_NS::literals;
                LOGGER_INFO("MQTT 客户端连接成功:{}、{}、{}", client_id.to_string(), username ? username.value().to_string() : "none", password ? password.value().to_string() : "none");
                auto sp = wp.lock();
                std::lock_guard<std::mutex> lock(connections_mutex);
                connections->insert(sp);
                sp->connack(false, MQTT_NS::connect_return_code::accepted);
                return true;
            });
            ep.set_disconnect_handler([this, wp]
            {
                LOGGER_INFO("MQTT 客户端断开连接");
                auto sp = wp.lock();
                close_proc(sp);
            });
            ep.set_pingreq_handler([wp]
            {
                auto sp = wp.lock();
                sp->pingresp();
                return true;
            });

            ep.set_puback_handler([](packet_id_t packet_id)
            {
                // LOGGER_INFO("MQTT 客户端发布消息确认:{}", packet_id);
                return true;
            });
            ep.set_pubrec_handler([](packet_id_t packet_id)
            {
                // LOGGER_INFO("MQTT 客户端发布消息确认:{}", packet_id);
                return true;
            });
            ep.set_pubrel_handler([](packet_id_t packet_id)
            {
                // LOGGER_INFO("MQTT 客户端发布消息确认:{}", packet_id);
                return true;
            });
            ep.set_pubcomp_handler([](packet_id_t packet_id)
            {
                // LOGGER_INFO("MQTT 客户端发布消息确认:{}", packet_id);
                return true;
            });
            ep.set_publish_handler([this](MQTT_NS::optional<packet_id_t> packet_id, MQTT_NS::publish_options pubopts, MQTT_NS::buffer topic_name, MQTT_NS::buffer contents)
            {
                // LOGGER_INFO("MQTT 客户端发布消息:{}、{}", topic_name, contents);
                std::lock_guard<std::mutex> lock(subs_mutex);
                auto const &idx = subs->get<tag_topic>();
                auto r = idx.equal_range(topic_name);
                for (; r.first != r.second; ++r.first)
                {
                    r.first->con->publish(
                        topic_name,
                        contents,
                        std::min(r.first->qos_value, pubopts.get_qos())
                    );
                }
                return true;
            });
            ep.set_subscribe_handler([this, wp](packet_id_t packet_id, std::vector<MQTT_NS::subscribe_entry> entries)
            {
                std::vector<MQTT_NS::suback_return_code> res;
                res.reserve(entries.size());
                auto sp = wp.lock();
                for (auto const &e: entries)
                {
                    res.emplace_back(MQTT_NS::qos_to_suback_return_code(e.subopts.get_qos()));
                    std::lock_guard<std::mutex> lock(subs_mutex);
                    subs->emplace(std::move(e.topic_filter), sp, e.subopts.get_qos());
                }
                sp->suback(packet_id, res);
                return true;
            });
            ep.set_unsubscribe_handler([this, wp](packet_id_t packet_id, std::vector<MQTT_NS::unsubscribe_entry> entries)
            {
                auto sp = wp.lock();
                for (auto const &e: entries)
                {
                    std::lock_guard<std::mutex> lock(subs_mutex);
                    auto it = subs->find(std::make_tuple(sp, e.topic_filter));
                    if (it != subs->end())
                    {
                        subs->erase(it);
                    }
                }
                sp->unsuback(packet_id);
                return true;
            });
        });

        eventLoop = std::make_unique<std::jthread>(&MqttSvr::run, this);
    }

    MqttSvr::~MqttSvr()
    {
        LOGGER_INFO("MQTT 服务正在关闭");
        svr->close();
        ioc->stop();
        LOGGER_INFO("MQTT 服务已关闭");
    }
} // zsy
