#include "ServerConnection.hpp"

#include <spdlog/spdlog.h>

namespace tw::net {

ServerConnection::ServerConnection(Address address) :
    m_address(address),
    m_status(ConnectionStatus::Idle),
    m_started_at(Clock::now()) {
}

tl::expected<void, msg::MessageError> ServerConnection::start() {
    // Create the endpoint
    auto endpoint_r = msg::MessageEndpoint::create();
    if(!endpoint_r) {
        m_status = ConnectionStatus::Failed;
        m_error = endpoint_r.error().message();
        return tl::make_unexpected(endpoint_r.error());
    }

    m_endpoint = std::move(endpoint_r.value());

    // Connect to the server
    auto server_r = m_endpoint->connect(m_address.ip_string(), m_address.port());
    if(!server_r) {
        m_status = ConnectionStatus::Failed;
        m_error = server_r.error().message();
        return tl::make_unexpected(server_r.error());
    }

    m_server = server_r.value();
    m_status = ConnectionStatus::Connecting;
    m_started_at = Clock::now();
    spdlog::info("Attempting to connect to server at {}", m_address.to_string());

    return {};
}

void ServerConnection::update() {
    if(!m_endpoint) {
        return;
    }

    m_endpoint->update();

    // Check if the connection has become established
    if(m_status == ConnectionStatus::Connecting && m_server) {
        if(m_server->is_established()) {
            m_status = ConnectionStatus::Connected;
            spdlog::info("Connected to server at {}", m_address.to_string());
        } else {
            // Check for timeout
            auto elapsed = Clock::now() - m_started_at;
            if(elapsed >= CONNECT_TIMEOUT) {
                m_status = ConnectionStatus::Failed;
                m_error = "No response from " + m_address.to_string();
                spdlog::error("Connection timeout to {}", m_address.to_string());
            }
        }
    }
}

ConnectionStatus ServerConnection::status() const {
    return m_status;
}

const std::string& ServerConnection::error() const {
    return m_error;
}

const Address& ServerConnection::address() const {
    return m_address;
}

msg::MessageEndpoint* ServerConnection::endpoint() const {
    return m_endpoint.get();
}

msg::MessageConnection* ServerConnection::server() const {
    return m_server;
}

}
