#pragma once

#include <memory>
#include <string>
#include <chrono>

#include "Address.hpp"
#include "message_protocol/MessageEndpoint.hpp"
#include "message_protocol/MessageConnection.hpp"
#include "message_protocol/MessageError.hpp"

#include <tl/expected.hpp>

namespace tw::net {

/**
 * Status of a server connection attempt or established connection.
 */
enum class ConnectionStatus { Idle, Connecting, Connected, Failed };

/**
 * Encapsulates a connection to a game server.
 *
 * Owns the endpoint and connection, managing the state of the connection
 * attempt and providing non-blocking access to send/receive.
 */
class ServerConnection {
    using Clock = std::chrono::steady_clock;

    std::unique_ptr<msg::MessageEndpoint> m_endpoint;
    msg::MessageConnection*               m_server = nullptr;
    Address                               m_address;
    ConnectionStatus                      m_status = ConnectionStatus::Idle;
    std::string                           m_error;
    Clock::time_point                     m_started_at;

    static constexpr std::chrono::seconds CONNECT_TIMEOUT{5};

public:
    /**
     * Constructs a connection object for the given address, without starting I/O.
     */
    explicit ServerConnection(Address address);

    /**
     * Starts the connection process by creating an endpoint and connecting to
     * the server. Returns an error if the endpoint cannot be created.
     */
    tl::expected<void, msg::MessageError> start();

    /**
     * Updates the connection state: pumps the endpoint, and checks for timeout
     * or successful connection. Must be called regularly.
     */
    void update();

    ConnectionStatus        status() const;
    const std::string&      error() const;
    const Address&          address() const;
    msg::MessageEndpoint*   endpoint() const;
    msg::MessageConnection* server() const;
};

}
