#include "connection.hpp"
#include <utility>
#include <boost/log/trivial.hpp>
#include <boost/beast.hpp>
#include "server.hpp"


namespace http::server {
    const char* SERVER_HEADER_VALUE = "TerminalWebServer v0.1";

    Connection::Connection(boost::asio::ip::tcp::socket socket, ConnectionManager &manager, request_handler handler)
        : socket_(std::move(socket)), connection_manager_(manager), request_handler_(std::move(handler)), request_(), reply_() {
    }

    void Connection::start() {
        doRead();
    }

    void Connection::stop() {
        socket_.close();
    }

    Connection::~Connection() = default;

    void Connection::doRead() {
        request_parser_.reset();
        request_.headers.clear();
        request_.method.clear();
        request_.queryUri.clear();
        if (request_.url != nullptr) {
            request_.url.reset(nullptr);
        }

        auto self(shared_from_this());
        socket_.async_read_some(boost::asio::buffer(buffer_), [this, self](boost::system::error_code ec, std::size_t bytes_transferred) {
            if (!ec) {
                RequestParser::result_type result;
                std::tie(result, std::ignore) = request_parser_.parse(
                    request_, buffer_.data(), buffer_.data() + bytes_transferred);

                if (result == RequestParser::good) {
                    request_handler_(request_, reply_);
                    doWrite();
                } else if (result == RequestParser::bad) {
                    stockReply(bad_request, reply_);
                    doWrite();
                } else {
                    doRead();
                }
            } else {
                connection_manager_.stop(shared_from_this());
            }
        });
    }

    void Connection::doWrite() {
        reply_.headers.push_back({.name = "Server", .value = SERVER_HEADER_VALUE});
        if (!reply_.content.empty()) {
            reply_.headers.push_back({.name = "Content-Length", .value = std::to_string(reply_.content.size())});
        }
        if (request_.http_version_major == 1) {
            reply_.headers.push_back({.name = "Connection", .value = "keep-alive"});
        }

        BOOST_LOG_TRIVIAL(info) << "HTTP query " << reply_.status << " " << request_.method << " " << request_.queryUri;

        auto self(shared_from_this());
        async_write(socket_, reply_.to_buffers(), [this, self](boost::system::error_code ec, std::size_t) {
            if (!ec) {
                // keep alive Connection
                doRead();
            } else {
                connection_manager_.stop(shared_from_this());
            }
        });
    }

    SslConnection::SslConnection(boost::asio::ip::tcp::socket socket, ConnectionManager &manager, request_handler handler, const std::shared_ptr<boost::asio::ssl::context>& ctx):
        stream_(std::move(socket), *ctx), connection_manager_(manager), request_handler_(std::move(handler)), request_(), reply_() {
    }

    void SslConnection::start() {
        get_lowest_layer(stream_).expires_after(std::chrono::seconds(30));

        // Perform the SSL handshake
        stream_.async_handshake(boost::asio::ssl::stream_base::server, boost::beast::bind_front_handler([this](auto ec) {
            boost::ignore_unused(ec);
            doRead();
        }));
    }

    void SslConnection::stop() {
    }

    SslConnection::~SslConnection() = default;

    void SslConnection::doRead() {
        get_lowest_layer(stream_).expires_after(std::chrono::seconds(30));

        request_parser_.reset();
        request_.headers.clear();
        request_.method.clear();
        request_.queryUri.clear();

        auto self(shared_from_this());
        stream_.async_read_some(boost::asio::buffer(buffer_), [this, self](boost::system::error_code ec, std::size_t bytes_transferred) {
            if (!ec) {
                RequestParser::result_type result;
                std::tie(result, std::ignore) = request_parser_.parse(
                    request_, buffer_.data(), buffer_.data() + bytes_transferred);

                if (result == RequestParser::good) {
                    request_handler_(request_, reply_);
                    doWrite();
                } else if (result == RequestParser::bad) {
                    stockReply(bad_request, reply_);
                    doWrite();
                } else {
                    doRead();
                }
            } else {
                connection_manager_.stop(shared_from_this());
            }
        });
    }

    void SslConnection::doWrite() {
        reply_.headers.push_back({.name = "Server", .value = SERVER_HEADER_VALUE});
        if (!reply_.content.empty()) {
            reply_.headers.push_back({.name = "Content-Length", .value = std::to_string(reply_.content.size())});
        }
        if (request_.http_version_major == 1) {
            reply_.headers.push_back({.name = "Connection", .value = "keep-alive"});
        }

        BOOST_LOG_TRIVIAL(info) << "HTTPS query " << reply_.status << " " << request_.method << " " << request_.queryUri;

        auto self(shared_from_this());
        async_write(stream_, reply_.to_buffers(), [this, self](boost::system::error_code ec, std::size_t) {
            if (!ec) {
                // keep alive Connection
                doRead();
            } else {
                connection_manager_.stop(shared_from_this());
            }
        });
    }

} // namespace http::Server
