// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
// SPDX-License-Identifier: AGPL-3.0-or-later

/**
 * @file sockpool.h
 * @brief Dynamic socket recycling: an LRU connection-slot pool (DETWS_ENABLE_SOCKPOOL).
 *
 * A device serves a bounded number of concurrent connections. When the pool saturates, the right move is
 * not to drop the new connection but to *recycle* the least-recently-active slot (the one most likely to
 * be a dead / idle keep-alive) and hand it to the new peer, returning the evicted id so the transport can
 * close it cleanly. This is the transport-pool half left open by `services/netadapt`.
 *
 * This is that pure policy: a fixed table of connection slots (each an id + last-used tick), with acquire
 * (free slot, else LRU-recycle), touch (mark active), release, and find. The app owns the real sockets;
 * this owns *which* slot a connection lives in and which to reclaim under pressure. No heap, no stdlib,
 * host-testable.
 */

#ifndef DETERMINISTICESPASYNCWEBSERVER_SOCKPOOL_H
#define DETERMINISTICESPASYNCWEBSERVER_SOCKPOOL_H

#include "ServerConfig.h"
#include <stddef.h>
#include <stdint.h>

#if DETWS_ENABLE_SOCKPOOL

/** @brief One connection slot. */
struct SockSlot
{
    bool in_use;
    uint32_t id;        ///< application connection id (e.g. a socket fd / handle).
    uint32_t last_used; ///< tick of the last acquire/touch (for LRU).
};

/** @brief A fixed pool of connection slots (storage is caller-owned). */
struct SockPool
{
    SockSlot *slots;
    size_t n;
};

/** @brief Acquire outcome. */
enum
{
    SOCK_ACQ_FREE = 0,     ///< a free slot was used.
    SOCK_ACQ_RECYCLED = 1, ///< the pool was full; the LRU slot was recycled (see evicted_id).
    SOCK_ACQ_FAIL = 2      ///< the pool has zero slots / bad args.
};

/** @brief Initialize a pool over caller storage; all slots start free. */
void detws_sockpool_init(SockPool *p, SockSlot *slots, size_t n);

/**
 * @brief Acquire a slot for connection @p id at tick @p now.
 *
 * Uses a free slot if one exists, otherwise recycles the least-recently-used in-use slot. On a recycle,
 * @p evicted_id (may be null) receives the id that was kicked out so the caller can close that socket.
 * @param idx  (may be null) receives the chosen slot index.
 * @return SOCK_ACQ_FREE / SOCK_ACQ_RECYCLED / SOCK_ACQ_FAIL.
 */
int detws_sockpool_acquire(SockPool *p, uint32_t id, uint32_t now, size_t *idx, uint32_t *evicted_id);

/** @brief Mark slot @p idx active at tick @p now (refreshes its LRU position). */
void detws_sockpool_touch(SockPool *p, size_t idx, uint32_t now);

/** @brief Free slot @p idx. @return true if it was a valid, in-use slot. */
bool detws_sockpool_release(SockPool *p, size_t idx);

/** @brief Find the slot holding connection @p id. @p idx (may be null) gets the index. @return found. */
bool detws_sockpool_find(const SockPool *p, uint32_t id, size_t *idx);

/** @brief Count of in-use slots. */
size_t detws_sockpool_in_use(const SockPool *p);

#endif // DETWS_ENABLE_SOCKPOOL
#endif // DETERMINISTICESPASYNCWEBSERVER_SOCKPOOL_H
