#include "Listener.h"
#include "BoostLog.h"
#include "HttpSession.h"
#include "SharedState.h"
#include <boost/asio/strand.hpp>
#include <boost/beast/core/error.hpp>

Listener::Listener(boost::asio::io_context &ioContext, const boost::asio::ip::tcp::endpoint &endpoint,
                   const std::shared_ptr<SharedState> &state)
    : m_ioContext(ioContext), m_acceptor(ioContext), m_state(state) {
    boost::beast::error_code error;

    // Open the acceptor
    m_acceptor.open(endpoint.protocol(), error);
    if (error) {
        LOG(error) << error.message();
        return;
    }

    // Allow address reuse
    m_acceptor.set_option(boost::asio::socket_base::reuse_address(true), error);
    if (error) {
        LOG(error) << error.message();
        return;
    }

    // Bind to the server address
    m_acceptor.bind(endpoint, error);
    if (error) {
        LOG(error) << error.message();
        return;
    }

    // Start listening for connections
    m_acceptor.listen(boost::asio::socket_base::max_listen_connections, error);
    if (error) {
        LOG(error) << error.message();
        return;
    }
}

void Listener::startAccept() {
    auto client = std::make_shared<boost::asio::ip::tcp::socket>(boost::asio::make_strand(m_ioContext));
    m_acceptor.async_accept(*client, [self{shared_from_this()}, client](const boost::system::error_code &ec) {
        self->onAccept(ec, client);
    });
}

void Listener::onAccept(boost::beast::error_code errorCode, std::shared_ptr<boost::asio::ip::tcp::socket> socket) {
    if (errorCode) {
        if (errorCode == boost::asio::error::operation_aborted) return;
        LOG(error) << errorCode.message();
    } else { // Launch a new session for this connection
        if (!m_state.expired()) {
            auto state = m_state.lock();
            auto session = std::make_shared<HttpSession>(std::move(*socket), state);
            session->run();
        }
    }
    startAccept();
}
