#pragma once

#include <cstdint>
#include <optional>
#include <string>
#include <unordered_map>
#include <vector>

struct _ENetHost;
struct _ENetPeer;

// Lockstep networking session for COOPERATE (network) mode.
//
// Design goals:
// - Non-blocking polling (caller drives poll from the main loop)
// - Reliable, ordered delivery for inputs and control messages
// - Host provides seed + start tick (handshake)
// - Only inputs/state hashes are exchanged (no board sync)
class NetSession {
public:
    enum class Mode {
        None,
        Host,
        Client,
    };

    enum class ConnState {
        Disconnected,
        Connecting,
        Connected,
        Error,
    };

    struct Handshake {
        uint32_t rngSeed = 0;
        uint32_t startTick = 0;
        uint8_t startLevel = 0;
    };

    struct InputFrame {
        uint32_t tick = 0;
        uint8_t buttons = 0;
    };

    NetSession();
    ~NetSession();

    NetSession(const NetSession&) = delete;
    NetSession& operator=(const NetSession&) = delete;

    // If bindHost is empty or "0.0.0.0", binds to ENET_HOST_ANY.
    bool host(const std::string& bindHost, uint16_t port);
    bool join(const std::string& hostNameOrIp, uint16_t port);
    void shutdown();

    void poll(uint32_t timeoutMs = 0);

    Mode mode() const { return m_mode; }
    ConnState state() const { return m_state; }
    bool isConnected() const { return m_state == ConnState::Connected; }

    // Host-only: send handshake once the peer connects.
    bool sendHandshake(const Handshake& hs);

    // Client-only: becomes available once received from host.
    std::optional<Handshake> takeReceivedHandshake();

    // Input exchange --------------------------------------------------------
    // Send local input for a given simulation tick.
    bool sendLocalInput(uint32_t tick, uint8_t buttons);

    // Returns the last received remote input for a tick (if any).
    std::optional<uint8_t> getRemoteButtons(uint32_t tick) const;

    // Hash exchange (for desync detection) ---------------------------------
    bool sendStateHash(uint32_t tick, uint64_t hash);
    std::optional<uint64_t> takeRemoteHash(uint32_t tick);

    // Diagnostics
    std::string lastError() const { return m_lastError; }

private:
    enum class MsgType : uint8_t {
        Handshake = 1,
        Input = 2,
        Hash = 3,
    };

    bool ensureEnetInitialized();
    void setError(const std::string& msg);

    bool sendBytesReliable(const void* data, size_t size);
    void handlePacket(const uint8_t* data, size_t size);

    Mode m_mode = Mode::None;
    ConnState m_state = ConnState::Disconnected;

    _ENetHost* m_host = nullptr;
    _ENetPeer* m_peer = nullptr;

    std::string m_lastError;

    std::optional<Handshake> m_receivedHandshake;

    std::unordered_map<uint32_t, uint8_t> m_remoteInputs;
    std::unordered_map<uint32_t, uint64_t> m_remoteHashes;

    // Debug logging (rate-limited)
    uint32_t m_inputsSent = 0;
    uint32_t m_inputsReceived = 0;
    uint32_t m_hashesSent = 0;
    uint32_t m_hashesReceived = 0;
    uint32_t m_handshakesSent = 0;
    uint32_t m_handshakesReceived = 0;
    uint32_t m_lastRecvInputTick = 0xFFFFFFFFu;
    uint32_t m_lastRecvHashTick = 0xFFFFFFFFu;
    uint32_t m_lastStatsLogMs = 0;
};
